Translate C++ to Delphi, or convert to a dll

1k views Asked by At

I am trying to connect to a Graphtec plotter through a USB port in Delphi. The following snipet of C++ code is provided by the manufacturer:

#include <windows.h>
#include <stdio.h>

typedef HANDLE (APIENTRY *PROC_GITK_OPENUSB)(int, int, DWORD);

int main(int argc, char* argv[])
{
    HMODULE hUsbLib = LoadLibrary("GITKUSBP.DLL");

    PROC_GETK_OPENUSB lpfnGITK_OpenUsb = (PROC_GITK_OPENUSB)GetProcAddress(hUsbLib,            "GITK_OpenUsb");

    HANDLE hWrite = (lpgnGITK_OpenUsb)(0, 0, FILE_FLAG_OVERLAPPED);
    HANDLE hRead  = (lpgnGITK_OpenUsb)(0, 1, FILE_FLAG_OVERLAPPED);

    OVERLAPPED WriteEvt, ReadEvt;
}

There is more to it, of course, but this appears to be the critical code. In Delphi, I am not successful with attempts to translate. I either need some expertise on translation, or some guidance on converting the C++ code into a dll that I can call from Delphi.

2

There are 2 answers

1
David Heffernan On

You don't need to compile the C++ code into a DLL. You've already got a DLL containing the code – GITKUSBP.DLL. What you don't have is a way to link to it from your Delphi code.

From C++ you typically link using a header file (.h) and an import library (.lib). Although the sample here uses runtime linking and, as it happens, implements it poorly. It performs no error checking at all. I would not count that C++ code as a good example to follow.

From Delphi you need a translation of the header file to Pascal. It might look like this:

function GITK_OpenUsb(
  param1: Integer; 
  param2: Integer;
  param3: DWORD
): THandle; stdcall; external 'GITKUSBP.DLL';

You can find more meaningful names for the parameters from the documentation of the library.

The calling convention of stdcall comes from the APIENTRY in the C++ code. That is a macro for WINAPI which evaluates to __stdcall.

And that's all there is to it. You can call the function now from your Delphi code like this:

var
  hWrite, hRead: THandle;
....
hWrite := GITK_OpenUsb(0, 0, FILE_FLAG_OVERLAPPED);
hRead := GITK_OpenUsb(0, 1, FILE_FLAG_OVERLAPPED);

I've used load time linking here. That's always much more convenient and cleaner. And it absolves you of the need to write boilerplate to load the library, import the functions, and perform error checking. So, if you can, opt to use load time linking.

There are no doubt more functions to convert. You'll have to do them in a similar way. Do make use of existing resources to learn.

  • You must of course read the Delphi documentation: Importing Functions from Libraries.
  • A great source of header translations is the Windows unit in Delphi.
  • The JEDI project has a lot of header translations.
  • And of course here on Stack Overflow are large numbers of questions on this topic.
3
kludg On

Well your code snippet in Delphi is:


Updated: I improved the snippet to handle DLL load errors:

uses Windows;

type
  TPROC_GITK_OPENUSB = function(A1, A2: Integer; A3: DWORD): THandle; stdcall;

procedure main;
var
  hUsbLib: THandle;
  fnGITK_OpenUsb: TPROC_GITK_OPENUSB;
  hWrite, hRead: THandle;
  WriteEvt, ReadEvt: OVERLAPPED;

begin
  hUsbLib:= LoadLibrary('GITKUSBP.DLL');
  if (hUsbLib <> 0) then begin
    @fnGITK_OpenUsb:= GetProcAddress(hUsbLib, 'GITK_OpenUsb');
    if @fnGITK_OpenUsb <> nil then begin
      hWrite:= fnGITK_OpenUsb(0, 0, FILE_FLAG_OVERLAPPED);
      hRead:= fnGITK_OpenUsb(0, 1, FILE_FLAG_OVERLAPPED);
// ....    
    end;
    FreeLibrary(hUsbLib);
  end;
end;

begin
  main;
end.