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?

Jerry Dodge
  • 26,858
  • 31
  • 155
  • 327
Morhem Howk
  • 85
  • 1
  • 5
  • It looks like your calling convention may be wrong among other issues. I'm not a Delphi expert but this may help http://stackoverflow.com/questions/16601423/calling-a-delphi-method-in-a-dll-from-c-sharp – Eric J. Jul 05 '16 at 17:10
  • Eww, DLL functions should never return any type of string, that has the potential to cause issues. – Jerry Dodge Jul 05 '16 at 17:10
  • Is there a chance that this Delphi DLL uses ShareMem? – Jerry Dodge Jul 05 '16 at 17:15
  • From what I read and tried by myself after I posted this, the problem is in return value. Apparetly, WideString CANNOT be used as return value like that, period. Is there no hope? – Morhem Howk Jul 05 '16 at 17:37
  • @JerryDodge unfortunately, there is no documentation or source code available for this dll. – Morhem Howk Jul 05 '16 at 17:56
  • 2
    http://stackoverflow.com/questions/9349530/why-can-a-widestring-not-be-used-as-a-function-return-value-for-interop – MBo Jul 05 '16 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 Jul 05 '16 at 18:25
  • @MBo thats what I am doing for past 20 minutes.. well, it seems Im not the only one who decided to handle it this way. Its really a "do or die" thing, making this pretty is not as important. – Morhem Howk Jul 05 '16 at 18:35
  • 1
    The question that I asked which @MBo linked is the key to this – David Heffernan Jul 05 '16 at 20:46

2 Answers2

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
);
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
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.

Morhem Howk
  • 85
  • 1
  • 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. – Rudy Velthuis Jul 06 '16 at 07: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`. – David Heffernan Jul 06 '16 at 08: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. – Morhem Howk Jul 07 '16 at 09: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. – David Heffernan Jul 07 '16 at 10:08
  • @DavidHeffernan Thank you, accepted your answer now! – Morhem Howk Jul 07 '16 at 11:04