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