c++cdllvtableloadlibrary

Loading external class c++


I am trying to load a class defined in a .dll file. However there are two slightly different ways of defining the class in the dll. I'm not sure which is the more legit way to do it, and I don't know why the second way also works. Here is a simple example:

Method 1: In main.cpp:

#include <iostream>
#include <windows.h>
#include <memory>
#include "bar.h"

using namespace std;

typedef bar* (* MYDLLPROC)();

int main()
{
    unique_ptr<bar> theresult;
    auto thelib = LoadLibrary(TEXT("foo.dll"));

    if (thelib != NULL) {
        MYDLLPROC theprocs = (MYDLLPROC)GetProcAddress(thelib, "Myfoo");
        cout << "load successfully" << endl;
        theresult.reset(theprocs());
        theresult->printmsg();
    } else {
        cout << "cannot load the dll" << endl;
    }

    return 1;
}

The bar is defined as a pure virtual class in bar.h:

class bar {
public:
    virtual ~bar() {};
    virtual void printmsg() = 0;
};

In the foo.dll source file:

#include <iostream>
#include <windows.h>
#include "bar.h"

using namespace std;

class foo: public bar {
public:
    foo() { cout << "foo is instantiated" << endl; }
    ~foo() {}
    void printmsg() final { cout << "msg from foo print" << endl; }
};

extern "C" __declspec(dllexport) foo* __cdecl Myfoo()
{
    return new foo();
}

In the first method the pure virtual class bar is used as an interface, and it makes sense that its member function(s) is overridden by that in foo when the dll is loaded.

However, I found that foo doesn't have to be derived from bar, everything still works as long as foo has a Vtable:

In the second method, everything is the same except for the definition of foo:

#include <iostream>
#include <windows.h>

using namespace std;

class foo {
public:
    foo() { cout << "foo is instantiated" << endl; }
    virtual ~foo() {}
    virtual void printmsg() final { cout << "msg from foo print" << endl; }
};

extern "C" __declspec(dllexport) foo* __cdecl Myfoo()
{
    return new foo();
}

Could anyone please let me know why the second method works? I'm a bit confused because foo and bar are not related but the member function in bar can still be overridden.


Solution

  • So you cast a function returning foo* as a function returning bar* and then invoking it.

    The end result is that you have a pointer to foo which is a pointer to an unrelated type bar. Using it in any way will lead to undefined behavior.

    It appears to work in this specific case because the position of the printmsg virtual function in both vtables is the same, so invoking bar::printmsg on an instance of foo simply calls the "Nth entry in the vtable". If you add another virtual member to foo before printmsg, then it might get called instead (or the program might crash).