How to initialize a C++ class inside a DLL which was loaded via LoadLibrary()?

508 views Asked by At

I got a problem from my clients.

They removed some VC++ projects from their product, only have the dlls .

Now they want to process some data with some functions inside these dlls.

Let's say this is one class inside a dll.

//MyClass.h
class __declspec(dllexport) MyClass{
public:
    int a;
    int b;
public:
    MyClass();
    int Sum(int, int);
}
//MyClass.cpp
MyClass::MyClass(){
    a=0;
    b=0;
}

int MyClass::Sum(int c, int d){
    return c+d;
}

And, here is the exe.

//Test.exe
typedef void(__stdcall *TCon)();    // for constructor
typedef int(__stdcall *TSum)(int, int);    // for function Sum()

int main(){
    HMODULE myDll = LoadLibrary(TEXT("MyClass.dll"));
    FARPROC con = GetProcAddress(myDll, "??0MyClass@@QAE@XZ");  // I got this from DUMPBIN.exe
    FARPROC sum = GetProcAddress(myDll, "?Sum@MyClass@@QAEHHH@Z");  // I got this from DUMPBIN.exe, too

    TCon f_con = (TCon)con;
    TSum f_sum = (TSum)sum;

    f_con();    //Here I got access violation exception
    printf("Sum is:%d¥n", f_sum(1,2));

    return 0;
}

If MyClass only has the Sum() function, no constructor, no member like a and b, the Sum() function can be called without problem, I tested this with VS2017.

But when the class has constructor and members, the memory access violation happens.

I guess this is maybe MyClass should be initialized first, so I tried to call the constructor, that's why I added f_con, but f_con got the same access violation problem.

What the clients asked, is we only have the dll files to import class.

As I know, there are few ways to import class from other dlls by include their headers. Because at least we need the declaration of class which we want to import.

Is there something I can do to get Sum() work?

2

There are 2 answers

0
hzh On BEST ANSWER

I found what I need here.

As I tested, use the inline assembler syntax will solve my problem:

__asm {MOV ECX, p};  //p points to a malloc area for instance.

Now I can associate the INSTANCE of that class for the constructor.

Because in EXE side I have to do everything myself, so I need to use that assembler syntax everytime I want to call a function of that class. Like this:

...
    __asm {MOV ECX, p};  //move instance's address into the ECX register
    f_con();  //call constructor
    __asm {MOV ECX, p};  //move instance's address into the ECX register again
    int r = f_Sum(1,2);  //call member function
...

Now I can call a class member function without any headers!

3
Pepijn Kramer On

I have the feeling you are missing something. All that mucking around with entry points should not be necessary. Unless you want to do this as an excercise.

When your client code is also C++ you should be able to include the header file for the class and just use it as normal.

However (to get runtime linking) you must be able to switch between dllexport and dllimport for your class declaration. dllexport must be used when you compile the dll, and dllimport when you use the dll.

So compile your dll with COMPILING_DLL (command line preprocessor definition) Then add a header file with this and use it from your class header.

#if COMPILING_DLL
    #define DLLEXPORT __declspec(dllexport)
#else
    #define DLLEXPORT __declspec(dllimport)
#endif

And change your class declaration to :

class DLLEXPORT  MyClass{...};