第2章 API函数的声明和调用 ll_hDC = GetDC(ll_WinHandle)
//绘制多边形
Polygon(ll_hDC, lstr_Point, 5)
33
2. 通过Blob型变量间接向外部函数传递数组
除了上面将参数直接声明为动态数组方法外,在PB中还可以通过将数组元素打包到Blob类型的变量中,然后将Blob变量传递给外部函数,从而达到相同的目的。
修改多边形绘制外部函数Polygon声明如下:
FUNCTION Long Polygon ( Long hdc, Blob lpPoint, Long nCount) LIBRARY \
按钮“绘制多边形”的Clicked事件脚本,演示了如何向外部函数传递数组,函数脚本如下:
Long ll_hDC, ll_WinHandle
POINTAPI lstr_Point[] //声明一个结构型数组 Blob lb_Points Integer I, li_Pos = 1
//指定多边形各顶点的坐标 lstr_Point[1].x = 10 lstr_Point[1].y = 10 lstr_Point[2].x = 250 lstr_Point[2].y = 30 lstr_Point[3].x = 166 lstr_Point[3].y = 80 lstr_Point[4].x = 150 lstr_Point[4].y = 150 lstr_Point[5].x = 50 lstr_Point[5].y = 20
//取得窗口的句柄
ll_WinHandle = Handle(Parent)
//获得窗口的设备场景 ll_hDC = GetDC(ll_WinHandle)
//为Blob变量分配存储空间,每个点x、y坐标各占用4个字节,共10个值 lb_Points = Blob(Space(4*10))
34 For i = 1 To 5
//绘制多边形
PowerBuilder Win32 API函数调用参考手册 //将多边形各顶点的坐标拷贝到Blob变量中
li_Pos = BlobEdit(lb_Points, li_Pos, lstr_Point[i].x) li_Pos = BlobEdit(lb_Points, li_Pos, lstr_Point[i].y)
Next
Polygon(ll_hDC, lb_Points, 5)
上述脚本执行后,也会绘制出图2.1中的多边形。
2.2.6 NULL值传递
某些 DLL 函数过程希望接收到的参数为字符串或NULL值,如果要将NULL值传递给函数,有以下两种处理方式。
1. 传递NULL字符串
如果要将NULL值传递到字符串参数,需要将参数声明为 String型,并传递常数 pbNullString。也许读者会说PB中并不存在这样一个常量,的确PB没有类似Visual Basic 字符串NULL值常量 vbNullString,但我们自己可以定义,脚本如下:
String pbNullString SetNull(pbNullString)
这样在后续脚本中,就可以引用pbNullString字符串NULL值常量。例如,FindWindow 函数能够确定系统中是否有另外的应用程序正在运行。它需要两个字符串参数,一个表示应用程序的类名,另一个表示窗口的标题栏:
FUNCTION Long FindWindow ( String lpClassName, String lpWindowName) LIBRARY \FOR \
这两个参数都可以传递 NULL值。而传递零长度的字符串 (\将不起作用,因为这将传递指向零长度字符串的指针。该指针的值不会是 0。可以用实际值 0 来作为参数传递。为了保证参数的正确性,最简单的办法是使用自定义NULL字符串 pbNullString:
hWndExcel = FindWindow(pbNullString, \
2. 传递0值
处理上述情况的另外一种办法是改写声明部分,将需要传递NULL 的参数的数据类型声明为 Long。这样修改以后,在调用时将该参数设置为 0 即可,如下所示:
FUNCTION Long FindWindow (Long lpClassName, String lpWindowName) LIBRARY \\
调用脚本如下:
hWndExcel = FindWindow(0, \
第2章 API函数的声明和调用 35 2.2.7 传递属性
一些API函数过程需要传入窗口的句柄hWnd,或设备场景的句柄hDC等属性(有关概念我们将在后续章节中详细介绍)。属性必须以值方式传递。之所以以值方式传递,因为句柄实际上是一个32位的长整型值。如果某个参数被声明为传值,那么它可以直接传递属性。例如,可以使用下面的API函数确定屏幕或打印机的大小(以像素为单位):
FUNCTION Long GetDeviceCaps ( Long hdc, Long nIndex) LIBRARY \
还可以将窗口或其它对象的 hDC 属性传递到上面的过程中,从而得到屏幕或当前选定的打印机支持的颜色数。如下面程序脚本片段,首先创建与屏幕兼容的设备场景,并将返回的设备场景句柄传递给GetDeviceCaps函数过程,进而可以取得当前显示设备支持的颜色位数:
Long ll_ScreenDC, li_ColorBit
//创建与屏幕兼容的设备场景
ll_ScreenDC = CreateDC(\
//获取当前屏幕的颜色的位如16, 24, 32等
li_ColorBit = GetDeviceCaps(ll_ScreenDC, BITSPIXEL)
2.2.8 能传递函数指针吗?
熟悉 C 语言的程序员一定会熟悉函数指针的概念。对于不熟悉 C 语言的读者,有必要对此进行一些解释。
函数指针是一种约定,程序员可以用它将一个自定义的函数的地址作为参数传递到另一个函数。后面一个函数可以不是自己编写的,但是已经进行了声明,所以可以在应用程序中使用。利用函数指针,可以调用 EnumWindows 等函数列出系统中打开的窗口,利用 EnumFontFamilies 列出所有的当前字体。利用函数指针还可以访问 Win32 API 中的其它许多函数,Visual Basic从5.0版本开始支持对函数指针的传递,即提供了关键字AddressOf来获取函数过程的地址。但PB迄今,尚未支持函数指针的传递。
PowerBuilder不支持函数指针的传递,因此在PowerBuilder中不能使用回调函数(Callback Funcion)。这在一定程度上限制了Win32 API函数在PowerBuilder中的使用。
2.2.9 能传递Any数据类型吗?
某些 API函数的同一个参数能够接受多种数据类型。如果需要传递多种类型的数据,那么是否可以将参数声明为Any型数据类型,从而取消数据类型限制呢?
36 PowerBuilder Win32 API函数调用参考手册 例如,我们前面曾经提到的用来发送消息的函数SendMessage, 后两个参数wParam和lParam的含义随消息wMsg参数不同而改变,可以设置为不同的数据类型。如果我们将其在PB中声明如下:
FUNCTION Long SendMessage (Long hwnd, Long wMsg, Long wParam, Any lParam) LIBRARY \ALIAS FOR \
当我们在脚本中调用函数时,无论为第四个参数传入何种类型的数据,都将会产生“DLL函数不支持参数类型”的错误信息,如下图2.2所示:
图2.2 传递Any型参数产生的错误
事实上,所谓Any类型,很多情况下是允许两种数据类型,一般是字符串类型和数值(长整型)类型。这样,我们在函数声明时可以通过Alias For为函数定义“别名”,来具体指明函数参数所需的数据类型。如上面介绍的SendMesssage函数,如果希望用来传递字符串数据,则可声明为:
FUNCTION Long SendMessageString (Long hwnd, Long wMsg, Long wParam, String lParam) LIBRARY \
如果希望发送数值型数据,则可声明为:
FUNCTION Long SendMessageLong (Long hwnd, Long wMsg, Long wParam, Long lParam) LIBRARY \
2.3 API函数调用错误信息及排错技巧
当调用动态库中的API函数时,不可避免的会产生这样或那样的问题。本节将对常见的
错误信息给出说明,并且介绍如何避免函数调用时产生错误。
2.3.1 常见错误信息解析
以下是一些外部函数声明和调用时常见错误信息解释。 错误信息一:Unknown Function Name:XXXXXX
这种错误在编辑环境中就可以发现,表示所引用的函数没有定义。错误产生的原因除了根本就未定义引用的函数外,就是常见的函数名拼写错误引起的。
错误信息二:Reference Argument must be a non-constant and non-readonly variable
第2章 API函数的声明和调用 37 reference.
这种错误在编辑环境中就可以发现,是由于直接为引用方式传递的参数传入常量值引起的,如:
FUNCTION Long GetWindowsDirectory ( REF String lpBuffer, REF Long nSize) LIBRARY \ALIAS FOR \
如果我们在调用脚本时书写如下,将会产生上述错误:
GetWindowsDirectory (ls_GetWinDir, 256) String ls_GetWinDir ls_GetWinDir = Space(256)
函数GetWindowsDirectory第二个参数由于声明为REF传递,在调用时直接传入256数值而产生上述错误。
要避免产生这种错误,可采用以下两种方式:一是修改函数的声明,将不需要声明为引用传递参数,声明为值传递,如上述函数:
FUNCTION Long GetWindowsDirectory ( REF String lpBuffer, Long nSize) LIBRARY \ALIAS FOR \
二是借助一个变量,将变量赋值后再传递给函数,如上述脚本可修改为(黑体部分):
GetWindowsDirectory (ls_GetWinDir, ll_BufferSize) ls_GetWinDir = Space(256) ll_BufferSize = 256 String ls_GetWinDir Long ll_BufferSize
错误信息三:Incompatible property XXXX for type XXXX.
这种错误在编辑环境中就可以发现,错误是由于在引用结构成员时,结构成员名拼写错误或不存在,如我们为函数Polygon定义了结构POINTAPI如下:
Long xpos Long ypos
//多边形顶点的x坐标 //多边形顶点的y坐标
Long ll_hDC, ll_WinHandle //声明一个结构型数组 POINTAPI lstr_Point[]
//指定多边形各顶点的坐标 lstr_Point[1].x = 10