如所见,例如在这里[1],我们可以从.NET导出一个函数,以便稍后静态链接到另一个可执行文件。
using System;
using System.Runtime.InteropServices;
public static class NativeExports
{
[UnmanagedCallersOnly(EntryPoint = "multiply")]
public static int Multiply(int a, int b)
{
return a * b;
}
}
其他方向也可以吗?
像这样的东西(伪代码),就是我正在寻找的:
using System;
using System.Runtime.InteropServices;
public static class NativeImports
{
[UnmanagedImpl(EntryPoint = "multiply")]
public static extern int Multiply(int a, int b);
}
稍后与此静态链接:
// multiply.c
int multiply(int a, int b)
{
return a*b;
}
总体目标是拥有一个单一、静态链接、无依赖的可执行文件,主要用 C# 编写。
我了解 P/Invoke 等。等人。这是我目前的解决方法。
在获得一些帮助后回答我自己的问题(https://github.com/dotnet/runtime/issues/89044):
这是我们想要静态链接到用 C# 编写的可执行文件的函数
#include <stdio.h>
#include <string.h>
double UnmanagedTest (const char* name, double num)
{
printf("Hello, %s\n", name);
return num * strlen(name);
}
这是我们的 C# 应用程序。要静态链接的外部函数的声明方式与普通 P/Invoke 函数相同。
using System.Runtime.InteropServices;
internal static class App
{
[DllImport("nativelib", EntryPoint = "UnmanagedTest", CallingConvention = CallingConvention.Cdecl)]
public static extern double UnmanagedTest ([MarshalAs(UnmanagedType.LPStr)] string name, double num);
public static void Main()
{
Console.WriteLine("Hello, World!");
var val = UnmanagedTest("World!", 7);
Console.WriteLine($"I got the number '{val}' back");
}
}
项目文件的这一部分负责生成直接调用并静态链接到用 C: 编写的库
app.csproj
<ItemGroup>
<!-- Generate direct PInvokes for Dependency -->
<DirectPInvoke Include="nativelib" />
<!-- Specify library to link against -->
<NativeLibrary Include="nativelib.lib" Condition="$(RuntimeIdentifier.StartsWith('win'))" />
<!-- Specify the path to search for libraries -->
<LinkerArg Include="/LIBPATH:..\\clib" Condition="$(RuntimeIdentifier.StartsWith('win'))" />
</ItemGroup>
我实际上找到了一种方法来做到这一点......但它利用了一种非常利基的互操作方法。您基本上可以将委托传递到您的 C# 代码中!不幸的是,这可能有一些限制,但我不确定。我也只尝试动态链接已编译的 C#,但我确信您也可以静态链接它。无论如何,这是一个例子:
main.c
typedef int(*FunctionPointer)(int, int);
extern void InitializeLib(FunctionPointer ptr);
extern void CSMain();
int multiply(int a , int b)
{
return a * b;
}
int main()
{
InitializeLib(&multiply);
CSMain();
}
然后你的 C# 文件将如下所示:
程序.cs
using System.Runtime.InteropServices;
namespace MyProgram;
unsafe class Program
{
public static delegate*<int, int, int>multiply { get; set; }
[UnmanagedCallersOnly(EntryPoint = "InitializeLib")]
public static void InitializeLib(delegate*<int, int, int> ptr)
{
multiply = ptr;
}
[UnmanagedCallersOnly(EntryPoint = "CSMain")]
public static void CSMain()
{
int val = multiply(5, 5);
}
}
我写完这段代码后没有测试。但我相信这会起作用......无论如何,这里的想法是编译 C 并将 C# 静态链接到 C 二进制文件。从那里您可以调用您的 C# 主函数,也可以将委托传递到您的 C# 代码中。
我已经写了一个简单的概念证明以及如何在我的 github 上构建它的说明: