【问题标题】:.NET Core project with native library depedency具有本机库依赖项的 .NET Core 项目
【发布时间】:2019-11-20 13:38:59
【问题描述】:

我正在使用 [DllImport] 在 .NET Core 中编写依赖本机代码的应用程序。我有用于 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 not found 异常。

我曾尝试使用 MSBuild 目标解决方案 here,但这只会在编译时将一个 dll 复制到主输出文件夹。但是,我希望输出包含与上述运行时文件夹相同结构的输出文件夹中的所有三个本机库,并将兼容本机库的选择留给 .NET Core 运行时主机。

所以如果用户在 Windows x86 中运行应用程序,它将使用 win-x86,依此类推。

我注意到,当我从 NuGet 引用像 SkiaSharp 这样的原生包装器时,它实际上会很好地集成到我的应用程序中,并且会将所有资产包含在运行时文件夹结构中,以便在运行时在多个环境中工作。我该怎么做?

编辑:

我最终为本地库绑定创建了一个 nuget 包,并在我的其他项目中引用了该 nuget。

【问题讨论】:

标签: .net-core cross-platform native


【解决方案1】:

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

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

以防万一,有一种方法可以更好地控制您如何使用 DllImport by 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();
}

【讨论】:

  • 看起来很有趣,但我如何告诉它在 runtimes/win-x86/native/somelib.dll 中搜索 Windows x86,并在 runtimes/win-x64/native/somelib.dll 中搜索 Windowx x64。由于DllImportSearchPath 仅具有特定的搜索位置,并且不太灵活。请注意,这仅适用于 .NET Standard 2.1 / Core 3.0,这意味着它不适用于 .NET Framework。
  • 我已经更新了一个答案 - 还有另一种从路径 public static bool TryLoad (string libraryPath, out IntPtr handle); 加载的方法
  • 谢谢你,@Andriy。我猜想使用 NativeLibrary 类是 .NET Core 3 和 .NET Standard 2.1 的方法,但这使得 .NET Framework 仅支持 .NET Standard 2.0。
  • @Ghasan 是的,但是您的问题与 .net 核心以及如何将文件复制到输出文件夹有关,NativeLib 只是有关如何在需要时控制库加载的附加信息,所以我认为它回答了你的问题。对于其他框架版本还有其他方法,可以使用 System.Runtime.Loader 包中的 AssemblyLoadContext 等。
  • NativeLibrary 似乎不包含在任何版本的 .NET Standard 中,只有在您直接引用 .NET Core 3.0+ 时才会出现。见here
【解决方案2】:

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

根据可用信息,最可能的罪魁祸首是 P/Invoke 系统不知道如何找到您的本地库。您的代码库是否有某种机制可以将搜索路径添加到运行时?或者您是否有机会使用custom load context

【讨论】:

    猜你喜欢
    • 2016-11-26
    • 2017-07-18
    • 2017-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-03-22
    相关资源
    最近更新 更多