Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Export dll method from C++ to C#. Why I need: " extern "C" "

Tags:

c++

c#

dllexport

In my dll there is a method that I want to export.

//Works:

extern "C" __declspec(dllexport)

//Wont work

__declspec(dllexport)

C++ Export:

 extern "C" __declspec(dllexport) int Test();

C# import:

[DllImport("CircleGPU2_32.DLL", EntryPoint = "Test", 
    CallingConvention = CallingConvention.StdCall)]
public static extern int Test();

Why I need the extern "C" ?

like image 865
Pedro77 Avatar asked Dec 16 '11 13:12

Pedro77


People also ask

What does __ Declspec Dllexport do?

The __declspec(dllexport) attribute exports the definition of a symbol through the dynamic symbol table when building DLL libraries.

What is DLL export and import?

1. Overview. A Dynamic-link library, or DLL, file is a shared library used on Windows that contains functions and data. Like other executables on Windows, DLL files are of the Portable Executable (PE) file format. PE files have a variety of data in their file headers, which include import tables and export tables.

Can DLL export classes?

You can export an entire C++ class by placing the __declspec(dllexport) before the class name, or you can export a single method by placing __declspec(dllexport) before the method name. Make class methods static and public. The C/C++ DLL Adapter does not allow you to call public non-static methods.


2 Answers

This is due to name mangling done by C++.

extern "C" vs no extern "C"

As an example here is what CFF Explorer shows for a dll's export table. The first was built with borland's c++ compiler. The second was built with msvc.

Ordinal     FunctionRVA     Name RVA    Name
00000001    0020E140        0032C2B6    createDevice
00000002    0020E244        0032C2C3    createDeviceEx
0000000D    00328DA4        0032C0C1    @irr@core@IdentityMatrix
0000000E    00328DE4        0032C28A    @irr@video@IdentityMaterial

0000000C    000F9C80        001EE1B6    createDevice
0000000D    000F9CE0        001EE1C3    createDeviceEx
00000001    00207458        001EDDC7    ?IdentityMaterial@video@irr@@3VSMaterial@12@A
00000002    001F55A0        001EDDF5    ?IdentityMatrix@core@irr@@3V?$CMatrix4@M@12@B

The first 2 functions createDevice and createDeviceEx contain the extern "C" signature in its prototype while the others do not. Notice the difference in encoding when C++ mangling is used. The difference actually goes deeper than that.

ABI & Standardization

As explained in the other answers, the C++ standard does not specify an Abstract Binary Interface. That means the vendors that design their tools can pretty much do whatever the heck they want when it comes to how function calls are handled and how overloading works under the hood -- as long as it exhibits the expected behavior as dictated by the standard.

With all these different encoding schemes there's no way another language can have any hope of working with modules compiled with C++. Heck modules compiled with one C++ compiler is unlikely to work with another! Compiler vendors are free to change their encoding between versions at their discretion.

In addition, no common ABI means there's no common expected way to call into these functions/methods. For example, one compiler might pass its arguments on the stack while another compiler might pass it on the register. One might pass arguments left-to-right while another could be reversed. If just one of these aspects don't match exactly between the caller and the callee then your app will crash... that is if you're lucky. Instead of dealing with this, vendors simply say no by forcing a build error with the different encodings.

OTOH, while C doesn't have a standardized ABI either, C is a much simpler language to deal with by comparison. Most C compiler vendors handle function decoration and call mechanism in a similar way. As a result there is a sort of 'de-facto' standard even if an ABI isn't explicitly specified in the standard. With this commonality it makes it much easier for other languages to interface with modules compiled with C.

For example, a __stdcall decoration in a function signature is understood to follow a specific call convention. Arguments are pushed right-to-left and the callee is responsible for cleaning the stack afterwards. __cdecl is similar but it's understood that the caller is responsible for cleaning the stack.

The Bottomline

If the module in question must be interoperable with languages outside of C++, decorating it appropriately and exposing it as a C API is your best bet. Note that you are giving up some flexibility by doing this. In particular, you won't be able to overload those functions since the compiler can no longer generate unique symbols for each overload with name mangling.

If interoperability is not important for the module in question -- it's only going to be used with the same tool it was built with, then omit the extern "C" decoration from your prototypes.

like image 179
greatwolf Avatar answered Sep 22 '22 05:09

greatwolf


The main reason is to prevent the C++ name mangler from mangling the name of the function.

Try exporting it without the extern "C" and inspect the resulting DLL in Dependency Walker and you will see a quite different name for the exported function.

like image 22
David Heffernan Avatar answered Sep 19 '22 05:09

David Heffernan