8

I have a third party "mystery dll" written with Delphi(unknown version), working example in delphi (past 2009), dire need to use said dll in my C# code, and almost no relevant knowledge on how to do it.

Here is Delpi example in using this dll:

type
TD_Query = function(host: WideString; port : Word;pud,query : WideString):WideString; stdcall;
procedure TForm11.Button6Click(Sender: TObject);
var
   Handle         : LongWord;
   D_Query        : TD_Query;
   sss            : WideString;
begin

 Handle := LoadLibrary('kobrasdk.dll');
 sss:='';
 if Handle <> 0 then
 begin
  @D_Query := GetProcAddress(Handle, 'D_Query');
  sss:=D_Query('host',8201,'pud','query');
  FreeLibrary(Handle);
 end;
end;

And here is my attempts to interpret it in C#:

class Program
{
    [DllImport("C:\\Games\\kobrasdk.dll", CallingConvention = CallingConvention.StdCall,
        CharSet = CharSet.Ansi)]
    [return: MarshalAs(UnmanagedType.LPStr)]
    public static extern string D_Query(string host, ushort port, string pud, string query);


    static void Main(string[] args)
    {
        D_Query("test", 8201, "test", "test");
    }
}

Unfortunately, what I have is an error: Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

From what I read during the day, I probably fudged up with return type, or parameter types. Help?

9
  • It looks like your calling convention may be wrong among other issues. I'm not a Delphi expert but this may help stackoverflow.com/questions/16601423/…
    – Eric J.
    Commented Jul 5, 2016 at 17:10
  • Eww, DLL functions should never return any type of string, that has the potential to cause issues. Commented Jul 5, 2016 at 17:10
  • 2
    stackoverflow.com/questions/9349530/…
    – MBo
    Commented Jul 5, 2016 at 18:17
  • 1
    There is a hope - I'd like to create proxy dll in Delphi that returns data in the manner compatible with CSharp
    – MBo
    Commented Jul 5, 2016 at 18:25
  • 1
    The question that I asked which @MBo linked is the key to this Commented Jul 5, 2016 at 20:46

2 Answers 2

6

The Delphi ABI differs from the Microsoft ABI for certain types. A Delphi WideString is a managed type (in Delphi terminology) and as return types use an ABI that is incompatible with Microsoft tools.

The Delphi ABI translates a managed return type into a hidden var parameter. So the compiler transforms:

function(host: WideString; port: Word; pud, query: WideString): WideString; stdcall;

into

procedure(var result: WideString; host: WideString; port: Word; pud, query: WideString); 
  stdcall;

You can therefore access your original Delphi function from C# by importing the function in its transformed guise.

[DllImport(@"...", CallingConvention = CallingConvention.StdCall)]
public static extern void My_D_Query(
    [MarshalAs(UnmanagedType.BStr)]
    out string result,
    [MarshalAs(UnmanagedType.BStr)]
    string host,
    ushort port,
    [MarshalAs(UnmanagedType.BStr)]
    string pud,
    [MarshalAs(UnmanagedType.BStr)]
    string query
);
0

I mostly figured it out. For some reason unclear to me, C# cant handle WideString return values. If you have access to delphi source code, it might be appropriate to exchange function with procedure, and pass return value as "out" parameter. In my case, I did not have access to source, so I was forced to write a proxy DLL to do so. For example above, "proxy" dll code:

  type
  TD_Query = function(host : WideString;port : Word;pud,query : WideString):WideString; stdcall;

procedure My_D_Query(host: WideString; port: Word; pud, query: WideString; out return : WideString); stdcall;
var
   Handle: LongWord;
   D_Query : TD_Query;
   sss : WideString;
begin
 Handle := LoadLibrary('kobrasdk.dll');
 sss:='';
 if Handle <> 0 then
 begin
  @D_Query:=GetProcAddress(Handle, 'D_Query');
  sss:=D_Query(host,port,pud,query);
  FreeLibrary(Handle);
 end;
return := sss;
end;

Then C# code to access it:

[DllImport("C:\\MyDll.dll", CallingConvention = CallingConvention.StdCall,
    CharSet = CharSet.Ansi)]
public static extern void My_D_Query(
[MarshalAs(UnmanagedType.BStr)]
        string host,
        int port,
[MarshalAs(UnmanagedType.BStr)]
        string pud,
[MarshalAs(UnmanagedType.BStr)]
        string query,
[MarshalAs(UnmanagedType.BStr)]
        out string result
);

Its not pretty, but for me, it was the answer.

5
  • Did you see the link that @MBo posted? AFAICS, the WideString return value is in reality an out parameter already and should be declared as such on the C# side. I would try that first. Commented Jul 6, 2016 at 7:39
  • No need for a proxy DLL, see my answer. That said, a proxy DLL is a clean way to deal with tricky imports, it just isn't needed here. Also, Word maps to ushort. Commented Jul 6, 2016 at 8:16
  • @DavidHeffernan I tried to get rid of proxy DLL like you suggested, but I keep getting "Attempted to read or write protected memory." error. Changed int to ushort btw. Commented Jul 7, 2016 at 9:16
  • I made a mistake in my answer. The transformation puts the result as the first parameter and not the last parameter. My updated answer will now work. Sorry, I mis-remembered this detail. Commented Jul 7, 2016 at 10:08
  • @DavidHeffernan Thank you, accepted your answer now! Commented Jul 7, 2016 at 11:04

Not the answer you're looking for? Browse other questions tagged or ask your own question.