7
votes

I have a DLL compiled in Delphi 2007 and an example using it in other Delphi project. Here is a part of code:

TErrorCallback = function(Msg:PChar):byte of object;
TSaveEventCallback = function (Line:PChar; HiCode:PChar; LoCode:PChar; MobileNo:PChar):byte of object;

function InitModule(ErrorCallback:TErrorCallback; SaveEventCallback :TSaveEventCallback; MainWindowHandle:THandle; Sock_Event:integer):byte; stdcall; external 'My.dll' name 'InitModule';

function DLLSocketEvent(var msg: TMessage): byte; stdcall; external 'My.dll' name 'DLLSocketEvent';

function InitObjList(Objs: array of PChar; NumObjs: byte; Name: PChar):byte; stdcall; external 'My.dll' name 'InitObjList';

And here is my C# analog:

class Message
{
  unsigned int msg;
  int wParam;
  int lParam;
  int result;
};
delegate byte ErrorCallbackDelegate(string msg);
delegate byte SaveEventCallbackDelegate(string line, string hiCode, string loCode, string mobileNo);

[DllImport("My.dll")]
static extern byte InitModule(ErrorCallbackDelegate errorCallback, SaveEventCallbackDelegate saveEventCallback, IntPtr mainWindowsHandle, Int32 sockEvent);

[DllImport("My.dll")]
static extern byte DllSocketEvent(Message msg);

[DllImport("My.dll")]
static extern byte InitObjList(string[] objs, byte numObjs, string name);

The point is I've tried only InitModule method and it throwed an exception: A call to PInvoke function 'ProjTest!ProjTest.MyClass::InitModule' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

Please, help me with this. How should I describe these DLL functions in C#?

1

1 Answers

11
votes

You can't call that DLL from C#. The main problem are the two of object callbacks. There's no way in C# to match that. You will need to modify the existing DLL or add an intermediate adapter DLL. As it stands your DLL is only accessible from Delphi or C++ Builder.

If you can modify the DLL then the modification you need to make is to remove the of object. If you need the callback to act on an instance then you will need to pass the instance as a parameter. However, C# delegates can wrap all that up transparently so you would only need to pass the instance as a parameter if you needed the DLL to be accessible from other languages, e.g. Delphi.

The other issue is the open array parameter. That is also not easily accessed from other languages. Although there are tricks, I would recommend passing a reference to the first element rather than an open array. Open arrays are unique to Delphi.

I also do not understand why you are using the byte type to hold array length. You should use Integer for this. There's nothing to gain from using byte and you simply invite overflow. Also, MainWindowHandle should not be THandle in Delphi. It should be HWND.

My recommendation to you would be to modify the DLL to have a C compatible interface and thus be accessible from all languages that support that. In practice this would make it accessible from all mainstream programming languages.