I have a delphi dll I want to return strings to a C# API.
I am using Delphi 11, and .NET 7
I have asked a similar question before, which I got to work, with sending a buffer to delphi which got filled and returned as an out
param.
Now I need the function to return a string of unknown length, and I am struggling getting it to work.
Here is a small sample that shows the errors:
Delphi:
library Project1;
uses
Sharemem;
{$R *.res}
function TestWide(aInput: WideString): WideString;
begin
result := aInput;
end;
function TestPChar(aInput: PChar): PChar;
begin
result := aInput;
end;
function TestString(aInput: string): string;
begin
result := aInput;
end;
exports
TestWide, TestPChar, TestString;
begin
end.
C#: (The specific DllImport
attributes might look random because I've tried all combinations)
using System.Runtime.InteropServices;
internal class Program
{
[DllImport("Project1.dll", EntryPoint = "TestWide", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode, BestFitMapping = true)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string TestWide([MarshalAs(UnmanagedType.BStr)] string input);
[DllImport("Project1.dll", EntryPoint = "TestPChar", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string TestPChar([MarshalAs(UnmanagedType.LPStr)] string input);
[DllImport("Project1.dll", EntryPoint = "TestString", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.BStr)]
public static extern string TestString([MarshalAs(UnmanagedType.BStr)] string input);
private static void Main(string[] args)
{
while (true)
{
Console.WriteLine("Press key to test...");
Console.WriteLine("W for WideString");
Console.WriteLine("P for PChar");
Console.WriteLine("S for String");
var key = Console.ReadKey().KeyChar;
try
{
switch (key)
{
case 'w':
Console.WriteLine(TestWide("Wide")); //Runtime error 203 at address
break;
case 'p':
Console.WriteLine(TestPChar("Pchar")); //Another -big number exit code
break;
case 's':
TestString("str"); //System.AccessViolation
break;
default:
break;
}
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
}
}
Can someone please point me in the correct direction.
I tried all other answers but got it to work like this:
function TestWide(text: PWideChar): PWideChar
begin
//DoStuff
end;
procedure DisposeStr(text: PWideChar);
begin
StrDispose(text);
end;
C# call:
[LibraryImport("MyDll.dll", EntryPoint = "TestWide", StringMarshalling = StringMarshalling.Utf16)]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
private static partial IntPtr TestWide(string parameters);
[LibraryImport("MyDll.dll", EntryPoint = "DisposeStr")]
[UnmanagedCallConv(CallConvs = new Type[] { typeof(System.Runtime.CompilerServices.CallConvStdcall) })]
private static partial void DisposeStr(IntPtr str);
public static void Test()
{
IntPtr widePtr = TestWide("TextMessage");
string? wideStr = Marshal.PtrToStringUni(widePtr);
DisposeStr(widePtr);
}
It takes a little more management than I would like, but could not get it to work otherwise.