Search code examples
c#.net.net-coredll

Exporting a Function from a net 8 class library and consuming it in .NET Framework executable


I'm trying to export a function from a .NET 8.0 library. I'm using the 3F DLL export project (https://github.com/3F/DllExport) to achieve this.

public class Class1
{
    [DllExport]
    public static int AddOne(int x)
    {
        Console.WriteLine("Inside AddOne");
        return x + 1;
    }
}

The code for the .NET Core and .NET Framework test programs is this:

static void Main(string[] args)
{
    Console.WriteLine("Hello, DotNetCore");

    try
    {
        Console.WriteLine("1+1=" + AddOne(1));
    }
    catch (Exception ex)
    {
        Console.WriteLine(ex.ToString());
    }

    Console.ReadKey();
}

[DllImport("ClassLibrary.dll")]
private static extern int AddOne(int x);

Importing the DLL in a test .NET Core application and in a .NET Framework results in error:

.NET Core:

System.Runtime.InteropServices.SEHException (0x80004005): External component has thrown an exception.

at ConsoleCore.Program.AddOne(Int32 x)
at ConsoleCore.Program.Main(String[] args) in C:\work\stackoverflow\core and framework\Console\ConsoleCore\Program.cs:line 12

.NET Framework:

System.IO.FileNotFoundException: Could not load file or assembly 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' or one of its dependencies. The system cannot find the file specified.

File name: 'System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

at ConsoleApp.Program.AddOne(Int32 x)
at ConsoleApp.Program.Main(String[] args) in C:\work\stackoverflow\core and framework\Console\Console\Program.cs:line 17

=== Pre-bind state information === LOG: DisplayName = System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a (Fully-specified) LOG: Appbase = file:///C:/work/stackoverflow/core and framework/Console/Console/bin/Debug/ LOG: Initial PrivatePath = NULL Calling assembly : (Unknown).

LOG: This bind starts in default load context.
LOG: Using application configuration file: C:\work\stackoverflow\core and framework\Console\Console\bin\Debug\Console.exe.Config
LOG: Using host configuration file:
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Post-policy reference: System.Runtime, Version=8.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a
LOG: Attempting download of new URL file:///C:/work/stackoverflow/core and framework/Console/Console/bin/Debug/System.Runtime.DLL.
LOG: Attempting download of new URL file:///C:/work/stackoverflow/core and framework/Console/Console/bin/Debug/System.Runtime/System.Runtime.DLL.
LOG: Attempting download of new URL file:///C:/work/stackoverflow/core and framework/Console/Console/bin/Debug/System.Runtime.EXE.
LOG: Attempting download of new URL file:///C:/work/stackoverflow/core and framework/Console/Console/bin/Debug/System.Runtime/System.Runtime.EXE.

What do I want to achieve in the end? I need a .NET Core DLL and their functions I need to use in a .NET Standard 2.0 library (we can`t upgrade the library at this moment because of outdated dependencies).

Full solution ready for cloning is here: https://github.com/JYPDWhite/DllExportTest

Edit: tl;dr;

Library written in .NET 8.0 shall be used in a .NET Standard 2.0 library via P/Invoke. This .NET Standard 2.0 library is later used in .NET Core and .NET Framework applications.

The .NET Core application in this example is only for troubleshooting and is not the final use case.


Solution

  • Make sure that you published the class library (not just built) and does not mess with the bitness (e.g. you are not trying to load 64-bit dll into 32-bit exe). I was able to run the code with the following changes

    • the DllExport nuget was removed

    • the AddOne function was marked with the UnmanagedCallersOnlyAttribute

      [UnmanagedCallersOnly(EntryPoint = "AddOne")]
      public static int AddOne(int x){ /*code goes here*/}
      
    • the class library was published with a native AOT with the following command (run from the folder containing the Class library csproj)

      dotnet publish -f net8.0 -c Release -r win-x64 -p:PublishAot=true -p:NativeLib=Shared -p:SelfContained=true
      

      This will give you the native dll somewhere in the folder like \bin\Release\net8.0\win-x64\native\ClassLibrary.dll. Note the \Native\ part of the path.

    • reference the dll from the net framework project with the [DllImport] (you can replace the absolute path with the relative)

      [DllImport(@"path-to-project\ClassLibrary\bin\Release\net8.0\win-x64\native\ClassLibrary.dll")]
      static extern int AddOne(int x);
            
      
    • call function as usual.

    • make sure that your net framework app is 64-bit: check the <Prefer32bit>false</Prefer32bit> property in the csproj.

    • If you need 32-bit app, then you need to publish the class library as a 32-bit dll. If you need AnyCPU app, then you can use COM interop as suggested in the comments (and it will handle the bitness mismatch automatically, see here for details) or you will need to build both 32-bit and 64-bit dlls and choose which one to use in the runtime.