VBA API详解

Office版本变动

 在office2010以前微软只提供了32-bit版本,但在2010以后出现了32-bit和64-bit两个版本的office。后者能处理更大的数据和VBA代码运行速度得到提升。代码的兼容性也变得复杂,特别是API的声明。
 Office 2010以后有两个版本的office。64位的Excel能处理更大的数据,性能也得到提升。随着64位的引入同时也引入了新版的VBA即VBA 7。它能同时在两个版本运行。在安装office2010时,默认的安装版本是32位的,如果需要64位版本需手动选择。
 在VBA 7中,必须重新声明以前版本的API才能在64位中运行。必须更新语句中使用的地址指针和窗口句柄的声明。

Windows系统版本

 64位的offic能引用更大的内存地址空间,比以往使用更多的物理内存。除了引用应用程序用于存储数据或存储代码指令(指针)外,还可以用地址来引用显示的窗口句柄。根据使用是32位或64位系统来决定指针或句柄的大小(以字节位单位)。

运行64位的Office时,将面临两个基本问题:

  • 在office中的本机64位进程不能加载32位二进制文件。当使用 ActiveX控件和现有插件时,将不兼容。
  • VBA 7以前没有提供指针数据类型,如果开发人员使用32位变量来存储指针或句柄时,使用Declare定义的API返回的64位的值时部分值被丢弃。

VBA7 代码变化

 VBA7 是一个新的代码库,它取代了早期的VBA版本,它适用于32位和64位的Office。它提供了两个条件编译常数:VBA7和Win64。

VBA7常数:
 判断应用程序是否使用VBA7或者以前版本的VBA,确保代码的向后兼容性。

Win64常数:
 判断代码是32位或64位的形式运行。

ActiveX Control 和 COM Add-in兼容性

 现有的32位ActiveX控件(包括第三方和微软提供的)与64位版本的Office不兼容。对于ActiveX控件和Com对象,有三种可能的解决方法:

  • 如果有源代码,可以生成64位版本。
  • 联系供应商更新。
  • 寻找其它解决方案。

 Office中本机64位进程不能加载32位二进制文件。包括MSComCtl通用控件(TabStrip, Toolbar, StatusBar, ProgressBar, TreeView, ListViews, ImageList, Slider, ImageComboBox)和MSComCt2控件(Animation, UpDown, MonthView, DateTimePicker, FlatScrollBar),这些控件由以前Office或现在32位office安装,当迁移到64位Office中时,必须替换现有的控件。64位Office不提供64位通用的控件。

Windows API接口兼容性

 VBA类型库提供了许多功能,但是有时候必须直接与计算机的操作系统和其它组件通信时,例如管理内存和进程时或使用用户界面时或修改注册表时,在这些使用场景,最好的选择是嵌入动态链接库(DLL)的到出函数。在VBA中使用Delcare语句导入Dll函数。

Delare语句语法结构如下,看是否有返回类型:

Public/Private Declare Sub SubName Lib "LibName" Alias "AliasName" (argument list)

Public/Private Declare Function FunctionName Lib "Libname" alias "aliasname"(argument list) As Type


 SubName函数或FunctionName函数替换DLL中的导出函数名,如果要确定调用ASCII或Unicode版本的API,可以指定别名(AliasName),Lib后面紧跟到入函数所在的Dll名称。参数列表必须包含传递给DLL中的导出函数一致。
 下面的API函数在Windows注册表中打开一个子健并替换它的值。

Declare Function RegOpenKeyA Lib "advapi32.dll" (ByVal Key As Long, ByVal SubKey As String, NewKey As Long) As Long

 该函数在Windows API 动态链接库 Advapi32.dll中,其函数名称为RegOpenKeyW。其原型定义如下:

LSTATUS RegOpenKeyW(HKEY    hKey,LPCWSTR lpSubKey,PHKEY   phkResult);

 在C和C++中,该API可以针对32和64位进行编译。这是因为HKEY被定义为一个指针,它能正确反映编译代码所在平台的内存大小。
 在早期的VBA版本中,没有特定的指针数据类型,所以使用Long数据类型,而且Long类型是32位的,它在64位内存中的系统上使用时,这种情况就被中断,因为32位可能会被截断或覆盖其他内存地址。这些情况可能会导致不可预测的行为或系统奔溃。
 为了解决这个问题,VBA现在包含了一个真正的指针数据类型LongPtr。这个新的数据类型可以正确的编写API导入语句。

Declare PtrSafe Function RegOpenKeyW Lib "advapi32.dll" (ByVal hKey as LongPtr,ByVal lpSubKey as string,Byval phkResult as LongPtr) as Long

 LongPtr数据类型和PtrSafe特性可以在32位和64位系统上正确声明API导入函数。PtrSafe特性向VBA编译器表明声明语句是真的64位Office的。如果没有这个特性在64位系统中Delcare语句将导致编译错误。注意PtrSafe语句在32位本质的Office中是可选的。下表提供了一些函数和数据类型数说明。

Type Item Description
修饰符 PtrSafe 指示声明API与64位兼容,在64位系统中是必须的。
Data Type LongPtr 不是一个真实的数据类型,在32位版本上是4个字节数据类型(Long),在64位版本上是8个字节的数据类型(LongLong)。这是VBA 7以后声明指针和句柄推荐的方法,它只在32位和64位的VBA7中得到支持。
Data Type LongLong 8个字节的数据类型,仅在64位Office中可用。
Conversion Operator CLngPtr 将表达式转换为LongPtr类型。
Conversion Operator CLngLng 将表达式转换为LongLong类型。
Function VarPtr 变体类型地址转换,在64位中返回LongPtr,在32位中返回Long数据类型。
Function ObjPtr 对象地址转换,在64位中返回LongPtr,在32位中返回Long数据类型。
Function StrPtr 字符串地址地址转换,在64位中返回LongPtr,在32位中返回Long数据类型。

 注意:没有PtrSafe特性的API声明语句被认为与64位的Office不兼容。
 综上所述,有两个条件编译常量:VBA7和Win64。为了确保与以前版本的Office向后兼容,可以使用VBA7常量来防止早期版本中使用64位代码。对于32位和64位版本间不同代码,例如一个API,它在64位版本中使用LongLong,在32位版本中使用Long,则可以使用Win64常量,下面举例使用这个两个常量:

#if Win64 Then
	Declare PtrSafe Function MyTestFunc Lib "DllName"(Byval N as LongLong) as LongLong
#else
	Declare  MyTestFunc Lib "DllName"(Byval N as Long) as Long
#End if

#if VBA7 then
	Declare PtrSafe Sub MessageBeep Lib "User32"(Byval N as Long)
#else
	Declare  Sub MessageBeep Lib "User32"(Byval N as Long)
#end if

 总之,如果在写64位代码且希望与早期Office兼容,那么使用VBA7条件编译常量。如果在编写32位Office代码,那么代码工作原理与以前版本Office一样,不需要条件编译常量。如果想编写兼容32位和64位Office,那么使用Win64编译常量。

使用StrPtr、VarPtr、ObjPtr

 用这些函数来返回字符串类型、变体类型和对象类型的的指针(内存地址)。

动态链接库基础知识

  A dynamic-link library(DLL)是一个包含函数和数据的模块,可以被另外一个模块或应用程序使用。
 DLL定义两种函数:导出函数和内部函数。导出函数可以被其它应用程序使用,内部函数仅在内部使用。
 Windows API就是一组Dll的集合,各Dll模块导出了不同的函数供外部模块使用。
 每个加载Dll的进程都将Dll映射到它的虚拟内存地址空间中,在进程将Dll加载到它的虚拟地址之后,改进程就可以调用Dll的导出函数。

VBA声明API

 先在微软官网上找到API函数原型,然后根据VBA语法规则导入函数。如下函数:

int MessageBox(
  HWND    hWnd,
  LPCTSTR lpText,
  LPCTSTR lpCaption,
  UINT    uType
);

 API函数都是C语言格式定义的,如果是指针类型则定义为VBA的长整数,如果是二级指针,则定义为Any类型。在office 2010 以后为了兼容32位和64位指针类型定义位LongPtr。
 API函数一般又ASCII和Unicode两个版本,别名指定一个确定的版本,建议用Unicode版本,W结尾的API函数。

Office 2010 以后定义方式:
'选择VBA变量和C语言字节一致的变量,如 C的int是4Byte等同于VBA的long。VBA7以后多了一个LongPtr类型,在32位为Long类型在64位为LongLong类型。
#if VBA7 Then

public declare ptrSafe Function MessageBox lib "user32.dll" alias MessageBoxW _
(byval hWnd as LongPtr,byval lpText as string,byval lpCaption as string, byval uType as long) as long

#else

public declare  Function MessageBox lib "user32.dll" alias MessageBoxW _
(byval hWnd as Long,byval lpText as string,byval lpCaption as string, byval uType as long) as long

#end if
'上面定义一个Unicode版本的API,同时能兼容各个版本的Office。

 可以加群共同交流:794568082。

猜你喜欢

转载自blog.csdn.net/qq_25686631/article/details/111191241
vba