[返回]
计算机世界2001年第6期

再谈PowerBoilder的ref关键字

张家界市电信局计算机信息中心 邓振兴

《计算机世界》周报在2001年1月8日C17版刊出了笔者的讨论变量地址处理方式的文章《PB调用Win32 API时变量地址的处理方式》。近日来,有朋友问笔者在PowerBuilder调用外部函数的过程中,Microsoft 原型为LPSTR的参数在声明Powersoft原型时,为什么加不加ref都可以成功调用?下面笔者就该问题,谈谈PowerBuilder调用外部函数的机理及ref关键字的使用。

众所周知,PowerBuilder在生成本地二进制代码前,要将PowerScript转换成C代码,笔者通过Project Painter生成了C++代码,并对其进行分析,从中略微了解到一些PowerBuilder调用外部函数的机理。下面列出的一段C++代码是由Project Painter生成的类自定义用户对象(Class Custom User Object) ccuo_window的局部外部函数(Local External Function)

getwindowrect。

C++ 代码 (本文仅列出部分重要代码行):

int ccuo_window::getwindowrect( unsigned long hwnd, stc_rect & lprect, int & pb_result )

{

…… //其他初始化代码

PBIInstance * plprect = (PBIInstance *)

GetInterfaceFrom(lprect);

PBIValue * ppb_VALUE = NULL;//接收返回值

PBIString * ppb_NAME = NULL;//存放函数名

//原始参数列表

PBIArgument ** pppb_ARGUMENTS = new PBIArgument * [pb_ARGCOUNT];

//最终参数列表

PBIValue ** pppb_ARGVALUES = new PBIValue * [pb_ARGCOUNT];

//分配存放函数名空间

pb_RESULT=CreatePBIString(PBTEXT(“getwindowrect" ), &ppb_NAME);

//分配存放返回值空间

pb_RESULT=CreatePBIValue(PBValueTypeBoolean

,ppb_SESSION, &ppb_VALUE);

//为原始参数列表分配空间

pb_RESULT = CreatePBIValue(PBValueTypeULong, ppb_SESSION, &pppb_ARGVALUES[0]);

//为参数赋值

pb_RESULT = pppb_ARGVALUES[0]->

SetValueFromULong(hwnd);

//按调用转换类型生成最终参数列表

pb_RESULT = CreatePBIArgument(pppb_

ARGVALUES[0], PBArgCallConventionByValue, &pppb_ARGUMENTS[0]);

pb_RESULT=CreatePBIValue(PBValueTypeInstance,

ppb_SESSION, &pppb_ARGVALUES[1]);

pb_RESULT = pppb_ARGVALUES[1]->

SetValueFromInstance(plprect);

plprect->AddRef(); //引用传值

pb_RESULT = CreatePBIArgument

(pppb_ARGVALUES[1],PBArgCallConventionByReference, &pppb_ARGUMENTS[1]);

//调用外部函数

pb_RESULT = ppb_INSTANCE->DoMethod

(ppb_NAME, pppb_ARGUMENTS, pb_ARGCOUNT, &ppb_VALUE, &ppb_EXCEPTION, &pb_BADPAR

MINDEX);

……

//获得返回值

pb_RESULT = ppb_VALUE->GetValueAsULong(&pb_result);

…… //异常处理及空间释放

}

从上述代码中,笔者注意到SetValueFromULong(PBULong ulValue)函数,是用来为参数列表成员赋值的,与该函数类似的还有SetValueFromChar(PBCHAR cValue)、SetValueFromInt(PBINT iValue)、SetValueFromDate( PBIDate * pDate )等函数。笔者根据经验将所有变量类型分为简单类型和复杂类型两大类。

典型的简单类型有:

字符型:SetValueFromChar( PBCHAR cValue );

整型:SetValueFromInt( PBINT iValue );

无符号整型:SetValueFromUInt( PBUINT

uiValue )等。

典型的复杂类型有:

日期型:SetValueFromDate( PBIDate *pDate );

字符串型:SetValueFromString( PBIString * pString );

数组:SetValueFromArray( PBIArray *

pArray )等。

这两类函数的区别是:SetValueFrom*()对简单类型参数以拷贝方式传值;对复杂类型参数以地址方式传值。这正是string和ref string变量都能传地址给LPSTR变量的原因。

对简单类型参数是否被ref修饰的区别在于:被修饰了的参数将以地址方式传值;未被修饰的参数将以拷贝方式传值。

对复杂类型参数是否被ref修饰的区别是相似的。由于复杂类型参数都以地址方式传值,因而不被修饰时参数传递的是拷贝的地址;被修饰时参数传递的是真实地址。

下表说明了参数类型和ref 的关系。
  简单类型 复杂类型
    加ref 地址 真实地址
    不加ref 拷贝 拷贝的地址

值得注意的是:结构的成员变量如果是复杂类型的话,虽不能在结构声明中加ref,但它们将以真实地址方式传值。此外,结构本身也是复杂类型。因此对结构变量的操作要小心,防止无意中的更改。

在《PB调用Win32 API时变量地址的处理方式》一文中,笔者有多处“乱”加ref关键字,是因为对应的Microsoft类型参数都为const,这就使笔者不必判断变量是否接收Win32 API的参数返回。不过笔者不建议大家这样做,毕竟规范代码更重要。