Jump to content

Calling CDT(gupta/centura) DLL Function with LPHSTRING, HSTRING parameters


Gabriel
 Share

Recommended Posts

Calling CDT(gupta/centura) DLL Function with LPHSTRING, HSTRING parameters

Im trying to call CDT/Gupta Functions, but im having problems with strings.

HSTRING and LPHSTRING are data types used only by Team Developer on 32-bit platforms to call SAL functions.

CDT Reference: http://www.unify.com/onlinedocs/sb/books/s...ernal_funcs.htm

Im using this reference "Delphi calling CDT Functions":

http://www.iceteagroup.com/Portals/0/Newsl...01/20010402.pdf

Now lean back or get yourself a glass of wine because you don't have to find the solution yourself. As

you already know, CTD can call functions residing in an external DLL. But the other way around,

external functions are able to call CTD functions within the DLL. Cool, isn't it?

The string type within CTD is just a 4-byte handle and we can declare it in Delphi like this:

Type

THString = DWORD;

The DLL that exports CTD's functions is called CDLLI20.DLL. The number in the DLLs name simply

reflects the version of CTD. For version 1.5 the DLL would be called CDLLI15.DLL.

This DLL exports a few functions which are used to handle CTDs internal string type:

BOOL CBEXPAPI SWinInitLPHSTRINGParam(LPHSTRING, LONG);

LPSTR CBEXPAPI SWinStringGetBuffer(HSTRING, LPLONG);

From the perspective of a Delphi programmer this is ugly code so let's see if we can do better when

declaring the interface to the DLL in Pascal language:

const

DLLName = 'cdlli20.dll'; // cdlli15.dll when using CTD1.5

function SWinInitLPHSTRINGParam (var StringHandle:THString; Len:DWORD) : BOOL; stdcall

external DLLName;

function SWinStringGetBuffer (StringHandle:THString; var Len:DWORD) : PChar; stdcall

external DLLName;

Much nicer, isn't it? Ok, ok, weve made it beautiful but how do we deal with it?

SWinInitLPHSTRINGParam is the key to create CTD strings. It takes 2 parameters, a handle and a

length. The handle will be created by the CTD runtime if you initialize it with 0 prior to calling this

function. The second parameter tells the runtime how many bytes to allocate for the actual string and

this is how to call the function:

Var

Hdl : THString;

begin

Hdl := 0;

SWinInitLPHSTRINGParam (Hdl, 200);

end;

The above sample creates a CTD string handle that points to a 200 byte string buffer. Fine, but how to

stuff the text into that buffer? Nothing easier than that.

Look at the SWinStringGetBuffer function. It expects 2 parameters. The first one is the handle that we

just created. The second one returns the length of the buffer where this handle is pointing to. The

result of the function is what we need: a PChar pointer that points directly to the buffer. This pointer

can be used to manipulate the strings content.

Now we have all we need to create a CTD native string and fill it with text but what if the Delphi DLL

receives a handle from the calling CTD application? The answer: simply forget about the

SWinInitLPHSTRINGParam function because the handle is already created and most probably will

already contain text. All you need is to call SWinStringGetBuffer and you get the length of the string

plus a pointer to its content.

Though the above already worked fine, I was still unsatisfied. I like my Pascal functions to be most

flexible and easy to handle so I wrote a few wrapper functions that are more intuitive:

interface

function SWinCreateString (StringValue:PChar) : THString; overload;

function SWinCreateString (StringValue:String) : THString; overload;

function SWinCreateString (StringLength:DWORD) : THString; overload;

procedure SWinSetString (StringHandle:THString; Value:PChar); overload;

procedure SWinSetString (StringHandle:THString; const Value:string); overload;

function SWinGetString (StringHandle:THString) : string; overload;

procedure SWinGetString (StringHandle:THString; var Value:PChar); overload;

implementation

function SWinCreateString (StringValue:PChar) : THString;

var

Len : DWORD;

begin

result := 0;

if SWinInitLPHSTRINGParam (result, StrLen (StringValue) + 1) then

StrCopy (SWinStringGetBuffer (result, Len), StringValue);

end;

function SWinCreateString (StringValue:String) : THString;

begin

result := SWinCreateString (PChar (StringValue));

end;

function SWinCreateString (StringLength:DWORD) : THString;

begin

SWinInitLPHSTRINGParam (result, StringLength);

end;

procedure SWinSetString (StringHandle:THString; Value:PChar);

begin

SWinSetString (StringHandle, string (Value));

end;

procedure SWinSetString (StringHandle:THString; const Value:string);

var

Len : DWORD;

begin

Len := Length (Value);

if SWinInitLPHSTRINGParam (StringHandle, Len + 1) then

StrPCopy (SWinStringGetBuffer (StringHandle, Len), Value);

end;

function SWinGetString (StringHandle:THString) : string;

var

Len : DWORD;

begin

result := StrPas (SWinStringGetBuffer (StringHandle, Len));

end;

procedure SWinGetString (StringHandle:THString; var Value:PChar);

var

Len : DWORD;

begin

Value := SWinStringGetBuffer (StringHandle, Len);

end;

The above functions provide means for creating, setting and getting strings in many ways. It is

possible to create strings by passing either a PChar or a Delphi native string type. Setting and getting

a strings content can also be done by using either PChar or the native string type.

You might have noticed already that there are some functions with identical names but different

parameters. They are marked as overloaded so the compiler will decide by the list of specified

parameters which function has to be called.

The functions themselves dont do any spectacular things, they just use SWinInitLPHSTRINGParam and

SWinStringGetBuffer to access the strings.

Armed with the above functions it should be a snap to handle CTD strings inside Delphi so go ahead

and use them. I dont want to see any SalStrSetBufferLength in your CTD applications anymore.

My Code:

#Include <WinAPI.au3>
#Include <Array.au3>
#Include <Memory.au3>

$APP_PATH = "d:\cdttest\"
$APP_TITLE = "CDT Test Application"

$gtlsi20 = DllOpen($APP_PATH & "gtlsi20.dll")
$gobji20 = DllOpen($APP_PATH & "gobji20.dll")
$gctli20 = DllOpen($APP_PATH & "gctli20.dll")
$snumi20  = DllOpen($APP_PATH & "snumi20.dll")
$SQLNGCI  = DllOpen($APP_PATH & "SQLNGCI.dll")
$SQLWNTM  = DllOpen($APP_PATH & "SQLWNTM.dll")
$srvci20  = DllOpen($APP_PATH & "srvci20.dll")
$ssti20  = DllOpen($APP_PATH & "ssti20.dll")
$tabli20 = DllOpen($APP_PATH & "tabli20.dll")
$mtbl20 = DllOpen($APP_PATH & "mtbl20.dll")
$cdlli20 = DllOpen($APP_PATH & "cdlli20.dll")

; Function Declaration
Func Debug($msg)
    ConsoleWrite($msg & @LF)
EndFunc 

Func DebugArray($array)
    For $xyz = 0 to UBound($array) - 1
        Debug("   $array[" & $xyz & "] = " & $array[$xyz])
    Next 
EndFunc 

Func CheckError()
    if @error Then _WinAPI_ShowError(Hex(_WinAPI_GetLastError()) & ": " & _WinAPI_GetLastErrorMessage())
EndFunc 

Func SalGetVersion() 
    Debug("Call SalGetVersion()" )
    $array = DllCall( $cdlli20, "int", "SalGetVersion")
    CheckError()
    DebugArray($array)
    Return $array[0]
EndFunc 

Func SalTblSetFocusRow($hWndTbl, $nRow) 
    Debug("Call SalTblSetFocusRow($hWndTbl: 0x" &  Hex($hWndTbl) & ", $nRow: " & $nRow & ")")
    $array = DllCall( $cdlli20, "int", "SalTblSetFocusRow", "hwnd", $hWndTbl, "int", $nRow)
    CheckError()
    DebugArray($array)
    Return $array[0]
EndFunc 

Func SWinInitLPHSTRINGParam($nLength)
    Debug("Call SWinInitLPHSTRINGParam($nLength: " &  $nLength & ")")
    $stringHandleStruct = DllStructCreate("dword")
    DllStructSetData($stringHandleStruct, 1, 0)
    $stringHandleStructPointer = DllStructGetPtr($stringHandleStruct)
    $array = DllCall( $cdlli20, "int", "SWinInitLPHSTRINGParam", "dword", $stringHandleStructPointer, "long", $nLength)
;test: $array = DllCall( $cdlli20, "int", "SWinInitLPHSTRINGParam", "dword*", $stringHandleStructPointer, "long", $nLength)

    CheckError()
    DebugArray($array)
    Return $array[0]; I will change this return to DllStructGetData($stringHandleStruct, 0)
EndFunc 

Func SalTblGetColumnText($hWndTbl, $nCol) 
    Debug("Call SalTblGetColumnText($hWndTbl: 0x" &  Hex($hWndTbl) & ", $columnId: " & $columnId & ")")
    $stringHandle = SWinInitLPHSTRINGParam(1024)
    $array = DllCall( $cdlli20, "int", "SalTblGetColumnText", "hwnd", $hWndTbl, "int", $columnId, "dword", $stringHandle)
    CheckError()
    DebugArray($array)
    Return $array[0]
EndFunc 

; Macro Start

; Find Centura:Dialog Handle.
$appHWnd = WinGetHandle($APP_TITLE)

; Find Centura:ChildTable Handle.
$hWnd = ControlGetHandle ($APP_TITLE, "", 0x1001)

; Get Centura Version
SalGetVersion()
; Set Table Focus on line 3
SalTblSetFocusRow($hWnd, 2)

;Create a CTD string handle LPHSTRING. ERROR!!!
$stringHandle = SWinInitLPHSTRINGParam(1024)

; Macro End


DllClose($cdlli20)
DllClose($mtbl20)
DllClose($tabli20)
DllClose($ssti20)
DllClose($srvci20)
DllClose($SQLWNTM)
DllClose($SQLNGCI)
DllClose($snumi20)
DllClose($gobji20)
DllClose($gctli20)
DllClose($gtlsi20)

The error occurrs on this line:

$array = DllCall( $cdlli20, "int", "SWinInitLPHSTRINGParam", "dword", $stringHandleStructPointer, "long", $nLength)

Error Message:

AutoIt v3: autoit3.exe - Application Error

The instruction at "0x02f38f9f" referenced memory at "0x00000030". The memory could not be "read".

If change dword to dword* occurs this error:

$array = DllCall( $cdlli20, "int", "SWinInitLPHSTRINGParam", "dword*", $stringHandleStructPointer, "long", $nLength)

AutoIt v3: autoit3.exe - Application Error

The instruction at "0x02ef801d" referenced memory at "0x00000008". The memory could not be "read".

I tried a lot of possibilities, like "dword*", "str", "ptr", "long", "ptr*", "str*", combined with DllStructCreate("dword"), or DllStructCreate("char[" & $nLength & "]")...

Console Output:

Call SalGetVersion()

$array[0] = 200

Call SalTblSetFocusRow($hWndTbl: 0x000E28A8, $nRow: 2)

$array[0] = 1

$array[1] = 0x000E28A8

$array[2] = 2

Call SWinInitLPHSTRINGParam($nLength: 1024)

>Exit code: -1073741819 Time: 1.191

Can anyone help me?
Link to comment
Share on other sites

Im using old version of Centura/CDT dll's.

version 2.0.0

$gtlsi20 = DllOpen($APP_PATH & "gtlsi20.dll")

$gobji20 = DllOpen($APP_PATH & "gobji20.dll")

$gctli20 = DllOpen($APP_PATH & "gctli20.dll")

$snumi20 = DllOpen($APP_PATH & "snumi20.dll")

$SQLNGCI = DllOpen($APP_PATH & "SQLNGCI.dll")

$SQLWNTM = DllOpen($APP_PATH & "SQLWNTM.dll")

$srvci20 = DllOpen($APP_PATH & "srvci20.dll")

$ssti20 = DllOpen($APP_PATH & "ssti20.dll")

$tabli20 = DllOpen($APP_PATH & "tabli20.dll")

$mtbl20 = DllOpen($APP_PATH & "mtbl20.dll")

$cdlli20 = DllOpen($APP_PATH & "cdlli20.dll")

Now i'm getting this error

Unhandled exception at 0x01be8f9f in centuraapp.exe: 0xC0000005: Access violation reading location 0x00000030.

With this C++ code:

#include "stdafx.h"
#include <windows.h>
typedef ULONG HSTRING;
typedef HSTRING FAR *LPHSTRING;

int _tmain(int argc, _TCHAR* argv[])
{
    HMODULE gtlsi20 = LoadLibrary("D:\\cdttest\\gtlsi20.dll");
    HMODULE gobji20 = LoadLibrary("D:\\cdttest\\gobji20.dll");
    HMODULE gctli20 = LoadLibrary("D:\\cdttest\\gctli20.dll");
    HMODULE snumi20  = LoadLibrary("D:\\cdttest\\snumi20.dll");
    HMODULE SQLNGCI  = LoadLibrary("D:\\cdttest\\SQLNGCI.dll");
    HMODULE SQLWNTM  = LoadLibrary("D:\\cdttest\\SQLWNTM.dll");
    HMODULE srvci20  = LoadLibrary("D:\\cdttest\\srvci20.dll");
    HMODULE ssti20  = LoadLibrary("D:\\cdttest\\ssti20.dll");
    HMODULE tabli20 = LoadLibrary("D:\\cdttest\\tabli20.dll");
    HMODULE mtbl20 = LoadLibrary("D:\\cdttest\\mtbl20.dll");
    HMODULE cdlli20 = LoadLibrary("D:\\cdttest\\cdlli20.dll");

    FARPROC SWinInitLPHSTRINGParamProcAddress = GetProcAddress(cdlli20, "SWinInitLPHSTRINGParam");

    //Function signature
    BOOL (__stdcall *SWinInitLPHSTRINGParam)(LPHSTRING, LONG) = (BOOL (__stdcall *)(LPHSTRING, LONG)) SWinInitLPHSTRINGParamProcAddress;

    HSTRING stringHandle = 0;
    LPHSTRING stringHandlePointer = &stringHandle;
    BOOL returnValue = (*SWinInitLPHSTRINGParam)(stringHandlePointer, 8);

    FreeLibrary(cdlli20);
    FreeLibrary(mtbl20);
    FreeLibrary(tabli20);
    FreeLibrary(ssti20);
    FreeLibrary(srvci20);
    FreeLibrary(SQLWNTM);
    FreeLibrary(SQLNGCI);
    FreeLibrary(snumi20);
    FreeLibrary(gctli20);
    FreeLibrary(gobji20);
    FreeLibrary(gtlsi20);

    return 0;
}

I need some help with this...

Edited by Gabriel
Link to comment
Share on other sites

  • 7 months later...

I'm also having the described problems as described above at the moment when trying to execute the SalTablGetColumnText command. Has anyone solved this problem?

Thanks for your information

Edited by madben2000
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...