具有本地库依赖项的.NET Core项目

8
我正在使用.NET Core编写一个应用程序,该程序依赖于使用[DllImport]的本机代码。我拥有win-x64、win-x86和linux-x64的本机编译库文件。
我的项目结构如下:
MyApp
|--Main.cs
|--runtimes
   |--win-x86
      |--native
         |--somelibrary.dll
   |--win-x64
      |--native
         |--somelibrary.dll
   |--linux-x64
      |--native
         |--libsomelibrary.so

当我运行应用程序时,出现了DLL未找到的异常。
我尝试使用MSBuild目标解决方案here,但这只会在编译时将一个dll复制到主输出文件夹中。然而,我希望输出包含所有三个本地库以及与上述运行时文件夹相同结构的输出文件夹,并留下兼容本地库的选择给.NET Core运行时宿主。
因此,如果用户在Windows x86上运行应用程序,则会使用win-x86,依此类推。
我注意到当我从NuGet引用像SkiaSharp这样的本地包装器时,它实际上会很好地集成到我的应用程序中,并且会包含所有资产以在运行时适用于多个环境。我该怎么做?
编辑:
最终,我创建了一个NuGet包用于本地库绑定,并在其他项目中引用了该NuGet。

@vassalware 你好,看起来这是设计上的问题。NuGet包处理和msbuild项目引用不同。 请参考以下链接:https://github.com/dotnet/sdk/issues/8645 和 https://github.com/NuGet/Home/issues/8623 - Ghasan غسان
也许你可以从这个类似的项目中获取一些线索:https://github.com/BrannonKing/NLoptNet。我已经有一段时间没有工作了,所以无法立即解释它。 - Brannon
2个回答

8

这将复制您的文件夹结构到输出文件夹中,只需将其直接放置在MyApp.csproj中第一个</PropertyGroup>下方:

<ItemGroup>
    <None Update="runtimes\**" CopyToOutputDirectory="PreserveNewest"/>
</ItemGroup>

如果需要更多地控制使用 DllImport 加载本地库的方式,您可以通过为程序集指定 DllImportResolver 委托来实现。

public delegate IntPtr DllImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath);

还有一个名为 NativeLibrary 的类,允许为 .NET Core 设置和加载本地库。以下是一些示例代码:

static class Sample
{
    const string NativeLib = "NativeLib";

    static Sample()
    {
        NativeLibrary.SetDllImportResolver(typeof(Sample).Assembly, ImportResolver);
    }

    private static IntPtr ImportResolver(string libraryName, Assembly assembly, DllImportSearchPath? searchPath)
    {
        IntPtr libHandle = IntPtr.Zero;
        //you can add here different loading logic
        if (libraryName == NativeLib && RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && Environment.Is64BitProcess)
        {
            NativeLibrary.TryLoad("./runtimes/win-x64/native/somelib.dll", out libHandle);
        } else 
        if (libraryName == NativeLib)
        {
            NativeLibrary.TryLoad("libsomelibrary.so", assembly, DllImportSearchPath.ApplicationDirectory, out libHandle);
        }
        return libHandle;
    }

    [DllImport(NativeLib)]
    public static extern int DoSomework();
}

看起来很有趣,但我该如何告诉它在Windows x86中搜索runtimes/win-x86/native/somelib.dll,并在Windows x64中搜索runtimes/win-x64/native/somelib.dll。由于DllImportSearchPath只有特定的搜索位置,而不是很灵活。请注意,这仅适用于.NET Standard 2.1 / Core 3.0,这意味着它无法在.NET Framework上工作。 - Ghasan غسان
我已经更新了一个答案 - 有另一种从路径加载的方法 public static bool TryLoad (string libraryPath, out IntPtr handle); - Andriy Kizym
谢谢,@Andriy。我猜使用NativeLibrary类是.NET Core 3和.NET Standard 2.1的正确方法,但这只适用于支持最多.NET Standard 2.0的.NET Framework。 - Ghasan غسان
1
@Ghasan 是的,但是你的问题与 .net core 有关,关于如何将文件复制到输出文件夹,NativeLib 只是额外提供了一些信息,以便在需要时控制库的加载,因此我认为它回答了你的问题。 对于其他框架版本,还有其他方法,例如可以使用 System.Runtime.Loader 包中的 AssemblyLoadContext 等。 - Andriy Kizym
NativeLibrary 在任何 .NET Standard 版本中都似乎没有被包含,只有在直接引用 .NET Core 3.0+ 时才会出现。请参见此处 - Justin Skiles

0

由于您没有提供任何代码片段来展示您对 DllImportAttribute 或其他应用程序可能执行的任何环境初始化的使用,因此很难猜测出到底出了什么问题。

根据可用信息,最有可能的罪魁祸首就是P/Invoke系统不知道如何找到您的本地库。您的代码库是否有某种机制来向运行时添加搜索路径?或者您是否偶然使用了自定义加载上下文


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接