加载外部类 C++

问题描述

我正在尝试加载 .dll 文件中定义的类。但是,在 dll 中定义类有两种稍微不同的方法。我不确定哪种方法更合法,我不知道为什么第二种方法也有效。这是一个简单的例子:

方法一: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;
}

bar 定义为 bar.h 中的纯虚拟类:

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

foo.dll文件中:

#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();
}

在第一种方法中,纯虚拟类 bar 用作接口,当加载 dll 时,它的成员函数foo 中的成员函数覆盖是有道理的。

然而,我发现 foo 不必从 bar 派生,只要 foo一个 Vtable,一切仍然有效:

在第二种方法中,除了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();
}

谁能告诉我为什么第二种方法有效?我有点困惑,因为 foobar 不相关,但 bar 中的成员函数仍然可以被覆盖。

解决方法

因此,您将返回 foo* 的函数转换为返回 bar* 的函数,然后调用它。

最终结果是您有一个指向 foo 的指针,它是一个指向无关类型 bar 的指针。以任何方式使用它都会导致undefined behavior

它似乎在这种特定情况下有效,因为 printmsg 虚函数在两个 vtable 中的位置相同,因此在 bar::printmsg 的实例上调用 foo 只需调用“ vtable 中的第 N 个条目”。如果您在 foo 之前向 printmsg 添加另一个虚拟成员,则它可能会被调用(否则程序可能会崩溃)。

,

第一个例子非常脆弱,因为它隐含地依赖于 foobar 是指针可互换的。

第二个示例被破坏,因为函数 Myfoo 返回一个指向类 foo 的指针,该指针与 bar 无关,并且取消引用它会导致未定义的行为。

函数签名必须与 dll 中实现的函数相匹配。您现在拥有的基本上是从 reinterpret_castfoo * 的狂野 bar *