Office 95 API 的企业开发者指南
发表于:2007-07-14来源:作者:点击数:
标签:
摘要 这篇技术文章探讨如何编写Microsoft Office 解决方案 代码,以便使用16位和32位版本的Office产品顺利进行应用编程接口(API)调用。特别是,本文适用于Microsoft Access 、Visual Basic 、Microsoft Word 、Microsoft Excel 和Microsoft Project 。实现
摘要
这篇技术文章探讨如何编写Microsoft Office
解决方案代码,以便使用16位和32位版本的Office产品顺利进行应用编程接口(API)调用。特别是,本文适用于Microsoft A
clearcase/" target="_blank" >ccess 、Visual Basic 、Microsoft Word 、Microsoft Excel 和Microsoft Project 。实现这种API调用的方法有三类:使用REGISTER、使用Declare语句和使用一个类型库。本文分别对这三种API调用方法进行了探讨和举例。在本文中,假设读者已对我先前的一篇技术文章“将你基于16位Office的解决方案移植到32位Office ”较为熟悉,并且假设读者是一位经验丰富的Office
开发者,他需要移植或编写解决方案,以满足16位到32位互操作性的新要求。
引言
对于编写一个面向多个平台多个版本Microsoft Office 产品的解决方案问题,它是大多数编译语言开发者所不熟悉的。编译语言应用程序开发者喜欢在编辑程序开发中选择一个时间段,将解决方案加到一个执行文件中,而很少需要解决由产品开发引起的问题。一个Office 解决方案应具有一个可在所有Office 产品和所有操作系统中正确执行的解决方案文件(一个.XLS、.MDB、.MPP或.DOT文件)。Office 开发者必须编写具有对前五年内发布的版本的向后
兼容性和对将发布的下一版本的向前兼容性。例如,一个在Microsoft Excel 4.0和Microsoft Excel 5.0上运行的费用报告应能在Microsoft Excel NT、Microsoft Excel 95和Microsoft Excel 2001(假定存在)上运行。
Office 95将32位Office 产品引入到主流企业环境中,它们将与16位Office 产品共存许多年。在我以前的一篇技术文章“将你基于16位Office的解决方案移植到32位Office ”中,对不能从一个32位应用程序调用16位API的问题以及不能从一个16位应用程序调用32位API的问题进行了阐述,在那篇文章中列出的16位/32位互操作性解决方案将在本文中作进一步的探讨。
注:根据经验,我把
Windows 3.1中的API调用转换为Win32 应用编程接口(API)调用称之为32A规则,即:“如果在任何调用参数中使用了名称字符串,给DLL名添加一个32,给函数名添加一个A(对于ANSI)”。这个经验对于绝大多数API调用是可行的,但对于某些函数,这个经验可能行不通,因此它并不是绝对正确的解决方案。
16位/32位互操作性解决方案比16位API调用移植到相当的32位API调用(换句话说,使用32A规则)的要求更多;它需要解决方案在16位或32位Office 产品中执行时能选择适当的API。
术语
为了避免用语使用的混淆,下列表中定义了在本技术文章中使用的术语:
术语 定义
Office 产品 不将代码编译为一个可执行程序的产品。下列产品之一:Microsoft Project 、Microsoft Access、Word 和Microsoft Excel 。
编译语言 将代码编译为一个可执行程序的产品,例如Visual Basic 、FORTRAN、PowerStation 、Visual FoxPro 和Visual C++。
解决方案 由第三方或开发者使用某个Office 产品及其格式(如Microsoft Excel (.XLS)、Word (.DOT)、Microsoft Project (.MPP)或者Microsoft Access(.MDB))开发的应用程序。
解决方案代码 编写有解决方案的代码(XLM或Basic或者结合使用)
平台 Microsoft Windows和Apple Macintosh 操作系统。一些Office 产品适用于所有Windows和Macintosh操作系统。通过将解决方案从一个操作系统复制到另一操作系统, Microsoft Excel 可在Macintosh和Windows PC中执行同一应用程序(在一定的限制条件下)。
API调用方法
解决方案代码可采用下列三种方法之一进行编写--使用Microsoft Excel 宏(XLM)、Basic(Access Basic、Visual Basic 、
VBA和WordBasic)或者混合使用XLM和Basic。XLM使用REGISTER和CALL命令来进行API调用,Basic使用Declare语句、类型库或者这两者的结合使用来进行API调用。经一个开发者传给另一开发者的Microsoft Excel 解决方案可以使用所有这些方法进行API调用。这三种方法对于最新版本的Microsoft Excel 都适用。
表1 API调用方法描述
方法 语言 进行API调用的方法
REGISTER XLM宏 从宏中使用REGISTER和CALL函数
Declare Access Basic、
Visual Basic 、
VBA、WordBasic
使用Declare语句
类型库 仅VBA 从对象浏览器上选择类型库方法
这些方法不会产生某个特定版本的解决方案,它取决于开发者编写代码的引用或解决方案所必须运行的产品范围。例如,为了使解决方案在Microsoft Excel 4.0中也有效,开发者可以在Microsoft Excel 95的XLM中编写一个解决方案。表2中列出了各种API调用可在哪些Microsoft 产品中使用。
表2 Microsoft 产品的API调用方法
产品版本 REGISTER方法 Declare方法 类型库方法
Microsoft Excel 3.0,4.0 X
Microsoft Excel 5.0,5.0NT,95 X X X
Word 2.0,6.0,95 X
Visual Basic 1.0,2.0,3.0 X
Visual Basic 4.0 X X
Project 4.0,95 X
Microsoft Access1.0,1.1,2.0 X
Microsoft Access95 X X
FoxPro 2.5 X
FoxPro 3.0 X X
我们在讨论16位和32位API调用时,都将使用同一个简单的API调用示例--GetTickCount来进行论述。
REGISTER方法
对于在编写Office 解决方案前先在Visual Basic 中编写过代码的开发者,他们并不熟悉使用REGISTER方法的API调用。旧版本Microsoft Excel 的开发者从XLM中进行API调用,并避免使用VBA。我们不能把XLM看作是过时的技术--虽然从Microsoft Excel 4.0开始未对其更改。如果
性能问题变得很重要时,XLM仍是最好的选择。Microsoft Excel 5.0开发者工具包中陈述了直接从XLM中进行API调用的优点。
由于C API最易于在Microsoft Excel 宏和工作表上使用,因此对于Visual Basic 所用的外部函数的编写,它并不是非常好(虽然VB 和C API可被融入混合解决方案中)。
为了从XLM进行API调用,要求使用REGISTER函数来引用动态连接库(DLL)以及使用CALL函数来执行它。REGISTER函数与VBA中的Declare语句的作用是一样的。推荐的REGISTER句法如下:
REGISTER(module_text,procedure,type_text,function_text,argument_text,macro_type,category,shortcut_text)
要返回到所给出的API调用示例,我们可以在XLM的一些代码行中采用GetTickCount。
这个代码在16位Microsoft Excel 中产生下列输出:
如果我们在32位的Microsoft Excel 中运行这个代码,结果是失败的。API调用返回#VALUE!,表明传递给DLL或从DLL中传递变量是失败的:
我们必须将这个代码转换为一个32位的API调用,因此,我们使用32A规则,在DLL名上添加一个32,如果是字符串,在其上添加一个A。采用32A规则的代码如下所示。
在调用TestGetTickCount32A时,我们会遇到另一种错误。返回#NAME?变量,这意味着在DLL中不存在这个函数。它可能是错误的DLL或错误的函数名(记住,函数名是区分大小写的)。这必定是32A规则的一个例外(我承认,我是有意选用这个函数的)。
在MSDN库中,使用关键字索引在PlatformSDK(平台软件开发包)中查找GetTickCount。在主题的顶端弹出的快速信息(Quick Info)会告诉你该函数在KERNEL32库中(在Window 3.x中,该函数在USER库中)。下列代码列出修改后的32位Microsoft Excel 宏。
在32位Microsoft Excel 中运行此代码,我们得到正确的结果:
为说明在16位和32位Microsoft Excel 中的API调用方式,参阅表3,表中显示了这些宏的结果。使用32A规则的宏(“32A位”)在16位和32位解决方案中产生的结果相同。它说明了REGISTER函数从16位或者32位解决方案中定位(或定位失败)16位和32位DLL中的函数名的能力。返回#VALUE!表明在传递或接收参数中出现了问题。
表3:从16位和32位Microsoft Excel 进行调用的结果
Microsoft Excel 4.0(16位版) Microsoft Excel 95(32位版)
我们能够编写宏以进行16位API调用或32位API调用,但是,我们必须编写可进行16位API调用和32位API调用的宏,这取决于Office 产品的版本。解决方案代码必须在16位Microsoft Excel 和32位Microsoft Excel 上均能运行。在我以前的文章(“将你基于16位Office的解决方案移植到32位Office ”)中描述的VBA函数Engine32不能在Microsoft Excel 4.0(Microsoft Excel 4.0不包含VBA)中正常工作。Engine32可在Microsoft Excel 5.0及更高版本上正常工作。一个与Microsoft Excel 4.0兼容的Engine32函数必须使用宏代码。
XLM的Engine32函数
对于XLM,解决方案与VBA解决方案类似。如果Microsoft Excel 是一个32位版,函数信息(“osversion(操作系统版本)”)将包含32。如果Microsoft Excel 是一个32位版,下列所示的Engine32宏返回TRUE;如果Microsoft Excel 是16位版,该宏返回FALSE。
REGISTER方法解决方案的示例
由于Enging32已定义,GetTickCount的宏代码很简单:
Engine32将在所有版本的Microsoft Excel 中运行正常。GetTickCount函数与在Windows3.1中的API调用是一样的。
注:如果对性能关心,你应在装载这个解决方案时注册所有API调用。
REGISTER方法解决方案的步骤
如果你正将Microsoft Excel 解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤:
创建一个名为APICALLS的新宏;
在APICALLS宏中创建Engine32函数;
定位解决方案中的所有REGISTER函数,并将它们移到APICALLS中,一个一栏;
使用上面的REGISTER解决方案示例中的宏代码作为一个模板,在你的宏表中为每个API创建函数;
添加所需的ARGUMENT行;
添加所需的RESULT行;
添加16位API REGISTER行,在函数名后加上16;
添加32位API REGISTER行,在函数名后加上32;
进行任何数据处理以进行API调用;
添加一个IF行以调用相应的API;
RETURE返回变量(如果有);
测试函数;
为Microsoft Excel 定义函数。
这个过程可使宏表中现有的API调用保持原样。一旦你创建了宏表APICALLS(并进行了测试),你可以把它加到其他解决方案中,重复使用这个宏,这样可节省转换时间。该宏表的内容独立于解决方案(它仅包含Windows API调用)并可在其他解决方案中重复使用。这个宏表APICALLS成为将来开发16位/32位解决方案的罗塞达石碑。
Declare方法
VBA的引入给Microsoft Excel 开发者提供了XLM的替代方法。Visual Basic 、WordBasic和Microsoft Access开发者也能毫无困难地编写Microsoft Excel 和Microsoft Project解决方案。Basic代码可在广泛的产品中进行交换。年轻一代的Office 开发者将在VBA中编写代码,而很少使用XLM。
从VBA中调用API需要使用API Declare语句。一个经声明的API可在Basic代码(或在一个宏中)的任何地方被调用。编写Declare语句有两种方法,如下所示:
Word Declare语句
注释:Word是首选的编写Declare 语句的方法
注释:它的格式是固定的,具有向后兼容性。
Declare Sub SubName Lib LibName$ [(ArgumentList)] [ Alias Routine$]
Declare Function FunctionName[$] Lib LibName$ [( ArgumentList)] [Alias Routine$] As Type
VBA、Basic和Microsoft Access Declare语句
Declare Sub globalname Lib "libname" [Alias "aliasname" ][([ argumentlist])]
Declare Function globalname Lib libname [Alias aliasname ] [([ argumentlist])] [As type]
因为我已在“将你基于16位Office的解决方案移植到32位Office ”中进行了这方面的一些讨论,并得出了上面的分类,我将简要地利用一个函数示例来说明Declare解决方案。
Basic Engine32函数
如果32位API调用是正常的(16位API调用失败),Engine32函数返回True;如果16位API调用是正常的(32位API调用失败),该函数返回False。在“将你基于16位Office的解决方案移植到32位Office ”一文中给出的Engine32函数被设计成可说明它们在每个Office 产品中的差异的示例。下面给出的是经修改后的函数,它具有更好的性能并能在更多的版本上运行。
利用在第一个函数中初始化静态变量可改善性能。随后的所有调用使用这些静态变量,而不是重复进行额外的函数调用。改善性能的另一方法是初始化一个全局或公有变量;然而,它存在一个
缺陷,在一些产品中发生Reset(重置)时,在全局变量得到重新初始化前,随后的所有API调用可能失败。
Microsoft Excel 5或更高版本和Project 4或更高版本
如果Microsoft Excel 和Microsoft Project 是32位的产品,它们中的Application.OperatingSystem属性通常包含32。由于Microsoft Excel 5.0既有16位产品又有32位产品,因此产品版本号是不充分的。
Function Engine32%()
Static sEngine32%,SEval% 注释:Statics用于性能的改善。
If SEval% Then Engine32%=sEngine32%: Exit Function
If instr(Application.OperatingSystem,"32") then sEngine32%=True
Seval%=True
Engine32%=sEngine32%
End Function
Microsoft Access 1.1或更高版
Microsoft Access不具有既有16位又有32位产品的版本。通过调用SysCmd检查版本号,确定正在使用的Microsoft Access的版本。Microsoft Access 1.1在构造时没有加入一个版本号常量,因此,我们通常使用7,以确保代码可在Microsoft Access中正常工作。这种方法可用来查看你的解决方案代码进行的是一个16位还是一个32位API调用。
Function Engine32% ()
Static sEngine32%,SEval%
If SEval% Then Engine32%=sEngine32%: Exit Function
If SysCmd(7) > 2 Then sEngine32% = True
Seval%=True
End Function
Word for Wndows 2.0或更高版
由于Word不支持静态变量,Word 必须每次对Engine32函数赋值。首先,我们检查产品版本号是否表明其是一个32位版本,然后我们检查操作系统的版本,查看它是否是一个32位的操作系统。这两个步骤是需要的,因为Word 6.0以前的版本不能使用GetSystemInfo,并且Word 6.0既具有16位版又具有32位版。
Function Engine32
Engine32 = 0
If Val(AppInfo$(2)) > 5 Then
OS$ = GetSystemInfo$(23)
If Val(OS$) > 6.3 Or Len(OS$) = 0 Then Engine32 = - 1
End If
End Function
Visual Basic
虽然Visual Basic 不使用解决方案代码,但Basic代码通常要与包含上述的产品之间进行交换。Visual Basic 4.0没有 Application.OperatingSystem 属性(它不是VBA所专有的一部分),而是使用条件编译#IF和 #ELSE。如果你打算将你的代码与其他Microsoft 产品共享,你应创建下列函数(不要在别处使用条件编译):
Function Engine32%()
注释:这仅针对 VB4。
#IF WIN16
Engine32% = False
#ELSE
Engine32% = True
#ENDIF
End Function
对于更早版本的Visual Basic ,使用下列函数:
Function Engine32%()
注释:这是用于 VB1 - VB3的; 不支持#IF。
Engine32% = False
End Function
Declare方法解决方案的示例
下列代码演示了Declare方法解决方案(不包括Word ):
Declare Function GetTickCount32 Lib "Kernel32" Alias "GetTickCount" () As Long
Declare Function GetTickCount16 Lib "USER" Alias "GetTickCount" () As Long
Function GetTickCount() As Long
If Engine32() Then
GetTickCount = GetTickCount32()
Else
GetTickCount = GetTickCount16()
End If
End Function
Engine32函数可用来确定进行的API调用。Declare语句指示实际的API函数名的Alias(别名),以避免偶尔的大小写变化(32位API调用是区分大小写的),然后在函数后添加16或32以表明API函数的位数(是16位还是32位)。
这个代码可在除Word 外的所有Office 产品中复制和粘贴。WordBasic在其他Office 产品使用Basic之前已经存在,并与之不同。我们将在后面探讨Word 解决方案。
Declare方法解决方案的步骤
如果你正将解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤:
创建一个名为APICalls的新模块;
在APICalls宏中创建Engine32函数;
定位解决方案中的所有Declare函数,并将它们移到APICalls中;
使用“Declare方法解决方案的示例”中的宏代码作为一个模板,为每个API创建函数;
自变量应与Windows 3.1 版API调用匹配;
结果应是Win32 API调用的结果(若需要,Visual Basic 将自动转换为Windows 3.1版);
添加16位API Declare行,在函数名后加上16;
添加32位API Declare行,在函数名后加上32;
进行任何数据处理;
添加一个IF行以调用相应的API;
RETURE返回变量(如果有);
测试函数。
这个过程使得在其他模块中现有的调用保持原样。一旦开发者定义并测试了APICalls模块,她或他可以将这个模块加入到其他解决方案中并可重复使用,这样可节省开发时间。这个模块的内容独立于解决方案,并且开发者可以重复使用这个模块。模块APICalls成为将来开发VBA中16位/32位解决方案的一个组成部分。
API包装的替代解决方案
我通常在API调用上加一层包装,而不是将API调用暴露在代码中。例如,我会将代码包装在GetProfileString上,以创建一个名为
vbGetWinIni的函数,该函数采用相同的自变量,但返回的是字符串。我在“创建有用的内在Visual Basic和Microsoft Access函数”一文中论述了公用API包装到DLL的转换问题。
如果你以这种方式编写代码,你可能想要修改API包装函数以调用相应的API,而不是额外创建函数。
Word Declare方法解决方案示例
Word具有不同的Declare格式和句法。Word解决方案比较复杂,这是因为你不能将16位和32位Declare语句都放在相同的宏中。
解决方案将创建三个宏库:APICALL16、APICALL32(它们包含针对每个操作环境的Declare语句)以及一个具有16位/32位互操作性的宏APICALLS。这可能听起来很混乱,那么让我们一步一步讨论。
首先,我们创建一个名为APICALL16的宏库。这个宏包含所有16位API Deca
lre语句。
注释:这是APICALL16 -- 所有16位Declare语句都包含在此处。
Declare Function GetTickCount16 Lib "USER" Alias "GetTickCount"() As Long
Function GetTickCount
GetTickCount = GetTickCount16
End Function
其次,我们创建一个名为APICALL32的宏库。这个宏包含所有32位API Declare语句。
注释:这是 APICALL32 -- 所有32位 Declare 语句都包含在此处。
Declare Function GetTickCount32 Lib "KERNEL32"() Alias "GetTickCount" As Long
Function GetTickCount
GetTickCount = GetTickCount32
End Function
第三步,我们创建一个名为APICALLS的宏库。这个宏包含Engine32和你的解决方案代码所要调用的过程。
注释:这是APICALLS -- 这个宏中不包含任何Declare语句。
Function Engine32
Engine32 = 0
If Val(AppInfo$(2)) > 5 Then
OS$ = GetSystemInfo$(23)
If Val(OS$) > 6.3 Or Len(OS$) = 0 Then Engine32 = - 1
End If
End Function
Function GetTickCount
If Engine32 Then
GetTickCount = APICall32.GetTickCount
Else
GetTickCount = APICall16.GetTickCount
End If
End Function
注释:其他API函数调用包含在此处。
你现在可以从你的解决方案代码中调用这个函数。你必须以APICALLS开始你的调用,例如:
Sub MAIN
MsgBox Str$(APICalls.GetTickCount)
End Sub
Word Declare方法解决方案的步骤
如果你正将Word解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤:
创建一个名为APICALLS的新模块;
在APICALLS中创建Engine32函数;
创建一个名为APICALL16的模块;
定位解决方案中的所有16位Declare语句,并将它们移到APICALL16中;
创建一个名为APICALL32的新模块;
创建相当的32位Declare语句,并将它们置于APICALL32中。
使用上述模板,为三个宏库中的每个API创建函数;
在调用所有API前,在你的解决方案代码中添加APICALLS;
测试每个函数。
这个过程使得在其他模块中现有的调用保持原样。一旦开发者定义和测试了这些宏,她或他可将这些宏添加到NORMAL.DOTMO模板中,并可在其他解决方案中重复使用这些宏,以节省时间。
类型库方法
进行API调用的类型库方法对于大多数开发者来说都是较新的内容。在Bruce McKinney的一本将由Microsoft出版的书《Visual Basic核心》中,包含了16位API调用(WIN16.TLB)的Windows API Functions函数类型库以及一个与32位API调用(WIN32.TLB)匹配的类型库。一旦这些类型库被注册,对于16位或32位版本的Office产品,相应的类型库将被装载。
一个类型库可提供Microsoft Excel 5.0或更高版、Microsoft Project 4.0或更高版、Visual Basic 4.0或更高版以及Microsoft Access 95或更高版产品的API调用的简易途径。所有这些Windows API调用变成了内在函数。(参阅我的“创建有用的内在Visual Basic和Microsoft Access函数”一文中的相关材料,它们仅适用于早期版本的Visual Basic和Microsoft Access)。由于对于我的读者来说,类型库方法是一种新的方法,我会详细逐步进行分析。
类型库的注册
下列步骤可完成在你注册中添加一个调用Windows API函数的类型库。这样,该类型库对于所有使用VBA的产品都可用,而不仅仅限于你进行该函数注册的产品。
打开任何VBA产品(例如,Microsoft Excel 5.0)。
为了创建一个模块,从“插入”菜单中选择“宏模块”。
从“工具”中选择“引用”。“引用”对话框的显示如图1。
图1:“引用”对话框
假设你未注册Windows API函数类型库,单击“浏览”按钮并定位到Win32.TLB,然后单击“确定”。对于WIN16.TLB,重复进行这个步骤。这样WIN16.TLB和WIN32.TLB都被注册。参见图2。
图2:选择类型库
在“浏览器”对话框关闭后,滚动到“引用”对话框的底部,你可以在列表中看到Windows API函数类型库(图3)。
图3:Windows API 函数的注册
关闭“引用”对话框。
从“视图”菜单中选择“对象浏览器”。
在“对象浏览”对话框上,在库/工作表下拉列表框中选择Win(Windows API 函数类型库)。所有可用的函数将显示在对象/模块和方法/属性列表框中(如图4)。
图4:Microsoft Excel 5.0“对象浏览器”显示Bruce McKinney的Windows API函数类型库中可用的对象和方法
在对象/模块列表框中,选择“Kernel”;然后在方法/属性列表框中选择“GetTickCount”。
单击“粘贴”按钮,GetTickCount出现在模块中。
下面示例列出的代码可实现在消息框中显示GetTickCount API调用的返回值。
Sub Demo()
MsgBox Str$(GetTickCount)
End Sub
不需要REGISTER命令和Declare语句。上面的代码就是你所需要的全部代码。
类型库方法解决方案的步骤
如果你正将解决方案转换为能在16位和32位产品上运行的解决方案,我建议你采用下列步骤--假设Windows API函数类型库已注册:
假设你仅使用标准的16位API调用,删除你的所有Declare语句;
检查“引用”对话框中的Wondows API函数检查框。
这就是全部步骤,你已经完成了移植。
类型库问题
类型库是一项发展中的技术,商业上可用的类型库还非常少。Bruce先生在使用Windows 3.1 API调用名设计16位和32位产品的类型库方面,作出了优异的工作。尽管如此,类型库仍存在一些问题留待解决。
Windows API函数类型库在VBA中添加了超过1000个的新保留词。所有包含在类型库中API调用变成了语言的保留词。如果你已经有一个名为ordShell的函数,你必须更改它的名称,这样才不会发生与类型库中定义的ordShell函数的冲突。
Windows API函数类型库不包括需要用户定义的类型(UDT)的API调用。这可能在下一版本中得到改善。
Windows API函数类型库方法在Microsoft Excel的speadsheet(电子数据表)中是不可用的。
这种技术是非常有希望的,并将在开发Office解决方案中简化API调用的使用。
原文转自:http://www.ltesting.net