pb009参考手册API函数的声明和调用(2)

2019-02-17 10:41

第2章 API函数的声明和调用 23 (2)使用Alias For为外部函数指定不同名称

为了避免使用同名过载函数带来的不确定性,所以最好不定义依靠不同数值类型区别的外部过载函数。同时为了程序代码的一目了然,可以使用Alias For为外部函数指定不同名称,例如:

Function Long SendMessageLong (Long hwnd, Long wMsg, Long wParam, Long lParam) Library \

Function Long SendMessageString (Long hwnd, Long wMsg, Long wParam, String lParam) Library \

第一个函数声明表示SendMessage参数类型为长整形,第二个函数声明表示参数类型为字符型,这样做的好处是显而易见的。

3. 增加API函数名称的可读性

大多数API函数的名称间接也表示了该函数的作用,如GetWindowsDirectory表示函数可以返回Windows系统的安装路径。但也有个别的 API函数的名称看起来有些稀奇古怪,如RtlMoveMemory函数,函数的作用是在内存块中拷贝数据,无论是在微软的MSDN的C语言例程中,还是在众多的Visual Basic例程中,已经很少有程序员使用RtlMoveMemory名称,而赋予了一个更加通俗易懂的名称CopyMemory,在PB中如果我们还沿用这种习惯,它可声明为:

FUNCTION Long CopyMemory( Any dest, Any src, Long length ) LIBRARY \\

在声明中,CopyMemory 是PB中使用的过程名称,而 RtlMoveMemory 则是 DLL 中可以识别的真实名称。

4. 避免API函数的不标准名称

有时,个别的 API函数的名称不是有效的标识符。例如,它可能包含了非法的字符(如连字符),或者名称是PB的关键字。在这种情况下,可以使用 Alias For 关键字。

例如,动态库中的某些过程名以下划线开始。尽管在PB标识符中允许使用下划线,但是下划线不能作为标识符的第一个字符。为了使用这种过程,必须先声明一个名称合法的过程,然后用 Alias For子句引用过程的真实名称,例如:

Function Long lopen (String lpPathName, Long iReadWrite) Library \\

在上例中,lopen 是PB中使用的过程名称。而 _lopen 则是 DLL 中可以识别的真实名称。

此外,有极少数API函数名称与PB的关键字(保留字)相同,如Winsock中的select、connect等网络连接Socket API函数,这时就必须通过Alias For子句为函数提供别名,例如:

FUNCTION Long pbselect ( Long nfds, REF fd_set readfds, REF fd_set writefds, REF fd_set exceptfds, Long timeout ) LIBRARY \

FUNCTION Long ConnectToHost ( Long s, REF sockaddr_in name, Long namelen ) LIBRARY \

24 PowerBuilder Win32 API函数调用参考手册 在上述两个声明中,pbselect 和ConnectToHost是作者自定义的PB中使用的函数别名,而 select、connect 则是 DLL 中函数的真实名称。

LIBRARY:关键字用来告诉PB如何找到包含过程的 .dll 文件。如果引用的过程属于 Windows 核心库(User32、Kernel32 或 GDI32),则可以不包含文件扩展名.dll。

2.2 PowerBuilder与API函数之间的参数传递

2.2.1 参数的值传递与引用传递

调用Windows系统动态库中的函数时,大部分函数需要传入一个或多个参数。参数的传递方式可以是值传递(Passed By Value),也可以是引用传递(Passed By Reference,也称之为地址传递)。缺省情况下,在PowerBuilder的脚本中调用DLL中的函数是通过传值方式来传递参数,也就是说PowerBuilder将对要传递的参数做一份拷贝,然后通过堆栈将这份拷贝传递给外部函数,外部函数中对该参数的任何修改并不影响到PowerBuilder中原参数的值。如果希望DLL中的函数可以改变调用参数的原值,就可以通过引用方式来传递参数,即在参数数据类型前面加REF关键字来声明该参数将要用引用方式传递参数。通过引用方式传递参数时,外部函数得到指向该参数的指针,外部函数可以对该参数的值进行修改,并且将修改后的结果返回到PowerBuilder中。

下面我们通过一个实例进一步说明两种传递方式的不同。存放在系统动态库Kernel32.dll中的函数GetWindowsDirectory可以在其第一个参数中返回当前计算机Windows系统的安装路径,该函数的PB声明如下:

FUNCTION Long GetWindowsDirectory ( REF String lpBuffer, Long nSize) LIBRARY \ALIAS FOR \

函数的第一个参数为存放Windows目录的完整路径字符串缓冲区,加REF关键字,定义为引用方式传递;第二个参数定义为值传递,用来指定字符串缓冲区的大小,如通常将字符串缓冲区大小指定为常量MAX_PATH。函数调用成功后,将在lpBuffer中返回Windows路径,完整的程序脚本如下:

//声明函数和常量

FUNCTION Long GetWindowsDirectory ( REF String lpBuffer, Long nSize) LIBRARY \ALIAS FOR \

CONSTANT Long MAX_PATH = 260

//获取Windows目录路径 Long ll_RTN

String ls_GetWinPath

ls_GetWinPath = Space(MAX_PATH)

第2章 API函数的声明和调用

If ll_RTN <> 0 Then

//使用Trim()函数剔除多余的空字符 ls_GetWinPath = Trim(ls_GetWinPath) ls_GetWinPath = “ ”

ll_RTN = GetWindowsDIrectory(ls_GetWinPath, MAX_PATH)

25 Else End If

在上述脚本中,用于返回Windows路径的字符串参数必须被设置为“传址”引用,并且在调用之前一定要使用Space函数为其分配内存空间。

上PowerBuilder与DLL库中函数参数传递的两种方式――“值传递”和“引用传递”,只是PB与外部函数间传递参数的一种原则。具体到每一个外部函数声明时,每一个参数的传递方式是如何确定的呢?事实上,每一个参数的传递方式都必须遵循微软在编制动态库时对函数参数的定义,即参数是“传入参数”还是“传出参数”。所谓“传入参数”就是为动态库中的函数过程传入所需值的参数,而“传出参数”则是用于返回动态库中的函数过程执行结果的参数。在微软出版的MSDN中,以[in]或[out]方式标示出了API函数各个参数的是“传入参数”,还是“传出参数”,甚至一些参数可以同时是“传入参数”和“传出参数”,以[in/out]表示。

下图3.3给出了API函数GetUserNameEx在MSDN中的定义,该函数用于检索当前系统用户名称。

图3.3

MSDN中以[in]、[out]表示参数传递方式

26 PowerBuilder Win32 API函数调用参考手册 函数的各个参数定义如下:

NameFormat [in] 表示要检索名称格式;

lpNameBuffer [out] 用于返回用户名称的字符串缓冲区;

nSize [in/out] 当为输入参数时,用于指定字符串缓冲区的大小;

对于参数nSize如果我们初始指定的缓冲区长度太小,函数将在该参数中返回所需的缓冲区大小,此时参数转换为传出参数。有时,我们为了获取字符串缓冲区所需的确切长度,我们故意在第一次调用函数时,传入一个较小的值,然后再次调用函数,把函数第一次调用时返回的长度传递给函数,确保缓冲区为所需的长度,我们把这种调用方法称之为“两步调用法”。

显然,对于“传入参数”,其传递方式一定为“值传递”,而对于“传出参数”其传递方式一定为“引用传递”。因此,GetUserNameEx函数在PB中的声明格式为:

FUNCTION Long GetUserNameEx ( Long NameFormatID, REF String lpNameBuffer, REF Long nSize) LIBRARY \

综上所述,参数是否定义为“引用传递”,即在声明为PB外部函数时是否加关键字REF,必须依据MSDN中函数定义确定,而不能随意添加。

2.2.2 传递数值型数据

下面通过实例来说明如何通过“值传递”和“引用传递”方式传递数值型参数。假如在PB中声明了一个外部函数TEMP,函数返回Integer值,并且需通过引用的方式为函数传入一个Integer参数,即:

FUNCTION Integer TEMP( REF Integer Degree) LIBRARY \

同样的语句在C语言中将为:

int _stdcall TEMP(int * degree)

由于参数是引用传递,函数能改变参数的内容,这种改变将直接影响到PB中原变量的值。例如,C语句 *degree = 75将把参数degree值改变为75,并将75返回到PowerBuilder中。

假如在PB中声明了一个外部函数TEMP2,函数返回Integer值,并且需通过“值传递”的方式为函数传入一个Integer参数,即:

FUNCTION Integer TEMP( Integer Degree) LIBRARY \

同样的语句在C语言中将为:

int _stdcall TEMP2(int degree)

由于参数以传值的方式传递,尽管函数可以改变参数的内容,但这种改变只能影响到参数在过程中的拷贝的值,PB中的原变量值不受影响。

第2章 API函数的声明和调用 27 2.2.3 传递字符串型数据

对于字符串型参数,传递情况比较特殊。在Visual Basic中似乎无论是否指明参数传递方式,其统一按引用方式传递。在PB中,由于PB只能访问属于自己的内存,因此外部函数不能返回一个指向字符串缓冲区地址的指针给PB,即不能向PB返回内存地址。

但是,当PB向外部函数传递字符串时,不管通过引用还是通过值传递,PB都传递一个指向该字符串的指针给外部函数。如果通过值传递字符串,那么在外部函数中对该字符串所做的修改,PB是不能访问到的,也就是说不会影响到字符串原来的值;如果通过引用的方式传递字符串,外部函数过程改变字符串的值后,则可以访问到,即会引起字符串值的变化。下面通过两个实例进一步说明这两种传递方式。

示例一:在PB中声明一个名为FunName的外部函数,这个函数将通过值传递一个字符串。

FUNCTION String FunName(String Arg) LIBRARY “LibName.Dll”

在C中需要指向一个包含字符串的缓冲区,FunName的外部函数C原型应为:

Char *_stdcall FunName(Char *Arg)

因为字符串是通过值进行传递的,所以C函数过程能够改变Arg的局部副本的值,但不能影响PB原变量的值。

示例二:在PB中声明一个名为FunName1的外部函数,这个函数将通过引用传递一个字符串。

FUNCTION String FunName1(REF String Arg) LIBRARY “LibName.Dll”

FunName1的外部函数C原型与通过值传递的原型相同:

Char *_stdcall FunName1(Char *Arg)

因为字符串参数是通过引用进行传递的,C函数过程能够改变参数的值以及PB原来的变量值。例如字符串拷贝API函数Strcpy(ArgStr1, REF ArgStr2)将把ArgStr1值改变为ArgStr2,同时也把调用这个函数的PB脚本中的值改变为ArgStr2。

如果例二中FunName1函数的作用是传入用户ID,然后在函数内部转换为用户名。那么,PowerScript字符串变量Arg必须足够长,以便能够容纳由函数返回的用户名。为了确保字符串变量足够长,可以在声明字符串变量后,用Space函数填充字符串。如假设用户ID长度为5个字符,返回的用户名可能最大长度为40个字符,那么在调用FunName1函数前应在字符串中至少再填充35个字符,即:

String ls_Arg

ls_Arg = ID + Space(35)

FunName1(ls_Arg)

下面我们再通过两个实际的外部函数来进一步说明字符串参数的传递,第一个是我们上面提到的函数GetWindowsDirectory,其PB声明表示第一个参数为引用传递:


pb009参考手册API函数的声明和调用(2).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:经济法基础支付结算法律制度习题

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: