简体   繁体   中英

C# Interop Delphi DLL

I Have a third party DLL write in Delphi "a.dll" (without source).

And this DLL has one method with this signature.

function GetAny(pFileName: String): String;

I can't do a interop call from c# because 'String type' has private access in delphi.

So a build another DLL in delphi to wrapper that call.

Delphi.

function GetAny(pFileName: String): String; external 'a.dll'

function GetWrapper(url : PChar) : PChar; stdcall;
begin
    Result := PChar(GetAny(url)); // I need avoid this String allocation, is throwing a exception.
end;

C#.

[DllImport("wrapper.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public static extern IntPtr GetWrapper(String url);

Inside "GetWrapper" i make a call to external "GetAny", the result is OK (in delphi i can debug), but before i get this result back in c# side, it's throwing a Exception.

IntPtr test = GetWrapper("a");
String result = Marshal.PtrToStringAnsi(test);

Your wrapper DLL also cannot call GetAny because string is a managed Delphi type that cannot be passed across module boundaries.

The problem is that the return value of GetAny is allocated in one module and deallocated in a different module. It is allocated in the DLL that implements GetAny, and deallocated in the DLL that calls GetAny. Since the two DLLs use different memory managers, you end up trying to deallocate memory that was allocated on a different heap.

If the DLL that implements GetAny can be persuaded to share a memory manager then you could solve that problem readily.

I do question the facts that you present though. As it stands, unless the DLL is designed to be used with ShareMem, that function can never be called safely.

If you were prepared to leak the memory you could try this:

Delphi

function GetAny(pFileName: string): PChar; external 'a.dll'

procedure GetWrapper(url: PChar; out value: WideString); stdcall;
var
  P: PChar;
begin
    P := GetAny(url);
    if Assigned(P) then
      Value := P
    else
      Value := '';
end;

C#

[DllImport("wrapper.dll"]
public static extern void GetWrapper(
    string url,
    [MarshalAs(UnmanagedType.BStr)]
    out string value
);

I've downloaded your code...

The solution could be like this:

  • Create a wrapper procedure in Delphi with "cdecl" declaration with 2 parameters of PChar type

    • the first one is IN parameter
    • the second one is OUT parameter

Original Delphi function:

function GetAny(pFileName: String): String; external 'a.dll';

Delphi – DLL with wrapped function:

 procedure GetWrapper (url: PChar; var urlNew: PChar) cdecl;
 var str: string;
 begin
      urlStr = string(url);
      urNewStr := GetAny(urlStr);
      urlNew := PChar(urNewStr);
 end;

 exports
    GetWrapper;
 begin
 end.
  • In Visual Studio:

Make the project x32 bit (not a x64 as it was in your sample)

  • Import DLL as Cdecl

[DllImport("wrapper.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]

  • Marshaling

public static extern void GetWrapper ([MarshalAs(UnmanagedType.LPStr)]string url, [MarshalAs(UnmanagedType.LPStr)] out string urlNew);

  • Calling in C#:

    string fileName;// = @"wertwertwertwertwer";

    GetWrapper("2.jpg", out fileName);

    Console.WriteLine(fileName);

In my environment it worked. (Delphi 5 and VS2012).

Hopefully it will work for you also.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM