.NET不仅可以使用C静态库和动态库,还可以将.NET实现的功能导出到C静态库和动态库中。在 Native Aot 之前,.NET 只能通过 P/Invoke 享受 C/C++ 生态系统。有了Native Aot之后,你不仅可以享受这些生态,还可以开发其他语言调用的SDK。

.NET Native AOT 的 NativeLib 参数用于指定本机库的类型。在.NET 7中,该参数有两个选项:静态和共享。

  • Static:生成静态库,意味着所有依赖项都将被编译到生成的可执行文件中,因此更适合独立应用程序或需要最小化依赖项的应用程序。
  • 共享:生成动态库,这意味着依赖项将被编译成单独的本机库并在运行时动态加载。这种方法可以减少生成的文件大小,更适合需要共享依赖的应用程序,因此也称为共享库。

使用 UnmanagementCallersOnly 属性提供对 C 调用的 C# 函数导出。 EntryPoint 属性用于指定导出的方法名称。

.NET函数导出

使用 UnmanagedCallersOnly 功能提供 C# 函数导出到 C 调用。 EntryPoint 属性用于指定导出的方法名称。

公共静态类 MyFunctions
{
    [UnmanagedCallersOnly(EntryPoint = "添加")]
    公共静态 int 添加(int a,int b)
    {
        返回a+b;
    }

    [UnmanagedCallersOnly(EntryPoint = "PrintString")]
    公共静态无效PrintString(IntPtr str)
    {
        Console.WriteLine(Marshal.PtrToStringAnsi(str));
    }[UnmanagedCallersOnly(EntryPoint = "GetSystemInfoWrite")]
    公共静态无效 GetSystemInfo()
    {
        Console.WriteLine($"ProcessorCount: {Environment.ProcessorCount}");
        Console.WriteLine($"机器名称: {Environment.MachineName}");
    }

}

只需将 PublishAot 添加到项目属性即可:


 真实

您可以使用以下命令指定 NativeLib 参数:

dotnet 发布 -r win-x64 -c 发布 /p:NativeLib=静态

dotnet 发布 -r win-x64 -c 发布 /p:NativeLib=共享

使用JetBrains dotPeek工具检查DLL文件是否包含多个导出函数:

在C++中使用Native dll

C++中调用DLL函数也可以分为隐式调用和显式调用两种方式。

隐式调用

附加库目录---添加文件引用的lib动态库路径:

Project->Properties->Configuration Properties->Linker->General->Additional Library Directory:添加lib文件的存放目录;

其他依赖---添加项目引用的lib文件名:

项目->属性->配置属性->链接器->输入->附加依赖项:添加lib文件名。

隐式调用是指直接在代码中调用函数名,编译器会根据参数类型自动匹配合适的函数。

例如:

#包括
外部“C”
{
typedef int(AddFunc)(int, int);
typedef void(PrintStringFunc)(const char*);
typedef void(GetSystemInfoWriteFunc)();

__declspec(dllimport) AddFunc 添加;
__declspec(dllimport) PrintStringFunc PrintString;
__declspec(dllimport) GetSystemInfoWriteFunc GetSystemInfoWrite;
}

int main()
{
int 结果 = 添加(1, 2);
std::cout << "结果:" << 结果 << std::endl;
PrintString("你好,世界!");
获取系统信息写入();
返回0;
}

然后编译项目并运行C++应用程序,如下所示。

结果:3
你好世界!
处理器数量:12
机器名称: DESKTOP-MJL9J4R

显式调用

显式调用是指通过代码中的函数指针或GetProcAddress等API获取DLL中导出函数的地址,并通过该地址进行调用。

  • 加载DLL文件并返回句柄:HMODULE hDll = LoadLibraryA(PathToLibrary);
  • 获取导出函数在DLL中的地址,并赋值给指针变量:AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "Add");
  • 通过指针变量显式调用DLL导出的add函数:int c = pAdd(a,b);

例如:

#包括
#define PathToLibrary "C:\\Users\\hueifeng\\OneDrive\\InteropSample\\dotnetSample\\bin\\Release\\net7.0\\win-x64\\native\\dotnetSample.dll"

typedef int (*AddFunc)(int, int); // 定义函数指针类型

int main()
{
HMODULE hDll = LoadLibraryA(PathToLibrary); //加载dotnetSample.dll文件并返回句柄
AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "添加"); // 获取Add函数的地址并赋值给pAdd

整数a = 1,b = 2;
int c = pAdd(a, b); // 通过pAdd指针显式调用从DLL导出的add函数
}