Reputation: 464
I have been trying to reuse a C dll file in delphi based on this documentation.
Server was running well, i could access and used the database on local server with java and php.
On delphi i used dynamic load and worked well on all functions that return variables but failed on those that return interface.
unit for library :
unit SQLDBC_C;
interface
uses windows, classes, sysutils;
type
SQLDBC_IRuntime = interface
end;
var
getSDKVersion : function :Pchar; stdcall;
ClientRuntime_GetClientRuntime: function (errorText:Pchar; errorTextSize:Integer) : SQLDBC_IRuntime; stdcall;
implementation
var
libhandle : THandle;
procedure initLibrary;
begin
libhandle := LoadLibrary('libSQLDBC_C.dll');
if libhandle>=23 then begin
@getSDKVersion:=GetProcAddress(libhandle,'getSDKVersion');
@ClientRuntime_GetClientRuntime:=
GetProcAddress(libhandle,'ClientRuntime_GetClientRuntime');
end;
end;
initialization
begin
initLibrary;
end;
finalization
begin
if libhandle>=32 then
FreeLibrary(libhandle);
end;
end.
here is the test procedure :
procedure TForm1.Button1Click(Sender: TObject);
var
err : array [0..200] of char;
rt : SQLDBC_IRuntime;
begin
Memo1.Clear;
FillChar(err, sizeof(err), 0);
Memo1.Lines.Add(getSDKVersion); //this function successed
rt := ClientRuntime_GetClientRuntime(@err,200);
//this function had no return value, (rt always nil) but no error return at err variable
if assigned(rt) then begin
......
end;
end;
I've read the similar problems asked by geskill, Dan Hacker, max and Ron but it could not solve my problem.
Could anyone told me what's wrong here?
Upvotes: 1
Views: 1542
Reputation: 612844
A C++ function that returns an interface does not easily map to a Delphi function. The calling convention for return values that are managed types in Delphi does not match that used by C++.
To illustrate, I created a simple C++ test DLL that exports this function:
extern "C" __declspec(dllexport) IUnknown* __stdcall GetInterface()
{
CoInitialize(NULL);
IUnknown* result;
CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER,
IID_IUnknown, (void**) &result);
return result;
}
The obvious way to map this to Delphi is like this:
function GetInterface: IUnknown; stdcall; external DLL name '_GetInterface@0';
However, when we call this function it always returns nil
.
The workaround is exactly as suggested by Serg:
function GetInterface: Pointer; stdcall; external DLL name '_GetInterface@0';
and we can then call it like this:
var
intf: IUnknown;
....
Pointer(intf) := GetInterface;
When we do this, intf
is not nil
and we can call methods on it quite happily.
So, what we have learnt here is that Delphi cannot easily call external functions that return interfaces, unless those external functions were also implemented in Delphi. But at least we have a viable workaround.
Unfortunately this workaround is of no immediate use to you. That's because SQLDBC_IRuntime
is a C++ class. It is not a COM compatible interface. Note that SQLDBC_IRuntime
does not implement IInterface
. So it doesn't provide _AddRef
, _Release
and QueryInterface
. Delphi's interface support is predicated on the availability of IInterface
. And this means that that you cannot use SQLDBC_IRuntime
from Delphi.
You will need to make a C++ bridge DLL that exposes the functionality in a way that Delphi can invoke. For example by exposing plain old functions that call the C++ methods of SQLDBC_IRuntime
.
Upvotes: 3
Reputation: 27493
I cannot test it because I have no libSQLDBC_C.dll
.
The problem was already explained . As a workaround for your case you can return a pointer in Delphi ClientRuntime_GetClientRuntime
declaration
ClientRuntime_GetClientRuntime: function (errorText:Pchar;
errorTextSize:Integer): Pointer; stdcall;
and cast it to SQLDBC_IRuntime
interface:
var
err : array [0..200] of char;
rt : SQLDBC_IRuntime;
begin
Pointer(rt):= ClientRuntime_GetClientRuntime(@err,200);
Upvotes: 3