DefineProc Method

Applies to TestComplete 14.93, last modified on October 12, 2021

Description

Before calling a routine from a DLL, you must define the routine type in TestComplete. DefineProc registers the routine type in TestComplete. Note that the DLL object can call only the routines of the stdcall type. (This type is used by Windows API functions).

Note: If the DLL was built with debug information, there is no need to define the routine type manually. TestComplete takes information on the routine type from the debug information in this case.

Declaration

IDefineDLLObj.DefineProc(ProcName, type1, type2, ..., typeN, ResType)

IDefineDLLObj An expression, variable or parameter that specifies a reference to an IDefineDLL object
ProcName [in]    Required    String    
type1 [in]    Optional    Integer    
type2 [in]    Optional    Integer    
...
typeN [in]    Optional    Integer    
ResType [in]    Required    Integer    
Result None

Applies To

The method is applied to the following object:

Parameters

The method has the following parameters:

ProcName

The routine name as it is declared in the desired DLL. For instance, if you want to call the GetWindowText function from the User32.dll, you should use GetWindowTextA or GetWindowTextW, but not GetWindowText. (However, you can call GetWindowTextA as GetWindowText. For this purpose, you need to create an alias for this function. See IDefineDLL.DefineAlias.)

type1, type2, ..., typeN

Specify data types of the first, second, third, etc. function parameters. They can be one of the constants built in TestComplete or an ID created by the DLL.DefineType or IDLLAccessProcess.DefineType method.

Make sure that you correctly specified the types of the parameters. Specifying incorrect data types in a DLL function’s definition may later cause TestComplete to shut down when calling the function from tests (this happens because erroneous definitions may cause incorrect stack processing).

ResType

The type of the value returned by the function. It can be one of the constants built in TestComplete or an ID generated by DLL.DefineType (or IDLLAccessProcess.DefineType). This parameter is required, and it must always be specified. If a routine does not return any value, ResType must be either vt_empty or vt_void.

TestComplete cannot receive string values returned as the results from a DLL function. If a function returns a string value (for instance, a char* or wchar_t* value in Visual C++, or an AnsiString or WideString value in Delphi), you will not be able to obtain the function’s result in your test.

Note for Delphi users: the string type is not supported by TestComplete (neither as function parameters, nor as result values).

Result Value

None.

Remarks

  • If the method registers a routine that was registered earlier in the current test or in the previous test (if the ClearSettingsBeforeEachRun option is enabled), it posts a warning message to the test log.

  • The purpose of specifying data types is to let TestComplete know the size of memory blocks it should reserve to hold values of structure members and how it should interpret routine parameters and push them to the stack, etc. As the type size can differ from one compiler to another or even between two versions of the same compiler, it is recommended that you refer to the compiler documentation to learn the type size.

  • Most compilers can align structure members at certain addresses multiple by 1, 2, 4 or more bytes. For instance, often an application uses the double word alignment (4 bytes). In this case the address of each member is a multiple of four. So, if a structure has a boolean member, you should specify its type as vt_b4 instead of vt_b1. TestComplete does not align structure members itself.

    By default, compilers use alignment for those structures that are defined by default, that is, without the use of the #pragma pack(...) statement in C++ code or packed record in Delphi. When creating a data type for the “default” structures in scripts, pay attention to the size of the structure members.

  • For JavaScript, JScript, Python, C#Script and C++Script users: These scripting languages do not support passing variables by references through parameters of a function being called. Therefore, you cannot pass a variable of an ordinary data type (like integer, boolean, etc.) by a reference to a DLL routine and get the value modified by the routine in your JavaScript, JScript, Python, C#Script or C++Script (this restriction applies to C#Script and C++Script too since these languages are based on JScript and use the JScript engine) test. In this case, the value of the variable passed to the DLL routine from your JavaScript, JScript, Python, C#Script or C++Script test actually remains the same after the routine execution is over because the routine works with a copy of the variable.

    However, objects are passed through routine parameters by references in these languages, and if the callee routine modifies some members of an object passed through its parameters, the caller routine can access the updated object. Therefore, if you pass a custom structure, array or string value created by the DLL.New or IDLLAccessProcess.New method (see Using Custom Data Structures in DLL Function Calls, Using Arrays in DLL Function Calls and Using String Parameters in DLL Function Calls) from your JavaScript, JScript, Python, C#Script or C++Script test to a DLL routine, you can then access the modified structure, array or string after the routine execution is over. The point is that such structures, arrays and strings are created by the New methods as wrapper objects and such objects passed to a DLL routine can be modified and returned from the routine to which they are passed by references.

Constants

The following table lists the constants you use to define data types. You can find examples below and in the Parameter Types for C++ Routines and Structures and Parameter Types for Delphi Routines and Structures topics. These constants are also used in the DLL.DefineType and IDLLAccessProcess.DefineType methods where they specify the types of structure members.

Constant Description
vt_b1

One-byte boolean. True = -1, False = 0.

vt_b2

2-byte boolean. True = -1, False = 0.

vt_b4

4-byte boolean. True = -1, False = 0.

vt_bool

4-byte boolean. True = -1, False = 0.

vt_bool is the same as vt_b4 or varBoolean.

vt_bstr

BSTR. OLE Automation string type.

vt_byref

vt_byref should be used together with one of other constants to indicate that the parameter is passed by a reference. For instance, the following code specifies that the parameter is a pointer to an integer:

JavaScript, JScript

vt_byref | vt_i4

Python

vt_byref | vt_i4

VBScript

vt_byref or vt_i4

DelphiScript

vt_byref or vt_i4

C++Script, C#Script

vt_byref | vt_i4

vt_clsid

CLSID, GUID.

vt_cy

Currency (8 bytes).

vt_date

Date.

vt_dispatch

IDispatch *.

vt_empty

Empty value.

vt_empty is the same as vt_void or varEmpty.

vt_error

SCODE values.

vt_hresult

Standard return type. Normally, vt_hresult is used to specify the result type of a function.
HRESULT is not a Variant-compatible type, so if the function to be called uses HRESULT, do not use the vt_hresult constant. You can declare HRESULT as vt_ui4. HRESULT * can be declared as vt_byref or vt_int (VBScript, DelphiScript), or as vt_byref | vt_ui4 (JavaScript, JScript, Python, C++Script, C#Script).

vt_i1

Signed char (one byte).

vt_i2

2-byte signed integer.

vt_i4

4-byte signed integer.

vt_i8

8-byte signed integer.

vt_int

Signed machine integer (4 bytes).
vt_int is the same as vt_i4.

vt_lpstr

Null-terminated string.

vt_lpwstr

Null terminated wide-string.

vt_r4

4-byte float.

vt_r8

8-byte float.

vt_ui1

Unsigned char (one byte).

vt_ui2

Unsigned short integer (2 bytes).

vt_ui4

Unsigned integer (4 bytes).

vt_ui8

Unsigned integer (8 bytes).

vt_uint

Unsigned machine integer (4 bytes).
vt_uint is the same as vt_ui4.

vt_variant

VARIANT type (16 bytes).

vt_void

The C void type.
This type is used to point out that a routine does not return any value. vt_void is the same as vt_empty or varEmpty.

Example

The code below defines the GetWindowText procedure, then creates an alias for this procedure and calls it from script using the created alias.

JavaScript, JScript

function DefineProcDemo()
{
  // Defines the DLL type
  var DefDLL = DLL.DefineDLL("user32");
  // Loads the DLL in memory
  var Lib = DLL.Load("C:\\Windows\\System32\\user32.dll");
  // Defines the procedure
  DefDLL.DefineProc("GetWindowTextA", VT_UI4, VT_LPSTR, VT_I4, VT_I4);
  // ...

  // Creates an alias for the procedure
  DefDLL.DefineAlias("GetWindowText", "GetWindowTextA");
  // ...

  var hndl = Sys.Process("Notepad").Window("Notepad").Handle;
  var winText = DLL.New("LPSTR", 256);
  // Calls the routine using the alias
  Lib.GetWindowText(hndl, winText, 20);
  Log.Message(winText)
}

Python

def DefineProcDemo():
  # Defines the DLL type
  DefDLL = DLL.DefineDLL("user32")
  # Loads the DLL in memory
  Lib = DLL.Load("C:\\Windows\\System32\\user32.dll")
  # Defines the procedure
  DefDLL.DefineProc("GetWindowTextA", VT_UI4, VT_LPSTR, VT_I4, VT_I4)
  # ...
  # Creates an alias for the procedure
  DefDLL.DefineAlias("GetWindowText", "GetWindowTextA")
  # ...
  hndl = Sys.Process("Notepad").Window("Notepad").Handle
  winText = DLL.New("LPSTR", 256)
  # Calls the routine using the alias
  Lib.GetWindowText(hndl, winText, 20)
  Log.Message(winText)

VBScript

Sub DefineProcDemo
  ' Defines the DLL type
  Set DefDLL = DLL.DefineDLL("user32")
  ' Loads the DLL in memory
  Set Lib = DLL.Load("C:\Windows\System32\user32.dll")
  ' Defines the procedure
  Call DefDLL.DefineProc("GetWindowTextA", VT_UI4, VT_LPSTR, VT_I4, VT_I4)
  ' ...

  ' Creates an alias for the procedure
  Call DefDLL.DefineAlias("GetWindowText", "GetWindowTextA")
  ' ...

  hndl = Sys.Process("Notepad").Window("Notepad").Handle
  Set winText = DLL.New("LPSTR", 256)
  ' Calls the routine using the alias
  Call Lib.GetWindowText(hndl, winText, 20)
  Call Log.Message(winText)
End Sub

DelphiScript

procedure DefineProcDemo;
var DefDLL, Lib, hndl, winText;
begin

  // Defines the DLL type
  DefDLL := DLL.DefineDLL('user32');
  // Loads the DLL in memory
  Lib := DLL.Load('C:\Windows\System32\user32.dll');
  // Defines the procedure
  DefDLL.DefineProc('GetWindowTextA', VT_UI4, VT_LPSTR, VT_I4, VT_I4);
  // ...

  // Creates an alias for the procedure
  DefDLL.DefineAlias('GetWindowText', 'GetWindowTextA');
  // ...

  hndl := Sys.Process('Notepad').Window('Notepad').Handle;
  winText := DLL.New('LPSTR', 256);
  // Calls the routine using the alias
  Lib.GetWindowText(hndl, winText, 20);
  Log.Message(winText);
end;

C++Script, C#Script

function DefineProcDemo()
{
  // Defines the DLL type
  var DefDLL = DLL["DefineDLL"]("user32");
  // Loads the DLL in memory
  var Lib = DLL["Load"]("C:\\Windows\\System32\\user32.dll");
  // Defines the procedure
  DefDLL["DefineProc"]("GetWindowTextA", VT_UI4, VT_LPSTR, VT_I4, VT_I4);
  // ...

  // Creates an alias for the procedure
  DefDLL["DefineAlias"]( "GetWindowText", "GetWindowTextA" );
  // ...

  var hndl = Sys["Process"]("Notepad")["Window"]("Notepad")["Handle"];
  var winText = DLL["New"]("LPSTR", 256);
  // Calls the routine using the alias
  Lib["GetWindowText"](hndl, winText, 20);
  Log["Message"](winText)
}

See Also

Calling DLL Functions From Tests - Tutorial
Parameter Types for C++ Routines and Structures
Parameter Types for Delphi Routines and Structures
DefineAlias Method
DLL Object
IDLLAccessProcess Object

Highlight search results