Functions in dynamic link libraries use parameters of different data types. Some functions require passing values of simple data types like integer or boolean through their parameters, while other functions require passing custom structures. This topic explains how you can use custom data structures when calling DLL functions from TestComplete tests.
First of all, to create a data structure, you must define its data type in your test. For this purpose, call the DLL.DefineType
method. It registers the new data type (normally, a structure) and returns its ID. The ID can be used later to define members of other structure types or DLL function parameters.
When calling DLL.DefineType
, you need to specify the name of the new data type (the name is used later to create a variable of this type) and the list of structure members. The members are specified as Member type, Member name pairs separated by commas. The member names are used to access the structure members in scripts. That is why they must agree with the naming rules of the scripting language you are using. Also, member names must be unique within the structure. Member types are specified by special constants built in TestComplete (see DefineProc Method for the list of these predefined constants) or by IDs returned by previous calls to the DefineType
method when defining other custom data types. Note that if a structure member is a pointer to a variable of some data type, you must specify the additional vt_byref
constant when defining the member type (see the example below).
The following example illustrates how you can define a custom structure with members of different data types:
JavaScript, JScript
vt_i4, "IntMember", // integer member
vt_byref | vt_r4, "FloatPointerMember", // pointer to float
MyStruct2ID, "MyStruct2Member"); // structure MyStruct2
Python
StructID = DLL.DefineType("MyStruct", # name of the new structure
VT_I4, "IntMember", # integer member
VT_BYREF | VT_R4, "FloatPointerMember", # pointer to float
MyStruct2ID, "MyStruct2Member") # structure MyStruct2
VBScript
vt_i4, "IntMember", _ ' integer member
vt_byref or vt_r4, "FloatPointerMember", _ ' pointer to float
MyStruct2ID, "MyStruct2Member") ' structure MyStruct2
DelphiScript
vt_i4, 'IntMember', // integer member
vt_byref or vt_r4, 'FloatPointerMember', // pointer to float
MyStruct2ID, 'MyStruct2Member'); // structure MyStruct2
C++Script, C#Script
vt_i4, "IntMember", // integer member
vt_byref | vt_r4, "FloatPointerMember", // pointer to float
MyStruct2ID, "MyStruct2Member"); // structure MyStruct2
Make sure that you correctly specified the member types. Incorrect definition of data types may cause TestComplete to shut down when passing such structures to DLL functions (this happens because erroneous definitions may cause incorrect stack processing). |
From the Parameter Types for C++ Routines and Structures and Parameter Types for Delphi Routines and Structures topics, you can learn which data type constants built in TestComplete correspond to some standard data types frequently used in C++ and Delphi applications.
Compilers can align structure members at certain addresses multiplied by 1, 2, 4 or more bytes. For instance, the double word (4 byte) alignment is often used in applications. The alignment may enlarge the actual size of structure members whose data type normally takes less memory than the alignment value. For instance, Boolean occupies one byte. In case of double word alignment, the actual size of a Boolean member will be four bytes. You should remember this and use appropriate data type constants (in this case, vt_b4 instead of vt_b1 for a Boolean member with double word alignment) when defining members of custom structures since TestComplete does not align structure members itself when passing them to a DLL function being called.The compilers use alignment for those structures that are defined by default, that is, without the use of the |
The DefineType
method
only registers the data type but does not create a variable of this type. To
create a structure of the new type, call the DLL.New
method. It creates and returns an object that represents the specified structure type in tests. The memory block allocated for the structure is filled with zero values. To access structure members and initialize them from tests, use the member names passed to the DefineType
method when defining the structure type. The example below demonstrates how you can create and initialize a structure variable:
JavaScript, JScript
struct1.IntMember = 42;
struct1.FloatPointerMember = pFloatVar;
struct1.MyStruct2Member = MyStruct2Var;
Python
struct1 = DLL.New("MyStruct")
struct1.IntMember = 42
struct1.FloatPointerMember = pFloatVar
struct1.MyStruct2Member = MyStruct2Var
VBScript
struct1.IntMember = 42
struct1.FloatPointerMember = pFloatVar
Set struct1.MyStruct2Member = MyStruct2Var
DelphiScript
struct1.IntMember := 42;
struct1.FloatPointerMember := pFloatVar;
struct1.MyStruct2Member := MyStruct2Var;
C++Script, C#Script
struct1["IntMember"] = 42;
struct1["FloatPointerMember"] = pFloatVar;
struct1["MyStruct2Member"] = MyStruct2Var;
Note that the DLL.New
method has the second parameter, Count, that is optional and not used in the example above. This parameter specifies the number of instances of the data type to create. Using the second parameter, you can create an array of elements of the specified data type. See Using Arrays in DLL Function Calls for details.
Note: | The above-mentioned DLL.DefineType and DLL.New methods are used if you are going to work with 32-bit DLLs or with 64-bit DLLs only and do not create an environment for loading DLLs of certain “bitness” and calling routines from them (see Specifics of Using 32- and 64-bit DLLs). However, if you explicitly create and use such an environment for DLLs, you need to call the IDLLAccessProcess.DefineType and IDLLAccessProcess.New methods respectively of an IDLLAccessProcess object that represents an environment for DLLs.For instance, if you are using a 32-bit environment for 32-bit DLLs, you should create structures in the following manner: JavaScript, JScript var MyEnvironment = DLL.DefineEnvironment(true);
var StructID = MyEnvironment.DefineType("MyStruct", vt_i4, "IntMember", vt_byref | vt_r4, "FloatPointerMember", MyStruct2ID, "MyStruct2Member"); var struct1 = MyEnvironment.New("MyStruct"); struct1.IntMember = 42; struct1.FloatPointerMember = pFloatVar; struct1.MyStruct2Member = MyStruct2Var; Python
VBScript Set MyEnvironment = DLL.DefineEnvironment(True)
StructID = MyEnvironment.DefineType("MyStruct", _ vt_i4, "IntMember", _ vt_byref or vt_r4, "FloatPointerMember", _ MyStruct2ID, "MyStruct2Member") Set struct1 = MyEnvironment.New("MyStruct") struct1.IntMember = 42 struct1.FloatPointerMember = pFloatVar Set struct1.MyStruct2Member = MyStruct2Var DelphiScript
MyEnvironment := DLL.DefineEnvironment(True);
StructID := MyEnvironment.DefineType('MyStruct', vt_i4, 'IntMember', vt_byref or vt_r4, 'FloatPointerMember', MyStruct2ID, 'MyStruct2Member'); struct1 := MyEnvironment.New('MyStruct'); struct1.IntMember := 42; struct1.FloatPointerMember := pFloatVar; struct1.MyStruct2Member := MyStruct2Var; C++Script, C#Script var MyEnvironment = DLL.DefineEnvironment(true);
var StructID = MyEnvironment["DefineType"]("MyStruct", vt_i4, "IntMember", vt_byref | vt_r4, "FloatPointerMember", MyStruct2ID, "MyStruct2Member"); var struct1 = MyEnvironment["New"]("MyStruct"); struct1["IntMember"] = 42; struct1["FloatPointerMember"] = pFloatVar; struct1["MyStruct2Member"] = MyStruct2Var; |
After you defined the structure type, created and initialized a variable of this type, you can pass the structure through the parameters of the needed DLL function in the same manner as you pass parameters of any other data type. See Calling DLL Functions From Tests - Tutorial for more information on this.
Note: | Currently, it is impossible to call DLL routines that use structures containing array-type members (for instance, string or array fields):
Delphi // A definition of structure in the DLL source C++ // A definition of structure in the DLL source When you try to do this, you will not get an error, but the array-type field will be undefined even if you assign a value to it. You can workaround this limitation by using pointer-type members: Delphi // A definition of structure in the DLL source C++ // A definition of structure in the DLL source |
See Also
Calling DLL Functions From Tests - Tutorial
Calling DLL Functions From Tests
Using String Parameters in DLL Function Calls