VB拦截WM_CLOSE和WM_QUIT消息

在写一些关于安全之类的工具时,有时我们不想让其它程序(如病毒)关掉我们自己的程序,怎么办呢?就说最简单的,如何防止其它程序关掉我们的窗口?要防止自己的窗口被关掉就要拦截一些系统消息,比如,当我们单击一个窗口右上角的关闭按钮或按下Alt+F4时,会产生一个WM_CLOSE的系统消息,窗口接收到这个消息后,会试图调用DestroyWindow函数销毁自己,销毁过程中会产生WM_DESTROY和WM_NCDESTROY消息,窗口接收到这两个消息后,会调用PostQuitMessage函数产生WM_QUIT消息放入消息队列中,最后GetMessage函数收到WM_QUIT消息后返回0,从而结束消息循环导致线程退出。那么我们要防止自己被关闭只要拦截这些消息即可,我们可以从上面的任何一个环节来拦截。

1.拦截WM_CLOSE消息

拦截WM_CLOSE消息最简单的方法是在窗体的Unload或者QueryUnload事件中加入Cancel=True这一命令,注意如果是在Form的QueryUnload事件中加和入Cancel=True命令的话,那么不只拦截了窗口的退出,还拦截了注销、关机等命令,这一点可以根据UnloadMode的参数去判断。用这种方法防止窗口的退出有一个缺点,即虽然防止了其它程序发送WM_CLOSE消息来关闭自己,但这样一来,我们自己也无法关闭自己了,那么怎么才能只防止外部程序关掉自己呢?我们可以用SetWindowLong函数改为窗口的消息处理过程,代码如下:

'窗体中
Private Sub Form_Load()

Me.Show

hWnd=Me.hwnd

lpPrevWndProc = SetWindowLong(hWnd, GWL_WNDPROC, AddressOf WindowProc)

End Sub

Private Sub Form_Unload(Cancel As Integer)

'恢复原窗口函数
Call SetWindowLong(hWnd, GWL_WNDPROC, lpPrevWndProc)

End Sub

'模块中
Public hWnd As Long
Public lpPrevWndProc As Long

Public Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal hwnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function CallWindowProc Lib "user32" Alias "CallWindowProcA" (ByVal lpPrevWndFunc As Long, ByVal hWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

Public Const GWL_WNDPROC = -4& '指定替换原窗口函数常数
Public Const WM_CLOSE = &H10

'新的窗口处理函数
Public Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long

'如果收到关闭消息就拦截它

If uMsg = WM_CLOSE Then Exit Function

'调用原窗口函数

WindowProc = CallWindowProc(lpPrevWndProc, hWnd, uMsg, wParam, lParam)

End Function

好了,有了以上代码,其它程序就无法通过发送WM_CLOSE消息来关掉我们的窗口了,然而我们可以通过单击右上角的关闭按钮来关闭自己,至于为什么我也一直不太清楚,我猜可能是这样:单击右上角的关闭后并不会产生WM_CLOSE消息,而会产生WM_SYSCOMMAND消息与SC_CLOSE消息,而窗口收到这两个消息后直接调用DestroyWindow来销毁自己,至于VB的Cancel=True会同时拦截WM_CLOSE与WM_SYSCOMMAND两个消息,从而使得不论调用PostMessage还是单击右上角的关闭都无法关闭自己。我试过在窗体的Unload事件中加入一句MsgBox语句来拦截WM_CLOSE,然后我用另一程序向它发送WM_CLOSE,结果并没有弹出那个对话框,但是单击右上角的关闭却可以弹出,一直弄不清怎么回事。

2.拦截WM_QUIT消息

拦截WM_QUIT消息就不像拦截WM_CLOSE消息那么容易了,为什么呢?因为WM_QUIT消息不会出现在消息循环中,就是说,在窗口处理函数中拦截WM_QUIT是没有用的,因为GetMessage接到WM_QUIT消息后就会返回False退出消息循环,那么,要拦截WM_QUIT消息只能在消息循环的位置来拦截,但VB中如何控制消息循环呢?这个问题我想了好长时间,最后想到了一个方法,我们可以像VC那样自己写一个Do While循环,然后自动处理收到的消息,把它的条件设为真让它无限循环,这样就能阻止其它程序通过PostMessage或者PostThreadMessage发送WM_QUIT消息使我们的程序退出,从而也保证了窗口不被销毁。代码:

'在Form中
Private Declare Function GetMessage Lib "user32" Alias "GetMessageA" (lpMsg As MSG, ByVal hwnd As Long, ByVal wMsgFilterMin As Long, ByVal wMsgFilterMax As Long) As Long
Private Declare Function TranslateMessage Lib "user32" (lpMsg As MSG) As Long
Private Declare Function DispatchMessage Lib "user32" Alias "DispatchMessageA" (lpMsg As MSG) As Long

Private Type POINTAPI
        x As Long
        y As Long
End Type


Private Type MSG
        hwnd As Long
        time As Long
        pt As POINTAPI
        wParam As Long
        lParam As Long
        message As Long
End Type

Dim uMsg As MSG

Private Sub Form_Load()
Me.Show '先显示窗体

'建立消息循环并处理
While True
   Call GetMessage(uMsg, Me.hwnd, 0, 0)
   Call TranslateMessage(uMsg)
   Call DispatchMessage(uMsg)
   DoEvents
Wend
End Sub

Private Sub Form_Unload(Cancel As Integer)
End '结束程序
End Sub

远方

2009.1.24

猜你喜欢

转载自blog.csdn.net/zzmzzff/article/details/80298620