Go Premium for a chance to win a PS4. Enter to Win

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 370
  • Last Modified:

How do I detect a MessageBox dialog?

Hi,
I have two applications (src and target) that asynchronously communicate with each other via  a TextBox. Target application has many forms and src application (amongst other things) monitors which (Target) Form is open and whether a MessageBox is being displayed at the time.

I know how to determine active form but don't know how to determine if a MessageBox is being displayed at the time (keeping in mind that communication between the two applications is asynchronous and I don't want to resort to setting some global flag every time I show a MessageBox).

Thanks,
     Michael

P.S. I am using VB2008 and .Net 3.5
0
gem56
Asked:
gem56
  • 7
  • 5
1 Solution
 
abelCommented:
Is it OK for you to check whether a certain top level window is a messagebox? From your explanation, I understand so far that you already have the top level window, so I assume you have the handle of it. But you still need a trick. It goes as follows:

  1. Get the parent window handle
  2. Use GetwindowLong on the parent handle
  3. If the parent has the flag set for WS_DISABLED, the child window is a modal window.
This is explained in more detail with an example in this knowledge base article: http://support.microsoft.com/kb/77316
0
 
gem56Author Commented:
Not sure if I understand your comment so here's a bit more info wile I'm following up your suggestion.

Src app sends a message to target app (by writing to a TextBox in target app) and it's within the target app that I determine what is the active form, etc. and return that information back to src app. The bit missing is how to determine if the active window (that I've identified) has a MessageBox shown.

/Michael
0
 
abelCommented:
I thought it was the reverse, that the source window would determine prior to sending whether the target window was a dialog window or not.

If the target app is going to determine it by itself, you may be in trouble, normally the processing of input stops while the dialog is open. In which case the target app cannot determine whether a dialog is open.

If I had to choose, I would go for a simple OO way of solving this. For instance, I assume you are using ShowDialog to show a modal window. If that is the case, you can use this in your form to set the property for HasDialogOpen, and anybody calling ShowDialog, even your current code, will automatically set that property:

Public HasDialogOpen As Boolean
 
Public Shadows Function ShowDialog() As DialogResult
    ' may pass null, same as ShowDialog() without args '
    Return Me.ShowDialog(Nothing)
End Function
 
Public Shadows Function ShowDialog(ByVal owner As IWin32Window) As DialogResult
    Dim dlgResult As DialogResult
    HasDialogOpen = True
    dlgResult = MyBase.ShowDialog(owner)
    HasDialogOpen = False
    Return dlgResult
End Function

Open in new window

0
Concerto's Cloud Advisory Services

Want to avoid the missteps to gaining all the benefits of the cloud? Learn more about the different assessment options from our Cloud Advisory team.

 
gem56Author Commented:
"If the target app is going to determine it by itself, you may be in trouble, normally the processing of input stops while the dialog is open. In which case the target app cannot determine whether a dialog is open."
I'm aware that input processing stops while dialog is open however I seem to be able to handle incoming events even while the MessageBox is displayed. Maybe that's because I'm handling messages from the Src application via a 'TextBox.TextChanged' event (asynchronously). Basically I do something on the target app and get Src to verify the result/status via the TextBox interface.
The target is an MDI application with each window being an MDI child that I display using frmForm.Show so the ShowDialog is not troubling me but I can see how I can (perhaps) use the same principle to solve my MessageBox problem.
If I'm reading you right, should I be able to also override the MessageBox.Show function with a locally declared function that has (inbuilt) functionality to set 'HasDialogOpen' flag?
If that's correct, how would i go about shadowing a MessageBox.Show function, as I'm not so familiar with shadowing/overriding of functions?
 
0
 
abelCommented:
Ah, that's different. Yes, normally you should be able to override any function in a class that you inherit of. However, you do not inherit from MessageBox, but from Form. I assumed a modal dialog form, which is stupid, as it is just as possible to use a MessageBox.

Unfortunately Microsoft has decided to make the MessageBox sealed. That means that it will cause a whole lot more trouble to get this to work with MessageBox and actually: MS tries to prevent it (a sealed class cannot be inherited, and if you cannot inherit, you cannot shadow).

Now there are several ways out of this, you can of course still make some global method, but now that I know that you do not have any trouble with the asynchronous methods calling. You can use the GetWindowLong(Me.Hwnd) to check the settings of the current window, which basically brings us back to the first answer i gave.

Here's a try, not tested:

Const GWL_STYLE As Integer = (-16)
Const WS_DISABLED = &H8000000
 
<DllImport("user32.dll", SetLastError:=True)> _
Private Shared Function GetWindowLong( _
     ByVal hWnd As IntPtr, _
     ByVal nIndex As Integer) As Integer
End Function
 
Function HasDialogOpen() As Boolean
    Dim WinStyle As Integer = GetWindowLong(Me.Handle, GWL_STYLE)
    Return WinStyle And WS_DISABLED   ' bitwise compare '
End Function

Open in new window

0
 
gem56Author Commented:
Makes sense and I tried exactly as you suggested however it always returns False.
Actually I had to change teh declaration a bit as it gave me a error, as per below.

Maybe it's getting confused with the fact that MessageBox is involved and not other forms/dialogs.
The value that GetWindowLong returns is 1475280896 (0x57EF 0000)

I tried all sorts of combinations but no joy!

Any more suggestions?

Private Const GWL_STYLE As Integer = (-16)
Public Const WS_DISABLED As Integer = &H8000000
Public Declare Function GetWindowLong Lib "user32" Aias "GetWindowLongA" _
    (ByVal hWnd As IntPtr, ByVal nIndex As Integer) As Integer
 
 
Function HasDialogOpen(ByVal frm As System.Windows.Forms.Form) As Boolean
   Dim WinStyle As Integer = GetWindowLong(frm.Handle, GWL_STYLE)
   Return CBool(WinStyle And WS_DISABLED)   ' bitwise compare '
End Function

Open in new window

0
 
abelCommented:
Mmm, it is correct that it returns false, check the bit mapping:

01010111111011110000000000000000 (0x57EF0000)
00001000000000000000000000000000 (0x08000000)

this sounds that the window is not "disabled" when your messagebox pops up. Can you click that window and give it focus? When I try it myself I get:

00011110110011110000000000000000 (0x1ECF0000, disabled/msgbox)
00010110110011110000000000000000 (0x16CF0000, enabled/no msgbox)
00001000000000000000000000000000 (0x08000000)

which is correct. The "1" of the 0x8000000 gets set in one situation and unset in the other, giving false in one and true in another.

Strange you got an error on that declaration. I used that declaration (taken from pinvoke.net) and it works for me...

To resolve this better, take a look at Spy++ (under your Visual Studio tools in the start menu). Use the Find Window command and hover with the target-mouse over your window (the disabled parent) and you should find the properties of your window when you click OK (see screenshots). The result of GetWindowLong is under the tab  "Styles". Hope you find the/a difference between with / without dialog (do not compare the list, compare the hexadecimal value of the Styles or Extended Styles).

-- Abel --


ScreenShot296.png
ScreenShot295.png
ScreenShot294.png
ScreenShot297.png
0
 
gem56Author Commented:
Hi abel,
Firstly thanks for pointing out how to use Spy++, as I haven't used it before, and secondly I've got it working as you indicated so that's great and you've got the points.

If I use the ActiveFom handle, in this case being a child of the MDI container, in GetWindowLong statement then it always shows False. If however I use the handle of the MDI container then I get the correct results, as you indicated. Maybe I've miss interpreted your directions and that's what you intended all along. It seems that if an MDI child is involved then one must use the MDI parent handle whereas if a dialog Form is involved (not MessageBox?) then you need to use the dialog Form handle. I'm still verifying the last bit about the dialog but that's how it's looking at this stage.

In Spy++ I can see the MessageBox window and the text that it shows so I'm wondering if there is an easy way of associating that window to it's parent, be that dialog or the MDI itself. If you already know the answer to that question I'm happy to create another post with extra points.

/Michael
0
 
abelCommented:
The ActiveForm is not necessarily the same as the parent of the dialog form if you use ShowDialog (the first parameter can set the actual owner/parent). Using ActiveForm is equal to using the GetForegroundWindow API function. If a dialog is showing, the ActiveForm will return the dialog, or null / Nothing if it is a MessageBox.

You should not use ActiveMdiChild, which will return something else (the active MDI child) which is not usable for you here.

Both a MessageBox and a ShowDialog will block the owner, which is the main form, not a particular MdiChild (iirc). So you are correct in using the main form for checking for being WS_DISABLED. It will always be the handle of the form that is currently blocked, not the blocking window / form itself.

-- Abel --
0
 
abelCommented:
PS, on this one:

> In Spy++ I can see the MessageBox window and the text that it shows
> so I'm wondering if there is an easy way of associating that window to it's parent,


If you call MessageBox without an owner parameter (the first one, you can set it to Me), it will be associated with the current active window. Internally, the GetActiveWindow API call is used, which is slightly different from GetForegroundWindow. The latter works system wide (giving the one that the user is currently working with, which is why ActiveForm returns Nothing when that's not eq. to the .NET form), the previous works only on the current thread (and thus guarantees that the returned handle, if any, belongs to a form of your app). But I'm quite in the details now, not sure if you need all that intel... ;-)
0
 
gem56Author Commented:
Thanks a lot for all your patience and of course solution.
/Michael
0
 
abelCommented:
You're welcome, glad to be of help :)
0

Featured Post

Vote for the Most Valuable Expert

It’s time to recognize experts that go above and beyond with helpful solutions and engagement on site. Choose from the top experts in the Hall of Fame or on the right rail of your favorite topic page. Look for the blue “Nominate” button on their profile to vote.

  • 7
  • 5
Tackle projects and never again get stuck behind a technical roadblock.
Join Now