【VB.NET】也谈跨进程消息钩子

写给VB.NET程序员^_^

老生常谈了:子类化。

      我们都知道在VB6里面可以用API函数来进行子类化,以处理自身的窗体过程;如果跨进程,这就麻烦了,由于我们的函数在我们的进程中(废话),而目标进程的窗口的消息处理函数在目标进程(还是废话),所以只能想办法把我们的代码放到对方进程中去执行——并且要告知我们的进程得到了什么消息。恐怕写汇编就有点吓人了,于是大家都写DLL,其原理就是把回调函数放到一个DLL里面注入到对方进程,DLL去修改目标窗口的默认处理函数——把消息发送给我们。

      当然也有“另类”一点的:http://www.it-berater.org/ThueDownloads/index.shtml上面有一个DLL包,其中含有一个dssubcls.dll,用它,可以轻松的完成我们的工作:就像调用一个API一样简单,而且在我们的程序中使用回调函数!呵呵,省去了自己写DLL的麻烦之后,这些好处足以吸引各位观众了吧?

      好了,VB6的代码大家可以在下载的压缩包中找到,作者提供了一个以记事本为基础的实例(在\dssubcls目录下),非常详细无需详细叙述了。关键是在VB.NET里面如何使用它——如何声明API,如何进行回调,看用来子类化的API的VB6声明先:

Declare Function SubClass& Lib "dssubcls" (ByVal HwndSubclass&, _
                                           Optional ByVal Address& = 0, _
                                           Optional ByVal OldStyle& = 0, _
                                           Optional ByVal NewStyle& = 0, _
                                           Optional ByVal Ext& = 0, _
                                           Optional ByVal SubClass& = 0)
转化成VB.NET的声明类似下面的样子(习惯使然,我把&展开成了As Integer):

Declare Function SubClass Lib "dssubcls" (ByVal HwndSubclass As Integer, Optional ByVal Address As Integer = 0, Optional ByVal OldStyle As Integer = 0, Optional ByVal NewStyle As Integer = 0, Optional ByVal Ext As Integer = 0, Optional ByVal SubClass As Integer = 0) As Integer

这不是很好嘛?问题来了,这样的声明在VB6里面可以使用Addressof function来传入第二个参数(参见你下载的源码),但是在VB.NET里面直接Addressof就不成了——我们需要委托一个回调:

Private Delegate Function HookCallBack(ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer

这个委托,对应的是以下函数:

    Private Function mCallback(ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
            ‘在这里处理得到的消息

    End Function

使用时,需要注意先实例化这个委托:

    Private fix_COCD = New HookCallBack(AddressOf mCallback)

此时,fix_COCD就是我们的mCallback函数引用了,用更直观的观点来看,fix_COCD就是一个指向mCallback的指针,相当于VB6里面的Addressof function得到的结果,看似问题解决了,于是我们写了以下代码来搞对方的进程窗体消息:

SubClass(Handle, fix_COCD, 0, 0, 0, 1)   '修改处理函数

问题真是接踵而至!IDE提示变量类型不符!!事实确实如此,我们把一个HookCallBack类型当做Integer来传递,无法通过检查,那么强行转换吧?当然,你可以去试试。这时,我所做的是,修改这个API声明:

Private Declare Function SubClass Lib "dssubcls" (ByVal HwndSubclass As Integer, Optional ByVal Address As HookCallBack = Nothing, Optional ByVal OldStyle As Integer = 0, Optional ByVal NewStyle As Integer = 0, Optional ByVal Ext As Integer = 0, Optional ByVal SubClass As Integer = 0) As Integet

使之符合我们的调用?有点倒行逆施?并非如此,当你习惯了修改API声明之后,会发现有些事变得如此简单,有些事需要你重新认识——对于WIN32 API也是如此。

 

至此,大功告成:

较为完整的代码如下:

Code

 

用这个代码的时候,可能会碰见一些“意外情况“,例如wm_datacopy,此时,我们需要进一步去获取LPARTM所指向的结构并对其进行解析(我们要读的是对方窗口所在进程的内存,具体地址由lParam确定——实际上lParam一直是一个指针——IntPrt,但它与Integer完全就是一回事(如果你使用VB2005可能需要使用Intprt.toint32或intprt=new intprt(integer)这些):

Code

这个类提供了Readmsg方法来读取一些内容——但这并不是完整的,我们知道,LPARAM指向的结构是这样的:

<StructLayout(LayoutKind.Sequential)> _
Public Structure COPYDATASTRUCT
    Public dwData As Integer
    Public cbData As Integer
    Public lpData As IntPtr
End Structure

其中dwData我们不是很关心,当然其中也可能存在一些有用信息(这里不想多说,网上有些文章纯属误导)

而cbData是一个长度:lpData的长度

lpData这里被声明为指针,看起来更直观了——它就是地址

有了地址和长度,如何读取代码就自己写吧。

提示一下:参考我重载的ReadProcessMemory可能对你有不少帮助。

当然,上面提到的只是“特殊情况”中的一个典型,还有很多时候,进程是用自定义消息(>&H40A)来传递数据的,例如我所开发的这个工程,打印mCallBack的参数后,得到的是如下结果(十六进制,只提取了有用的信息):

473  14  42257D0

其中lParam就是一个指针,我读了其中的一部分:

    Function readmsg(ByVal address As Integer) As Byte()
        Dim buf(19) As Byte
        ReadProcessMemory(hProc, address, buf, 20, 0)
        Return buf
    End Function

现在就明白为什么上面的代码是那样了:)

然后进行了一个处理,得到了我想要的信息:

    '消息解码后得到的移动棋子信息:玩家,起X,起Y,止X,止Y,棋子编号,走棋总步数
    Event Move(ByVal player As Byte, ByVal sx As Byte, ByVal sy As Byte, ByVal dx As Byte, ByVal dy As Byte, ByVal name As Byte, ByVal [step] As Byte)
    Private Function mCallback(ByVal wMsg As Integer, ByVal wParam As Integer, ByVal lParam As Integer) As Integer
        If wParam = &H14 Then
            Dim s As Byte() = msg.readmsg(lParam)
            RaiseEvent Move(s(1), s(10), s(11), s(12), s(13), s(14), s(16))
        End If
    End Function

当然,在我的工程里面重载的ReadProcessMemory并没有被使用。

 

 

 

补充一下咯:

在VB.NET中,处理自己的窗体的消息只需要重载窗体消息处理过程就可以了,无需子类化:)

有补充一下:

对于wm_datacopy来说,还有一些数据获取的问题没有说清楚,实际上都可以用一些方法来解决。

真的要回家咯。饿。。贴一个重载可能更说明问题:

 

Code

 

posted @ 2009-06-23 11:16  zcsor~流浪dè风  Views(2258)  Comments(0Edit  收藏  举报