Results 1 to 11 of 11

Thread: Can't Show Non-Modal Form...

  1. #1

    Thread Starter
    Fanatic Member clarkgriswald's Avatar
    Join Date
    Feb 2000
    Location
    USA
    Posts
    799

    Exclamation 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:
    1. '***IN A FORM
    2.  
    3. Private Sub Form_Activate()
    4.     MsgBox "Test Message"
    5. End Sub
    6.  
    7. Private Sub Form_Load()
    8.     Timer1.Interval = 1000
    9. End Sub
    10.  
    11. Private Sub Timer1_Timer()
    12.  
    13.     Dim lProcessID As Long
    14.     Dim lID As Long
    15.    
    16.     Timer1.Interval = 0
    17.    
    18.     'get the process ID for prosper
    19.     lProcessID = GetWindowThreadProcessId(Me.hWnd, lID)
    20.    
    21.     'close any open message boxes
    22.     CloseMessageBox lProcessID
    23.    
    24.     Form2.Show
    25.  
    26. End Sub
    27.  
    28. '***IN A MODULE
    29.  
    30. 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
    31. Private Declare Function EnumChildWindows Lib "user32" (ByVal hWndParent As Long, ByVal lpEnumFunc As Long, ByVal lParam As Long) As Long
    32. Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal lWindowHwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
    33. Private Declare Function EnumThreadWindows Lib "user32" (ByVal dwThreadId As Long, ByVal lpfn As Long, ByVal lParam As Long) As Long
    34.  
    35. 'used for message box closing
    36. Public Declare Function GetWindowThreadProcessId Lib "user32" (ByVal lWindowHwnd As Long, lpdwProcessId As Long) As Long
    37.  
    38. Private Const MSGBOX_CLASS_ID = "#32770"
    39. Private Const WM_GETTEXT = &HD
    40. Private Const WM_CLOSE = &H10
    41. Private Const BUFFER_SIZE As Integer = 255
    42.  
    43. Private lMsgBoxHwnd As Long
    44. Private sMsgText As String
    45. Private bMsgBox As Boolean
    46.  
    47. Public Sub CloseMessageBox(ByVal lOwnerProcess As Long)
    48.    
    49.     On Error GoTo ErrorHandler:
    50.    
    51.     Dim lProcessID As Long
    52.     Dim lID As Long
    53.    
    54.     'reset the handle
    55.     lMsgBoxHwnd = 0
    56.    
    57.     'enumerates top-level windows
    58.     Call EnumThreadWindows(lOwnerProcess, AddressOf EnumWindowProc, 0&)
    59.    
    60.     'if a valid message box handle was found
    61.     If (lMsgBoxHwnd <> 0) Then
    62.        
    63.         'send a message to close the message box
    64.         SendMessage lMsgBoxHwnd, WM_CLOSE, 0&, 0&
    65.        
    66.     End If
    67.    
    68.     Exit Sub
    69.  
    70. ErrorHandler:
    71.  
    72. End Sub
    73.  
    74. Private Function EnumWindowProc(ByVal lWindowHwnd As Long, ByVal lParam As Long) As Long
    75.    
    76.     On Error GoTo ErrorHandler:
    77.    
    78.     Dim sClassName As String
    79.    
    80.     'create a buffer
    81.     sClassName = Space(BUFFER_SIZE)
    82.    
    83.     'truncate the class name buffer
    84.     sClassName = Left(sClassName, GetClassName(lWindowHwnd, ByVal sClassName, BUFFER_SIZE))
    85.    
    86.     'if a dialog class was found
    87.     If StrComp(Left(sClassName, Len(MSGBOX_CLASS_ID)), MSGBOX_CLASS_ID, vbTextCompare) = 0 Then
    88.    
    89.         'verify it's a standard message box style dialog
    90.         If IsMsgBoxDialog(lWindowHwnd) Then
    91.             lMsgBoxHwnd = lWindowHwnd
    92.             lWindowHwnd = 0
    93.         End If
    94.        
    95.     End If
    96.    
    97.     'return the window handle
    98.     EnumWindowProc = lWindowHwnd
    99.  
    100.     Exit Function
    101.  
    102. ErrorHandler:
    103.  
    104. End Function
    105.  
    106. Private Function IsMsgBoxProc(ByVal lWindowHwnd As Long, ByVal lParam As Long) As Long
    107.    
    108.     On Error GoTo ErrorHandler:
    109.  
    110.     Dim sClass As String
    111.     Dim sCaption As String
    112.    
    113.     'create a buffer
    114.     sClass = Space(BUFFER_SIZE)
    115.    
    116.     'extract the class name of this child window
    117.     sClass = Left(sClass, GetClassName(lWindowHwnd, ByVal sClass, BUFFER_SIZE))
    118.    
    119.     'if a button class
    120.     If InStr(LCase(sClass), "button") Then
    121.        
    122.         'create a buffer
    123.         sCaption = Space(BUFFER_SIZE)
    124.        
    125.         'see if it's caption qualifies as a Msgbox button caption
    126.         sCaption = Left(sCaption, SendMessage(lWindowHwnd, WM_GETTEXT, BUFFER_SIZE, ByVal sCaption))
    127.        
    128.         'is it a valid message box
    129.         If InStr(",OK,ABORT,RETRY,YES,NO,CANCEL,IGNORE,", "," & Replace(UCase(sCaption), "&", "") & ",") Then
    130.             lWindowHwnd = 0
    131.             bMsgBox = True
    132.         End If
    133.        
    134.     End If
    135.    
    136.     'return the handle
    137.     IsMsgBoxProc = lWindowHwnd
    138.  
    139.     Exit Function
    140.  
    141. ErrorHandler:
    142.  
    143. End Function
    144.  
    145. Private Function IsMsgBoxDialog(ByVal lWindowHwnd As Long) As Boolean
    146.    
    147.     On Error Resume Next
    148.    
    149.     'reset the flag
    150.     bMsgBox = False
    151.    
    152.     'enumerate child windows
    153.     Call EnumChildWindows(lWindowHwnd, AddressOf IsMsgBoxProc, 0&)
    154.    
    155.     'return the success value
    156.     IsMsgBoxDialog = bMsgBox
    157.    
    158. End Function

  2. #2
    Hyperactive Member Fedhax's Avatar
    Join Date
    Aug 2006
    Posts
    293

    Re: Can't Show Non-Modal Form...

    Quote Originally Posted by clarkgriswald
    VB Code:
    1. Private Sub Timer1_Timer()
    2.  
    3.     Dim lProcessID As Long
    4.     Dim lID As Long
    5.    
    6.     Timer1.Interval = 0
    7.    
    8.     'get the process ID for prosper
    9.     lProcessID = GetWindowThreadProcessId(Me.hWnd, lID)
    10.    
    11.     'close any open message boxes
    12.     CloseMessageBox lProcessID
    13.    
    14.     Form2.Show   '<----- DANGER, WILL ROBINSON, DANGER
    15.  
    16. 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?

  3. #3

    Thread Starter
    Fanatic Member clarkgriswald's Avatar
    Join Date
    Feb 2000
    Location
    USA
    Posts
    799

    Exclamation 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.

  4. #4
    Hyperactive Member Fedhax's Avatar
    Join Date
    Aug 2006
    Posts
    293
    [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

  5. #5
    Member
    Join Date
    Oct 2006
    Posts
    53

    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.

  6. #6

    Thread Starter
    Fanatic Member clarkgriswald's Avatar
    Join Date
    Feb 2000
    Location
    USA
    Posts
    799

    Exclamation 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?

  7. #7
    Hyperactive Member Fedhax's Avatar
    Join Date
    Aug 2006
    Posts
    293

    Re: Can't Show Non-Modal Form...

    Quote 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.

  8. #8

    Thread Starter
    Fanatic Member clarkgriswald's Avatar
    Join Date
    Feb 2000
    Location
    USA
    Posts
    799

    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.

  9. #9
    Hyperactive Member Fedhax's Avatar
    Join Date
    Aug 2006
    Posts
    293

    Re: Can't Show Non-Modal Form...

    Quote 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?

  10. #10

    Thread Starter
    Fanatic Member clarkgriswald's Avatar
    Join Date
    Feb 2000
    Location
    USA
    Posts
    799

    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).

  11. #11
    Hyperactive Member Fedhax's Avatar
    Join Date
    Aug 2006
    Posts
    293

    Re: Can't Show Non-Modal Form...

    Quote 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
  •  



Click Here to Expand Forum to Full Width