控件中国网现已改版,您看到的是老版本网站的镜像,系统正在为您跳转到新网站首页,请稍候.......
中国最专业的商业控件资讯网产品咨询电话:023-67870900 023-67871946
产品咨询EMAIL:SALES@COMPONENTCN.COM

也谈跨进程消息钩子

作者:流浪dè风 出处:博客园 2010年02月03日 阅读:

我们都知道在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也是如此。

 

至此,大功告成:

较为完整的代码如下:


    Private Declare Function SubClass Lib "dssubcls" (ByVal HwndSubclass As IntegerOptional ByVal Address As HookCallBack = NothingOptional ByVal OldStyle As Integer = 0Optional ByVal NewStyle As Integer = 0Optional ByVal Ext As Integer = 0Optional ByVal SubClass As Integer = 0As Integer
    
Private Declare Function UseSendMessage Lib "dssubcls" (ByVal use As IntegerAs Integer
    
'实例化的委托
    Private fix_COCD = New HookCallBack(AddressOf mCallback)
    
'委托
    Private Delegate Function HookCallBack(ByVal wMsg As IntegerByVal wParam As IntegerByVal lParam As IntegerAs Integer
    
Public Sub Hook(ByVal Handle As Integer)
        proc 
= SubClass(Handle, fix_COCD, 0001)   '修改处理函数
        UseSendMessage(1)
    
End Sub

    
Private Function mCallback(ByVal wMsg As IntegerByVal wParam As IntegerByVal lParam As IntegerAs Integer

    
End Function

 

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


Public Class GetMsg
    
Public Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As IntegerByVal lpBaseAddress As IntegerByVal lpBuffer() As ByteByVal nSize As IntegerByRef lpNumberOfBytesWritten As IntegerAs Integer
    
Public Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As IntegerByVal lpBaseAddress As IntegerByRef int As IntegerByVal nSize As IntegerByRef lpNumberOfBytesWritten As IntegerAs Integer
    
Public Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As IntegerByVal bInheritHandle As IntegerByVal dwProcessId As IntegerAs Integer
    
Public Declare Function CloseHandle Lib "kernel32" (ByVal hObject As IntegerAs Integer
    
Private hProc As IntPtr
    
Sub New(ByVal PID As Integer)
        hProc 
= OpenProcess(&HFFFF, False, PID)
    
End Sub

    
Function readmsg(ByVal address As IntegerAs Byte()
        
Dim buf(19As Byte
        ReadProcessMemory(hProc, address, buf, 
200)
        
Return buf
    
End Function

    
Protected Overrides Sub Finalize()
        CloseHandle(hProc)
        
MyBase.Finalize()
    
End Sub
End Class

这个类提供了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来说,还有一些数据获取的问题没有说清楚,实际上都可以用一些方法来解决。

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

 


   Protected Overrides Sub DefWndProc(ByRef m As System.Windows.Forms.Message)
        
Select Case m.Msg
            
Case WM_COPYDATA
                
Dim mMessage As New COPYDATASTRUCT()
                mMessage 
= DirectCast(m.GetLParam(mMessage.[GetType]()), COPYDATASTRUCT)

               
Dim bs(mMessage.cbData - 1As Byte
               Marshal.Copy(mMessage.lpData, bs, 
0, mMessage.cbData)

               debig.print System.BitConverter.ToString(bs, 
0, mMessage.cbData)

        
End Select

    
End Sub

热推产品

  • ActiveReport... 强大的.NET报表设计、浏览、打印、转换控件,可以同时用于WindowsForms谀坔攀戀Forms平台下......
  • AnyChart AnyChart使你可以创建出绚丽的交互式的Flash和HTML5的图表和仪表控件。可以用于仪表盘的创......
首页 | 新闻中心 | 产品中心 | 技术文档 | 友情连接 | 关于磐岩 | 技术支持中心 | 联系我们 | 帮助中心 Copyright-2006 ComponentCN.com all rights reserved.重庆磐岩科技有限公司(控件中国网) 版权所有 电话:023 - 67870900 传真:023 - 67870270 产品咨询:sales@componentcn.com 渝ICP备12000264号 法律顾问:元炳律师事务所 重庆市江北区塔坪36号维丰创意绿苑A座28-5 邮编:400020
在线客服
在线客服系统
在线客服
在线客服系统