'FindWindow(ClassName, WindowTitle) - FindWindow 返回符合指定的类 '名( ClassName )和窗口名( WindowTitle )的窗口句柄。对我们来说,可以让 'ClassName 为空( Null ),只给出游戏的 WindowTitle。 Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal _ lpClassName As String, ByVal lpWindowName As String) As Long
'GetWindowThreadProcessId(WindowHandle, ProcessId) - 在这里我们把 'FindWindow 函数中得到的句柄作为参数,来获得进程标识符(ProcessId )。 Declare Function GetWindowThreadProcessId Lib "user32" _ (ByVal hwnd As Long, lpdwProcessId As Long) As Long
'OpenProcess(DesiredAccess, Inherit, ProcessId) - 这个函数将返回一 '个我们目标进程的句柄,可以用来对目标进行读写操作。 DesiredAccess 参 '数的值决定了句柄对进程的存取权利,对我们来说,要使用 'PROCESS_ALL_ACCESS (完全存取权限)。Inherit 应该总是 False。 'ProcessId 是从 GetWindowThreadProcessId 函数中取得的。 Declare Function OpenProcess Lib "kernel32" (ByVal dwDesiredAccess As Long, _ ByVal bInheritHandle As Long, ByVal dwProcessId As Long) As Long
'CloseHandle(ProcessHandle) - 每一个打开的句柄必须呼叫这个函数来关闭? Declare Function CloseHandle Lib "kernel32" (ByVal hObject As Long) As Long
'WriteProcessMemory(ProcessHandle, Address, value, Sizeofvalue, 'BytesWritten) - 把指定的值 value 写入由 Address 指定的目标地址。 Declare Function WriteProcessMemory Lib "kernel32" (ByVal hProcess _ As Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal _ nSize As Long, lpNumberOfBytesWritten As Long) As Long
'ReadProcessMemory(ProcessHandle, Address, value, Sizeofvalue, 'BytesWritten) - 把 Address 指定的目标地址的值存入 value 位置的变量中 Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As _ Long, ByVal lpBaseAddress As Any, ByVal lpBuffer As Any, ByVal nSize _ As Long, lpNumberOfBytesWritten As Long) As Long
Private Sub Command1_Click()
Dim hwnd As Long ' 储存 FindWindow 函数返回的句柄 Dim pid As Long ' 储存进程标识符( Process Id ) Dim pHandle As Long ' 储存进程句柄 ' 首先取得目标窗口的句柄 hwnd = FindWindow(vbNullString, "计算器") If (hwnd = 0) Then MsgBox "未启动" Exit Sub End If ' 取得进程标识符 GetWindowThreadProcessId hwnd, pid ' 使用进程标识符取得进程句柄 pHandle = OpenProcess(PROCESS_ALL_ACCESS, False, pid) If (pHandle = 0) Then MsgBox "得不到窗口进程信息" Exit Sub End If ' 在内存地址中写入数据 WriteProcessMemory pHandle, &H4603F0C, "3", 1, 0& ' 关闭进程句柄 CloseHandle hProcess
End Sub If (pHandle = 0) Then MsgBox "得不到窗口进程信息" Exit Sub End If 问题在这里,找不到窗口进程
'标准的做法SwapStr Sub SwapStr(sA As String, sB As String) Dim sTmp As String sTmp = sA: sA = sB: sB = sTmp End Sub
【程序二】:
'用指针的做法SwapPtr Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" _ (Destination As Any, Source As Any, ByVal Length As Long)
Sub SwapPtr(sA As String, sB As String) Dim lTmp As Long CopyMemory lTmp, ByVal VarPtr(sA), 4 CopyMemory ByVal VarPtr(sA), ByVal VarPtr(sB), 4 CopyMemory ByVal VarPtr(sB), lTmp, 4 End Sub
[entry("VarPtr"), hidden] long _stdcall VarPtr([in] void* Ptr); [entry("VarPtr"), hidden] long _stdcall StrPtr([in] BSTR Ptr); [entry("VarPtr"), hidden] long _stdcall ObjPtr([in] IUnknown* Ptr);
即然它们是VB运行时库中的同一个函数,我们也可以在VB里用API方式重新声明这几个函数,如下:
Private Declare Function ObjPtr Lib "MSVBVM60" Alias "VarPtr" (var As Object) As Long Private Declare Function VarPtr Lib "MSVBVM60" (var As Any) As Long
'看看我们的东西被拷贝到哪儿去了 Sub TestCopyMemory() Dim i As Long, k As Long k = 5 i = VarPtr(k) NOTE4: CopyMemory i, 40000, 4 Debug.Print k Debug.Print i i = VarPtr(k) NOTE5: CopyMemory ByVal i, 40000, 4 Debug.Print k End Sub
Public Function Compare(elem1 As Long, elem2 As Long) As Long ' End Function
Function FnPtrToLong(ByVal lngFnPtr As Long) As Long FnPtrToLong = lngFnPtr End Function
Sub PtrDemo() Dim l As Long, c As Byte, ca() As Byte, Pt As POINT Dim pl As Long, pc As Long, pv As Long, pPt As Long, pfnCompare As Long c = AscB("X") pl = VarPtr(l) '对应C里的long、int型指针 pc = VarPtr(c) '对应char、short型指针 pPt = VarPtr(Pt) '结构指针 pv = VarPtr(ca(0)) '字节数组指针,可对应任何类型,也就是void* pfnCompare = FnPtrToLong(AddressOf Compare) '函数指针 CopyMemory c, ByVal pc, LenB(c) '用指针取值 CopyMemory ByVal pc, AscB("Y"), LenB(c) '用指针赋值 pc = pc + LenB(c) : pl = pl - LenB(l) '指针移动 End Sub
'使用更安全的CopyMemory,明确的使用指针! Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, ByVal Length As Long) Sub SwapStrPtr2(sA As String, sB As String) Dim lTmp As Long Dim pTmp As Long, psA As Long, psB As Long pTmp = VarPtr(lTmp): psA = VarPtr(sA): psB = VarPtr(sB) CopyMemory pTmp, psA, 4 CopyMemory psA, psB, 4 CopyMemory psB, pTmp, 4 End Sub
'有点象【程序四】,但将常量40000换成了值为1的变量. Private Declare Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" (ByVal Destination As Long, ByVal Source As Long, Length As Long) Sub TestCopyMemory() Dim i As Long,k As Long, z As Interger k = 5 : z = 1 i = VarPtr(k) '下面的语句会引起类型不符的编译错误,这是好事! 'CopyMemory i, z, 4 '应该用下面的 CopyMemory i, ByVal VarPtr(z), 2 Debug.Print k End Sub
VB开发小组提供Any,就是想用ByRef xxx As Any来表达void* xxx。我们也完全可以使用VarPtr和Long型的指针来处理。我想,VB开发小组也曾犹豫过是公布VarPtr,还是提供Any,最后他们决定还是提供Any,而继续隐瞒VarPtr。的确,这是个两难的决定。但是经过我上面的分析,我们应该知道,这个决定并不符合VB所追求的"更安全"的初衷。因为它可能会隐藏类型不符的错误,调试和找到这种运行时才产生的错误将花贵更多的时间和精力。
'定义PROCESS_ALL_ACCESS: Const STANDARD_RIGHTS_REQUIRED = &HF0000 Const SYNCHRONIZE = &H100000 Public Const PROCESS_ALL_ACCESS As Long = STANDARD_RIGHTS_REQUIRED Or SYNCHRONIZE Or &HFFF