38 lstr_Point[1].y = 10
PowerBuilder Win32 API函数调用参考手册 由于在引用对象成员时,引用名称(黑体部分)与结构定义的名称不一致而发生上述错误。
错误信息四:Bad argument list for function :XXXX
这种错误在编辑环境中就可以发现,产生这种错误的原因有三种: a.为函数传递的参数数目与函数声明的数目不一致;
b.为函数传递的参数数据类型与函数声明的参数数据类型不一致,如函数声明了一个Long型参数,在调用时传入了String型变量等;
c.函数参数动态数组与定长数组混用。如外部函数声明为动态数组,但调用时传入了定长数组等。
错误信息五:Error calling external function XXXX at line XX in ...... 这种错误在运行时才能发现,如图2.3所示,错误含义为“错误调用了外部函数XXXX”。产生这种错误的原因有三种:
图2.3 “错误调用了外部函数XXXX”错误
a.函数名拼写错误;
b.引用了动态库中不存在的函数;
c.对于具有String型参数的外部函数,在声明时没有使用Alias For指定函数的真实名称,如将函数GetWindowsDirectory声明改为:
FUNCTION Long GetWindowsDirectory ( REF String lpBuffer, Long nSize) LIBRARY \
由于没有使用Alias For指定函数的真实名称GetWindowsDirectoryA,执行时产生图3.9的错误。
错误信息六:Error opening DLL Library XXXX for external function in ...... 这种错误在运行时才能发现,如图2.4所示,错误含义为“错误打开DLL动态库XXXX”。产生这种错误的原因有两种:
图2.4 “错误打开DLL动态库XXXX”错误
a.外部函数声明时,动态库名称拼写错误;
第2章 API函数的声明和调用 39 b.引用的DLL动态库文件不存在或DLL动态库文件不在当前目录、Windows目录或Windows System目录。
2.3.2 防止错误的一些基本规则
PB作为一种高效Client/Server开发环境,它封装了部分Windows API函数,但也牺牲了一些API的功能。调用API时稍有不慎就可能导致API调用错误,出现难于捕获或间歇性错误,最常导致的就是Windows保护性错误,PB系统的崩溃,如死机、无任何提示的异常退出等。要减少API编程错误,提高PB调用API时的安全性和稳定性,应遵循以下一些原则:
1.注意检查参数类型和调用方式
参数类型和调用方式是API函数调用中最容易出现问题的部分,要特别注意以下两个方面:
(1)确保为函数参数声明了正确的数据类型。在Win32环境下,参数数据类型问题尤为突出。因为16位和32位数值变量都可能按32位传递,当这些数值同时使用时很难发现其中的错误。
(2)确保调用方式的正确性。对于引用传递和值传递两种调用方式,在声明函数时,如果没有在参数前加REF,PB将默认为值传递。在声明函数时一定要检查每个参数是否缺少了必要的REF,或者添加了多余的REF,以确定每个REF是否都有使用的必要。
由于到目前为止,Sybase公司未发布官方的API浏览器,因此使用第三方提供的此类工具软件时,如果函数调用总是失败,应根据微软的MSDN检查函数的参数类型和传递方式是否正确。
2.不使用Any数据类型
尽管PowerBuilder支持Any数据类型,我们在第2章已经提到使用Any数据类型具有很大的不确定性和不安全性,因此应尽量不使用Any数据类型。
3.为引用传递的字符串开辟足够大的缓冲区
为了说明为引用传递的字符串开辟缓冲区的重要性,我们先看以下脚本,脚本中调用API函数GetWindowsDirectory 获取系统的安装路径:
//声明函数和常量
FUNCTION Long GetWindowsDirectory ( REF String lpBuffer, Long nSize) LIBRARY \ALIAS FOR \
CONSTANT Long MAX_PATH = 260
//获取Windows目录路径 Long ll_RTN
String ls_GetWinPath
ll_RTN = GetWindowsDIrectory(ls_GetWinPath, MAX_PATH)
40
If ll_RTN <> 0 Then
PowerBuilder Win32 API函数调用参考手册 //使用Trim()函数剔除多余的空字符 ls_GetWinPath = Trim(ls_GetWinPath) ls_GetWinPath = “ ”
Else End If
上述脚本乍一看很完美,其实该段脚本隐含着一个致命的错误,那就是在调用函数之前,没有为返回Windows安装路径的字符串ls_GetWinPath分配存储空间。脚本执行后会时而正常,时而异常。要克服这样的错误,就是在调用函数前使用Space函数为ls_GetWinPath分配内存空间,即:
ls_GetWinPath = Space(MAX_PATH)
之所以要为引用传递的字符串分配存储空间,这是因为如果API函数要求一个字符型参数为指向某段内存缓冲区的指针,以便向其中写入数据。因为外部函数无法知道字符串缓存区所需的长度,其总是假定已为其分配有足够的长度。如果没有初始化字符串,分配给字符串的缓冲区有可能会不足,外部函数将有可能在缓冲区末尾反复改写,或“侵犯”别的变量的内存缓存区,将会使应用程序运行表现为异常终止、间歇性错误,或Windows系统的保护性错误的发生。
4.及时释放使用过的资源,防止内存泄漏
如果在使用动态库中的函数时使用了GDI对象,一定要及时释放它们,否则会使Windows因申请GDI资源失败死机或崩溃。例如在程序中如果调用了GetDC函数,当一系列针对设备场景操作指令结束后,一定要记得调用另外一个API函数ReleaseDC释放设备场景DC。为什么要调用这一函数呢?这是因为DC是Windows系统在内存中划出一部分内存作为绘图的虚拟设备,也就是说DC会占用相当程度的内存及系统资源。因此,绘图结束后,及时释放DC所占用的内存资源是十分必要的。对于CreateDC或CreateCompatibleDC函数创建的设备场景,当使用结束后,应使用DeleteDC函数删除创建的DC。
5.注意检查参数和返回值
在调用API函数时,如果函数的执行结果与所期望的不一致,往往是由于某一个或几个的参数类型不正确,或为参数传入的值(如标识常量)错误。例如,假定函数需要指向设备场景的句柄,而直接传送了窗口句柄,则显然会导致函数执行错误。因此,应随时对程序进行跟踪,了解参数的来源,检查参数的类型是否正确,传送的值是否是函数所需要的。
绝大多数API函数都会返回一个结果,用来表示函数是否调用成功。如返回0值表示函数调用失败,返回非0值表示函数调用成功(有时正好相反)。对函数的返回值进行测试,对判断外部函数是否正确调用非常有益,如果发现函数调用失败,就可回过头检查函数的参数类型、传递方式、传送值是否有问题。
6.注意经常保存正在编辑的程序
上面我们已经提到调用系统动态库中的API函数,将有可能破坏PowerBuilder原有的
第2章 API函数的声明和调用 41 安全机制,引起系统的不稳定运行,从而所带来的一些潜在风险,如数据或脚本代码的丢失等。减少风险的良策除了不断提高程序设计水平外,就是经常地保存和备份自己所做的工作。
42 PowerBuilder Win32 API函数调用参考手册 第2章 API函数的声明和调用 ..................................................................................... 18
2.1 PowerBuilder中API函数声明 .................................................................... 18 2.1.1 API函数数据类型的转换 ............................................................................. 18 2.1.2 API函数的声明约定 ........................................................................................ 20 2.2 PowerBuilder与API函数之间的参数传递 ................................................ 24 2.2.1 参数传递的两种方式 ....................................................................................... 24 2.2.2 传递数值型数据 ............................................................................................ 26 2.2.3 传递字符串型数据 ........................................................................................ 27 2.2.4 传递结构型数据 ............................................................................................ 28 2.2.5 传递数组 ........................................................................................................ 30 2.2.6 NULL值传递 ................................................................................................... 34 2.2.7 传递属性 ........................................................................................................ 35 2.2.8 能传递函数指针吗? .................................................................................... 35 2.2.9 能传递Any数据类型吗? ............................................................................ 35 2.3 API函数调用错误信息及排错技巧 .............................................................. 36 2.3.1 常见错误信息解析 ........................................................................................ 36 2.3.2 防止错误的一些基本规则 ............................................................................ 39