Excel VBA程序开发

ivccav Lv.6

关注
本帖最后由 ivccav 于 2023-3-17 16:59 编辑




64位API声明,虽然没有技术含量,但还是有很多童鞋不太了解,因此我花了点时间撸点文字,希望能对需要的人有所帮助。

从Office 2010开始,分32位和64位两个版本可供安装,同时VBA6升级到VBA7。Office2010之前的版本只有32位,没有64位。

在32位平台上的API函数中指针和句柄都是32位,由于VBA6和早期版本中没有指针或句柄的特定数据类型,
可使用Long数据类型(4字节)代表指针和句柄,相安无事。
但到了64位环境,指针和句柄升级到64位(8字节),64位数据无法保存在32位数据类型中,
当64位API函数使用或者返回指针和句柄时,会被截断,造成的后果是内存溢出、代码异常,甚至应用程序崩溃。

为了解决此问题,并使VBA代码能够在32位和64位环境中都能正常工作,
VBA7中新增了2种数据类型LongPtr和LongLong ,和一个声明关键字PtrSafe:

LongPtr:指针数据类型。LongPtr会根据Office版本进行不同解析,32位版本的Office中解析为Long,64位版本的Office中解析为LongLong。
LongPtr只能用于VBA7中(Office2010开始VBA版本才升级到VBA7),用来代表指针和句柄。

LongLong:带符号的64位整数。该数据类型只能在64位版本的Office中使用。在需要使用非常巨大的数据时可使用 LongLong类型。

PtrSafe:指针安全关键字。PtrSafe关键字只能用在Declare声明语句中。
说是指针安全,但并不会真的安全,API函数中所有64位的参数和接收返回值,都必须重新定义为64位。

以上3个新功能无法在VBA6及其之前版本中使用。

想要代码在32位和64位环境下通用,可以使用条件编译。
条件编译的语法和if语句一样,只在if、else、elseif和end if关键字前面加“#”,下面举例说明:

先看一段代码:
#If VBA7 Then
         Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
#Else
         Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
#EndIf

关键字前面带“#”,表示条件编译,VBA7是编译常数。
这段代码的意思是,如果VBA版本是VBA7,编译Declare PtrSafe Function...语句块,否则编译Declare Function语句块。
特别强调的是,VBA7常数不代表Office版本是32位或64位,仅仅指示VBA的版本是第7版。
在VBA7中,LongPtr数据类型可自动解析为32位和64位数据类型,这取决于安装的Office版本。
例子中hwnd是句柄,定义为LongPtr,VBA7会根据Office版本自动解析为32位或者64位数据类型。

因为LongLong只能用在64位Office版本中,如果需要使用该数据类型,
使用#If VBA7 And Win64 Then(当VBA7和Win64都为True时,才能确定Office是64位版本),如:

#if VBA7 then '  运行在VBA7中,可能是32位或64位
     Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
     #if Win64 then '  运行在64位Office
         Dim i As LongLong
     #else '  运行在32位Office
             Dim i As Long
     #end if
#else ' 运行在VBA6及其之前版本,只有32位
    Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
#end if

条件编译可用于代码的任何位置,甚至是函数或过程中:

#If VBA7 Then
    Declare PtrSafe Function OpenClipboard Lib "user32" (ByVal hwnd As LongPtr) As Long
    Declare PtrSafe Function GetClipboardData Lib "user32" Alias "GetClipboardDataA" (ByVal wFormat As Long) As LongPtr
#Else
    Declare Function OpenClipboard Lib "user32" (ByVal hwnd As Long) As Long
    Declare Function GetClipboardData Lib "user32" Alias "GetClipboardDataA" (ByVal wFormat As Long) As Long
#End If

Const CF_BITMAP = 2&

Sub test()
    #If VBA7 Then
        Dim hBmp As LongPtr
    #Else
        Dim hBmp As Long
    #End If
   
    If OpenClipboard(0) Then
        hBmp = GetClipboardData(CF_BITMAP)
    End If
End Sub

注意上例中,GetClipboardData函数返回一个剪贴板上的位图句柄,需要根据不同Office版本,定义接收参数hBmp,hBmp存放句柄。
无论是API或函数的返回值,还是API或者自定义函数的参数,只要是64位数据类型,如句柄或指针,都需要重新定义,否则程序运行会异常。
所以需要了解API函数的参数或者返回值是什么,才能定义恰当的数据类型进行传递或接收。这可以查询互联网了解函数参数,也可以用API浏览。


我之前上传了一个API Viewer,方便大家写API声明,需要的可下载:

https://club.excelhome.net/thread-1424969-31-1.html

301,302楼

8 人给楼主打赏
935阅读
6回复 倒序

ivccav 楼主 2楼

如果不使用LongLong数据格式,只需要
#If VBA7 then
#else
#end if
句式就能保证32/64位通用了

386026398 Lv.3 3楼

看了你的解析,你是一个vba方面的高手。试问一下用vb6编译的dll,32位excel调用无问题,64位excel调用就提示有问题,请问有捷径解决吗。麻烦给个提示

perfect131 Lv.5 4楼

我一般直接谷歌

ivccav 楼主 5楼

引用: 386026398 发表于 2023-3-21 14:01
看了你的解析,你是一个vba方面的高手。试问一下用vb6编译的dll,32位excel调用无问题,64位excel调用就提 ...

按微软官方说法:64位进程没法装载32位DLL, 而VB6只能编译32位的DLL,故不能使用。

我不是高手,也不吃编程这碗饭的,只偶尔逛逛论坛回答练练手,免得把学到的vb皮毛忘记了

ivccav 楼主 6楼

引用: perfect131 发表于 2023-3-21 14:12
我一般直接谷歌


还能谷歌吗?需要科学上网吧

perfect131 Lv.5 7楼

引用: ivccav 发表于 2023-3-21 15:05
还能谷歌吗?需要科学上网吧

用镜像,github,chatgpt,谷歌都用镜像

已显示全部内容