第2章 API函数的声明和调用
在PowerBuilder中,API函数的调用属于外部函数的范畴。外部函数的调用与PowerBuilder的内部函数有许多不同之处,如在调用前必须预先声明、为函数传入正确的参数、指明函数传递的方式,以及定义函数返回值的类型等等。
2.1 PowerBuilder中API函数声明
2.1.1 PowerBuilder与API函数数据类型的转换
在微软出版的MSDN中给出了每个API函数C语言格式,函数的格式及参数的数据类型
完全是按照C的要求定义的。例如检索打印设备驱动性能的API函数DeviceCapabilities,在MSDN中的C定义如下:
DWORD DeviceCapabilities(
LPCTSTR pDevice, // printer name LPCTSTR pPort, // port name WORD fwCapability, // device capability LPTSTR pOutput, // output buffer CONST DEVMODE *pDevMode // device data buffer );
该声明中还包括一个名为DEVMODE的结构,在结构中包含打印机设备的初始化和环境信息,结构的C语言定义如下:
typedef struct _devicemode {
BCHAR dmDeviceName[CCHDEVICENAME]; WORD dmSpecVersion; WORD dmDriverVersion; WORD dmSize; WORD dmDriverExtra; DWORD dmFields; union { struct {
short dmOrientation; short dmPaperSize; short dmPaperLength; short dmPaperWidth; };
第2章 API函数的声明和调用 POINTL dmPosition; };
short dmScale; short dmCopies; short dmDefaultSource; short dmPrintQuality; short dmColor; short dmDuplex; short dmYResolution; short dmTTOption; short dmCollate;
BCHAR dmFormName[CCHFORMNAME]; WORD dmLogPixels; DWORD dmBitsPerPel; DWORD dmPelsWidth; DWORD dmPelsHeight; union {
DWORD dmDisplayFlags; DWORD dmNup; }
DWORD dmDisplayFrequency; #if(WINVER >= 0x0400) DWORD dmICMMethod; DWORD dmICMIntent; DWORD dmMediaType; DWORD dmDitherType; DWORD dmReserved1; DWORD dmReserved2;
#if (WINVER >= 0x0500) || (_WIN32_WINNT >= 0x0400) DWORD dmPanningWidth; DWORD dmPanningHeight; #endif
#endif /* WINVER >= 0x0400 */ } DEVMODE;
19 在上述定义中,包含了各种各样的C的数据类型,由于这些数据类型无论从形式上还是从内在含义上都与PB中的数据类型有一定的差异,要在PB中调用这些过程,需要将它们转换成合法的PB外部函数声明语句,并使用正确的参数调用它们。表2-1中列出了API函数的C原型的数据类型与PB中相应的数据类型对应关系。
C语言中的远指针,如LPBYTE、LPDWORD、LPRINT、LPLONG、LPVOID和LPWORD在PB中
20 PowerBuilder Win32 API函数调用参考手册 被声明为Long的数据类型。用于指示对象的句柄HANDLE表示环境设备的hDC被定义为一个32位无符号数,在PB中声明为无符号长整形ulong。在PB中并不支持C语言中诸如PSTR和NPSTR的近指针,并且关键字REF只适用于32位远指针。Windows中的32位远指针LPCTSTR、LPSTR在PB中被声明为String,LBYTE有时也声明为String。Windows中的BOOL是16位有符号数,在PB中被声明为Boolean或Long型。Windows中定义的WORD在PB中声明为UnsignedInteger型,Windows中定义的DWORD在PB中声明为UnsignedLong。PB中不支持Windows的80位双精度浮点数。PB中的Date、DateTime和Time有其特定的格式,与C中的数据类型没有直接的对应关系。
表2-1 C数据类型与PB数据类型的转换
Windows C原型 Bool PB数据类型 Boolean Windows C原型 Lpcwstr PB数据类型 Ref Blob (Unicode字符要使用PB ToUnicode函数进行转换) Byte, Char Char* Colorref Double Dword Float Handle Hdc Hfile Hinstance Hwnd Int Long Lparam Lpbyte Char Ref String Ulong Double Ulong N/A Ulong Ulong Ulong Ulong Ulong Int Long Ulong Ref Long Lpcvoid Lpdword Lpfiletime Lpint Lpstr,Lpcstr Lpvoid Lpword Mcierror Pbyte Short Structure Uint Void** Word Wparam Ref String Ref Ulong Ref Time Ref Long Ref String Ref Struct struct_inst Ref Ulong Long Ref Long[#] Int Ref Struct Struct_inst Ulong SUBROUTINE Uint Ulong 2.1.2 API函数的声明约定
对于PowerBuilder应用程序来讲,动态链接库中的函数是外部函数,为了调用这些函数,必须按照固定格式的约定向应用程序提供必要的信息,这种提供信息的的操作称为外部函数的声明。在PowerBuilder任何脚本中使用外部函数之前,必须首先声明外部函数。PowerBuilder就是通过这种声明来访问动态库中的。声明的作用就是“告知”PB系统,可以使用外部函数了。
PB系统为窗口、菜单、函数、用户对象提供了一个声明区域,专门用于声明外部函数和
第2章 API函数的声明和调用 21 变量,同PowerBuilder中的用户自定义函数一样,用户可以声明以下两种类型的外部函数:
(1)全局外部函数(Global External Functions):可以在应用的任意位置调用; (2)对象级外部函数(Local External Functions):在窗口、菜单、用户对象或用户自定义函数等对象中定义。
在应用的开发过程中可以根据以下原则决定使用全局外部函数或局部对象级外部函数: (1)若外部函数作为一般用途并适用于整个应用,则定义为全局外部函数;
(2)若外部函数作为特殊用途只适用于特定对象,则将函数定义为对象级外部函数。 对于对象级外部函数,用户仍可在应用的任何位置调用,只是需要把它作为一个特定的对象类型。
PB中外部函数的声明书写必须符合一定的约定,否则PB将警告错误调用外部函数。在PB中根据外部函数代表的过程是否有返回值,函数的声明书写有所不同。
外部函数的声明以两个关键字FUNCTION或SUBROUTINE之一为开始,如果函数代表的过程返回一个值,应将其声明为FUNCTION,声明语法如下:
{Access} FUNCTION ReturnDataType FunctionName ({REF}{DataTypel Arg1,...,DataTypeN ArgN})LIBRARY LibName {ALIAS FOR ExternalName}
如果函数代表的过程无返回值,或者在C语言中返回一个Void数据类型,声明的语法是:
{Access} SUBROUTINE SubroutineName({REF}{DataType1 Arg1,...,DataTypeN ArgN}) LIBRARY LibName {ALIAS FOR ExternalName}
在上面声明语句中,大括号中的内容是可选的。此外,PB对上面语句中的各个部分大小写不敏感,如FUNCTION与Function是一样的。
声明中的其它各个关键字作用如下:
Access :表示函数的访问级别,用户可以为对象级外部函数指定Public、Protected或Private三个类型的访问级别,缺省为Public,此参数只能在对象级外部函数中使用;
ReturnDataType:表示函数返回值的数据类型。返回值的数据类型必须与PB支持的数据类型相匹配;
FunctionName/SubroutineName :在PowerScript脚本中引用的外部函数或过程的名称。该名称可以由用户自己定义,但如果指定了与动态库中函数不一致的名称,就必须通过Alias For子句给出动态库中函数过程的真正名称;
REF:标明变量通过引用方式传递;
DataType:外部函数中参数的数据类型;
Arg:外部函数的参数名。对于具有多个参数的过程,各参数以逗号分开;
LibraryName:表示外部函数的动态库DLL或EXE的文件名。如果引用的过程属于 Windows 核心库(User32、Kernel32 或 GDI32),则可以不包含文件扩展名;
ExternalName:表示在DLL库中的外部函数或子程序名。如果调用的 Windows API 过程要使用字符串,或为函数指定别名,那么在声明语句中必须增加一个 Alias For子句,以指定正确的字符集。Alias For关键词后面的字符串必须是动态库中函数过程的真正名称。Alias For的使用大致可分为以下四种情况:
1. 区别ANSI和Unicode字符集
22 PowerBuilder Win32 API函数调用参考手册 如果调用的 API函数或过程要使用字符串,那么在声明语句中必须增加一个 Alias For子句,以指定正确的字符集。包含字符串的API函数实际有两种版本,即ANSI版本 和 Unicode版本。因此,在Windows头文件中,每个包含字符串的函数都同时定义了 ANSI 版本和 Unicode 版本。Windows NT、Windows 2000 、Windows XP同时支持 Unicode 和ANSI字符集,而 Windows 95/98 只支持ANSI字符集。请注意,Alias For子句后面的字符串必须是过程的真正名称。
例如,下面是 SetWindowText 函数的两种 C 语言描述。可以看到,第一个描述将函数定义为 SetWindowTextA,尾部的“A”表明它是一个 ANSI 函数:
SetWindowTextA(HWND hWnd,LPCSTR lpString);
第二个描述将它定义为 SetWindowTextW,尾部的“W”表明它是一个 Unicode 函数:
SetWindowTextW(HWND hWnd,LPCWSTR lpString);
因为两个函数实际的名称都不是“SetWindowText”,要引用正确的函数就必须增加一个 Alias For子句:
FUNCTION Long SetWindowText(Long hwnd,REF String lpString) LIBRARY \FOR \
为了保证使用PB开发的应用系统能在所有主流Windows操作系统下运行,无论是Windows NT、Windows 2000还是Windows 95/98,可统一使用Alias For子句指定为ANSI格式的版本。仅当应用程序只运行在 Windows NT 、Windows 2000平台上的时候才可以使用 Unicode 版本。
2. 为具有不同参数类型函数指定别名
许多API函数的参数的数据类型被定义为Any型,表示该参数可接受多种数据类型的传入。为了避免使用Any型参数带来的不确定性,在实际应用中我们常常显式给出参数的数据类型,如SendMessage函数常用来向Windows下的对象发送消息,其后两个参数用来表示消息的信息,参数数据类型随消息而变化。如果在同一段脚本中需要同时使用同一函数的不同参数类型的定义,这时有两种处理方式:
(1)使用同名外部过载函数
所谓过载函数是指可以定义一个具有相同函数名、不同类型参数的函数。当PB要调用函数时,首先把参数与函数原型进行比较,以决定应该调用哪一个函数,例如:
Function Long SendMessage (Long hwnd, Long wMsg, Long wParam, Long lParam) Library \Alias For \
Function Long SendMessage (Long hwnd, Long wMsg, Long wParam, String lParam) Library \Alias For \
由于参数的数据类型决定了PB要调用那一个版本的函数,因此使用同名过载函数时要注意参数类型的差别。例如,当过载函数的一个版本处理数值类型,另外一个版本处理字符型数据,用两类没有联系的数据类型定义的过载函数在使用时不会产生任何问题。但是,如果使用相关类型的参数,如不同的数值类型数据(例如Long、Ulong、Integer)或字符型数据(例如String、Char)定义了不同的过载函数,由于PB在调用函数时可能对数据类型进行转换,从而导致调用错误的函数,产生引发系统发生错误的潜在可能性。