VBA:若要在 64 位系统上使用,则必须更新此项目中的代码。请检查并更新 Declare 语句,然后用 PtrSafe 属性标记它们

注:本文为 “ VBA 加载 / 运行报错”相关文章合辑。


问题现象

编译错误:
若要在 64 位系统上使用,则必须更新此项目中的代码。请检查并更新 Declare 语句,然后用 PtrSafe 属性标记它们
 
img

Compile error:
The code in this project must be update for use on 64-bit systems. Please review and update Declare statements and then mark them with the PtrSafe attribute.
 
VBA error

隐藏的模块中的编译错误:mod_openFile。
此错误通常会在代码与此应用程序的版本、平台或基础结构不兼容时发生。单击 “帮助” 以获取如何更正此错逞的信息。

出现场景

1、在 Office 32 位(Access、Excel……) 上开发的 VBA,迁移到 Office 64 位中,运行报错

2、在 MATLAB 中使用 notebook 时,编译报错

3、在 CAD 加载 VBA 时,编译错误

解决方案

在所有出现问题的声明函数 Declare ,加上 PtrSafe

CAD:在64位系统环境下,需要将声明函数中的 Declare 改为 Declare PtrSafe,同时将参数和返回值中的 Long 改成 LongLong,以确保指针安全。

示例

示例 1 Private

原为

Private Declare Function ...

改为

Private Declare PtrSafe Function ...

示例 2 Public

原为

Public Declare Function ...

改为

Public Declare PtrSafe Function ...

Private / Public

在 Visual Basic (VB) 编程语言中,Private Declare FunctionPublic Declare Function 用于声明外部函数,即那些定义在其他动态链接库(DLL)中的函数。

两种声明方式的主要区别在于它们的作用域:

  1. Private Declare Function

    • Private 关键字限制了函数的作用域,使得该函数只能在声明它的模块或窗体中被访问和使用。
    • 适用于那些只需要在局部范围内使用的外部函数。
  2. Public Declare Function

    • Public 关键字使得函数可以在项目的任何地方被访问和使用,包括不同的模块、类或窗体。
    • 适用于那些需要在多个地方被调用的外部函数。

这两种声明方式都使用 Declare 关键字来指示函数是在外部库中定义的,而 Function 关键字则表示该外部函数有返回值。如果外部函数没有返回值,应该使用 Sub 而不是 Function。这些声明允许 VB 程序调用 DLL 中的函数,实现更广泛的功能扩展。

注意事项:

  • PtrSafe 仅在 64 位系统中需要,32 位系统仍使用原有的 Declare 语法。
  • LongLong 是 64 位整数类型,仅适用于6 4 位系统。在32位系统中应继续使用 Long

Microsoft Visual Basic for Applications (VBA) 在 Office 中的相关情况

HHAAMM 发表于 2011-7-17 23:58

VBA 版本 6 及更早版本以 32 位平台为目标,其 Declare 语句调用的 Windows API 使用 32 位数据类型的指针和句柄,用 Long 数据类型(32 位 4 字节的数据类型)引用指针和句柄,而 64 位环境中指针和句柄为 8 字节 64 位数,无法包含在 32 位数据类型中。

注意:只有在 64 位版本的 Microsoft Office 中运行 VBA 代码时,才需要修改代码。在 64 位 Office 中运行旧 VBA 代码,将 64 位加载到 32 位数据类型中会截断 64 位数,进而导致内存溢出、出现意外结果以及应用程序故障。

二、解决 64 位运行问题的新增语言功能

为使 VBA 代码能同时在 32 位和 64 位环境中正确运行,新版 VBA 增加了几项语言功能,其中三个重要的新增功能如下:

(一)LongPtr 类型别名

VBA 现在包含一种可变类型别名:LongPtr。其实际解析的数据类型取决于 Office 版本:在 32 位版本的 Office 中 LongPtr 解析为 Long,在 64 位版本的 Office 中 LongPtr 解析为 LongLong。LongPtr 用于指针和句柄。

(二)LongLong 数据类型

LongLong 数据类型为有符号的 64 位整数,仅在 64 位版本的 Office 中可用,用于 64 位整数。必须使用转换函数将 LongLong(包括 64 位平台上的 LongPtr)显式赋予较小的整型,不允许将 LongLong 隐式转换为较小的整数。

(三)PtrSafe 关键字

PtrSafe 关键字声明 Declare 语句可以在 64 位版本的 Office 中安全运行。在 64 位版本的 Office 中运行时,所有 Declare 语句必须都包括 PtrSafe 关键字。不过,仅添加 PtrSafe 关键字只是表明 Declare 语句针对 64 位,语句中需存储 64 位(包括返回值和参数)的数据类型仍须修改以保存 64 位数。

带有 PtrSafe 关键字的 Declare 语句为建议的语法,可同时在 32 位和 64 位平台上的 VBA7 开发环境中正确工作

确保向后兼容性的构造

可使用如下构造:

#If Vba7 Then
Declare PtrSafe Sub...
#Else
Declare Sub...
#EndIf

Declare 语句示例对比

  • 未修改的旧 VBA Declare 语句示例
Declare Function GetActiveWindow Lib "user32"() As Long

在 64 位版本的 Office 中运行此未经修改的语句会导致错误,提示 Declare 语句未包括 PtrSafe 限定符。

  • 修改后的 VBA Declare 语句示例,含 PtrSafe 限定符但仍用 32 位返回值
Declare PtrSafe Function GetActiveWindow Lib "user32"() As Long

此示例虽包含 PtrSafe 限定符,但返回值(指向活动窗口的一个指针)返回 Long 数据类型,在 64 位 Office 上是错误的,会因指针应为 64 位而被截断,返回错误值。

  • 修改后的 VBA Declare 语句示例,含 PtrSafe 关键字且更新为正确 64 位 (LongPtr) 数据类型
Declare PtrSafe Function GetActiveWindow Lib "user32"() As LongPtr

64 位版本 Office 中运行代码的相关修改要求

对于要在 64 位版本的 Office 中运行的代码,需进行以下操作:

  1. 找到并修改所有现有 Declare 语句以使用 PtrSafe 限定符。
  2. 修改 Declare 语句内引用句柄或指针的数据类型,使用新的 64 位兼容的 LongPtr 类型别名。
  3. 使用新的 LongLong 数据类型保存 64 位整数类型。
  4. 更新含指针或句柄以及 64 位整数的用户定义类型 (UDT) 为 64 位数据类型。
  5. 验证所有变量赋值的正确性,防止类型不匹配错误。

三、编写可跨不同版本 Office 运行的代码

(一)编写可同时在 32 位和 64 位 Office 上运行的代码

若要编写可同时在 32 位和 64 位版本的 Office 上运行的代码,只需对所有指针和句柄值使用新的 LongPtr 类型别名来代替 Long 或 LongLong 即可。LongPtr 类型别名会依据运行的 Office 版本解析为正确的 Long 或 LongLong 数据类型。若需执行不同逻辑(如在大型 Microsoft Excel 项目中使用 64 位值),可使用 Win64 条件编译常量。

(二)编写可同时在 Microsoft Office 2010(32 位或 64 位)以及以前版本的 Office 上运行的代码

若要编写可同时在新版本和旧版本的 Office 中运行的代码,可使用新的 VBA7 与 Win64 条件编译器常量的组合。VBA 7 条件编译器常量用来判断代码是否在 VB 编辑器版本 7(Office 2010 附带的 VBA 版本)中运行,Win64 条件编译常量用于判断正在运行的 Office 版本(32 位还是 64 位),示例如下:

#if Vba7 then
' 代码正在新 VBA7 编辑器中运行
#if Win64 then
' 代码正在 64 位版本的 Microsoft Office 中运行
#else
' 代码正在 32 位版本的 Microsoft Office 中运行
#end if
#else
' 代码正在 VBA 版本 6 或更早版本中运行
#end if
#If Vba7 Then
Declare PtrSafe Sub...
#Else
Declare Sub...
#EndIf

VBA7 语言更新总结

下表总结了新增的 VBA 语言功能,并给出每个新增功能的解释:

VBA7 语言更新功能解释

表格

名称类型                      ~~~~~~~~~~~~~~~~~~~~                     描述
PtrSafe关键字声明 Declare 语句针对 64 位系统。在 64 位上是必需的。
LongPtr数据类型该类型别名映射为 32 位系统上的 Long,或 64 位系统上的 LongLong。
LongLong数据类型8 字节的数据类型,只在 64 位系统上可用。属于数字类型,范围是 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807 内的整数。
LongLong 只是 64 位平台上的有效声明类型。
此外,不能将 LongLong 隐式转换为较小的类型(例如,不能将 LongLong 赋予 Long),这样做的目的是防止不慎将指针截断。允许显式强制转换,所以在上例中,可以将 CLng 应用于 LongLong,并将结果赋予 Long。(只在 64 位平台上有效。)
^LongLong类型声明字符显式将文字值声明为 LongLong。
声明大于最大 Long 值的 LongLong 文字时是必需的(否则它将隐式转换为 double)。
CLngPtr类型转换函数将简单表达式转换为 LongPtr。
CLngLng类型转换函数将简单表达式转换为 LongLong 数据类型。(只在 64 位平台上有效。)
vbLongLongVarType 常量VarType 常量。
DefLngPtrDefType 语句将一系列变量的默认数据类型设置为 LongPtr。
DefLngLngDefType 语句将一系列变量的默认数据类型设置为 LongLong。

微软官方:本项目中的代码必须更新以供 64 位系统使用

2023/04/07

更新 Declare 语句

完成错误消息:
 
必须更新此项目中的代码才能在 64 位系统上使用。 请查看并更新 Declare 语句,然后使用 PtrSafe 属性标记它们。

在 64 位版本的 Microsoft Office 中运行时,所有 Declare 语句现在必须包含 PtrSafe 关键字。 PtrSafe 关键字指示 Declare 语句对于在 64 位版本的 Microsoft Office 中运行是安全的。

将 “PtrSafe” 关键字添加到 “Declare” 语句只表明了 “Declare” 明确地以 64 位为目标,必须修改该语句中的所有需要存储 64 位的数据类型(包括返回值和参数) 以通过将 LongLong 用于 64 位整数或将 LongPtr 用于指针和句柄来保留 64 位数量。

PtrSafe 关键字

2023/04/07

PtrSafe 关键字用于以下上下文:Declare 语句

建议的语法是,将 Declare 语句与 PtrSafe 关键字一起使用。 包含“PtrSafe”的 Declare 语句在 32 位和 64 位的平台上的 VBA7 开发环境中正常工作,只是要在需要存储 64 位数量的“Declare”语句(参数和返回值)中的所有数据类型被更新以将 LongLong 用于 64 位整数,或将 LongPtr 用于指针和句柄。

若要确保与 VBA 版本 6 及更低版本向后兼容,请使用以下构造:

VB

#If VBA7 Then 
Declare PtrSafe Sub... 
#Else 
Declare Sub... 
#EndIf

在 64 位版本的 Office 中运行时, Declare 语句必须包含 PtrSafe 关键字。 The PtrSafe keyword asserts that a Declare statement is safe to run in 64-bit development environments.

PtrSafe 关键字添加到 Declare 语句仅表示 Declare 语句显式面向 64 位。 语句中需要存储 64 位 (包括返回值和参数) 的所有数据类型仍必须修改为保存 64 位数量,方法是使用 LongLong for 64 位整型或 LongPtr 来保存指针和句柄。


篇外 :VB 中 Declare 用法

Posted on 2009-12-29 16:15 LucasLee

Declare 语句

用于在模块级别中声明对动态链接库 (DLL) 中外部过程的引用。

语法 1

[Public | Private] Declare Sub name Lib libname [Alias aliasname] [([arglist])]

语法 2

[Public | Private] Declare Function name Lib libname [Alias aliasname] [([arglist])] [As type]

Declare 语句的语法包含下面部分:

部分描述
Public可选的。
用于声明对所有模块中的所有其它过程都可以使用的过程。
Private可选的。
用于声明只能在包含该声明的模块中使用的过程。
Sub可选的(但 SubFunction 二者需选其一)。
表示该过程没有返回值。
Function可选的(但 SubFunction 二者需选其一)。
表示该过程会返回一个可用于表达式的值。
name必需的。
任何合法的过程名。注意动态链接库的入口处(entry points)区分大小写。
Lib必需的。
指明包含所声明过程的动态链接库或代码资源。所有声明都需要 Lib 子句。
libname必需的。包含所声明的过程动态链接库名或代码资源名。
Alias可选的。
表示将被调用的过程在动态链接库 (DLL) 中还有另外的名称。
当外部过程名与某个关键字重名时,就可以使用这个参数。
当动态链接库的过程与同一范围内的公用变量、常数或任何其它过程的名称相同时,也可以使用 Alias
如果该动态链接库过程中的某个字符不符合动态链接库的命名约定时,也可以使用 Alias
aliasname可选的。动态链接库或代码资源中的过程名。
如果首字符不是数字符号 (#),则 aliasname 是动态链接库中该过程的入口处的名称。
如果首字符是 (#),则随后的字符必须指定该过程的入口处的顺序号。
arglist可选的。代表调用该过程时需要传递的参数的变量表。
type可选的。Function 过程返回值的数据类型;可以是 Byte、布尔、Integer、Long、Currency、Single、Double、Decimal(目前尚不支持)、Date、String(只支持变长)或 Variant,用户定义类型,或对象类型。

arglist 参数的语法以及语法各个部分如下:

[Optional] [ByVal | ByRef] [ParamArray] varname[( )] [As type]

部分描述
Optional可选的。
表示参数不是必需的。
如果使用该选项,则 arglist 中的后续参数都必需是可选的,而且必须都使用 Optional 关键字声明。
如果使用了 ParamArray,则任何参数都不能使用 Optional
ByVal可选的。
表示该参数按值传递。
ByRef表示该参数按地址传递。
ByRef 是 Visual Basic 的缺省选项。
ParamArray可选的。
只用于 arglist 的最后一个参数,表示最后的参数是一个 Variant 元素的 Optional 的数组。
使用 ParamArray 关键字可以提供任意数目的参数。
ParamArray 关键字不能与 ByVal、ByRefOptional 一起使用。
varname必需的。
代表传给该过程的参数的变量名;遵循标准的变量命名约定。
( )对数组变量是必需的。指明 varname 是一个数组。
type可选的。
传递给该过程的参数的数据类型;可以是 Byte、Boolean、Integer、Long、Currency、Single、Double、Decimal(目前尚不支持)、Date、String(只支持变长)、**Object、Variant、**用户自定义的类型或对象类型。

说明

Function 过程而言,过程的数据类型决定其返回值的数据类型。可以在 arglist 之后使用 As 子句来指定函数返回值的数据类型。在 arglist 中,可以使用 As 子句来指定任何传给该过程的参数的数据类型。不单可以指定为任何标准数据类型,还可以在 arglist 中指定 As Any 来禁止类型检查,从而允许将任意数据类型传递给该过程。

空圆括号表示该 SubFunction 过程没有参数,且 Visual Basic 应确保不会传递任何参数。在下面的示例中,First 不带任何参数。如果对 First 的调用中使用了参数,就会产生错误:

Declare Sub First Lib "MyLib" ()

如果带参数表,则每次调用该过程时都要检查参数的个数和类型。在下面的示例中,First 有一个 Long 参数:

Declare Sub First Lib "MyLib" (X As Long)

注意

  • Declare 语句的参数表中不能有定长的字符串;只有变长的字符串才能传给过程。定长的字符串可以作为过程参数使用,但在传递前都要被转换为变长的字符串。

  • 当所调用的外部过程需要一个值为 0 的字符串时,就要使用 vbNullString 常数。该常数与零长度字符串 (“”) 是不相同的。


via:

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值