切换到 64 位 Excel 后如何修复 VBA“类型不匹配”错误

Posted

技术标签:

【中文标题】切换到 64 位 Excel 后如何修复 VBA“类型不匹配”错误【英文标题】:How to fix a VBA "type mismatch" error after switching to 64-bit Excel 【发布时间】:2020-12-27 01:21:11 【问题描述】:

我使用的代码在运行 32 位版本的 Excel 时运行良好。在我切换到 64 位版本后,宏坏了。我更新了 dll 调用以在任何地方使用 LongPtr 而不是 Long。 对于特定的Declare Function,有什么方法可以确定 VBA7 需要更改哪些参数和返回类型,哪些不需要更改?

这是我更新的一些“声明函数”的示例(实际上还有几个)。

#If VBA7 Then
    Private Declare PtrSafe Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As LongPtr) As LongPtr
    Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As LongPtr, ByVal nWidth As LongPtr, ByVal nHeight As LongPtr) As LongPtr
    Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As LongPtr) As LongPtr
    Private Const LOGPIXELSY As Long = 90
#Else
    Private Declare CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, lpInitData As Long) As Long
    Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
    Private Const LOGPIXELSY As Long = 90
#End If

这段代码改编自这个问题的答案: vb macro string width

相关的sn-p复制如下:

Private Function GetLabelSize(text As String, font As StdFont) As SIZE
    Dim tempDC As Long
    Dim tempBMP As Long
    Dim f As Long
    Dim lf As LOGFONT
    Dim textSize As SIZE

    tempDC = CreateDC("DISPLAY", vbNullString, vbNullString, ByVal 0)
    tempBMP = CreateCompatibleBitmap(tempDC, 1, 1)

我收到一个运行时错误,它只显示“编译错误:类型不匹配”。对CreateDC 的函数调用被突出显示,并且调试器在函数GetLabelSize 上中断。我不知道哪个变量现在导致错误。我还假设一旦我修复了第一个错误,我也会遇到其他错误。

我是否需要将最后一个参数 (ByVal 0) 的值作为显式类型变量传递?如果有怎么办?

【问题讨论】:

(1) 一个确定的问题是您必须通过条件编译器常量将tempDCtempBMP 变量声明为LongPtrLong(即通过#If VBA7 Then 等) ...) (2) Afaik lpInitData 现在被声明为 As DEVMODE 指的是更长的 Type 定义而不是 As LongPtr;因此不知道ByVal 0 是否足够。 进一步的不兼容性(VBA7):a)nWidth/nHeightDeclare PtrSafe Function CreateCompatibleBitmap Lib "gdi32" Alias "CreateCompatibleBitmap" (ByVal hdc As LongPtr, ByVal nWidth As Long, ByVal nHeight As Long) As LongPtr 中保持Long 和b)Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As LongPtr) As Long 只期望句柄为LongPtr :-)跨度> 感谢@T.M,这确实解决了我的第一个错误。我编辑了我的问题。根据我在此阅读的内容,似乎所有这些“声明函数”都需要 LongPtr 而不是 Long,因为我还没有看到任何人这么说...... . 有没有办法可以找到哪些其他的仍然需要类型 *Long 作为参数?我只是尝试单独搜索它们,但我没有从 MS Docs 中获得任何有用的结果。 我也对 LongPtr 的实际作用感到有些困惑。根据 MS 的说法:“LongPtr 不是真正的数据类型,因为它在 32 位环境中转换为 Long,或在 64 位环境中转换为 LongLong。使用 LongPtr 可以编写可在 32 位和 64 位环境中运行的可移植代码 -位环境。将 LongPtr 用于指针和句柄。”这是否意味着不需要检查“#If VBA7”?在我看来,我应该能够将 LongPtr 用于 32 位和 64 位版本。 这是否意味着不需要检查“#If VBA7”? - 不,不需要。 VBA6 没有LongPtr 开头。你必须在那里使用Long,这不是问题,因为 VBA6 只是 32 位的。 有什么方法可以让我找到哪些其他人仍然需要输入Long 作为参数 - 是的,有(***.com/questions/46428459/…,***.com/a/50726918/11683)。 【参考方案1】:

我更新了 dll 调用以在任何地方使用 LongPtr 而不是 Long

你不应该那样做的。

通过将PtrSafe 添加到函数声明中,您promise to the compiler 已将LongPtr 放在所有需要的地方,而不是其他地方。

LongPtr 是一个指针大小的整数。它必须用于与指针大小相同的东西。

要了解应将哪些 Windows API 类型描述为 LongPtr,您必须查看原始函数签名,查阅 https://docs.microsoft.com/en-us/windows/win32/winprog/windows-data-types,通过所有 typedefs 跟踪使用的数据类型直至基本类型,以及对于指向事物的指针,请使用LongPtr

对于您展示的功能,那将是

#If VBA7 Then
    Private Declare PtrSafe Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As LongPtr) As LongPtr
    Private Declare PtrSafe Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As LongPtr, ByVal nWidth As Long, ByVal nHeight As Long) As LongPtr
    Private Declare PtrSafe Function DeleteDC Lib "gdi32.dll" (ByVal hdc As LongPtr) As Long
#Else
    Private Declare Function CreateDC Lib "gdi32.dll" Alias "CreateDCA" (ByVal lpDriverName As String, ByVal lpDeviceName As String, ByVal lpOutput As String, ByVal lpInitData As Long) As Long
    Private Declare Function CreateCompatibleBitmap Lib "gdi32.dll" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
    Private Declare Function DeleteDC Lib "gdi32.dll" (ByVal hdc As Long) As Long
#End If

当您声明变量来保存您的LongPtr 结果时,您也需要使用#If VBA7

#If VBA7 Then
    Dim tempDC As LongPtr
    Dim tempBMP As LongPtr
#Else
    Dim tempDC As Long
    Dim tempBMP As Long
#End If

如果你不必支持Office 2007 and older,你可以放弃#If VBA7s,只使用LongPtr分支。

【讨论】:

无论如何都有帮助!与我通过简单的 cmets 相比,以一种易于理解和更好的方式来消除一些常见的误解 (M) 是你的功劳。 - M1 “所有这些‘声明函数’都需要 LongPtr”M2 Any Long 参数现在将变为 LongPtr*。 M3 我不需要区分参数。 M4 有点混淆需要 LongPtr 或 #If VBA7 Then 等。(不是说实际安装的 Office 64 位与 64 位系统)。跨度> 也很有帮助Declaring API functions in 64 bit Office@GSerg

以上是关于切换到 64 位 Excel 后如何修复 VBA“类型不匹配”错误的主要内容,如果未能解决你的问题,请参考以下文章

excel VBA(32位)在win7 64位上运行错误

EXCEL如何让VBA程序自动切换下一个工作表

Excel 的 Application.Hwnd 属性是不是可用于 64 位 VBA?

Excel VBA 一段宏在64位系统中运行会报错,32位系统运行完美,求解决方案?

Excel VBA 到 PPT 在 Office 365 64 位中不起作用

64 位 Excel VBA 调用 JavaScript 函数