views:

88

answers:

2

I am having trouble importing my C++ functions. If I declare them as C functions I can successfully import them. When explicit loading, if any of the functions are missing the extern as C decoration I get a the following exception:

First-chance exception at 0x00000000 in cpp.exe: 0xC0000005: Access violation.

DLL.h:

extern "C" __declspec(dllimport) int addC(int a, int b);
__declspec(dllimport) int addCpp(int a, int b);

DLL.cpp:

#include "DLL.h"
int addC(int a, int b) {
    return a + b;
}
int addCpp(int a, int b) {
    return a + b;
}

main.cpp:

#include "..DLL/DLL.h"
#include <stdio.h>
#include <windows.h>

int main() {
    int a = 2;
    int b = 1;
    typedef int (*PFNaddC)(int,int);
    typedef int (*PFNaddCpp)(int,int);

    HMODULE hDLL = LoadLibrary(TEXT("../Debug/DLL.dll"));

    if (hDLL != NULL)
    {
        PFNaddC pfnAddC = (PFNaddC)GetProcAddress(hDLL, "addC");
        PFNaddCpp pfnAddCpp = (PFNaddCpp)GetProcAddress(hDLL, "addCpp");
        printf("a=%d, b=%d\n", a,b);
        printf("pfnAddC: %d\n", pfnAddC(a,b));
        printf("pfnAddCpp: %d\n", pfnAddCpp(a,b)); //EXCEPTION ON THIS LINE

    }
    getchar();
    return 0;
}

How can I import c++ functions for dynamic loading? I have found that the following code works with implicit loading by referencing the *.lib, but I would like to learn about dynamic loading.

Thank you to all in advance.

Update: bindump /exports

1 00011109 ?addCpp@@YAHHH@Z = @ILT+260(?addCpp@@YAHHH@Z)
2 00011136 addC = @ILT+305(_addC)

Solution:

  1. Create a conversion struct as found here
  2. Take a look at the file exports and copy explicitly the c++ mangle naming convention.

    PFNaddCpp pfnAddCpp = (PFNaddCpp)GetProcAddress(hDLL, "?addCpp@@YAHHH@Z");

+2  A: 

Inevitably, the access violation on the null pointer is because GetProcAddress() returns null on error.

The problem is that C++ names are mangled by the compiler to accommodate a variety of C++ features (namespaces, classes, and overloading, among other things). So, your function addCpp() is not really named addCpp() in the resulting library. When you declare the function with extern "C", you give up overloading and the option of putting the function in a namespace, but in return you get a function whose name is not mangled, and which you can call from C code (which doesn't know anything about name mangling.)

One option to get around this is to export the functions using a .def file to rename the exported functions. There's an article, Explicitly Linking to Classes in DLLs, that describes what is necessary to do this.

James McNellis
Is it possible to determine the naming convention from the bindump? See updated question for bindump.
Shiftbit
@Shiftbit: The name mangling scheme is quite complex; I believe it is an undocumented implementation detail, but I could be wrong.
James McNellis
@James I think you might be right. There is a function to demangle the names in something like dbghlp, but it's only a best guess "here is what I think your stacktrace really looks like".
Igor Zevaka
@James +1 Thanks The mangle names work... however its not elegant. I enjoyed that article and for larger projects I would consider using that solution. Right now I just want quick access.
Shiftbit
@James: The force_cast technique is seems to be necessary if you want to call the constructor on dynamic class loading. (assuming no constructor helper function)
Shiftbit
+1  A: 

It's possible to just wrap a whole header file in extern "C" as follows. Then you don't need to worry about forgetting an extern "C" on one of your declarations.

#ifdef __cplusplus
extern "C" {
#endif

__declspec(dllimport) int addC(int a, int b);
__declspec(dllimport) int addCpp(int a, int b);

#ifdef __cplusplus
} /* extern "C" */
#endif

You can still use all of the C++ features that you're used to in the function bodies -- these functions are still C++ functions -- they just have restrictions on the prototypes to make them compatible with C code.

Ken Bloom