How to get interface from C dll to Delphi?

I have a library written in C and I have a header file with a description of the interface in C. The DLL has a function to get this interface. How to describe it correctly and get it in the DELPHI application?

    using DllCallbackClassPtr = void*;
    using DllCallbackFunction = void(*)(const char *, DllCallbackClassPtr);

    #ifdef _WIN32

    #include <Windows.h>

    __interface IXeoma
    {

    public:
        enum ConnectErrorCode {
            OK = 0,
            SERVER_NOT_FOUND,
            WRONG_PASSWORD,
            UNKNOWN
        };

        // return ConnectErrorCode
        virtual int start(const char* connectionString) = 0;
        virtual bool isConnected() = 0;
        virtual void stop() = 0;

        virtual void requestData(const char* request, const char* additionalData, DllCallbackClassPtr classPtr, DllCallbackFunction callbackFunc) = 0;
        virtual const char* getRequestResult(const char* request) = 0;

        virtual void setCameraRenderHandle(const char* previewId, HWND hWnd) = 0;

    };

The library is loaded, but the function returns nil.

    type
       IXeoma = interface
         function Start(connectionString: PChar): integer;
       end;

    type
        TCreateXeomaInterface = function() : IXeoma; stdcall;

    var
        Form1: TForm1;
        CreateXeomaInterface: TCreateXeomaInterface;

    implementation

    {$R *.dfm}

    var
        LibraryHandle: THandle;

     procedure TForm1.Button1Click(Sender: TObject);
    var
        XeomaInt: IXeoma;
        i: integer;
    begin
        LibraryHandle := LoadLibrary(PChar('D:\Projects\XeomaSDK\Win32\Debug\xeomaclientdll.dll'));
        if LibraryHandle >= 32 then
        begin
            @CreateXeomaInterface := GetProcAddress(LibraryHandle, 'createXeomaInterface');
        end;
        XeomaInt := CreateXeomaInterface();
        // Here XeomaInt = nil
    end;

Upvotes: 0

Views: 351

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595339

The __interface extension in Visual C++, and the interface keyword in Delphi, are not the same thing, and are not compatible with each other.

IXeoma in the C++ code is just an ordinary class type, not a COM interface. But in Delphi, all interfaces derive from IUnknown, and all classes derive from TObject, neither of which you want in this situation. So, you are going to have to use a plain record instead, and declare TCreateXeomaInterface as returning a pointer to that record.

Also, note that a Delphi record can't have virtual methods, but the C++ class does have them, so you are going to have to manually account for the C++ class's vtable in Delphi.

Try something like this:

type
  DllCallbackClassPtr = Pointer;
  DllCallbackFunction = procedure(Param1: PAnsiChar; Param2: DllCallbackClassPtr); cdecl;

  IXeomaPtr = ^IXeoma; 

  IXeomaVTable = record
    start: function(_Self: IXeomaPtr; connectionString: PAnsiChar): Integer; cdecl;
    isConnected: function(_Self: IXeomaPtr): Boolean; cdecl;;
    stop: procedure(_Self: IXeomaPtr); cdecl;
    requestData: procedure(_Self: IXeomaPtr; request: PAnsiChar; additionalData: PAnsiChar; classPtr: DllCallbackClassPtr; callbackFunc: DllCallbackFunction); cdecl;
    getRequestResult: function(_Self: IXeomaPtr; request: PAnsiChar): PAnsiChar; cdecl;
    setCameraRenderHandle: procedure(_Self: IXeomaPtr; previewId: PAnsiChar; hWnd: HWND); cdecl;
  end;

  ConnectErrorCode = (
    OK = 0,
    SERVER_NOT_FOUND,
    WRONG_PASSWORD,
    UNKNOWN
  ); 

  IXeoma = record
    vtable: ^IXeomaVTable:
  end;

type
  TCreateXeomaInterface = function() : IXeomaPtr; stdcall;

var
  Form1: TForm1;
  CreateXeomaInterface: TCreateXeomaInterface;

implementation

{$R *.dfm}

var
  LibraryHandle: THandle;

procedure TForm1.Button1Click(Sender: TObject);
var
  XeomaInt: IXeomaPtr;
  i: integer;
begin
  XeomaInt := nil;
  LibraryHandle := LoadLibrary('D:\Projects\XeomaSDK\Win32\Debug\xeomaclientdll.dll');
  if LibraryHandle >= 32 then
  begin
    @CreateXeomaInterface := GetProcAddress(LibraryHandle, 'createXeomaInterface');
    XeomaInt := CreateXeomaInterface();
    if XeomaInt <> nil then
      XeomaInt^.vtable^.start(XeomaInt, '123:123@localhost:8090'); 
  end;
  ...
end;

Upvotes: 3

Related Questions