如果你下载并运行这个例程序,你会发现如果我们在托盘图标上点击鼠标右键,则会弹出一个右键菜单(如右图)。如果点击相应的菜单项,程序主窗体会随之变化,这样就可以控制程序的行为。而如果当主窗体处于最小化状态时,我们在托盘图标上点击左键,窗体会恢复到原来的大小。其实实现上述的功能都要依赖于WINDOWS操作系统的消息机制,要完全弄懂这个机制挺不容易的,但是我们可以按下述文字来理解它。
把WINDOWS操作系统看作人的大脑,它接收、处理、并发送各种各样的信息给我们的各个器官(当然是比喻各个应用程序了),也就是说它是消息的中枢。而每个应用程序(甚至每一个按钮、标签、窗体等等统称为窗口)在运行时都会被分配一个窗口过程WINDOWPROC,由这个窗口过程来接收和处理操作系统发来的消息(实际上存在一个消息队列),通常情况下这个窗口过程是由操作系统指定的,它会自动的响应并处理一些WINDOWS消息(如窗体移动、最大化、最小化、错误信息等)。好,到这我们先停一下,提出一个疑问,这些消息能否由我们自己写程序来处理呢?答案是肯定的,不过还得借助API函数的威力了,怎么用?我们还是先看看这些API函数的定义和参数吧。
程序中用到了SendMessage、CallWindowProc、SetWindowLong等API函数,其中SendMessage函数的作用是将一条消息发给某个窗口;CallWindowProc函数用来发送消息到一个窗口过程;而使用SetWindowLong函数来为窗口结构中为指定的窗口设置属性。使用API函数之前必须先在程序中声明如下:
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
Declare Function SetWindowLong Lib "user32" Alias "SetWindowLongA" (ByVal HWnd As Long, ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal HWnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
其中各参数的意义如下表:
CallWindowProc函数
参数: |
意义 |
lpPrevWndFunc |
Long,原来的窗口过程地址 |
HWnd |
Long,窗口句柄 |
Msg |
Long,发送的消息 |
wParam |
Long,消息类型,参考wParam参数表 |
lParam |
Long,依据wParam参数的不同而不同 |
返回值 |
Long,依据发送的消息不同而变化 |
SetWindowLong函数
参数: |
意义 |
hwnd |
Long,欲为其取得信息的窗口的句柄 |
nIndex |
Long,请参考GetWindowLong函数的nIndex参数的说明 |
dwNewLong |
Long,由nIndex指定的窗口信息的新值 |
返回值 |
Long,指定数据的前一个值 |
SendMessage函数
参数: |
意义 |
hwnd |
Long,要接收消息的那个窗口的句柄 |
wMsg |
Long,消息的标识符 |
wParam |
Long,具体取决于消息 |
lParam |
Any,具体取决于消息 |
返回值 |
Long,由具体的消息决定 |
我们要自己写程序来处理消息,必须先更改窗口的属性,从原来由默认的窗口过程来处理消息变成由我们自己写的消息处理过程来处理消息。方法是使用SetWindowLong函数来取得默认窗口过程的地址,然后转向为我们自己写的窗口过程的地址,具体的实现方法如下代码:
'GWL_WNDPROC获得该窗口的窗口过程的地址,AddressOf是取址函数,NewWindowProc是我们写的过程
OldWindowProc = SetWindowLong(frm.HWnd, GWL_WNDPROC, AddressOf NewWindowProc)
然后在NewWindowProc函数中写入如下代码,需要注意的是下面代码中红色的TRAY_CALLBACK是由托盘区图标传来的消息,要让托盘图标传回消息,必须在添加托盘图标时指定(参见上节内容):
Public Function NewWindowProc(ByVal HWnd As Long, ByVal Msg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long
'如果用户点击了托盘中的图标,则进行判断是点击了左键还是右键
If Msg = TRAY_CALLBACK Then
'如果点击了左键
If lParam = WM_LBUTTONUP Then
'而这时窗体的状态是最小化时
If TheForm.WindowState = vbMinimized Then _
'恢复到最小化前的窗体状态
TheForm.WindowState = TheForm.LastState
TheForm.SetFocus
Exit Function
End If
End If
'如果点击了右键
If lParam = WM_RBUTTONUP Then
'则弹出右键菜单
TheForm.PopupMenu TheMenu
Exit Function
End If
End If
'如果是其他类型的消息则传递给原有默认的窗口函数
NewWindowProc = CallWindowProc(OldWindowProc, HWnd, Msg, wParam, lParam)
End Function
这样我们就取得并处理了来自托盘图标的消息,现在的问题是在鼠标右键菜单弹出后,怎么控制程序主窗体的状态,这时我们需要用到SendMessage函数来向主窗体发送最大化、最小化、关闭、移动等消息,具体的代码实现如下,其中HWnd是主窗体的句柄,WM_SYSCOMMAND表示发送的是系统控制类的消息,SC_MOVE、SC_SIZE、SC_RESTORE便是要发送的消息了:
'托盘图标右键菜单上的“移动”项被点击时
Private Sub mnuTrayMove_Click()
SendMessage HWnd, WM_SYSCOMMAND, SC_MOVE, 0&
End Sub
'托盘图标右键菜单上的“恢复”项被点击时
Private Sub mnuTrayRestore_Click()
SendMessage HWnd, WM_SYSCOMMAND, SC_RESTORE, 0&
End Sub
'托盘图标右键菜单上的“退出”项被点击时
Private Sub mnuTraySize_Click()
SendMessage HWnd, WM_SYSCOMMAND, SC_SIZE, 0&
End Sub
最后要提醒你,在程序退出时一定要把窗口过程的地址恢复为默认值,同时把托盘图标移去哦。