Gabriel Posted November 18, 2008 Posted November 18, 2008 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: expandcollapse popup#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?
FireFox Posted November 18, 2008 Posted November 18, 2008 Hello, Perhaps that's problem with your dll because when i try to read dll it works...
Gabriel Posted November 18, 2008 Author Posted November 18, 2008 (edited) 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: expandcollapse popup#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 November 18, 2008 by Gabriel
madben2000 Posted July 3, 2009 Posted July 3, 2009 (edited) 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 July 3, 2009 by madben2000
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now