如何在Mac OS X上使用VBA读写内存?

15

在Windows环境下,声明函数RtlMoveMemory提供了一种从一个地址复制字节块到另一个地址的方法:

Private Declare PtrSafe Sub RtlMoveMemory Lib "kernel32" ( _
                              ByVal dest As LongPtr, _
                              ByVal src As LongPtr, _
                              ByVal size As LongPtr)

在 Mac OS X 上对应的是什么?


https://www.experts-exchange.com/questions/26694931/excel-vba-how-change-this-class-to-work-for-Mac-excel-2011.html 有一个被接受的答案。不知道你是否知道? - Luuklag
据我所见,他们编写了一个自定义的alloc。这在这里没有帮助,因为RtlMoveMemory可以访问所有进程内存。 - LS_ᴅᴇᴠ
你看过 https://stackoverflow.com/questions/22470863/calling-dylib-functions-in-office-for-mac-vba 和 https://blackcatsoftware.us/dynamic-libraries-dylib-for-office-using-xcode-5/ 吗?你可以尝试遵循类似的约定来调用 C 库中的 memmove 函数。 - kvr
2个回答

7
在Mac OS X上的等效操作是什么?
简短回答:
Private Declare PtrSafe Function CopyMemory Lib "libc.dylib" Alias "memmove" _
                                 ( _
                                    ByVal dest As LongPtr _
                                  , ByVal src As LongPtr _
                                  , ByVal size As LongLong _
                                  ) _
                        As LongPtr

长话短说:要看情况而定 ;)
下面是一个完整的例子,它使用条件编译*来允许在任何Mac/Windows/32位/64位计算机上运行。它还演示了声明和调用函数的两种不同方式(通过指针和变量)。
#If Mac Then
  #If Win64 Then
    Private Declare PtrSafe Function CopyMemory_byPtr Lib "libc.dylib" Alias "memmove" (ByVal dest As LongPtr, ByVal src As LongPtr, ByVal size As Long) As LongPtr
    Private Declare PtrSafe Function CopyMemory_byVar Lib "libc.dylib" Alias "memmove" (ByRef dest As Any, ByRef src As Any, ByVal size As Long) As LongPtr
  #Else
    Private Declare Function CopyMemory_byPtr Lib "libc.dylib" Alias "memmove" (ByVal dest As Long, ByVal src As Long, ByVal size As Long) As Long
    Private Declare Function CopyMemory_byVar Lib "libc.dylib" Alias "memmove" (ByRef dest As Any, ByRef src As Any, ByVal size As Long) As Long
  #End If
#ElseIf VBA7 Then
  #If Win64 Then
    Private Declare PtrSafe Sub CopyMemory_byPtr Lib "kernel32" Alias "RtlMoveMemory" (ByVal dest As LongPtr, ByVal src As LongPtr, ByVal size As LongLong)
    Private Declare PtrSafe Sub CopyMemory_byVar Lib "kernel32" Alias "RtlMoveMemory" (ByRef dest As Any, ByRef src As Any, ByVal size As LongLong)
  #Else
    Private Declare PtrSafe Sub CopyMemory_byPtr Lib "kernel32" Alias "RtlMoveMemory" (ByVal dest As LongPtr, ByVal src As LongPtr, ByVal size As Long)
    Private Declare PtrSafe Sub CopyMemory_byVar Lib "kernel32" Alias "RtlMoveMemory" (ByRef dest As Any, ByRef src As Any, ByVal size As Long)
  #End If
#Else
  Private Declare Sub CopyMemory_byPtr Lib "kernel32" Alias "RtlMoveMemory" (ByVal dest As Long, ByVal src As Long, ByVal size As Long)
  Private Declare Sub CopyMemory_byVar Lib "kernel32" Alias "RtlMoveMemory" (ByRef dest As Any, ByRef src As Any, ByVal size As Long)
#End If


Public Sub CopyMemoryTest()

  Dim abytDest(0 To 11) As Byte
  Dim abytSrc(0 To 11) As Byte
  Dim ¡ As Long

  For ¡ = LBound(abytSrc) To UBound(abytSrc)
    abytSrc(¡) = AscB("A") + ¡
  Next ¡

  MsgBox "Dest before copy = #" & ToString(abytDest) & "#"
  CopyMemory_byVar abytDest(0), abytSrc(0), 4
  MsgBox "Dest during copy = #" & ToString(abytDest) & "#"
  CopyMemory_byPtr VarPtr(abytDest(0)) + 4, VarPtr(abytSrc(0)) + 4, 4
  MsgBox "Dest during copy = #" & ToString(abytDest) & "#"
  CopyMemory_byPtr VarPtr(abytDest(8)), VarPtr(abytSrc(8)), 4
  MsgBox "Dest after copy = #" & ToString(abytDest) & "#"

End Sub

Public Function ToString(ByRef pabytBuffer() As Byte) As String
  Dim ¡ As Long
  For ¡ = LBound(pabytBuffer) To UBound(pabytBuffer)
    ToString = ToString & Chr$(pabytBuffer(¡))
  Next ¡
End Function

解释:

CopyMemory函数的Mac版本返回一个结果,而Win版本则没有。(该结果是目标指针,除非发生错误。请查看这里memmove参考文献。)但是,两个版本都可以以相同的方式使用,无需括号。

声明差异如下:

  • 64位 Mac/Win VBA7:

    • 使用PtrSafe关键字
    • 对于所有ByRef参数,使用Any类型
    • 对于ByVal句柄/指针参数/返回值,使用LongPtr类型
    • 适当使用LongLong类型用于其他返回值/参数
  • 32位 Win VBA7:

    • 使用PtrSafe关键字
    • 对于所有ByRef参数,使用Any类型
    • 对于ByVal句柄/指针参数/返回值,使用LongPtr类型
    • 适当使用Long不是 LongLong)类型用于其他返回值/参数
  • 32位 Mac/Win VBA6:

    • 没有PtrSafe关键字
    • 对于所有ByRef参数,使用Any类型
    • 对于ByVal句柄/指针参数/返回值,使用Long类型
    • 适当使用Long类型用于其他返回值/参数

注意事项:

  • 在Mac Excel 2016 64位上进行了测试。
  • 在Windows Excel 2007 32位上进行了测试。

    • 似乎Excel 2007存在与双字对齐相关的问题。在这个例子中:

      CopyMemory_byVar abytDest(0), abytSrc(0), 4 '-> ABCD CopyMemory_byVar abytDest(1), abytSrc(1), 8 '-> ABCDEFGHI

      CopyMemory()会跳过所有复制,直到达到双字对齐(在本例中有3个跳过),然后从第4个字节继续复制。


注意:如果您对我的变量命名约定感到好奇,它基于RVBA

*正确的方式。


非常清晰,但我认为这些有点过多了:您检查 #If VBA7 - 这确保了 LongPtr 被定义。然后就没有必要再检查 #If Win64 了。Private Declare PtrSafe Sub CopyMemory_byPtr Lib "kernel32" Alias "RtlMoveMemory" (ByVal dest As LongPtr, ByVal src As LongPtr, ByVal size As LongPtr) 可在32位和64位系统上运行。此外,在您的 Mac 声明中,您在64位上使用 Long - 这是正确的吗?对于 Windows,RtlMoveMemory 中的 size/lengthSIZE_T 类型,它是 ULONG_PTR 的别名 - 即 VBA 应该使用 LongPtr - 对于 Mac 的 memmove 是否也是如此? - Greedo

2
我在msdn.microsoft.com上查找了,它似乎只是从一个地址复制一定数量的字节到另一个地址,并处理重叠内存的特殊情况。
达尔文系统的等效函数是。
原始/更快的版本是,它不处理重叠的内存区域。
使用获取详细信息。

你如何从VBA访问memmove/memcpy?我假设OP正在编写一个VBA宏,用于Windows和Mac OS X,或者将脚本从Windows移植到Mac OS X。 - IInspectable
是的,RtlMoveMemorymemmove 的公共包装器。相当于 memcpy 的是 RtlCopyMemory。然而,问题在于为什么需要这样做。人们在 VBA 中使用 RtlMoveMemory 与 Windows API 进行交互。如果您正在与 Cocoa APIs 进行交互,应该调用 Cocoa 内存管理函数,而不是低级标准库函数。 - Cody Gray

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接