0

I have an application that was previously written in .NET Framework. Robert Giesecke UnmanagedExport worked perfect for adding the [DllExport] attribute to my functions to expose them to native languages.

Now that we have upgraded to .Net6.0 this doesn't seem to work. The functions export and are visible using dumpbin. They do load in the native language, and function address can be found. Although, when executing the function I am given an exception Unhandled exception at 0x00007FFB928CCD29 (KernelBase.dll) in NativeExportTest.exe: 0xE0434352

If I switch back to .NET Framework or .NET Standard, everything works again. I have tried the following packages;

  • UnmanagedExports.Repack
  • UnmanagedExports.Repack.Updgrade
  • DllExport
  • NativeAOT (Works, but doesn't support all the features used in .NET 6.0).

I also looked at [UnmanagedCallersOnlyAttribute] for .NET6.0 but the underlying native language is something wacky that gets compiled into c++ at runtime. I do not believe it supports callbacks.

Is there someone way to export the .NET6.0 c# functions to native languages without relying on a callback?

I only need a solution for windows.

C#

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFramework>net6.0-windows</TargetFramework>
    <ImplicitUsings>enable</ImplicitUsings>
    <Nullable>enable</Nullable>
    <Platforms>AnyCPU;x64</Platforms>
  </PropertyGroup>
  <ItemGroup>
    <PackageReference Include="UnmanagedExports.Repack.Upgrade" Version="1.2.1" />
  </ItemGroup>
</Project>

public class Class1
{
    [DllExport]
    public static int _add(int a, int b)
    {
        return a + b;
    }
}

Some C++ that works with .NET Framework but not .NET Core.

const TCHAR* pemodule = _T("path to release dll");
HMODULE lib = LoadLibrary(pemodule);
typedef int( *_add)(int a, int b);
auto pAdd = (_add)GetProcAddress(lib, "_add");
int c = pAdd(1,2); // Exception here

Following Hans Passants suggestion of enabling mixed debugging lead me to a new exception. Now the exception is shown in the console before any code gets to load:

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

clamchoda
  • 4,411
  • 2
  • 36
  • 74
  • 1
    Make sure the runtime version of Core6 is installed on deploy machine. Also see : https://github.com/3F/DllExport/issues/143 – jdweng Dec 05 '22 at 06:50
  • @jdweng But 3F only supports up to .netstandard 2.1. I can’t reference my .net6 project from that – clamchoda Dec 05 '22 at 12:29
  • 1
    I include link to show that the error was due to the version of Net/Core being used. You are getting a kernel error which means the error is due to compatibility between your code and the OS. The issue could be Core or you may need to upgrade the kernel on your machine. What OS are you using and what machine? – jdweng Dec 05 '22 at 12:45
  • @jdweng I understand now, thank you for explaining. I am on Windows 10 64bit. The odd thing about the incorrect runtime is these libraries run fine on this machine. The exception only happens when calling them from unmanaged code. I’m on my way to a hotel for my daughters birthday but as soon as the kiddos are sleeping I’ll still try to update the runtime and see how that goes. – clamchoda Dec 05 '22 at 16:37
  • The link at top of posting is for VS2008 and VS2010. VS2008 (WinXP) is 32 bit version of Net. VS2010 there was a early release of Win7 that was 32 bit and a later version of Win7 that was 64 bit. Does code run when build as Release (not debug)? See : https://social.msdn.microsoft.com/Forums/sqlserver/en-US/e01ff045-b35c-4fd0-ab4d-c0128cc8ac6a/64-bit-migration-declspecdllexport-function-call-crash?forum=vclanguage&force_isolation=true – jdweng Dec 05 '22 at 16:54
  • @jdweng Am I understanding correctly that the UnmanagedExports.Repack (https://www.nuget.org/packages/UnmanagedExports.Repack) is intended to work for .NET6? I will try both these suggestions tonight. Thanks for your help – clamchoda Dec 05 '22 at 16:59
  • @jdweng I tried both of your suggestions but I still get the same exception. I have added my project file and code for more clarity. – clamchoda Dec 07 '22 at 02:51
  • I think you are missing the calling convention which may be defaulting to Standard instead of c language. See following : https://github.com/3F/DllExport – jdweng Dec 07 '22 at 09:58
  • In general the scourge of the unmanaged exports hack. The CLR likes throwing exceptions to notify you of mishaps. But if there is only unmanaged code as a backstop then you can't find out what exactly went wrong. 0xE0434352 doesn't mean anything more than "unhandled managed exception". Try to get ahead with the debugger. In your C/C++ project use Project > Properties > Debugging > "Debugger Type" = Mixed. – Hans Passant Dec 07 '22 at 19:56
  • @jdweng I tried with both and no luck – clamchoda Dec 08 '22 at 00:01
  • @HansPassant Thank you! When I enable mixed debugging it is indeed complaining about the runtime. Please see my edit with the exception. I'm wondering why is the runtime only "missing" when .net core is called from native code? – clamchoda Dec 08 '22 at 00:03
  • Run the compatibility troubleshooter : https://www.thewindowsclub.com/faulty-module-name-kernelbase-dll-causing-application-crashes – jdweng Dec 08 '22 at 00:30
  • @jdweng it shows the same exception regarding `System.Runtime, Version=6.0.0.0` – clamchoda Dec 08 '22 at 02:40
  • I've been testing all this (and also calling the c++ code from this https://github.com/dotnet/samples/blob/main/core/hosting/src/NativeHost/nativehost.cpp after "STEP 2") and as far as I can test, all the tools you found (which are really based on the same "old" "It Just Works" aka IJW tech) will trigger the loading of clr.dll which is from .NET Framework. clr.dll is the one which is throwing FileNotFoundException. You could replace your dllexport C++ loading code by something similar to the hosting sample (w/o dllexports). – Simon Mourier Dec 11 '22 at 17:07
  • 1
    Not what you want to hear, but the [UnmanagedExport nuget page for **Frameworks**](https://www.nuget.org/packages/UnmanagedExports#package-reference) shows only *Product: .NET Framework. Versions: net.* Last update was on 8/16/2015. .NET 6 (or .NET Core) don't seem to be supported at all. – dbc Dec 14 '22 at 03:17
  • @dbc But what about the UnmanagedExports.Repack.Upgrade shouldn't it support .NET 6? https://www.nuget.org/packages/UnmanagedExports.Repack.Upgrade – clamchoda Dec 14 '22 at 21:53
  • 1
    @clamchoda - According to its [Frameworks tab](https://www.nuget.org/packages/UnmanagedExports.Repack.Upgrade) it looks it should but it was last updated 3 years ago, on 8/16/2019. Beyond that I don't know. Sorry I can't be of more help. – dbc Dec 14 '22 at 22:04

1 Answers1

0

I got this working by upgrading to .NET 7.0 and using built in export functionality [UnmanagedCallersOnly] instead of [DllExport]. Functions can be called from C++ and show up in dumpbin as expected.

Note the project must be published as PublishAot, Shared and SelfContained.

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

Unfortunately this still only seems like a solution for x64. I am interfacing to a legacy application that runs as x86 on x64 Windows. For this I created the .NET Framework x86 DllExport that acts as a layer to .NET Core using sockets. Hoping for better NativeAOT support in the future.

clamchoda
  • 4,411
  • 2
  • 36
  • 74
  • Are you saying you switched from using the UnmanagedExports plugin and are using [UnmanagedCallersOnly] instead? – runfastman Jan 20 '23 at 19:18
  • @runfastman Yes after upgrading to 7.0. But we actually ended up going a different route. I host a local API inside the .NETCore forms app (https://stackoverflow.com/a/75063318/591285). We went back to our previously written `UnmanagedExports` plugin and updated it's function calls to use an `HttpClient` instead of directly calling .NETCore. We ported all of our `Model` and `Extension` libraries to `.NETStandard` which can be targeted by the legacy `UnmanagedExports` plugin. This makes serializing strongly type data between the two applications simple. Not ideal, but works for .NET 6.0 x86. – clamchoda Jan 24 '23 at 15:58