TestComplete调用外部函数
陈能技
2007-9-10
TestComplete有三种调用外部函数的方法,分别是调用Win32API的函数,调用.NET Assembly的函数,调用DLL的函数。
调用Win32API的函数
TC能调用那些Win32平台通用的函数,包括Windows 95、Windows 98、Windows NT,Windows 2000和Windows XP,不能调用那些只在Windows NT,2000,XP或Windows Server 2003才有的函数。
在脚本编辑时,输入Win32API关键字后,使用Code Completion,会列出所有支持的函数调用。
调用Win32API的函数时需要注意两点:
1、 因为Microsoft OLE不支持指针,所以你不能使用那些有指针参数的或者是返回指针给数据结构的API函数。有一个例外是:在某些函数,你可以传入0作为指针值。这个技巧可以帮助你调用某些特定的函数,例如CreateDC。
但是,这个限制对那些使用数据结构的函数不生效。Win32API对象包括某些特定的方法(例如_DOCINFO)创建一个编程对象,为数据结构提供一个脚本接口。然后,你可以使用这个对象来填充结构区域并且传递这些结构给函数作为参数。例如,你可以调用_DOCINFO来为DOCINFO结构创建一个对象然后传递这个对象给StartDoc函数作为参数。
2、 如果一个参数是作为引用传递的并且用于返回值,则返回值对于TC脚本而言无效。但是如果传入的引用参数是一个结构,那么你可以使用这种类型的API函数。TC提供一些包装的对象(Wrapper Object),即预定的结构,当然你也可以自己定义结构。
例如,你可以使用TRect结构如下:
structRect := TRect;
这个包装对象包含跟结构成员一致的属性,使用相同的名称。例如,下面脚本设置TRect结构的left和top边界值:
structRect.Left := 10;
structRect.Top := 10;
调用.NET Assembly的函数
在TC脚本中你可以调用任何.NET assembly中的函数。可以是.NET Framework的,也可以是第三方.NET程序的。
为了调用.NET Assembly的函数,先要在CLR Bridge的属性页选中使用assembly。
在脚本中使用dotNET对象访问assembly提供的属性或方法。调用格式如下:
dotNET.namespace.class.subclass.method()
dotNET.namespace.class.subclass.property
注意:namespace和class、subclass的名称之间用点隔开,而namespace名称中包含的分隔点要用下划线替换。
如果类的实例不存在,你只能调用这个类的静态方法。要想调用属性和非静态方法,你应该首先通过这个类的构造器创建一个实例。通常类构造器的名称是_ctor。一个类可以有多个构造器(_ctor(),_ctor2(),_ctor3()等)。
但是在VBScript中不能识别由下划线开头的标识符,因此为了让_ctor方法与VBScript兼容,TC修改了这些方法的名称,替换成z开头,例如调用_ctor3,则应该使用zctor3。
下面例子说明如何在脚本中从mscorlib assembly创建String对象并调用其方法:
procedure AssemblyTest;
var
s, i : OleVariant;
begin
// Calling a constructor and a property
// Note the absence of the underscore
s := dotNET.System.String.ctor8($41, 3);
Log.Message(s.Length);
// Calling a static method
s := dotNET.System.String.Copy('Test');
Log.Message(s.OleValue); // Using the OleValue property
// Calling a non-static method
i := s.IndexOf('t');
Log.Message(i);
end;
调用DLL的函数
为了调用一个DLL的函数,你需要执行一系列的操作:
1、 定义DLL类型
2、 在TC中定义参数的数据类型
3、 定义函数类型
4、 创建数据结构并用数据填充
5、 加载DLL到内存
6、 调用函数
例如,如果我们要从User32DLL中调用一个名叫DrawTextEx的函数,则需要以下步骤:
1、 定义DLL类型。
使用DefineDLL方法来定义一个新的DLL类型如下:
Def_DLL:=DLL.DefineDLL(‘USER32’);
2、 看看函数定义和判断参数是否兼容OLE。TC引擎能处理OLE兼容的数据类型;OLE不兼容的数据类型则需要特殊处理。所以要判断参数类型。DreawTextEx定义如下:
int DrawTextEx(
HDC hdc,
LPTSTR lpchText,
int cchText,
LPRECT lprc,
UINT dwDTFormat,
LPDRAWTEXTPARAMS lpDTParams);
其中有一些是OLE不兼容的数据类型,所以下一步是在TC中定义这些数据类型。
3、 定义参数类型。
对于lprc,它是对RECT结构的指针,为了在TC中定义它的数据类型,调用DLL.DefineType方法。在TC中注册新的数据类型并返回ID。返回的ID可以用在其它数据类型或函数参数的定义上。例如下面脚本定义名叫MyStruct的结构类型:
DefType := DLL.DefineType('MyStruct', // name of the new structure
vt_i4, 'Value',
vt_byref or vt_i4, 'ValueByRef', // pointer to integer
vt_byref or myID, 'MyStruct2'); // pointer to structure MyStruct2
注意DefineType注册的只是数据类型,不会创建这个类型的变量。要想创建这个新类型的变量,需要调用DLL.New,例如下面脚本调用DLL.New创建RECT类型的变量:
r := DLL.New('RECT');
4、 定义函数类型
在调用DLL的函数之前,我们要在TC中使用DefineProc方法为它定义函数类型,告诉TC函数名称、参数和结果类型。例如下面脚本定义了DrawTextExA函数类型:
// Registers the function type in TestComplete
Def_DLL.DefineProc('DrawTextExA',// function name
vt_i4,// device context
vt_lpstr,// pointer to the string to be drawn
vt_i4,// the string length
Def_Rect,// pointer to the RECT structure
vt_i4,// additional drawing settings
Def_DrawTextParams,// pointer to DRAWTEXTPARAMS
vt_i4);// result type
5、 为结构填充数据
在使用前面创建的RECT类型变量之前,应该为其填充数据:
r := DLL.New('RECT');
r.left := 50;
r.top := 50;
r.right := 350;
r.bottom := 100;
6、 指定string参数的值
DrawTextEx方法的第二个参数是指定在屏幕上画出来的某个字符串,应该按下面方法准备这段字符串:
LpStr:=DLL.New(‘LPSTR’,256)
LpStr.Text:=’My text’;
7、 把DLL加载到内存
使用DLL.Load方法加载指定的DLL,例如:
Lib:=DLL.Load(‘C:/Windows/System32/USER32.DLL’,’USER32’);
对于系统DLL,例如上面的USER32.DLL,我们可以忽略第一个参数,直接使用:
Lib:=DLL.Load(‘USER32’);
8、 调用函数
现在,我们可以调用DLL的函数了,例如,以下脚本调用DrawTextExA方法:
i := Lib.DrawTextEx(dc, LpStr, Length(LpStr.Text), r,
DT_PATH_ELLIPSIS, dtp);
完整的调用USER32.DLL的DrawTextEx函数的脚本如下:
procedure CallRoutineFromDLL;
const
DT_PATH_ELLIPSIS = $4000;
var
p, w, s : OleVariant;
Def_DrawTextParams, Def_Rect, Def_DLL, Lib : OleVariant;
dtp, LpStr, r, dc, i : OleVariant;
begin
// Text to draw
s := 'My string: C:/Program Files/Files.txt';
// Defines the dll type
Def_DLL := DLL.DefineDLL('USER32');
// Registers the DRAWTEXTPARAMS type in TestComplete
Def_DrawTextParams := DLL.DefineType('DRAWTEXTPARAMS',
vt_i4, 'cbSize',
vt_i4, 'iTabLength',
vt_i4, 'iLeftMargin',
vt_i4, 'iRightMargin',
vt_i4, 'uiLengthDrawn');
// Registers the RECT type in TestComplete
Def_Rect := DLL.DefineType('RECT',
vt_i4, 'left',
vt_i4, 'top',
vt_i4, 'right',
vt_i4, 'bottom');
// Registers the function type in TestComplete
Def_DLL.DefineProc('DrawTextExA',// function name
vt_i4,// device context
vt_lpstr,// pointer to the string to be drawn
vt_i4,// the string length
Def_Rect,// pointer to the RECT structure
vt_i4,// additional drawing settings
Def_DrawTextParams,// pointer to DRAWTEXTPARAMS
vt_i4);// result type
// Creates an alias for DrawTextExA
Def_DLL.DefineAlias('DrawTextEx', 'DrawTextExA');
// Registers the types of the GetDC
// and ReleaseDC routines. They will
// be used to work with the device context
Def_DLL.DefineProc('GetDC', vt_i4, vt_i4);
Def_DLL.DefineProc('ReleaseDC', vt_i4, vt_i4, vt_i4);
// Creates the RECT structure and fills it with data
r := DLL.New('RECT');
r.left := 50;
r.top := 50;
r.right := 350;
r.bottom := 100;
// Creates the DRAWTEXTPARAMS structure and fills it with data
dtp := DLL.New('DRAWTEXTPARAMS');
dtp.cbSize := 20; // Structure size
dtp.iTabLength := 2;
dtp.iLeftMargin := 2;
dtp.iRightMargin := 2;
dtp.uiLengthDrawn := 0;
// Creates the string parameter
LpStr := DLL.New('LPSTR', 256);
LpStr.Text := s;
// Loads the dll
// We use such a brief notation,
// because USER32.DLL is a system library and
// the dll type name (USER32) coincides with
// the dll file name
Lib := DLL.Load('USER32');
// Gets the device context for drawing
dc := Lib.GetDC(0);
// Calls the function from the DLL
i := Lib.DrawTextEx(dc, LpStr, Length(LpStr.Text), r,
DT_PATH_ELLIPSIS, dtp);
// Releases the device context
Lib.ReleaseDC(0, dc);
end;