我在VBA / VB6中使用Windows函数ReadProcessMemory,但我不明白为什么当我lpBuffer
将ByVal的传递机制更改为ByVal时,该函数仍会修改通过此参数传递的原始对象的值。在文档中,此参数指定为应通过引用传递的输出。不应该将传递机制更改为按值,以防止修改原始实例吗?为什么不呢?
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
首先,ByVal .. As Any
因为_Out_
争论不是一个好主意(我什至不确定是否可行);如果您将其ByVal
用于此类用途As Long
(请参阅下文中的“为什么”)。
因此,对于具有一个或多个API_Out_
参数旨在代表一个缓冲/变量/内存位置,有两种方式(每个关注参数反正)写的声明,这取决于什么要传递:
ByRef lpBuffer As Any
,或简单地说lpBuffer As Any
:_Out_
如果您打算在调用API时将实际变量传递到应将数据复制到的地方,则可以在声明中使用此参数。例如,您可以使用Byte数组,如下所示:Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, _
ByVal lpBaseAddress As Long, lpBuffer As Any, ByVal nSize As Long, _
lpNumberOfBytesWritten As Long) As Long
'[..]
Dim bytBuffer(255) As Byte, lWrittenBytes As Long, lReturn As Long
lReturn = ReadProcessMemory(hTargetProcess, &H400000&, bytBuffer(0), 256, lWrittenBytes)
请注意,无论传递的变量的实际大小如何,被调用方(此处为ReadProcessMemory()
)都会填充您提供的任何lpBuffer
数据。这就是必须通过提供缓冲区大小的原因nSize
,因为否则被调用者无法知道所提供缓冲区的大小。还要注意,我们正在传递(字节)数组的第一项,因为这是被调用方应该开始向其写入数据的位置。
使用相同的声明,如果您愿意,甚至可以传递很长的时间(例如,如果要检索的是地址或某种DWord值),但是nSize
必须最多为4个字节。
另请注意,最后一个参数lpNumberOfBytesWritten
也是一个_Out_
参数,并通过ByRef传递,但是您无需提供被调用方的大小;这是因为在调用方和被调用方之间存在一个协议,即无论传递什么变量,都将始终始终精确地写入4个字节。
ByVal lpBuffer As Long
:_Out_
如果在调用API时打算以32位值(即指针)的形式传递内存位置,则可以在声明的声明中使用此参数;Long
被传递的值不会改变,将被那个值所引用的内存位置覆盖Long
。重用相同的示例,但声明稍有不同,我们得到:Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, _
ByVal lpBaseAddress As Long, ByVal lpBuffer As Long, ByVal nSize As Long, _
lpNumberOfBytesWritten As Long) As Long
'[..]
Dim bytBuffer(255) As Byte, lPointer As Long, lWrittenBytes As Long, lReturn As Long
lPointer = VarPtr(bytBuffer(0))
lReturn = ReadProcessMemory(hTargetProcess, &H400000&, lPointer, 256, lWrittenBytes)
' If we want to make sure the value of lPointer didn't change:
Debug.Assert (lPointer = VarPtr(bytBuffer(0)))
看,这实际上又是同一回事,唯一的区别是我们提供了一个指针(内存地址)bytBuffer
而不是bytBuffer
直接传递。我们甚至可以提供VarPtr()
直接返回的值,而不是使用Long
(此处的lPointer
):
lReturn = ReadProcessMemory(hTargetProcess, &H400000&, VarPtr(bytBuffer(0)), 256, _
lWrittenBytes)
警告#1:对于_Out_
参数,如果您声明它们ByVal
,则应始终为 As Long
。这是因为调用约定期望该值正好由4个字节组成(32位值/ DWORD)。Integer
例如,如果您通过一个类型传递值,您将得到意外的行为,因为将用作该存储位置值的是该位置的2个字节Integer
加上紧随其后的2个字节。Integer
内存中的变量,可以是任何变量。如果这恰好是被调用方要写入的内存位置,则可能会崩溃。
警告2:您不想使用VarPtrArray()
(无论如何都需要明确声明),因为返回的值将是数组的SAFEARRAY结构的地址(项目数,项目大小等),而不是指向数组数据的指针(与数组中第一项的地址相同)。
本质上,对于Win32 API(即stdcall),参数始终始终作为32位值传递。这些32位值的含义将取决于特定API的期望,因此其声明必须反映这一点。所以:
ByRef
,将使用正在传递的任何变量的内存位置;ByVal .. As Long
,将使用传递的任何变量的(32位)值(该值不一定是内存位置,例如的hProcess
参数ReadProcessMemory()
)。最后,即使你声明的_Out_
说法ByRef
(或者,例如,这是一个API声明的方式,因为如果来自一个类型库,你不能改变它),你可以随时通过增加传递一个指针,而不是实际的变量ByVal
之前,当打电话。回到ReadProcessMemory()
(当lpBuffer
被声明时ByRef
)的第一个声明,我们将执行以下操作:
Private Declare Function ReadProcessMemory Lib "kernel32" (ByVal hProcess As Long, _
ByVal lpBaseAddress As Long, lpBuffer As Any, ByVal nSize As Long, _
lpNumberOfBytesWritten As Long) As Long
'[..]
Dim bytBuffer(255) As Byte, lWrittenBytes As Long, lReturn As Long
lReturn = ReadProcessMemory(hTargetProcess, &H400000&, ByVal VarPtr(bytBuffer(0)), 256, _
lWrittenBytes)
加法ByVal
告诉编译器应该在栈上传递的不是地址,VarPtr()
而是由返回的值VarPtr(bytBuffer(0))
。但是,如果声明了该参数,ByVal .. As Long
则您别无选择,只能传递一个指针(即存储位置的地址)。
注意:在整个架构中所假设的答案是IA32或它的仿真
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句