我们都知道在VB里面可以用API函数来进行子类化以处理自身的窗体过程如果跨进程这就麻烦了由于我们的函数在我们的进程中(废话)而目标进程的窗口的消息处理函数在目标进程(还是废话)所以只能想办法把我们的代码放到对方进程中去执行——并且要告知我们的进程得到了什么消息恐怕写汇编就有点吓人了于是大家都写DLL其原理就是把回调函数放到一个DLL里面注入到对方进程DLL去修改目标窗口的默认处理函数——把消息发送给我们
当然也有另类一点的/ThueDownloads/indexshtml上面有一个DLL包其中含有一个dssubclsdll用它可以轻松的完成我们的工作就像调用一个API一样简单而且在我们的程序中使用回调函数!呵呵省去了自己写DLL的麻烦之后这些好处足以吸引各位观众了吧?
好了VB的代码大家可以在下载的压缩包中找到作者提供了一个以记事本为基础的实例(在\dssubcls目录下)非常详细无需详细叙述了关键是在VBNET里面如何使用它——如何声明API如何进行回调看用来子类化的API的VB声明先
Declare Function SubClass& Lib dssubcls (ByVal HwndSubclass& _
Optional ByVal Address& = _
Optional ByVal OldStyle& = _
Optional ByVal NewStyle& = _
Optional ByVal Ext& = _
Optional ByVal SubClass& = )
转化成VBNET的声明类似下面的样子(习惯使然我把&展开成了As Integer)
Declare Function SubClass Lib dssubcls (ByVal HwndSubclass As Integer Optional ByVal Address As Integer = Optional ByVal OldStyle As Integer = Optional ByVal NewStyle As Integer = Optional ByVal Ext As Integer = Optional ByVal SubClass As Integer = ) As Integer
这不是很好嘛?问题来了这样的声明在VB里面可以使用Addressof function来传入第二个参数(参见你下载的源码)但是在VBNET里面直接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的指针相当于VB里面的Addressof function得到的结果看似问题解决了于是我们写了以下代码来搞对方的进程窗体消息
SubClass(Handle fix_COCD ) 修改处理函数
问题真是接踵而至!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 = Optional ByVal NewStyle As Integer = Optional ByVal Ext As Integer = Optional ByVal SubClass As Integer = ) As Integet
使之符合我们的调用?有点倒行逆施?并非如此当你习惯了修改API声明之后会发现有些事变得如此简单有些事需要你重新认识——对于WIN API也是如此
至此大功告成
较为完整的代码如下
Code
Private Declare Function SubClass Lib dssubcls (ByVal HwndSubclass As Integer Optional ByVal Address As HookCallBack = Nothing Optional ByVal OldStyle As Integer = Optional ByVal NewStyle As Integer = Optional ByVal Ext As Integer = Optional ByVal SubClass As Integer = ) As Integer
Private Declare Function UseSendMessage Lib dssubcls (ByVal use As Integer) As Integer
实例化的委托
Private fix_COCD = New HookCallBack(AddressOf mCallback)
委托
Private Delegate Function HookCallBack(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As Integer
Public Sub Hook(ByVal Handle As Integer)
proc = SubClass(Handle fix_COCD ) 修改处理函数
UseSendMessage()
End Sub
Private Function mCallback(ByVal wMsg As Integer ByVal wParam As Integer ByVal lParam As Integer) As Integer
End Function
用这个代码的时候可能会碰见一些意外情况例如wm_datacopy此时我们需要进一步去获取LPARTM所指向的结构并对其进行解析(我们要读的是对方窗口所在进程的内存具体地址由lParam确定——实际上lParam一直是一个指针——IntPrt但它与Integer完全就是一回事(如果你使用VB可能需要使用Intprttoint或intprt=new intprt(integer)这些)
Code
Public Class GetMsg
Public Declare Function ReadProcessMemory Lib kernel (ByVal hProcess As Integer ByVal lpBaseAddress As Integer ByVal lpBuffer() As Byte ByVal nSize As Integer ByRef lpNumberOfBytesWritten As Integer) As Integer
Public Declare Function ReadProcessMemory Lib kernel (ByVal hProcess As Integer ByVal lpBaseAddress As Integer ByRef int As Integer ByVal nSize As Integer ByRef lpNumberOfBytesWritten As Integer) As Integer
Public Declare Function OpenProcess Lib kernel (ByVal dwDesiredAccess As Integer ByVal bInheritHandle As Integer ByVal dwProcessId As Integer) As Integer
Public Declare Function CloseHandle Lib kernel (ByVal hObject As Integer) As Integer
Private hProc As IntPtr
Sub New(ByVal PID As Integer)
hProc = OpenProcess(&HFFFF False PID)
End Sub
Function readmsg(ByVal address As Integer) As Byte()
Dim buf() As Byte
ReadProcessMemory(hProc address buf )
Return buf
End Function
Protected Overrides Sub Finalize()
CloseHandle(hProc)
MyBaseFinalize()
End Sub
End Class
这个类提供了Readmsg方法来读取一些内容——但这并不是完整的我们知道LPARAM指向的结构是这样的
_
Public Structure COPYDATASTRUCT
Public dwData As Integer
Public cbData As Integer
Public lpData As IntPtr
End Structure
其中dwData我们不是很关心当然其中也可能存在一些有用信息(这里不想多说网上有些文章纯属误导)
而cbData是一个长度lpData的长度
lpData这里被声明为指针看起来更直观了——它就是地址
有了地址和长度如何读取代码就自己写吧
提示一下参考我重载的ReadProcessMemory可能对你有不少帮助
当然上面提到的只是特殊情况中的一个典型还有很多时候进程是用自定义消息(>&HA)来传递数据的例如我所开发的这个工程打印mCallBack的参数后得到的是如下结果(十六进制只提取了有用的信息)
D
其中lParam就是一个指针我读了其中的一部分
Function readmsg(ByVal address As Integer) As Byte()
Dim buf() As Byte
ReadProcessMemory(hProc address buf )
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 = &H Then
Dim s As Byte() = msgreadmsg(lParam)
RaiseEvent Move(s() s() s() s() s() s() s())
End If
End Function
当然在我的工程里面重载的ReadProcessMemory并没有被使用
补充一下咯
在VBNET中处理自己的窗体的消息只需要重载窗体消息处理过程就可以了无需子类化)
有补充一下
对于wm_datacopy来说还有一些数据获取的问题没有说清楚实际上都可以用一些方法来解决