11

How does a Delphi application call an exported function (non-COM) dotNET assembly and have the function return a string?

COM is not a possible solution for my particular application. I have control over both ends of the call.

What I have tried so far - Delphi client side

type
  TStrProc = procedure( var x: widestring); stdcall;

function TryIt: string;
var
  Handle: THandle;
  Proc: TStrProc;
  InData: widestring;
  OutData: widestring;
begin
  Handle    := LoadLibrary( 'DelphiToDotNet.dll');
  if Handle = 0 then exit;
  @Proc := GetProcAddress( Handle, 'StrProc');
  if @Proc <> nil then
    begin
    InData := 'input';
    Proc( InData);
    OutData := InData;
    end;
  FreeLibrary( Handle);
  result := OutData
end;

dotNET dll side

public class DotNetDllClass
{
  [DllExport]
  public static string StrProc(ref string s)
  {
      return "Hello from .Net " + s;
  }
}

What works

I can successfully pass integers into and out of dotNET procedures. I can successfully pass strings (widestring on the Delphi side) into dotNET procedures.

What doesn't work

In the above two listings, the string parameter returned is junk. Accessing it causes an AV.

Environment

Delphi XE7, dotNET 4, Win 7, 32 bit application and dll.

Sean B. Durkin
  • 12,659
  • 1
  • 36
  • 65

1 Answers1

12

The C# code to match the Delphi should be:

[DllExport]
public static void StrProc(
    [MarshalAs(UnmanagedType.BStr)] 
    ref string s
)
{
    s = "Hello from .Net " + s;
}

Note that the return type is void to match your Delphi code. And I've used UnmanagedType.BStr to match WideString. That's the simplest way to marshal text since the allocation is performed automatically for you by the two compilers.

Don't get caught out by trying to pass a string as a return value marshaled as a BStr. Delphi doesn't use the same ABI as other compilers, see Why can a WideString not be used as a function return value for interop?

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • So, if I create an object inside StrProc, how is the memory management? Will Delphi side handle the memory or c# side? Will garbage collector handle the object and free the memory? – Ugur Jul 27 '18 at 10:49
  • It's just like any other .net object, handled by GC – David Heffernan Jul 27 '18 at 11:07
  • 1
    Points for me to get it working: in C# use MarshalAs and ref and in Delphi use var at the parameter declaration. That made it work for me – ArieKanarie Aug 21 '18 at 12:15
  • @Arie Er, isn't that what the answer says? – David Heffernan Aug 21 '18 at 12:33
  • 1
    Yes it is. The answer is perfect. It was more a note to myself (and others who find this answer) to the points that are key. E.g. the word var in the Delphi declaration is easily overlooked. Which was what happend to me, and therefore I was wondering how it could work. – ArieKanarie Aug 21 '18 at 13:04
  • @ArieKanarie The `var` is present in the question – David Heffernan Aug 21 '18 at 13:30