Writing C++ intended to be called from C#?
c#, c++, interop
Solution
C++ is a language that supports overloading. In other words, you can have more than one version of HelloWorld(). You could also export HelloWorld(int), a distinct version. It is also a language that requires a linker. In order to not confuzzle the linker about the same name for different functions, the compiler decorates the name of the function. Aka "name mangling".
The tool you want to use to troubleshoot problems like this is Dumpbin.exe. Run it from the Visual Studio Command Prompt on your DLL with the /exports option. You'll see this:
ordinal hint RVA name
1 0 000110EB ?HelloWorld@@YAXXZ = @ILT+230(?HelloWorld@@YAXXZ)
Bunch of gobbledegook, the exported name is shown in parentheses. Note the ? in the front and @@YAXXZ after the name, that's why the CLR cannot find the exported function. A function that takes an int argument will be exported as ?HelloWorld@@YAXH@Z (try it).
The [DllImport] directive supports this, you can use EntryPoint property to give the exported name. Or you can tell the C++ compiler that it should generate code that a C compiler can use. Put `extern "C"` in front of the declaration and the C++ compiler will suppress the name decoration. And won't support function overloads anymore of course. Dumpbin.exe now shows this:
ordinal hint RVA name
1 0 00011005 HelloWorld = @ILT+0(_HelloWorld)
Note that the name is still not plain "HelloWorld", there's an underscore in front of the name. That's a decoration that helps catch mistakes with the calling convention. In 32-bit code there are 5 distinct ways to call a function. Three of which are common with DLLs, __cdecl, __stdcall and __thiscall. The C++ compiler defaults to __cdecl for regular free functions.
This is also a property of the [DllImport] attribute, the CallingConvention property. The default that's used if it isn't specified is CallingConvention.StdCall. Which matches the calling convention for many DLLs, particularly the Windows ones, but doesn't match the C++ compiler's default so you still have a problem. Simply use the property or declare your C++ function like this:
extern "C" __declspec(dllexport)
void __stdcall HelloWorld() {
// etc..
}
And the Dumpbin.exe output now looks like:
ordinal hint RVA name
1 0 000110B9 _HelloWorld@0 = @ILT+180(_HelloWorld@0)
Note the added @0, it describes the size of the stack activation frame. In other words, how many bytes worth of arguments are passed. This helps catch a declaration mistake at link time, such mistakes are extremely difficult to diagnose at runtime.
You can now use the [DllImport] attribute as you originally had it, the pinvoke marshaller is smart enough to sort out the decoration of the actual function. You can help it with the ExactSpelling and EntryPoint properties, it will be slightly quicker but nothing you'd ever notice.
First question last: __declspec(dllexport) is just a hint to the compiler that you intend to export the function from a DLL. It will generate a wee bit of extra code that can help making the exported function call faster (nothing the CLR uses). And passes an instruction to the linker that the function needs to be exported. Exporting functions can also be done with a .def file but that's doing it the hard way.
Problem
So I am doing this as a learning moment and I'm not afraid to say I have no idea what I'm doing here. It might also be worth mentioning that I don't know much about C++ in this scenario. In C#, I've used `DllImport` plenty of times to bring in stuff from user32.dll or other DLLs that I haven't written, but I'm looking to better understand how the other half (the C++ half) is implemented to make this happen. The C++ code I have is simple and just to verify that the call went through successfully: ``` #include <iostream> using namespace std; __declspec(dllexport) void HelloWorld() { cout << "Hello, World" << endl; } ``` I don't know what the importance of `__declspec(dllexport)` is, but I've seen it on a couple websites that didn't touch much on its importance. My C# isn't very different than previous `DllImport`s I've done before: ``` [DllImport("TestDLL.dll")] static extern void HelloWorld(); static void Main(string[] args) { HelloWorld(); } ``` I'm compiled the C++ DLL and put it in the C# project and it's copied to the bin folder. When I run the C# project I get an `EntryPointNotFoundException` at the call to `HelloWorld()` inside the main function. My guess is that I need to either change the C++ code or the compilation flags of the C++ project. Currently "Use of MFC" is set to "Use Standard Windows Libraries" and there's no use of ATL or CLR. Any help would be greatly appreciated.