:Revision=6
:html
<<
128.122
DLL-Object|DLL
--
The IP-Object DLL enables the access to (almost) any DLL. Through this unknown driver-DLLs etc. can be integrated into the programm. 

However, due to the limited data type handling possibilities of the interpreter not every calling parameter type can be addressed.

The application of an external DLL is always done according to a certain pattern:
<ol indent=20>
<li>Opening of DLL - registration of DLL in the interpreter</li>
<li>Registration of the used functions / procedures - defines the number and the type of the parameter</li>
<li>Call of the functions / procedures</li>
<li>Closing of the DLL</li>
</ol>

The opening and the registration can be done in the start step, the definitions are valid until the od of the test. All the open DLLs are automatically closed when the test sequence or the programm is ended. 
>>
<<
128.122.1
Functions of the DLL-Object|Functions
--
>>
<<
128.122.1.1
DLL.Call|Call
!128.122.2.4
!128.122.2.5
$NoHelpDebug
!128.122.1.3
--
<!DEF>
function <!TW>DLL.Call (sDLLAlias, sProcAlias : string {; <i>Parameter</i> }) : <i>type</i>;
<!TXT>
Calls the previously registered function in the DLL and returns the value. The data type of the result depends on the registration.
>>

<<
128.122.1.2
DLL.GetValue|GetValue
!128.122.2.5
--
<!DEF>
function <!TW>DLL.GetValue : real;
<!TXT>
Returns the return value of the DLL-Routine called last. This value is valid only, if another routine has been previously called in a DLL, which returned an integer value. In all the other cases the return value is not defined.

Is identical to the <!RW>DLL.GetValue procedure, only as function.
>>

<<
128.122.1.3
DLL.SafeCall|SafeCall
!128.122.1.1
--
<!DEF>
function <!TW>DLL.SafeCall (sDLLAlias, sProcAlias : string {; <i>Parameter</i> }) : <i>type</i>;
<!TXT>
Calls the previously registered function in the DLL and returns the value. The data type of the result depends on the registration.
Is identical to the <!RW>DLL.call function and includes an exceptionhandler for floatingpoint exceptions.
>>

<<
128.122.2
Procedures of the DLL-Object|Procedures
--
>>

<<
128.122.2.1
DLL.Open|Open
--
<!DEF>
procedure <!TW>DLL.OPEN (sAlias, sDLLFilename : string [; rPersistent : real]);
<!TXT>
Opens the DLL in the file <!PW>sDLLFilename with the internal alias <!PW>sAlias. The standard file extension is DLL, the standard path is the directory with the EXE-file of the test programm. Path templates can be used. Errors are generated if the DLL is not found or if it cannot be opened. 

A DDL opened previously under the same alias will be closed.

 <!PW>rPersistent is a logical value which determines whether the DLL remains open after the end of a test run. <!RW>False (default): The DLL is automatically closed after the end of a test and must be reopened in the next test run. <!RW>True : The DLL is closed only at the end of the programm.
>>

<<
128.122.2.2
DLL.Close|Close
--
<!DEF>
procedure <!TW>DLL.CLOSE (sAlias : string);
<!TXT>
Closes the DLL with the alais <!PW>sAlias. All the registrations are deleted. If no such DLL is open, then nothing happens.
>>

<<
128.122.2.3
DLL.Register|Register
--
<!DEF>
procedure <!TW>DLL.REGISTER (sDLLAlias, sProcName, sParaDef : string [; sProcAlias : string]);
<!TXT>
<!DEF>
procedure <!TW>DLL.REGISTER (sDLLAlias : string; rProcIdx : real; sParaDef, sProcAlias : string);
<!TXT>
Searches the procedure under the name <!PW>sProcName in the DLL for the <!PW>sDLLAlias. If a <!PW>sProcAlias is specified, then the procedure is registered under this name, otherwise under its <!PW>sProcName. As an alternativ, the index of the procedure can be specified instead of its name. In this case, a <!PW>sProcAlias is mandatory. 

 <!PW>sParaDef determines the calling format of the procedure: 
The first character indicates the call type. 
 <!CW>'P' -> pascal, <!CW>'C' -> cdecl, <!CW>'S' -> stdcall
The call type MUST NECESSARILY correlate with the call type of the routine in the DLL.
The second character must be a colon ':'.
The length of the second part of the <!PW>sParaDef varies according to the number of the parameter of the registered routine. The codes for the individual parameters are separated by commas. The following codes are defined:

<!STBL>
<!+>Code<!+>Type<!+>Description<!>
<!>'I'<!>Integer<!>Integer, range -2<sup>31</sup>2<sup>31</sup>-1 resp. -2147483648..2147483647<!>
<!>'B'<!>Byte<!>Integer, range 0..2<sup>8</sup>-1  resp. 0..255<!>
<!>'W'<!>Word<!>Integer, range 0..2<sup>16</sup>-1  resp. 0..65535<!>
<!>'T'<!>DWord<!>Integer, range 0..2<sup>32</sup>-1  resp. 0..4294967295<!>
<!>'L'<!>Boolean<!>Logical value<!>
<!>'C'<!>Char<!>Characters (String with the length of 1 character)<!>
<!>'R'<!>Real<!>Floating point number<!>
<!>'E'<!>Extended<!>Floating point number<!>
<!>'D'<!>Double<!>Floating point number<!>
<!>'S'<!>Single<!>Floating point number<!>
<!>'P'<!>String<!>Character string<!>
<!>'A'<!>Array<!>Byte-Array<!>
<!ETBL>

If a 'V' is indicated before the code, parameter is Call-By-Reference (a pointer to the parameter is transferred, the parameter can be changed); without 'V' the parameter is Call-By-Value (the value of the parameter is transferred, the parameter cannot be modified). For the Call-By-Reference parameter a variable has to specified in the actual call, for Call-By-Value parameters, any expression may be used. The definition of the parameter list is extremely critical too, and must be be carried out very accurately, since erroneous parameter definitions can lead to errors during the call, to false results or to an abnormal end of the system (even with data loss).
In case of Call-By-Reference (with 'v') in combination with 'P' or 'A' (string and array) the size of the bufferr space for the return value of the DDL can be specified.
'P250' - 250 byte return buffer is provided
'A2097152' - a buffer of 2097152 byte (2MB!) is provided.
The DLL must not return more bytes than the length of the buffer! When making the actual call, a string is expected as parameter for both, 'A' and for 'P'. The difference is that in case of 'P' the return string is considered zero-ended while in case of 'A' the entire buffer is returned. In case of 'A', the interpreter step must determine itself which part contains the meaningful data.
If P and A is used with V you need a simple pointer and not a pointer to a pointer of the char string and the array pointer in the DLL.



New starting with Rev.6:
The interpreter can process further (except integer) return types. If the parameter list is followed by a colon (':'), behind it, the return data type specification is expected. The following data types are possible:

<!STBL>
<!+>Code<!+>Type<!>
<!>'I'<!>Integer<!>
<!>'B'<!>Byte<!>
<!>'W'<!>Word<!>
<!>'T'<!>DWord<!>
<!>'L'<!>Boolean<!>        
<!>'C'<!>Char<!>
<!>'VI'<!>Pointer to Integer <!>
<!>'VB'<!>Pointer to Byte (useable as bool / 1 byte)<!>
<!>'VW'<!>Pointer to Word<!>
<!>'VT'<!>Pointer to DWord<!>
<!>'VL'<!>Pointer to Boolean (longbool / 4 byte)<!>        
<!>'VC'<!>Pointer to Char<!>
<!>'VD'<!>Pointer to Double<!>               
<!>'VS'<!>Pointer to Single<!>            
<!>'VP'<!>Pointer to zero-ended string<!>
<!>'VAxx'<!>Pointer to array of the length xx<!>
<!ETBL>
In case of arrays there must be a length specification, while in case of a zero-ended string no length info may be specified. Pointers will be dereferenced.

The return value is provided when the <!RW>DLL.Call is made as function. When <!RW>DLL.Call is called as procedure other return types than integer are lost.

Example
<!CODE>
DLL.Open ('Kernel32', 'C:\Windows\System32\Kernel32.dll');

// Kernel32.Beep takes 2 Parameter : tone pitch in Hz and duration in ms
DLL.REGISTER ('Kernel32', 'Beep', 'S:I,I:L');
Debug.Show(1, DLL.Call ('Kernel32', 'Beep', 1000, 500));

// Kernel32.GetCommandLineA returns the command row of Exe
DLL.REGISTER ('Kernel32', 'GetCommandLineA', 'S::vP');
Debug.Show(1, DLL.Call ('Kernel32', 'GetCommandLineA'));

// GetEnvironmentVariableA returns the content of an environmet variable
DLL.REGISTER ('Kernel32', 'GetEnvironmentVariableA', 'S:P,vP200,I:I');
SetLen (sData, 200);
Debug.Show(1, DLL.Call ('Kernel32', 'GetEnvironmentVariableA', 'TEMP', sData, 200));
Debug.Show(1, sData);

// FormatMessage can convert a Windows error code into clear text.
// For an agreed pointer, which must be transferred to NIL, in the declaration 
// the data type integer is specified and 0 is set as value!
DLL.Register ('Kernel32', 'FormatMessageA', 'S:I,I,I,I,vP250,I,I:I');
rSize:=DLL.Call('Kernel32', 'FormatMessageA', $3000, 0,
                 8,           // the error code
                 0,
                 sData, 250,  // buffer and length
                 0);
sData:=trim(sData); // remove some control chars that are added by the API
Debug.Show (1, sData);

DLL.Close ('Kernel32');
<!TXT>
>>

<<
128.122.2.4
DLL.Call|Call
!128.122.1.1
--
<!DEF>
procedure <!TW>DLL.Call (sDLLAlias, sProcAlias : string [;  .Parameter ...]);
<!TXT>
Calls the procedure with the alias <!PW>sProcAlias in the DLL with <!PW>sDLLAlias. The number and type of the expected parameters depend on the specifications from the registration of the procedure. During the syntax check, the interpreter accepts any parameters. 
With this form of <!RW>DLL.Call, a possible return value (function result) is lost. Only an integer-result can be queried with <!RW>DLL.GetValue.

Therefore, for DLL-functions the <!RW>DLL.Call is preferred to be used as a function.
>>

<<
128.122.2.5
DLL.GetValue|GetValue
!128.122.1.2
--
<!DEF>
procedure <!TW>DLL.GetValue (var rResult :real);
<!TXT>
Returns the return value of the DLL-Routine called last in <!PW>rResult. This value is only valid, if a routine has been previously called in a DLL, which returned an integer value. In all the other cases, the return value is not defined.

Identical to the <!RW>DLL.GetValue function only as a procedure.
>>
