Theo
Theo

Reputation: 464

Using C dll in delphi return nothing

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

Answers (2)

David Heffernan
David Heffernan

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

kludg
kludg

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

Related Questions