1

据我所知,如果填充兼容,跨库传递结构是安全的。因此,我使用包含单个成员的结构编写了一个测试,但在读取返回的结构时仍然出现运行时错误。

插件接口.h:

#ifndef PLUGIN_INTERFACE_H
#define PLUGIN_INTERFACE_H

typedef struct {
    const char *name;
} DataStruct;

class PluginInterface {
public:
    virtual DataStruct __cdecl getData() = 0;
};

#endif // PLUGIN_INTERFACE_H

插件.h:

#ifndef PLUGIN_H
#define PLUGIN_H

#include "PluginInterface.h"

class Plugin : public PluginInterface {
public:
    DataStruct __cdecl getData();
};

#endif // PLUGIN_H

插件.cpp:

#include "Plugin.h"

DataStruct Plugin::getData() {
    DataStruct data;
    data.name = "name of plugin";
    return data;
}

extern "C" __declspec(dllexport) PluginInterface* getInstance() {
    return new Plugin;
}

主.cpp:

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

typedef PluginInterface* (*PluginCreator) ();

int main()
{
    HINSTANCE handle = LoadLibrary("Plugin.dll");
    if (handle == nullptr) {
        std::cout << "Unable to open file!" << std::endl; return 0;
    }

    PluginCreator creator = (PluginCreator)GetProcAddress(handle, "getInstance");
    if (creator == nullptr) {
        std::cout << "Unable to load file!" << std::endl; return 0;
    }

    PluginInterface* plugin = creator();
    if (plugin == nullptr) {
        std::cout << "Unable to create plugin!" << std::endl; return 0;
    }

    DataStruct data = plugin->getData();
    std::cout << "so far so good" << std::endl;

    std::cout << data.name << std::endl; // Access violation

    return 0;
}

我用 mingw 编译了插件,用 VS2012 编译了可执行文件。我还尝试用 int 替换 const char*,在这种情况下我得到一个随机整数。我知道传递带有单个元素的结构没有多大意义,但我仍然想知道问题出在哪里。

4

1 回答 1

1

问题不在于按值传递结构,因为如果返回结构的函数是非成员声明的,这将起作用extern "C"。问题在于对虚函数的调用getData()。在您的示例中,VS2012 编译器生成代码以通过指向对象的指针调用虚函数,但该对象是由不同编译器生成的代码创建的。这失败了,因为两个编译器之间的 C++ ABI 不同——这意味着虚函数的底层实现不同。

调用creator成功是因为两个编译器都具有相同的 C ABI 底层实现。如果要跨库边界使用 C++,则需要确保使用相同的 C++ ABI 版本编译库。请注意,这在同一编译器的不同版本之间可能会有所不同。

于 2013-10-03T11:07:08.233 回答