Search code examples
c++winapidllconstructorfunction-pointers

What are the different between calling constructor and calling function pointer of constructor in C++


This question is asked with purpose to enhance my understanding.

I was trying to dynamically load a native DLL, and notice a phenomenon that I can't get over my head.

So given my DLL has the following oversimplified exported class.

ClassA {

    SubClassA subClass;

    ClassA() {
        subClass = new SubClassA;
    }

    ~ClassA() {
        delete subClass;
    }
}

I tried two approach to link to the DLL and call the constructor in a separate C++ console project:

Approach 1: Static Link (success)

#include <ClassA.h>


void main() {

    ClassA* myClass;
    myClass = new ClassA();

}

Approach 2: Dynamically Link (failed with Memory Access Violation)

#include <ClassA.h>
#include <windows.h> 

void main() {

    HMODULE m_NativeLibraryHandle;
    
    ClassA* myClass;
    typedef ClassA* (__stdcall* ClassAConstructor)();
    ClassAConstructor p_Constructor;

    m_NativeLibraryHandle = ::LoadLibrary(L"ClassA.dll");
    p_Constructor = (ClassAConstructor)GetProcAddress(m_NativeLibraryHandle, "decoratedNameofConstructor");

    myClass = p_Constructor();
}

Debugging in the DLL project shows that the violation is thrown at subClass = new SubClassA;, where the this pointer shows "unable to read". (My guess is the new SubClassA is failed to be assigned to the subClass.)

What is the actual different that is between the above two method to caused the different in behavior? My understanding is that, LoadLibrary and GetProcAddress is basically doing what Linker is doing, so theoretically they should behave similar.


Solution

  • Calling a constructor and calling a function pointer of a constructor in C++ are very similar in the sense that they are both quite impossible:

    • You cannot call a constructor in C++.
    • You cannot obtain a pointer to a constructor in C++.

    According to the C++ spec about constructors:

    Constructors have no names and cannot be called directly.

    So, in C++, you do not call constructors. The language does that for you when you use the new keyword, when you declare an object on the stack, etc. The expression new MyClass() might look like a constructor invocation, and it does in fact contain a constructor invocation, and so it might be mistaken for a constructor invocation, but that's not what it is; it is a "new expression", which includes, among other things, an invocation of a constructor. But the other things are crucial: you cannot just skip them and directly invoke the constructor.

    Function pointers of constructors do not exist because normally you cannot take the address of a constructor. If you examine the names exported by the DLL, and somehow figure out which name corresponds to the constructor, and use that name to obtain the entry point of that constructor from the DLL, you are cheating: you are doing something which is not normally supported by the language. As a result, you should not attempt to invoke that entry point. A constructor is only supposed to be invoked by the language.

    Also according to the C++ spec:

    A constructor is a special non-static member function of a class.

    (Emphasis mine.)

    A constructor assumes that memory has been allocated, and proceeds to initialize that memory. The language takes care of the memory allocation, but invoking the constructor directly does not. Since a constructor is non-static, it expects an uninitialized this to be passed to it, but you are not allocating any memory and you are not passing anything to the constructor. The result is exactly as expected: a crash.

    And besides, think about it: the constructor could not be internally allocating the memory, because how would it work then if the object was allocated on the stack? And how would it work if the constructor was being invoked by the constructor of a derived class which presumably occupies more memory?

    You can get your DLL to work by using a factory method. A factory method is a public static method which executes new ClassA() and returns the result, or declares ClassA() and returns it by value. Then, obtain the address of the factory method from the DLL, and invoke it.