用于ReadProcessMemory函数的ByRef与ByVal

斯塔夫

在VBA / VB6中使用Windows函数ReadProcessMemory,但我不明白为什么当我lpBufferByVal传递机制更改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

MSDN ReadProcessMemory

约翰威特

首先,ByVal .. As Any因为_Out_争论不是一个好主意(我什至不确定是否可行);如果您将其ByVal用于此类用途As Long(请参阅下文中的“为什么”)。

因此,对于具有一个或多个API_Out_参数旨在代表一个缓冲/变量/内存位置,有两种方式(每个关注参数反正)写的声明,这取决于什么要传递:

  1. 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个字节。

  1. 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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章