8

I am writing an app in .NET Core that depends on native code using [DllImport]. I have the native compiled library artifacts for win-x64, win-x86, and linux-x64.

My project structure is like:

MyApp
|--Main.cs
|--runtimes
   |--win-x86
      |--native
         |--somelibrary.dll
   |--win-x64
      |--native
         |--somelibrary.dll
   |--linux-x64
      |--native
         |--libsomelibrary.so

I get then DLL not found exceptions when I run the app.

I have tried to use MSBuild targets solution here, but this only copies one dll to the main output folder at compile time. However, I want to the output to include all three native libraries in the output folder in the same structure as the runtimes folder above, and leave the selection of the compatible native library to .NET Core runtime host.

So if the user runs the app in Windows x86, it will use the win-x86, and so on.

I have noticed when I reference native wrappers like SkiaSharp from NuGet, it will actually integrate nicely into my app, and will include all assets in a runtimes folder structure to work on multiple environments at runtime. How can I do this?

Edit:

I ended up creating a nuget package for the native library binding, and reference the nuget in my other projects.

Ghasan غسان
  • 5,577
  • 4
  • 33
  • 44
  • @vassalware Hi,it is seems it is the way it is by design. NuGet package processing and msbuild project referncing are not the same. See here: https://github.com/dotnet/sdk/issues/8645 and: https://github.com/NuGet/Home/issues/8623 – Ghasan غسان Feb 08 '20 at 04:23
  • Maybe you could get some clues from a similar project here: https://github.com/BrannonKing/NLoptNet . It's been a while since I worked on it, so I can't explain it off the top of my head. – Brannon Feb 12 '20 at 23:20

2 Answers2

8

This should copy your folder structure to the output folder, just put it right away under the first </PropertyGroup> in your MyApp.csproj:

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

Just in case, there is a method to have more control on how you are loading the native libraries using DllImport by DllImportResolver delegate for your assembly.

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

There is also NativeLibrary class which allows to set and load native libraries for .net core. Some sample code:

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();
}
abdusco
  • 9,700
  • 2
  • 27
  • 44
Andriy Kizym
  • 1,756
  • 2
  • 14
  • 29
  • Looks interesting, but how would I tell it to search inside `runtimes/win-x86/native/somelib.dll` for Windows x86, and search inside `runtimes/win-x64/native/somelib.dll` for Windowx x64. Since `DllImportSearchPath` only has specific search places and not much flexible. Note that this only applies to .NET Standard 2.1 / Core 3.0, which means it won't work on .NET Framework. – Ghasan غسان Feb 17 '20 at 06:38
  • i've updated an answer - there is another method for loading from path `public static bool TryLoad (string libraryPath, out IntPtr handle);` – Andriy Kizym Feb 17 '20 at 10:08
  • Thank you, @Andriy. I guess using NativeLibrary class is the way to go for .NET Core 3 and .NET Standard 2.1, but that leaves .NET Framework which supports only up to .NET Standard 2.0. – Ghasan غسان Feb 19 '20 at 02:23
  • 1
    @Ghasan yes, but your question was related to .net core and about how to copy the files to the output folder, NativeLib was just additional information on how to control loading of the libraries if needed, so I think it answers your question. For other framework versions there are other methods, you can use AssemblyLoadContext in System.Runtime.Loader package, etc. – Andriy Kizym Feb 19 '20 at 09:47
  • `NativeLibrary` does not appear to be included in any version of .NET Standard and only appears if you are referencing .NET Core 3.0+ directly. See [here](https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices?view=netstandard-2.1). – Justin Skiles Apr 09 '20 at 18:29
0

Since you have not provided any code snippets which would show your usage of DllImportAttribute or any other environment initialization that your application might do then it is really difficult to hazard any guesses on what exactly could be going wrong.

With the information available the most likely culprit is simply that the P/Invoke system does not know how to find your native library. Does your codebase have some sort of mechanism to add search paths to the runtime? Or are you per-chance using a custom load context?