• 首页 首页 icon
  • 工具库 工具库 icon
    • IP查询 IP查询 icon
  • 内容库 内容库 icon
    • 快讯库 快讯库 icon
    • 精品库 精品库 icon
    • 问答库 问答库 icon
  • 更多 更多 icon
    • 服务条款 服务条款 icon

.NET Native AOT的静态库和动态库

武飞扬头像
HueiFeng
帮助1

.NET不仅可以使用 C静态库与动态库,也可以将.NET实现的函数导出为C静态库与动态库。在没有Native Aot之前,.NET只能通过P/Invoke享受C/C 生态,而在Native Aot之后,不仅可以享受这些生态,还可以开发SDK供其他语言调用。

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

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

使用UnmanagedCallersOnly特性可以将C#函数导出提供给C调用,EntryPoint属性用于指定导出的方法名称。

.NET函数导出

使用UnmanagedCallersOnly特性可以将C#函数导出提供给C调用,EntryPoint属性用于指定导出的方法名称。

  1.  
    public static class MyFunctions
  2.  
    {
  3.  
    [UnmanagedCallersOnly(EntryPoint = "Add")]
  4.  
    public static int Add(int a, int b)
  5.  
    {
  6.  
    return a b;
  7.  
    }
  8.  
     
  9.  
    [UnmanagedCallersOnly(EntryPoint = "PrintString")]
  10.  
    public static void PrintString(IntPtr str)
  11.  
    {
  12.  
    Console.WriteLine(Marshal.PtrToStringAnsi(str));
  13.  
    }
  14.  
     
  15.  
    [UnmanagedCallersOnly(EntryPoint = "GetSystemInfoWrite")]
  16.  
    public static void GetSystemInfo()
  17.  
    {
  18.  
    Console.WriteLine($"ProcessorCount: {Environment.ProcessorCount}");
  19.  
    Console.WriteLine($"MachineName: {Environment.MachineName}");
  20.  
    }
  21.  
     
  22.  
    }
学新通

在项目属性中加入PublishAot即可:

  1.  
    <PropertyGroup>
  2.  
    <PublishAot>true</PublishAot>
  3.  
    </PropertyGroup>

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

dotnet publish -r win-x64 -c release /p:NativeLib=Static

dotnet publish -r win-x64 -c release /p:NativeLib=Shared

通过JetBrains dotPeek工具查看DLL文件中是否包含导出的几个函数:

学新通

在C 中使用Native dll

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

隐式调用

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

项目->属性->配置属性->链接器->常规->附加库目录:加上lib文件的存放目录;

附加依赖项---添加工程引用的lib文件名:

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

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

例如:

  1.  
    #include <iostream>
  2.  
    extern "C"
  3.  
    {
  4.  
    typedef int(AddFunc)(int, int);
  5.  
    typedef void(PrintStringFunc)(const char*);
  6.  
    typedef void(GetSystemInfoWriteFunc)();
  7.  
     
  8.  
    __declspec(dllimport) AddFunc Add;
  9.  
    __declspec(dllimport) PrintStringFunc PrintString;
  10.  
    __declspec(dllimport) GetSystemInfoWriteFunc GetSystemInfoWrite;
  11.  
    }
  12.  
     
  13.  
    int main()
  14.  
    {
  15.  
    int result = Add(1, 2);
  16.  
    std::cout << "Result: " << result << std::endl;
  17.  
    PrintString("Hello, world!");
  18.  
    GetSystemInfoWrite();
  19.  
    return 0;
  20.  
    }
学新通

随后编译项目,运行C 应用程序即可,如下所示。

  1.  
    Result: 3
  2.  
    Hello, world!
  3.  
    ProcessorCount: 12
  4.  
    MachineName: DESKTOP-MJL9J4R

显式调用

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

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

例如:

  1.  
    #include <windows.h>
  2.  
    #define PathToLibrary "C:\\Users\\hueifeng\\OneDrive\\InteropSample\\dotnetSample\\bin\\Release\\net7.0\\win-x64\\native\\dotnetSample.dll"
  3.  
     
  4.  
    typedef int (*AddFunc)(int, int); // 定义一个函数指针类型
  5.  
     
  6.  
    int main()
  7.  
    {
  8.  
    HMODULE hDll = LoadLibraryA(PathToLibrary); // 加载dotnetSample.dll文件并返回句柄
  9.  
    AddFunc pAdd = (AddFunc)GetProcAddress(hDll, "Add"); // 获取Add函数的地址并赋值给pAdd
  10.  
     
  11.  
    int a = 1, b = 2;
  12.  
    int c = pAdd(a, b); // 显式地通过pAdd指针来调用从DLL中导出的add函数
  13.  
    }

这篇好文章是转载于:学新通技术网

  • 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
  • 本站站名: 学新通技术网
  • 本文地址: /boutique/detail/tanhfjahac
系列文章
更多 icon
同类精品
更多 icon
继续加载