-1

I am creating a DLL using C++ and importing it using C#. I have no problem compiling the DLL, and it works fine when calling it from .NET 4.7.1. However, when I try to call it from .NET 6.0, I get an EntryPointNotFoundException error:

Unhandled exception. System.EntryPointNotFoundException: 
Unable to find an entry point named 'E' in DLL 'test.dll'.
   at Program.E(int a, int b)
   at Program.Main(String[] args) in G:\C++\Test\Program.cs:line 6

Test.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!--<TargetFramework>net4.7.1</TargetFramework>-->
    <TargetFramework>net6.0</TargetFramework>
    <OutputType>Exe</OutputType>
    <Configuration>Debug</Configuration>
  </PropertyGroup>
</Project>

Test.cpp

extern "C"__declspec(dllexport) int E(int a, int b)
{
   return x + y;
}

Program.cs

public class Program
{
    [DllImport("test.dll")] public static extern int E(int a, int b);
    public static void Main(string [] args)
    {
        float r = E(11, 26);
        System.Console.WriteLine(r);
    }
}

My compiler flags are as follows:

g++ -shared -o test.dll Test.cpp -Wl,--out-implib,test.dll 

Research:

Because it is clear that this is an issue with .NET 6, I performed some specific searches using Google:

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
  • 7
    The question is being discussed on [meta](https://meta.stackoverflow.com/q/418302/11407695). – Oleg Valter is with Ukraine May 25 '22 at 01:56
  • And it is not related [to bitness](https://stackoverflow.com/q/270531/270545#270545)? (It can be detected at runtime - *for example*, if 32 bit is required for some reason, the .NET program can test itself and refuse to continue if it is not running as 32 bit (***with*** a \*\*\****meaningful***\*\*\* error message)—not relying on correct external configuration or other external factors to be correct.) For instance, [`IntPtr.Size`](https://learn.microsoft.com/en-us/dotnet/api/system.intptr.size) is 8 if running as 64 bit. [A Stack Overflow lead](https://stackoverflow.com/q/9206483). – Peter Mortensen May 25 '22 at 11:57
  • Use procmon from sysinternals and see where both versions are searching. – Simon Mourier May 25 '22 at 13:04
  • Hey Peter, I will have a look today. I ruled out bitness because both 4.7.1 and 6.0 can use both archs and when using the any cpu flags the csproj, results were no different. I will use procmon today though and see what I can find, thanks. –  May 25 '22 at 14:11

1 Answers1

11

I could reproduce the issue locally with the setup your described using Visual Studio 2022.

Preliminary note 1: The naming of all the .NET stuff can be quite confusing: ".NET" is not ".NET Framework". And ".NET Core" is the predecessor of ".NET". Roughly. See the documentation and this or this post.

Preliminary note 2: The main program code in .NET Core and .NET resides in a dll file which needs to be executed using dotnet myProject.dll. Starting with .NET Core 3.0, an additional exe file is created which simply wraps that command. On the other hand, .NET Framework produces an exe file directly. Compare e.g. this or this post.

Your problem: The issue in your case is now the following: First, you create your test.dll via g++. Then you build your Test.csproj file which, for .NET 6, produces not only Test.exe but also Test.dll. Since Windows' file system is case insensitive, building the csproj file overwrites your own g++ test.dll file. Thus, the resulting Test.dll is something entirely different. Attempting to execute Test.exe will attempt to import the function E from the dll, but since the dll was replaced, the function no longer exists. On the other hand, if you replace the Test.dll with the g++ test.dll after you built the csproj causes the Test.exe to no longer work (because, as described above, Test.exe does not really contain your code and instead calls Test.dll, which is no longer containing the correct .NET code).

For .NET Framework, on the other hand, the issue does not arise because building the Test.csproj file produces only a Test.exe and leaves your g++ test.dll untouched.

Solution: The simplest solution is to name your own dll produced by g++ differently, e.g. test2.dll, and import that in the C# project. Or choose a different name for the C# project (or rather, its output). Another solution would be to publish your .NET 6 project self-contained so that it consists of only an exe file. See the documentation or this or this post.

Sedenion
  • 5,421
  • 2
  • 14
  • 42
  • 2
    YES! This is correct and is such a sly problem. Thank you Sedenion, you have truly made my day! I can confirm, that by renaming the dll to something other than that of the csproj file name my dotnet project is in, resolves this issue!!! –  May 26 '22 at 03:50
  • 1
    Wow! That is some insightful intuition! I totally missed the name-collision from the OP's description. – Eljay May 27 '22 at 13:00
  • Note: Self-contained is not single-file. With .NET 6, you can publish self-contained without creating a single-file distribution, in which case you still have exe+dll. – PMF May 30 '22 at 17:51