如何从Delphi调用NativeAOT编译的C# DLL方法?
调用NativeAOT编译的C# DLL(带UnmanagedCallersOnly特性)的正确方法
问题背景
使用NativeAOT编译带有UnmanagedCallersOnly特性的C#类库为NativeLibrary.dll后,通过Delphi调用时遇到两个问题:
- 动态加载DLL后,
GetProcAddress无法获取Add函数指针; - 静态引用DLL时,程序启动报错
Application Error - The application was unable to start correctly (0xc000007b)。
解决步骤
一、修正NativeAOT编译配置
NativeAOT默认不会将标记UnmanagedCallersOnly的函数导出为DLL导出函数,且部分编译选项不适用于共享库场景,需修改项目文件:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Library</OutputType> <TargetFramework>net7.0</TargetFramework> <SelfContained>true</SelfContained> <PublishTrimmed>true</PublishTrimmed> <RuntimeIdentifier>win-x64</RuntimeIdentifier> <Platforms>x64</Platforms> <!-- 关键:指定生成共享原生库,确保函数被导出 --> <NativeLib>Shared</NativeLib> <!-- 移除不适用于共享库的选项 --> <!-- <PublishSingleFile>true</PublishSingleFile> --> <!-- <PublishReadyToRun>true</PublishReadyToRun> --> </PropertyGroup> </Project>
修改后重新发布DLL:
dotnet publish -c Release
二、Delphi调用的核心注意事项
- 平台匹配:必须使用x64架构的Delphi项目编译运行,否则会因32/64位不兼容触发
0xc000007b错误; - 调用约定:
UnmanagedCallersOnly特性默认使用Cdecl调用约定,Delphi声明函数时必须显式指定cdecl; - 内存管理:
sumstring函数返回的是C#通过Marshal.StringToHGlobalAnsi分配的内存,Delphi调用后需调用LocalFree手动释放,避免内存泄漏。
具体调用示例
方式1:动态加载DLL
unit NativeLibraryDynamicCaller; interface uses Winapi.Windows, System.SysUtils; type // 定义函数类型,匹配C#函数的参数、返回值和调用约定 TAddFunc = function(a, b: Integer): Integer; cdecl; TWriteLineFunc = function(pString: PAnsiChar): Integer; cdecl; TSumStringFunc = function(first, second: PAnsiChar): PAnsiChar; cdecl; procedure TestDynamicCall; implementation procedure TestDynamicCall; var hLib: THandle; AddFunc: TAddFunc; WriteLineFunc: TWriteLineFunc; SumStringFunc: TSumStringFunc; ResultStr: PAnsiChar; begin hLib := LoadLibrary('NativeLibrary.dll'); if hLib = 0 then begin Writeln('加载DLL失败: ' + SysErrorMessage(GetLastError)); Exit; end; try // 获取add函数地址并调用 @AddFunc := GetProcAddress(hLib, 'add'); if Assigned(AddFunc) then Writeln('add(2, 3) = ', AddFunc(2, 3)) else Writeln('获取add函数地址失败: ' + SysErrorMessage(GetLastError)); // 获取write_line函数地址并调用 @WriteLineFunc := GetProcAddress(hLib, 'write_line'); if Assigned(WriteLineFunc) then Writeln('write_line执行结果: ', WriteLineFunc(PAnsiChar(AnsiString('Hello from Delphi 动态调用!')))) else Writeln('获取write_line函数地址失败: ' + SysErrorMessage(GetLastError)); // 获取sumstring函数地址并调用 @SumStringFunc := GetProcAddress(hLib, 'sumstring'); if Assigned(SumStringFunc) then begin ResultStr := SumStringFunc(PAnsiChar(AnsiString('Delphi ')), PAnsiChar(AnsiString('Dynamic'))); try Writeln('sumstring结果: ', String(AnsiString(ResultStr))); finally // 释放C#分配的内存 Winapi.Windows.LocalFree(HLOCAL(ResultStr)); end; end else Writeln('获取sumstring函数地址失败: ' + SysErrorMessage(GetLastError)); finally FreeLibrary(hLib); end; end; end.
方式2:静态链接DLL
unit NativeLibraryStaticCaller; interface uses System.SysUtils, Winapi.Windows; // 直接声明外部函数,指定DLL路径和导出函数名(与C# EntryPoint一致) function add(a, b: Integer): Integer; cdecl; external 'NativeLibrary.dll' name 'add'; function write_line(pString: PAnsiChar): Integer; cdecl; external 'NativeLibrary.dll' name 'write_line'; function sumstring(first, second: PAnsiChar): PAnsiChar; cdecl; external 'NativeLibrary.dll' name 'sumstring'; procedure TestStaticCall; implementation procedure TestStaticCall; var ResultStr: PAnsiChar; begin Writeln('add(5, 7) = ', add(5, 7)); Writeln('write_line执行结果: ', write_line(PAnsiChar(AnsiString('Hello from Delphi 静态调用!')))); ResultStr := sumstring(PAnsiChar(AnsiString('Delphi ')), PAnsiChar(AnsiString('Static Link'))); try Writeln('sumstring结果: ', String(AnsiString(ResultStr))); finally // 释放C#分配的内存 Winapi.Windows.LocalFree(HLOCAL(ResultStr)); end; end; end.
静态链接注意:确保
NativeLibrary.dll位于Delphi程序的运行目录下,且项目编译目标为x64。
内容的提问来源于stack exchange,提问作者Greg Bishop
相关产品推荐
相关产品推荐

