【问题标题】:Does the bitness of the OS ever matter, or is it just the application I need to worry about?操作系统的位数是否重要,或者只是我需要担心的应用程序?
【发布时间】:2019-11-18 02:55:29
【问题描述】:

一些假设:(如果错了,请纠正我)

忽略 16 位内容,VBA 可以在 32 位或 64 位 Office 主机上运行。 64 位 Office 只能在 64 位操作系统上运行,而您可以在 32 64 位版本的 Windows/macOS/其他操作系统上运行 32 位 Office。

从 VBA7 开始,我们有 LongPtr 类型,它在 32 位 Office (#Win64 = False) 上变为 Long,在 64 位 Office (#Win64 = True) 上变为 LongLong不管操作系统位数

我的问题:在处理指针(内存中的地址)的 API 中,操作系统位数是否重要,或者仅仅是运行我们关心的代码的应用程序(32 /64 位 Office 主机/VBA)?


目前的理解:

一方面,我明白为什么它可能无关紧要:

  • 在 32 位 Excel 中运行的 VBA 将具有 32 位地址空间(与操作系统无关)
  • 因此,任何指向其自身内存的指针都应该是 32 位的(即 Longs - LongPtr 给了我们这个)
  • 类似的 64 位 VBA(必须在 64 位操作系统中,但实际上可以在任何地方)有一个 64 位地址空间(每个指针本身都是 64 位长,并且指的是一个 64 位宽的内存块)
  • 因此,任何指向其自身内存的指针都应该是 64 位的(即 LongLongs - LongPtr 给了我们这个)
  • 所以LongPtr 准确地表示了一个指向我的代码自己的内存的指针,而不管操作系统位数如何

但是我可以想象操作系统位数可能很重要的时候

  • 在 64 位操作系统上运行 32 位 VBA,但想要引用不同 64 位应用程序的内存区域,LongPtr 类型将评估为 Long,并且太短而无法表示最大值该操作系统中可用的指针大小。
  • 类似地,访问 32 位应用程序的内存的 64 位 VBA 会发现其指针类型太大而无法容纳地址
  • 现在备受信任的LongPtr 类型实际上是错误的长度来表示指向VBA 自身地址空间之外的内存地址的指针!

问题现在可能取决于操作系统位数,而不是 Office/VBA 位数。如果我们在 32 位操作系统上运行 VBA7,那么LongPtr 将是您想要投入的任何内存块的正确长度;在 99.9% 的情况下它是合适的指针数据类型,因为操作系统所做的一切都是 32 位(忽略 16 位)。

但是,在尝试使用 LongPtr 保存 32 位地址时,在 64 位操作系统上运行相同的 32 位 VBA7 代码会遇到困难。我觉得在 64 位操作系统上混合 32 位和 64 位应用程序是很常见的,所以这可能是一个真正的问题。

在 32 位和 64 位操作系统上运行的 32 位 VBA6 和更早的应用程序可能会面临类似的问题,除非 LongPtr 的帮助


现在我明白这是一个非常人为的情况,谁会想要访问另一个应用程序的内存,对吧?事实上,它太做作了,我不确定我是否能想出一个重要且值得担心的情况。一个应用程序能否获得另一个具有不同位数的应用程序的内存地址?也许有一些读写保护可以防止这种情况发生。

也许访问另一个应用程序的窗口句柄就是这样一种情况;那是公共内存,也许窗口句柄的位数反映了应用程序或操作系统的位数,在这种情况下,我的 32 位 VBA 想要持有对 64 位 Hwnd 的引用是可行的,我不确定...


PS 我知道除了指针长度之外,操作系统位数可能很重要。我知道一个; SetWindowLong 函数在 64 位和 32 位 Windows 上需要不同的声明——尽管 IIUC 现在已经用 SetWindowLongPtr 函数解决了,这两者都是相同的。但是任何其他类似的怪癖都会很有用,我在这里只关注指针长度,因为我有一个需要特定信息的问题。

PPS 想一想,你甚至可以在编译时获得操作系统位数吗?我想你可以从MAC_OFFICE_VERSION 推断出来,而ofc Win64 = True 表示64 位Office 和事实上的64 位操作系统。但我不确定是否有办法判断 32 位 VBA 是否在 64 位 Windows 上运行...

【问题讨论】:

  • 当然可以。正如您所指出的,操作系统的位数会影响 LongPtr 的字节数。这可能只是真正影响那些在 64 位系统上拥有 32 位办公室的人,并且 API 使用 8 个字节作为参数。我想,只要 API 规范知道 LongPtr 可以有不同的值,就使用正确的类型?
  • 32 位 VBA 要保留对 64 位 Hwnd 的引用 至少对于 Hwnd 来说这并不重要,因为即使是 64 位操作系统也可以使用 32 -Bit Handles:docs.microsoft.com/en-us/windows/win32/winprog64/… 该链接还包含一些有关您将如何处理此类事情的信息。
  • 还有:根据stackoverflow.com/a/1822738/10223558 Hwnd 是一个索引而不是一个指针,所以操作系统的位数不应该真的有影响。
  • @L8n 64 位返回值如何适合 32 位?
  • @RyanWildry 是的,如果 API 需要 8 字节(64 位)的数据,那么当 LongPtr 的数据不长时,32 位 Office 上的用户可能会被绊倒。想法。但首先,这并不是我真正关心的问题。假设 API 函数为QueryPerformanceFrequency。我会争辩说,在您的示例中,用户使用了错误的数据类型 - API 要求提供 LARGE_INTEGER,而不是指针,因此虽然 LongPtr 恰好在 64 位中为它们工作,但在 32 位中没有有点办公室,无论哪种方式都是错误的选择。

标签: vba pointers operating-system 32bit-64bit conditional-compilation


【解决方案1】:

LongPtr 始终是正确的您的流程。您无需担心它的大小。您不需要 WIN64 常量来使用它。事实上,您通常需要的唯一常量是VBA7,它告诉您LongPtr 是否可用。如果是,请使用它,如果不是,您肯定是 x86 所以使用Long

此外,Windows x64 有一个完整的兼容层,称为WoW64。作为在 64 位 Windows 上运行的 32 位应用程序,您不会注意到任何事情,并且您运行时就像操作系统是 32 位一样。您的指针大小是四个字节,您的指针大小的数据类型(例如HWNDs)是四个字节,所以再一次,如果您只咨询VBA7 并正确地将LongPtr 放入指针大小的参数必须出现的所有地方。

因此,对于进程中的日常事务以及与操作系统及其对象的交互,您无需担心您自己或操作系统的位数,而且您不必担心也不需要WIN64 常量。


现在,您特别提到获取和使用对位数与您自己不同的进程有效的指针。是的,这可能是个问题,但这个问题并不特定于 VBA。

如果作为 VBA 应用程序,您发现自己需要读取任意位数的任意进程的内存,您确实需要将自己的位数与其位数进行比较。此时您可以使用WIN64 常量,但在这种情况下,在运行时检查Len(long_ptr_variable) 比使用单独的代码分支要方便得多。

测试完毕,

  • 如果您的位数较高,您可以不受限制地读取其他进程的内存。
  • 如果您的位数较小,您可以在您的 4 字节指针允许的范围内到达大人物的虚拟内存。
    • 如果您需要超越这一点,您将不得不失败 (or not)。

但请注意,即使在这种情况下,您也不关心操作系统位数或 WIN64 常量!您只关心您的进程位数与其他进程位数。

【讨论】:

    【解决方案2】:

    当然,操作系统的位数很重要。

    您可以查看this answer,了解我们需要在 64 位 Windows 上用于 32 位 office 的特定代码(正确注册 COM DLL 以在 32 位应用程序中使用)。

    我在那里使用的测试是:

    #If Win64 Then
        Const Win64 = True
    #Else
        Const Win64 = False
    #End If
    If Not Win64 And Environ$("ProgramW6432") <> vbNullString Then
        '32 bits Office on Win 64
    Else
        'Either 32-bits Windows, or 64-bits Office on 64-bits windows
    End If
    

    Afaik,您无法在编译时确定这一点,只能在运行时确定。

    使用外部 API/应用程序时,这通常很重要。我不会编制一份可能的场景列表,因为它永远不会完整,但有很多可能的情况。

    但是,对于内存访问,这无关紧要,因为访问另一个进程的非全局内存应该会引发段错误并使您的应用程序硬崩溃。而且,如果您使用的是全局变量,那也不是什么大问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-12-11
      • 2015-01-16
      • 1970-01-01
      • 2021-04-30
      • 2012-02-06
      • 2010-10-15
      • 2018-02-07
      • 1970-01-01
      相关资源
      最近更新 更多