|
-
Oct 2nd, 2006, 09:17 AM
#1
Thread Starter
Fanatic Member
Can't Show Non-Modal Form...
I have an application that auto logs a user off of the program. I have seen an issue when the user has a Message Box open when the auto-logoff routine fires; "Can't show a non-modal form when a modal form is displayed".
So, what I did was write a routine that check's the current thread for any open message boxes and then use a SendMessage API to close the message box. The message box is closed successfully, but the error still occurs. If I remove the message box from the code then it works fine. Need HELP here!! I have even tried a DoEvents loop after the call to "CloseMessageBox" but the error still occurs, any ideas?!
Here is the code to test it: (can't run from VB as the timers do not fire when a message box is open, so it must be compiled into an exe first)
VB Code:
'***IN A FORM
Private Sub Form_Activate()
MsgBox "Test Message"
End Sub
Private Sub Form_Load()
Timer1.Interval = 1000
End Sub
Private Sub Timer1_Timer()
Dim lProcessID As Long
Dim lID As Long
Timer1.Interval = 0
'get the process ID for prosper
lProcessID = GetWindowThreadProcessId(Me.hWnd, lID)
'close any open message boxes
CloseMessageBox lProcessID
Form2.Show
End Sub
'***IN A MODULE
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal lWindowHwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal lWindowHwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
Private Declare Function EnumThreadWindows Lib "user32" (ByVal dwThreadId As Long, ByVal lpfn As Long, ByVal lParam As Long) As Long
'used for message box closing
Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal lWindowHwnd As Long, lpdwProcessId As Long) As Long
Private Const MSGBOX_CLASS_ID = "#32770"
Private Const WM_GETTEXT = &HD
Private Const WM_CLOSE = &H10
Private Const BUFFER_SIZE As Integer = 255
Private lMsgBoxHwnd As Long
Private sMsgText As String
Private bMsgBox As Boolean
Public Sub CloseMessageBox(ByVal lOwnerProcess As Long)
On Error GoTo ErrorHandler:
Dim lProcessID As Long
Dim lID As Long
'reset the handle
lMsgBoxHwnd = 0
'enumerates top-level windows
Call EnumThreadWindows(lOwnerProcess, AddressOf EnumWindowProc, 0&)
'if a valid message box handle was found
If (lMsgBoxHwnd <> 0) Then
'send a message to close the message box
SendMessage lMsgBoxHwnd, WM_CLOSE, 0&, 0&
End If
Exit Sub
ErrorHandler:
End Sub
Private Function EnumWindowProc(ByVal lWindowHwnd As Long, ByVal lParam As Long) As Long
On Error GoTo ErrorHandler:
Dim sClassName As String
'create a buffer
sClassName = Space(BUFFER_SIZE)
'truncate the class name buffer
sClassName = Left(sClassName, GetClassName(lWindowHwnd, ByVal sClassName, BUFFER_SIZE))
'if a dialog class was found
If StrComp(Left(sClassName, Len(MSGBOX_CLASS_ID)), MSGBOX_CLASS_ID, vbTextCompare) = 0 Then
'verify it's a standard message box style dialog
If IsMsgBoxDialog(lWindowHwnd) Then
lMsgBoxHwnd = lWindowHwnd
lWindowHwnd = 0
End If
End If
'return the window handle
EnumWindowProc = lWindowHwnd
Exit Function
ErrorHandler:
End Function
Private Function IsMsgBoxProc(ByVal lWindowHwnd As Long, ByVal lParam As Long) As Long
On Error GoTo ErrorHandler:
Dim sClass As String
Dim sCaption As String
'create a buffer
sClass = Space(BUFFER_SIZE)
'extract the class name of this child window
sClass = Left(sClass, GetClassName(lWindowHwnd, ByVal sClass, BUFFER_SIZE))
'if a button class
If InStr(LCase(sClass), "button") Then
'create a buffer
sCaption = Space(BUFFER_SIZE)
'see if it's caption qualifies as a Msgbox button caption
sCaption = Left(sCaption, SendMessage(lWindowHwnd, WM_GETTEXT, BUFFER_SIZE, ByVal sCaption))
'is it a valid message box
If InStr(",OK,ABORT,RETRY,YES,NO,CANCEL,IGNORE,", "," & Replace(UCase(sCaption), "&", "") & ",") Then
lWindowHwnd = 0
bMsgBox = True
End If
End If
'return the handle
IsMsgBoxProc = lWindowHwnd
Exit Function
ErrorHandler:
End Function
Private Function IsMsgBoxDialog(ByVal lWindowHwnd As Long) As Boolean
On Error Resume Next
'reset the flag
bMsgBox = False
'enumerate child windows
Call EnumChildWindows(lWindowHwnd, AddressOf IsMsgBoxProc, 0&)
'return the success value
IsMsgBoxDialog = bMsgBox
End Function
-
Oct 2nd, 2006, 09:53 AM
#2
Hyperactive Member
Re: Can't Show Non-Modal Form...
 Originally Posted by clarkgriswald
VB Code:
Private Sub Timer1_Timer()
Dim lProcessID As Long
Dim lID As Long
Timer1.Interval = 0
'get the process ID for prosper
lProcessID = GetWindowThreadProcessId(Me.hWnd, lID)
'close any open message boxes
CloseMessageBox lProcessID
Form2.Show '<----- DANGER, WILL ROBINSON, DANGER
End Sub
My shot in the dark would be the Form2.Show call in the timer loop is directly causing the error. If you call CloseMessageBox and it doesn't successfully close the MsgBox, then then it will try to show Form2 and blow up.
Have you stepped through your code and made sure that it is closing the MsgBox where and when it should be closing?
-
Oct 2nd, 2006, 09:58 AM
#3
Thread Starter
Fanatic Member
Re: Can't Show Non-Modal Form...
Yes, I have tested that the message box is being closed. I can't test it from VB due to the fact that once the message box is displayed timer events will NOT fire, but I have put in a DoEvents loop after the call the CloseMEssageBox and you will see the message box close...then hits the loop, then error pops up. Can't figure this out and it's driving me crazy, I need to handle this case.
-
Oct 2nd, 2006, 11:08 AM
#4
Hyperactive Member
[Edit]
I agree with comment below. You just have the .Show call in the wrong location.
Last edited by Fedhax; Oct 2nd, 2006 at 11:40 AM.
Reason: Completely Misread The Code; Comment Was Wrong
-
Oct 2nd, 2006, 11:31 AM
#5
Member
Re: Can't Show Non-Modal Form...
The MsgBox function is not finished before it has delivered the return value, i.e in the Form_Activate event. That's why. Move Form2.show to Form_Active() after the MsgBox function.
-
Oct 2nd, 2006, 12:16 PM
#6
Thread Starter
Fanatic Member
Re: Can't Show Non-Modal Form...
Ok, we are getting somewhere, but i guess my problem is in the LARGER application, I will have no idea of the source of the message box, I just want to close the window...is there any way I can handle this situation?
-
Oct 2nd, 2006, 12:33 PM
#7
Hyperactive Member
Re: Can't Show Non-Modal Form...
 Originally Posted by clarkgriswald
Ok, we are getting somewhere, but i guess my problem is in the LARGER application, I will have no idea of the source of the message box, I just want to close the window...is there any way I can handle this situation?
Do you have access to edit/modify the larger application?
You are only going to run into problems where you will want to open a form after closing a MsgBox. Under that scenario, you will need to make sure that the routine that calls the MsgBox--in this case, Form_Activate--ends completely or your .Show call follows the MsgBox call.
If you cannot know this information or edit the MsgBox calls, then you are might be up a creek. You could also look into this thread which runs into a similar problem with one of Outlook's modal windows. In his case, he wrote a 2nd program--an ActiveX EXE--to handle the modal form closing that you are trying to place in your program's timer. All it does is run an independent timer--since Outlook is a separate program from his program--to look for the window, simulate's end user input, and closes the window accordingly.
-
Oct 2nd, 2006, 01:16 PM
#8
Thread Starter
Fanatic Member
Re: Can't Show Non-Modal Form...
I actually do have access to the larger application, the one issue is that there are over 700 message box calls spread out over 10 separate referenced objects. This is an annoying issue to say the least, I may have to figure out a different approach altogether.
-
Oct 2nd, 2006, 01:21 PM
#9
Hyperactive Member
Re: Can't Show Non-Modal Form...
 Originally Posted by clarkgriswald
I actually do have access to the larger application, the one issue is that there are over 700 message box calls spread out over 10 separate referenced objects. This is an annoying issue to say the least, I may have to figure out a different approach altogether.
Depending on your deployment issues, I would recommend looking into CVMichael's approach to knocking out annoying Modal Windows.
Also, another approach may be needed because why is your program showing over 700 message boxes if you want to automatically close them? Why have modal windows in the first place if you don't user input to interact with them? I guess that I'm wanting to know what is the real problem here that we are trying to solve?
-
Oct 2nd, 2006, 01:58 PM
#10
Thread Starter
Fanatic Member
Re: Can't Show Non-Modal Form...
The application is a client-server app. If a user is logged on and happens to do something causing a message box to display, ie. "Operation cannot be completed. Please enter a valid ID." While this message is displayed, it's possible that a "ForceLogoff" command may be sent to the system. In this case, I want to unload all windows and display the login form. However, if a message box was open during this call, an error occurs (modal error).
-
Oct 2nd, 2006, 02:09 PM
#11
Hyperactive Member
Re: Can't Show Non-Modal Form...
 Originally Posted by clarkgriswald
The application is a client-server app. If a user is logged on and happens to do something causing a message box to display, ie. "Operation cannot be completed. Please enter a valid ID." While this message is displayed, it's possible that a "ForceLogoff" command may be sent to the system. In this case, I want to unload all windows and display the login form. However, if a message box was open during this call, an error occurs (modal error).
My initial gut instinct is that you're going to have to rewrite some of the Error Handling stuff and permit this kind of override functionality--like ForceLogOff--to your program. You can either rearrange the program calls like what we did to solve your initial problem, or you're going to have to take a step back and figure out a way to handle these kinds of errors without bringing the application to a screaming halt--which makes me think "redesign".
Unfortunately, the real lesson of this problem is "Use modal windows sparringly."
Posting Permissions
- You may not post new threads
- You may not post replies
- You may not post attachments
- You may not edit your posts
-
Forum Rules
|
Click Here to Expand Forum to Full Width
|