NativeAOT provides a sample c# class with methods marked as "UnmanagedCallersOnly", which I understand to be intended for use in unmanaged languages such as Delphi. The sample class the provide is:
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Runtime.InteropServices;
namespace NativeLibrary
{
public class Class1
{
[UnmanagedCallersOnly(EntryPoint = "add")]
public static int Add(int a, int b)
{
return a + b;
}
[UnmanagedCallersOnly(EntryPoint = "write_line")]
public static int WriteLine(IntPtr pString)
{
// The marshalling code is typically auto-generated by a custom tool in larger projects.
try
{
// UnmanagedCallersOnly methods only accept primitive arguments. The primitive arguments
// have to be marshalled manually if necessary.
string str = Marshal.PtrToStringAnsi(pString);
Console.WriteLine(str);
}
catch
{
// Exceptions escaping out of UnmanagedCallersOnly methods are treated as unhandled exceptions.
// The errors have to be marshalled manually if necessary.
return -1;
}
return 0;
}
[UnmanagedCallersOnly(EntryPoint = "sumstring")]
public static IntPtr sumstring(IntPtr first, IntPtr second)
{
// Parse strings from the passed pointers
string my1String = Marshal.PtrToStringAnsi(first);
string my2String = Marshal.PtrToStringAnsi(second);
// Concatenate strings
string sum = my1String + my2String;
// Assign pointer of the concatenated string to sumPointer
IntPtr sumPointer = Marshal.StringToHGlobalAnsi(sum);
// Return pointer
return sumPointer;
}
}
}
(from: Class1.cs)
How can the methods (Add, WriteLine, and sumstring) be called from Delphi?
Edit 1/1/2023
I've gone through the links (and also tried ChatGPT) but I'm still not getting this to work. I have a NativeLibrary.DLL file compiled from the C# code. Using dotPeek, I can see that the Add function is in the DLL. The project file I'm using is:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Library</OutputType>
<TargetFramework>net7.0</TargetFramework>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
<PublishTrimmed>true</PublishTrimmed>
<PublishReadyToRun>true</PublishReadyToRun>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<Platforms>x64</Platforms>
</PropertyGroup>
</Project>
I've tried calling the Add function from Delphi two different ways.
Method 1
unit TestClass;
interface
uses
System.SysUtils,
System.Classes, Windows;
const
MY_DLL = 'NativeLibrary.dll';
type
TAddFunc = function(a: Integer; b: Integer): Integer; stdcall;
TTestClass = class
public
class function TestAdd(a: Integer; b: Integer): Integer; static;
end;
implementation
class function TTestClass.TestAdd(a: Integer; b: Integer): Integer;
var
Handle: HMODULE; // THandle
Func: TAddFunc;
begin
Handle := LoadLibrary(MY_DLL);
try
Func := GetProcAddress(Handle, 'Add');
if Assigned(Func) then
begin
Result := Func(a, b);
end;
finally
FreeLibrary(Handle);
end;
end;
end.
This gets called by:
procedure TMainForm.Button1Click(Sender: TObject);
var
StringFromDLL: string;
begin
StringFromDLL := TTestClass.TestAdd(4,10);
ShowMessage(StringFromDLL);
end;
The application runs, but when TestAdd is called, a Handle is returned, but Func is nil and never gets assigned.
Method 2
unit TestClass2;
interface
uses
System.SysUtils,
System.Classes, Windows;
const
MY_DLL = 'NativeLibrary.dll';
function Add(a: Integer; b: Integer): Integer; stdcall;
external MY_DLL name 'Add';
type
TTestClass2 = class
public
class function TestAdd(a: Integer; b: Integer): Integer; static;
end;
implementation
class function TTestClass2.TestAdd(a: Integer; b: Integer): Integer;
begin
Result:=Add(a,b);
end;
end.
When I call TestClass2.TestAdd using this technique, upon application startup I get a prompt reading "Application Error - The application was unable to start correctly (0xc000007b). Click OK to close the application."