views:

307

answers:

4

Hopefully this is a brainlessly easy question, but it shows my lack of expertise with C++. I'm a C# programmer, and I've done extensive work with P/Invoke in the past with other people's C++/C dlls. However, this time I've decided to write a wrapper C++ dll (unmanaged) myself, and am then calling my wrapper dll from C#.

The problem I am immediately running into is that I am unable to define a C++ function that can be found by p/invoke. I don't know what the syntax for this is, but here's what I'm trying so far:

extern bool __cdecl TestFunc()
{
  return true;
}

Originally I simply had this, but it did not work either:

bool TestFunc()
{
  return true;
}

And then on the C# side, I have:

    public const string InterfaceLibrary = @"Plugins\TestDLL.dll";

    [DllImport( InterfaceLibrary, CallingConvention = CallingConvention.Cdecl,
        EntryPoint = "TestFunc" ), SuppressUnmanagedCodeSecurity]
    internal static extern bool TestFunc();

Everything compiles, but when I execute this C# p/invoke call, I get a System.EntryPointNotFoundException: Unable to find an entry point named 'TestFunc' in DLL 'Plugins\TestDLL.dll'.

Surely this must be something incredibly simple on the C++ end that I just don't know the syntax for.

+1  A: 

You have to expose this function with extern "C" otherwise the name gets mangled.

Yakeen
+6  A: 

You'll want to use extern "C" as well as __declspec(export), like so:

extern "C" _declspec(dllexport)  bool TestFunc()
{
    return true;
}

For full details, see MSDN on Marshalling Types.

Reed Copsey
Perfect, that did it! I had also tried just having extern "C" in the past, but that did not work. It fails until the _declspec(dllexport) is added in.
x4000
+1  A: 

The C++ compiler modifies the names of your functions to incorporate information about the parameters and return types. This is called name mangling. On the other hand, the C compiler doesn't mangle your function names.

You can tell the C++ compiler to work as a C compiler using extern "C":

extern "C" __declspec(dllexport) bool TestFunc { return true; }

To call functions from C# using P/Invoke, your names must not be mangled. Therefore, you can actually export C functions to C#. If you want the functionality to be implemented in C++, you can write a C function that just calls the C++ function implementing the functionality.

Eduardo León
+1  A: 

Extending Reed's correct answer.

Another issue you can run into when exposing a C++ function via PInvoke is using invalid types. PInvoke can really only support marshalling of primitive types and plain old data struct / class types.

For example, suppose TestFunc had the following signature

void TestFunc(std::string input);

Even adding extern "C" and __declspec(dllexport) would not be enough to expose the C++ function. Instead you would need to create a helper function which exposed only PInvoke compatible types and then called into the main function. For example

void TestFunc(const std::string& input) { ... }

extern "C" _declspec(dllexport)  void TestFuncWrapper(char* pInput) {
  std::string input(pInput);
  TestFunc(input);
}
JaredPar
Yeah, it's precisely this sort of reason that I'm writing a wrapper C++ dll, rather than trying to call all of the functions I need directly. My goal is to create a simpler interface for P/Invoke, while letting all the needed complexity with callbacks and complex types, etc, remain on the C++ side. Good points!
x4000
@x4000, I've taken the exact same approach before. Except instead of a new DLL, I just added the wrapper functions to the same DLL. More DLL's would have been cleaner.
JaredPar
The initial DLL that I'm wrappering is by a third party, so that wasn't an option for me. But you're right, I think it's cleaner in many respects to separate the wrappers from the content being wrappered when possible.
x4000