2008年8月17日星期日

在VB6中处理自定义窗口消息

在vb程序间进行跨进程通信不是一件容易的事。我曾经见过许多的程序开发员试图通过各种各样的方法来完成这一工作,这些办法包括从将信息写入共享的文本文档或注册表键到使用成熟的activeX程序通信服务器。每一种办法都有缺点:以前的办法容易产生错误,而现在的这些方法又由于在服务器中呼叫每一个对象的时候都会发生跨进程操作,又使其效率变得非常低。有一个经过实践检验的方法是,将要跨进程的通信挂接到你自己的VB程序的消息序列中,同时倾听从其它进程中传来的自定义窗口消息,一种被大家称之为"子类"的技术。


我曾经描述过如何以及为什么在VB编程语言中要运用"子类",而且我还展示了如何使用视窗应用编程接口来将消息发送到你的应用程序以及解锁的隐藏控制功能中去。在本篇文章中,我会向大家解释如何才能向其他的应用程序发送消息以及如何发送消息才能解决跨进程的通信难题。同时,我还会给大家提供可再度使用的ActiveX DLL, Messenger.dll,来让各位在将自定义消息运用于自己的程序的时候尽可能的简单起来。

传送信息时与其它程序之间的相互作用

因为消息传送是Windows和在它上面运行的程序之间通信的最基本方式,任何窗口或者控制任何的应用程序都可以通过使用SendMessage或者相关的软件来发送消息。此外,那些标准的消息对于所有运行于Windows的应用程序都有着相同的含义。有了这两点,我们就能得出一个令人吃惊的结论:通过发送消息,人们可以轻而易举的控制任何的当前正在运行的应用程序,甚至还包括那些并不应该被别人操控的应用程序。举个例子来说:


当你使用WM_xBUTTONDOWN 以及 WM_xBUTTONUP来传送消息的时候,你可以在别的应用程序中模拟鼠标的点击操作。

发送WM_KEYDOWN 以及 WM_KEYUP消息的时候,会模拟出按键盘的操作。

有恶意的程序员能够通过发送一条WM_CLOSE消息到最高级窗口来迫使其它的应用程序关闭,或者通过使用WM_ENDSESSION来让其它的应用程序误以为Windows操作系统正在关闭。

指出你是在和谁谈话

发送消息到另一个窗口的关键并不取决于你的应用程序,而是取决于窗口句柄。Windows操作系统的应用编程接口有许多的功能能够重新得到特定的窗口句柄,其中最经常使用的一个就是FindWindow功能。这个功能能够在标题说明中找到基于文本的最高级别的窗口(lpWindowName),并且返回到它的窗口句柄。在VB编程语言中关于FindWindow是如下这么描述的:


Private Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" (ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long


找到子窗体

你能通过使用FindWindowEx来找到子窗体句柄,就象是一个表格中的特殊控制一样。
------------------------------------------------------
您可以用API函数SetWindowLong指定处理消息的窗口过程(window procedure)为自定义的函数WindowProc,捕获消息ID为WM_USER+1的自定义消息或系统消息。并且,为了保证窗口能正确的响应消息,需要保存原来默认的窗口过程并在自定义函数WindowProc中调用。如下例,在按钮事件中发送自定义消息,WindowProc捕获了窗口大小变化的系统定义消息和用户自定义的消息并显示用户自定义的消息参数wParam:
在Moduel中加入以下代码,

Option Explicit
Private Const GWL_WNDPROC = -4
Public Const GWL_USERDATA = (-21)
Public Const WM_SIZE = &H5
Public Const WM_USER = &H400
Private 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 Declare Function GetWindowLong Lib "user32" Alias "GetWindowLongA" _
(ByVal hwnd As Long, ByVal nIndex As Long) As Long
Private Declare Function SetWindowLong Lib "user32" Alias _
"SetWindowLongA" (ByVal hwnd As Long, _
ByVal nIndex As Long, ByVal dwNewLong As Long) As Long
Public Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
Public Function Hook(ByVal hwnd As Long) As Long
Dim pOld As Long
‘指定自定义的窗口过程
pOld = SetWindowLong(hwnd, GWL_WNDPROC, _
AddressOf WindowProc)
‘保存原来默认的窗口过程指针
SetWindowLong hwnd, GWL_USERDATA, pOld
Hook = pOld
End Function

Public Sub Unhook(ByVal hwnd As Long, ByVal lpWndProc As Long)
Dim temp As Long
'Cease subclassing.
temp = SetWindowLong(hwnd, GWL_WNDPROC, lpWndProc)
End Sub

Function WindowProc(ByVal hw As Long, ByVal uMsg As Long, _
ByVal wParam As Long, ByVal lParam As Long) As Long
If uMsg = WM_SIZE Then
‘处理WM_SIZE消息
MsgBox "SIZE"
End If
If uMsg = WM_USER + 1 Then
MsgBox wParam
End If
Dim lpPrevWndProc As Long
‘查询原来默认的窗口过程指针
lpPrevWndProc = GetWindowLong(hw, GWL_USERDATA)
‘调用原来的窗口过程
WindowProc = CallWindowProc(lpPrevWndProc, hw, uMsg,wParam, lParam)
End Function

在Form中加入以下代码:

Dim wParam As Long
Dim lParam As Long
Dim lResult As Long
Private Sub Command1_Click()
wParam = 12345
lResult = SendMessage(Me.hwnd, WM_USER + 1, wParam, lParam)
End Sub

Private Sub Form_Load()
Me.Tag = Hook(Me.hwnd)
End Sub
Private Sub Form_Unload(Cancel As Integer)
Unhook Me.hwnd, Me.Tag
End Sub

没有评论: