在VBA中使⽤WindowsAPI
VBA是⼀种强⼤的编程语⾔,可⽤于⾃定义Microsoft Office解决⽅案。通过使⽤VBA处理⼀个或多个Office应⽤程序对象模型,可以容易地修改Office应⽤程序的功能或者能够使两个或多个Office应⽤程序协同⼯作以完成单个应⽤程序⽆法完成的任务。然⽽,使⽤VBA仅能控制操作系统的⼀⼩部分。Windows API提供了控制操作系统绝⼤多数⽅⾯的功能。下⾯,介绍在VBA中使⽤Windows API的⼀些知识。
理解APIs
API只是⼀组函数,可⽤于处理组件、应⽤程序或操作系统。通常,API由⼀个或多个提供某种特定功能的DLLs组成。
DLLs是包含函数的⽂件,能够从任何运⾏的Windows应⽤程序中调⽤DLLs。在运⾏时,DLL中的函数被动态链接到调⽤它的应⽤程序⾥。⽆论多少应⽤程序调⽤DLL中的函数,该函数仅存在于磁盘的单个⽂件中,并且DLL在内存中仅被创建⼀次。
您可能最经常听说的API是Windows API,它包括组成Windows操作系统的DLLs。每个Windows应⽤程序都直接或间接地与Windows API相交互,Windows API确保运⾏在Windows下的所有应⽤程序都按⼀致的⽅式⼯作。
除了Windows API外,还有其它发布的APIs可⽤。例如,邮件应⽤程序编程接⼝(MAPI)是⼀组⽤于编写电⼦邮件应⽤程序的DLLs。
APIs通常是由创建Windows应⽤程序的C和C++程序员编写,但能够使⽤VBA调⽤DLL中的函数。因为⼤多数DLLs最初都是由C/C++程序员编写和⽂档规范,所以调⽤DLL函数与调⽤VBA函数不同。为了使⽤API,必需理解如何传递参数到DLL函数。
为了调⽤Windows API中的函数,需要描述这些可⽤的函数的⽂档规范,如何在VBA中声明这些函数,以及如何调⽤它们。下⾯是两个有⽤的资源:
1、⽂件,包含Windows API中⼤多数函数的VBA Declare(声明)语句。可以使⽤API Viewer加载宏查和复制需要的Declare语句。可以在下⾯的站点下载API声明查看器:
<⽂件下载:
2、Microsoft Platform SDK,包含复杂的Windows API⽂档。可以在下⾯的地址中查看:。
此外,很多程序员还开发了⼀些声明并与⼤家共享,下⾯就是⼀个关于API声明的资源⽹站:。
使⽤Declare语句
在从VBA中调⽤DLL⾥的函数之前,必须为VBA提供在哪⾥到函数以及如何调⽤该函数的信息,有两种⽅法:
1、设置对DLL类型库的引⽤。
2、在模块中使⽤Declare语句。
设置对DLL类型库的引⽤是使⽤DLL中的函数的最容易的⽅法。⼀旦设置引⽤,就可以将其当作⼯程⾥的⼀部分⼀样调⽤DLL函数。然⽽,也要注意⼀些事项。⾸先,设置对多个类型库的引⽤会影响应⽤程序的性能;其次,不是所有的DLLs都提供类型库,虽然可以对没有提供类型库的DLL设置引⽤,但不能调⽤该DLL中的函数。
注意,组成Windows API的DLLs没有提供类型库,因此不能设置对它们的引⽤并调⽤其中的函数。要调⽤Windows API中的函数,必须在⼯程⾥模块的声明部分包括Declare语句。
Declare语句是⼀个定义,告诉VBA在哪⾥到特定的DLL函数以及如何调⽤该函数。在代码中添加Declare语句最简单的办法是使⽤API Viewer加载宏,其中包含Windows API中⼤多数函数的Declare语句,也包含⼀些函数所需要的常量和类型定义。
Declare语句声明的形式如下:
[Public|Private]Declare Sub name Lib "libname" [Alias "aliasname"][([arglist])]
[Public|Private]Declare Function name Lib "libname" [Alias "aliasname"] [([arglist])] [As type]
下⾯是GetTempPath函数的Declare语句的⽰例,该函数返回Windows临时⽂件夹的路径(默认为C:\Windows\Temp):
Private Declare Function GetTempPath Lib "kernel32" _
Alias "GetTempPathA" (ByVal nBufferLength As Long, _
ByVal lpBuffer As String) As Long
关键字Declare告诉VBA在⼯程中要包含的DLL函数的定义。在标准模块中的Declare语句可以是公共的或私有的,取决于你希望API函数仅⽤于单个模块还是整个⼯程。在类模块
中,Declare语句必须是私有的。
在关键字Function之后是函数的名字,具体地说,是从VBA中调⽤该函数时使⽤的名字。这个名字可以
与API函数本⾝的名字相同,也可以在Declare语句中使⽤关键字Alias指定打算在VBA中通过不同的名字(别名)调⽤该函数。
在上⾯的⽰例中,在DLL中API函数的名字是GetTempPathA,从VBA中调⽤该函数时使⽤的名字是GetTempPath。注意,DLL函数的实际名字出现在关键字Alias之后,同时也注意到GetTempPath是⽂件⽤于该函数的别名,但你可以将其改变为任何你想要的名字。
下⾯是为什么要在Declare语句中使⽤别名的⼀些理由:
⼀些API函数的名字以下划线(_)开始,在VBA中是不合乎语法的。为了从VBA中调⽤该函数,需要使⽤别名。
因为别名允许将DLL函数命名为你所希望的名字,所以可以使函数名字遵循你⾃已在VBA中的命名标准。
因为API函数是区分⼤⼩写的,⽽VBA函数则不,所以可以使⽤别名来改变函数名的⼤⼩写。
⼀些DLL函数带有接受不同数据类型的参数,这些函数的VBA声明语句定义这些参数为类型Any,调⽤带有声明为Any的参数的DLL函数是危险的,因为VBA不会执⾏任何数据类型检查。如果想避免传递类型为Any的参数的危险,可以声明相同的DLL函数的多个版本,每⼀个都具有不同的名字和不同的数据
类型。
Windows API为所有接受字符串参数的函数都包含两个版本:ANSI版和Unicode版。ANSI版带有"A"后缀,正如上例所⽰,⽽Unicode版带有"W"后缀。虽然VBA使⽤Unicode,但在调⽤DLL中的函数之前,它将所有的字符串转换为ANSI字符串,因此在从VBA中调⽤Windows API函数时通常使⽤ANSI版。API Viewer加载宏⾃动为所有接受字符串参数的函数命名别名,因此可以不必包含"A"后缀⽽调⽤该函数。
关键字Lib指定包含函数的DLL。注意,在声明语句⾥以字符串形式包含DLL的名字。如果在系统中没有到关键字Lib之后指定的DLL,对该函数的调⽤将失败,导致运⾏时错误:48,装载DLL错误。因为可以在VBA代码中处理这种错误,所以可以编写健壮的代码得体地处理错误。
下⾯列出了Windows API中最常使⽤的DLLs:
Kernel32.dll:低级别的操作系统函数,例如内存管理和资源处理。
User32.dll:Windows管理函数,例如消息处理、计时器、菜单和通讯。
GDI32.dll:图像设备接⼝(GDI)库,包含设置输出的函数,例如绘图、显⽰上下⽂和字体管理。
⼤多数DLLs,包括Windows API中的DLLs,都采⽤C/C++编写,因此,传递参数到DLL函数需要参数的理解以及C/C++接受的数据类型,⽽这些不同于VBA函数。
同时,DLL函数的许多参数按值传递。默认情况下,VBA中的参数按引⽤传递。因此,当DLL函数需要按值传递的参数时,在函数定义中包括关键字ByVal是必要的。在函数定义中忽略ByVal关键字可能会在应⽤程序中导致⽆效的页错误。有时,可能会发⽣VBA运⾏时错误:49,坏的DLL调⽤协议。
按引⽤传递参数传递该参数的内存位置到被调⽤的过程,如果该过程修改了参数的值,那么会修改该参数的唯⼀的副本,因此,当返回到调⽤过程时,参数包含的是修改后的值。
按值传递参数到DLL函数,将传递该参数的副本,函数操作该参数的副本,避免了修改实际参数的内容。当返回到调⽤过程时,该参数包含与调⽤其它过程前相同的值。
因为按引⽤传递允许在内存中修改参数值,如果不恰当地按引⽤传递参数,DLL函数可能会覆盖它不应该覆盖的内存,导致错误或者不可预料的结果。Windows维护许多值不应该被覆盖,例如,Windows为每个窗⼝赋惟⼀的32位标识符,称作句柄(handle)。句柄总是按值传递给API函数,因为如果Windows修改了某窗⼝的句柄,那么不再能够追踪到该窗⼝。(虽然关键字ByVal出现在String类型的⼀些参数前⾯,但是字符串总是按引⽤被传递到Windows API函数)
上述声明语句接受两个参数,⼀个为Long型,另⼀个为String型,并返回⼀个Long型值。
使⽤常量
除了DLL函数的声明语句外,⼀些函数还需要定义常量以及在函数中使⽤的类型。在模块的声明部分包括常量和⽤户定义类型。
如何知道函数需要的常量和⽤户定义类型呢?需要查看该函数的⽂档。⽂件包含函数的常量和⽤户定义类型的定义。可以使⽤API Viewer加载宏出这些常量和⽤户定义类型,并将它们复制到代码中。不巧的是,常量和⽤户定义类型不会以任何⽅式与需要它们的声明语句相联系,因此,仍然需要检查DLL函数的⽂档,决定哪个常量和类型与哪个声明语句匹配。
函数可能需要传递常量来指明想要函数返回的信息。例如,GetSystemMetrics函数接受75个常量,每⼀个都指定操作系统的不同⽅⾯,该函数返回的信息取决于传递给它的常量。要调⽤GetSystemMetrics,不需要包括所有的75个常量,只需包括要使⽤的就可以了。
建议定义常量⽽不是简单地传递它们代表的值。Microsoft确保在将来的版本中仍然会保留相同的常量,但不保证常量的值相同。
DLL函数需要的常量通常是隐含的,因此需要查阅函数的⽂档来确定传递的常量,以返回特定的值。
在《Professional Excel Development》中介绍了如何查常量的值的⽅法。即在Microsoft的站点下载并安装核⼼SDK软件包,其中有⼀个名为"include"的⼦⽬录,所有⽤于创建动态链接库(DLL)的C++头
⽂件都存放在这个⽬录中。通过搜索就能到常量所在的⽂件,例如查SM_CXSCREEN,会返回⽂件"winuser.h",打开该⽂件查询就可到相关的常量。
下⾯的⽰例是包括GetSystemMetrics函数的声明语句,接受两个常量,然后展⽰如何从属性过程中调⽤GetSystemMetrics,以像素为单位返回屏幕的⾼度。
Declare Function GetSystemMetrics Lib "User32" (ByVal nIndex As Long) As Long
Const SM_CXSCREEN As Long = 0 '屏幕宽度
Const SM_CYSCREEN As Long = 1 '屏幕⾼度
Public Property Get ScreenHeight() As Long
'以像素为单位返回屏幕的⾼度
ScreenHeight = GetSystemMetrics(SM_CYSCREEN)
End Property
Public Property Get ScreenWidth() As Long
'以像素为单位返回屏幕的宽度
ScreenWidth = GetSystemMetrics(SM_CXSCREEN)
End Property
使⽤⽤户定义类型
⽤户定义类型是⼀种数据结构,可以存储多个相关的不同类型的变量,与C/C++中的结构⼀致。有时,传递空的⽤户定义类型到DLL函数,函数填充值;有时,从VBA填充⽤户定义类型,并将其传递给DLL函数。
可以将⽤户定义类型作为⼀箱抽屉,每个抽屉可以包含不同类型的项⽬,但将它们组合在⼀起可以当作相关项⽬的单个箱⼦。可以从任何抽屉获得项⽬⽽不必担⼼存储在任何其它抽屉中的项⽬。
要创建⽤户定义类型,使⽤Type … End Type语句。在Type…End Type语句⾥,列出了每个项⽬,包含值和数据类型。⽤户定义类型的元素可以是数组。
下⾯的代码段展⽰如何定义RECT⽤户定义类型,和管理屏幕矩形块的⼏个Windows API函数⼀起使⽤。例如,GetWindowRect函数接受RECT类型的数据结构,使⽤关于窗⼝的左侧、顶部、右侧和底部位置的信息填充。
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
要传递⽤户定义类型到DLL函数,必须创建该类型的变量。例如,如果打算传递RECT类型的⽤户定义类型到DLL函数,那么就要包括变量声明,如下所⽰:
Private rectWindow As RECT
可以引⽤⽤户定义类型⾥的单个元素,如下所⽰:
Debug.Print rectWindow.Left
使⽤句柄
调⽤DLLs中的函数之前需要理解的另⼀个重要的概念是句柄(handle)。简单地说,句柄是32位正整数,Windows⽤于识别窗⼝或另⼀个对象,例如字体或位图。
在Windows中,窗⼝有许多不同的表现形式。事实上,在屏幕中看到的⼏乎所有事情都在窗⼝⾥,并且不能看到的⼤多数事情也在窗⼝⾥。窗⼝能够是⼀个绑定的屏幕矩形区域,就像您习惯看到的应⽤程序窗⼝⼀样。窗体中的控件,例如列表框或滚动条,也都是窗⼝,虽然不是所有类型的控件都是窗⼝。在桌⾯上显⽰的图标以及桌⾯本⾝,都是窗⼝。
因为所有这些类型的对象都是窗⼝,所以Windows能够相同地对待它们。Windows提供给每个窗⼝⼀个唯⼀的句柄,并使⽤该句柄去处理窗⼝。许多API函数返回句柄或者接受句柄作为其参数。
当窗⼝创建时Windows赋句柄给该窗⼝,当窗⼝销毁时Windows释放该句柄。虽然句柄保留的时间与窗⼝存在的时间相同,但不保证⼀个窗⼝在销毁并重新创建后有相同的句柄。因此,如果在变量中存储句柄,那么记住该窗⼝销毁后,该句柄不再有效。
GetActiveWindow函数是返回窗⼝句柄的函数⽰例,此时,应⽤程序窗⼝是当前活动的窗⼝。GetWindowText函数接受某窗⼝的句柄,并且如果窗⼝有标题的话返回该窗⼝的标题。下⾯的程序使⽤GetActiveWindow返回活动窗⼝的句柄,GetWindowText返回其标题:
Declare Function GetActiveWindow Lib "user32" () As Long
Declare Function GetWindowText Lib "user32" _
Alias "GetWindowTextA" (ByVal Hwnd As Long, _
ByVal lpString As String, ByVal cch As Long) As Long
Function ActiveWindowCaption() As String
Dim strCaption As String
Dim lngLen As Long
'创建使⽤空字符填充的字符串
strCaption = String$(255, vbNullChar)
'返回字符串的长度
lngLen = Len(strCaption)
'调⽤GetActiveWindow来返回活动窗⼝的句柄
'与字符串和其长度⼀起,传递句柄到GetWindowText
If (GetWindowText(GetActiveWindow, strCaption, lngLen) > 0) Then
'返回Windows已写⼊的值给字符串
ActiveWindowCaption = strCaption
End If
End Function
GetWindowText函数接受三个参数:窗⼝的句柄、将返回窗⼝标题⾥的空结尾的字符串、以及字符串的长度。
下⾯列出了Excel中常⽤的窗⼝类名称:
Excel主窗⼝:XLMAIN
Excel桌⾯:XLDESK
Excel⼯作表:EXCEL7
Excel⽤户窗体:ThunderDFrame(Excel 2000以后版本)、ThunderRT6DFrame(Excel 2000以后版本,⽤于作为COM加载项时)、ThunderXFrame(Excel 97)Excel状态栏:EXCEL4
Excel图表窗⼝:EXCELE(Excel2007以前版本)
FindWindow函数使⽤类名和窗⼝标题查窗⼝。下⾯的代码以像素为单位查Excel主窗⼝的位置和⼤⼩:
'包含窗⼝⼤⼩的⽤户定义类型
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
'查窗⼝的API函数
Declare Function FindWindow Lib "user32" _
Alias "FindWindowA" ( _
ByVal lpClassName As String, _
ByVal lpWindowName As String) As Long
'获取窗⼝⼤⼩的API函数
Declare Function GetWindowRect Lib "user32" ( _
ByVal hWnd As Long, _
lpRect As RECT) As Long
Sub ShowExcelWindowSize()
Dim hWnd As Long, uRect As RECT
'获取Excel主窗⼝的句柄
'Excel 2002及以后版本也可使⽤hWnd=Application.Hwnd
hWnd = FindWindow("XLMAIN", Application.Caption)
'将窗⼝⼤⼩信息存⼊到RECT结构中
GetWindowRect hWnd, uRect
'显⽰结果
MsgBox "这个Excel窗⼝的尺⼨为:" & _
vbCrLf & "左侧:" & uRect.Left & _
vbCrLf & "右侧:" & uRect.Right & _
vbCrLf & "顶部:" & uRect.Top & _
vbCrLf & "底部:" & uRect.Bottom & _
vbCrLf & "宽度:" & (uRect.Right - uRect.Left) & _
vbCrLf & "⾼度:" & (uRect.Bottom - uRect.Top)
End Sub
调⽤函数
虽然调⽤DLL函数的许多⽅式与调⽤VBA函数相似,但是开始时有⼀些不同可能会使DLL函数混淆。下⾯将介绍如何输⼊DLL函数中的参数并加前缀、如何返回字符串、如何传递数据结构、能够接受什么返回值、以及如何获取错误信息。
参数数据类型
在C/C++中使⽤的数据类型、⽤于描述它们的标记都不同于在VBA中的⽤法,下⾯描述了DLL函数中常⽤的数据类型以及它们在VBA中的等效表⽰。
C/C++数据类型匈⽛利前缀描述等效的VBA表⽰
BOOL b8位布尔值。0表⽰False;⾮0表⽰True Boolean或Long
BYTE ch8位⽆符号整数Byte
HANDLE h32位⽆符号整数,代表Windows对象的句柄Long
int n16位符号整数Integer
long l32位符号整数Long
LP lp32位对内存中C/C++结构、字符串、函数或其它数据的长指针Long
LPZSTR lpsz32位对C类型空结尾字符串的长指针Long
虽然您应该熟悉这些数据类型和前缀,但前⾯提到的⽂件包含了准备在VBA中使⽤的声明语句。如果在代码中使⽤这些声明语句,那么函数参数已经定义了正确的VBA数据类型。
在《》的第27章,详细介绍了如何将C-样式声明转换为VBA声明语句。
只要已经定义并传递了正确的数据类型,调⽤DLL函数与调⽤VBA函数采取相同的⽅法。当然也有例外,这将在下⾯的内容中介绍。
从DLL函数中返回字符串
DLL函数不会以VBA函数相同的⽅法返回字符串。因为字符串总是按引⽤传递到DLL函数,DLL函数能够修改字符串参数的值。宁可返回字符串作为函数的返回值,就像可能在VBA中做的那样,DLL函数返回字符串到传递给该函数的String类型的参数。函数的实际返回值经常是⼀个长整型值,指定写⼊到字符串参数的字节数量。
接受字符串参数的DLL函数获得指针,指向内存中该字符串的位置。指针只是内存地址,表明在哪⾥存储字符串。因此,当从VBA中传递字符串到DLL函数时,传递给DLL函数⼀个指针,指向内存中的字符串。接着,这个DLL函数修改存储在那个地址的字符串。
要调⽤写到String变量的DLL函数,需要采取额外的步骤合适地格式字符串。⾸先,String变量必须是空结尾字符串。⼀个空结尾字符串以特定的空字符结束,空字符通过VBA常量vbNullChar来指定。
其次,DLL函数不能修改已经创建的字符串的⼤⼩。因此,需要确保传递给函数的字符串⾜够⼤以容纳整个返回值。当传递字符串到DLL函数中时,通常需要指定在另⼀个传递的参数中字符串的⼤⼩。Windows追踪字符串的长度,以确保不会覆盖掉字符串已使⽤过的内存。
传递字符串到DLL函数中的⼀个好⽅法是创建String变量,并使⽤String$函数在其中填充空字符,使其⾜够⼤以容纳函数返回的字符串。例如,下⾯的代码创建⼀个144字节长的字符串,并使⽤空字符串填充:
Dim strTempPath As String
strTempPath = String$(144, vbNullChar)
当传递字符串到DLL函数中时,如果不知道字符串的长度,那么可以使⽤Len函数确定其长度。
获取Windows临时⽂件夹的GetTempPath函数,就是返回String值的DLL函数的例⼦。该函数接受两个参数,⼀个空结尾的字符串变量和⼀个包含字符串长度的数值变量。修改该字符串以便包含路径,例如C:\Temp\。(Windows需要⼀个临时⽂件夹存在,于是该函数应该总是返回该⽂件夹的路径。如果由于某种原因不存在临时⽂件夹,GetTempPath返回0)。
下⾯的程序调⽤GetTempPath函数获取Windows临时⽂件夹的路径:
Declare Function GetTempPath Lib "kernel32" Alias "GetTempPathA" _
(ByVal nBufferLength As Long, ByVal lpBuffer As String) As Long
Property Get GetTempFolder() As String
'返回⽤户临时⽂件夹的路径.
'对于根⽬录,Windows需要⼀个临时⽂件夹存在
'因此应该总是返回其路径
'以防万⼀,检查GetTempPath的返回值
Dim strTempPath As String
Dim lngTempPath As Long
'使⽤空字符填充字符串
strTempPath = String(144, vbNullChar)
'获得字符串的长度
lngTempPath = Len(strTempPath)
'调⽤GetTempPath,传递字符串长度和字符串
If (GetTempPath(lngTempPath, strTempPath) > 0) Then
'GetTempPath返回路径到字符串中.
'截去字符串开始的空字符
GetTempFolder = Left(strTempPath, InStr(1, strTempPath, vbNullChar) - 1)
Else
GetTempFolder = ""
End If
End Property
注意,当传递字符串到函数中时,使⽤空字符填充该字符串。函数写⼊返回的字符串值"C:\Temp"到字符串变量的第⼀部分中,并且剩下的保留空字符填充,接着使⽤Left函数截取字符串。
GetTempPath函数的实际返回值是已经被写到字符串变量中的字符数。如果返回的字符串是"C:\Temp\",那么GetTempPath函数返回8。
注意,这仅对从函数返回字符串时传递空结尾字符串及其⼤⼩是必需的。如果函数不返回字符串到字符
串参数中,⽽是接受对函数指定信息的字符串,那么只需传递正常的VBA字符串变量。
传递⽤户定义类型到DLL函数
许多DLL函数需要通过使⽤预定义的格式传递数据结构。当从VBA中调⽤DLL函数时,根据函数的需求传递已经定义的⽤户定义类型。
通过查看函数的声明语句,您能够理解什么时候需要传递⽤户定义类型以及需要在代码中包括哪种类型定义。需要数据结构的参数总是被声明为长指针:指向内存中数据结构的32位数字值。为长指针参数约定的前缀是"lp"。此外,参数的数据类型是数据结构的名称。
例如,看看GetLocalTime函数和SetLocalTime函数的声明语句:
Private Declare Sub GetLocalTime Lib "kernel32" _
(lpSystem As SYSTEMTIME)
Private Declare Function SetLocalTime Lib "kernel32" _
(lpSystem As SYSTEMTIME) As Long
两个函数都接受SYSTEMTIME类型的参数,即包含⽇期和时间信息的数据结构。下⾯是SYSTEMTIME类型的定义:
Private Type SYSTEMTIME
wYear As Integer
wMonth As Integer
wDayOfWeek As Integer
wDay As Integer
wHour As Integer
wMinute As Integer
wSecond As Integer
wMilliseconds As Integer
End Type
要将数据结构传递给函数,必须声明SYSTEMTIME类型的变量,如下所⽰:
Private sysLocalTime As SYSTEMTIME
当调⽤GetLocalTime时,传递SYSTEMTIME类型的变量到该函数,并且使⽤表⽰当前本地的年、⽉、⽇、星期⼏、⼩时、分、秒、毫秒的数字值填充该数据结构。例如,下⾯的Property Get程序调⽤GetLocalTime返回表明当前⼩时的值:
Public Property Get Hour() As Integer
'返回当前时间,然后返回⼩时
GetLocalTime sysLocalTime
Hour = sysLocalTime.wHour
End Property
当调⽤SetLocalTime时,也传递了SYSTEMTIME类型的变量,但⾸先提供数据结构的⼀个或多个元素的值。例如,下⾯的Property Let程序设置本地系统时间的⼩时值。⾸先,调⽤GetLocalTime函数获取
本地时间的当前值到数据结构中,然后使⽤传递给属性过程的值更新数据结构的sysLocalTime.wHour的值。最后,调⽤SetLocalTime函数,传递相同的数据结构,包含通过GetLocalTime加新⼩时值⽽取得的值。
Public Property Let Hour(intHou r As Integer)
'获取当前时间以便所有值都是当前的
'然后设计本地时间的⼩时部分
GetLocalTime sysLocalTime
sysLocalTime.wHour = intHour
SetLocalTime sysLocalTime
vba 字符串转数组End Property
GetLocalTime函数和SetLocalTime函数与GetSystemTime函数和SetSystemTime函数相似。主要的不同在于,GetSystemTime函数和SetSystemTime函数表达的时间为格林威治标准时间。例如,如果本地时
间是午夜12时,⽽您居住在西海岸,那么格林威治标准时间就是上午8时,有8⼩时的时差。GetSystemTime函数返回当前时间即8:00 A.M,⽽GetLocalTime返回午夜12:00。
理解Any数据类型
⼀些带有⼀个参数的DLL函数可以接受多个数据类型。在DLL函数的声明语句中,这样的参数被声明为类型Any。VBA允许传递任何数据类型到这个参数。然⽽,DLL函数可能被设计为接受仅仅两个或三个不同的数据类型,因此传递错误的数据类型可能会导致应⽤程序错误。
通常,当在VBA⼯程中编译代码时,VBA对传递给每个参数的值执⾏类型检查。也就是说,确保传递的值的数据类型与函数定义中的参数的数据类型相匹配。例如,如果参数定义为Long 型,⽽试图传递String型的数值,则会发⽣编译时错误。这适⽤于调⽤内置的VBA函数、⽤户定义函数、或者DLL函数。当将参数声明为类型Any时,不会进⾏类型检查,因此当传递值到这种类型的参数时应该谨慎。
⼀些具有⼀个参数的DLL函数可以接受字符串或者指向字符串的空指针。指向字符串的空指针是⼀个特别的指针,指令Windows忽略所给的参数。它与零长度字符串("")不同。在VBA 的早期版本中,程序员必须声明参数为类型Any,或者声明DLL函数的两个版本,即⼀个版本定义参数类型为String,⼀个版本定义参数类型为Long。现在VBA包括vbNullString常量,代表指向字符串的空指针,这样可以声明参数为String类型,并且在需要传递空指针的情形下传递vbNullString常量。
获取错误信息
DLL函数中发⽣的运⾏时错误的⾏为不同于VBA中的运⾏时错误,即没有错误消息框显⽰。当运⾏时错误发⽣时,DLL函数返回某值表时发⽣了错误,⽽且错误不会中断VBA代码的执⾏。
Windows API中的⼀些函数存储运⾏时错误的错误信息。如果使⽤C/C++编程,可以使⽤GetLastError函数获取关于发⽣的最后⼀次错误的信息。然⽽,从VBA中,GetLastError函数可能返回不确切的结果。要从VBA获得关于DLL错误的信息,可以使⽤VBA的Err对象的LastDLLError属性。LastDLLError属性返回发⽣的错误号。
为了使⽤LastDLLError属性,需要知道与错误相对应的错误号。在⽂件没有这⽅⾯的可⽤信息,⽽Microsoft Platform SDK中可以到。
下⾯的⽰例展⽰在已经调⽤了Windows API中的函数后如何使⽤LastDLLError属性。PrintWindowCoordinates程序接受窗⼝句柄,并调⽤GetWindowRect函数。GetWindowRect使⽤组成窗⼝的矩形的边的长度填充RECT数据结构。如果传递了⽆效的句柄,将发⽣错误,并且可以通过LastDLLError属性获得错误号。
Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, _
lpRect As RECT) As Long
Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Const ERROR_INVALID_WINDOW_HANDLE As Long = 1400

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。