简体   繁体   中英

Passing struct by value from an explicitly loaded dll built with a different compiler

As far as I know it is safe to pass a struct across libraries, if the padding is compatible. So I wrote a test with a struct containing a single member, yet I still get a runtime error when reading the returned struct.

PluginInterface.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

Plugin.h:

#ifndef PLUGIN_H
#define PLUGIN_H

#include "PluginInterface.h"

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

#endif // PLUGIN_H

Plugin.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;
}

main.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;
}

I compiled the plugin with mingw, the executable with VS2012. I also tried to replace the const char* with an int, in which case I get a random integer. I know passing a struct with a single element doesn't make much sense, but I still wonder what the problem is.

The problem is not with passing the struct by value, as this would work if the function returning the struct was a non-member declared extern "C" . The problem is with the call to the virtual function getData() . In your example, the VS2012 compiler generates the code to call the virtual function via a pointer to an object, but the object was created by code generated by a different compiler. This fails because the C++ ABI differs between the two compilers - which means that the underlying implementation of virtual functions is different.

The call to creator succeeds because both compilers have the same underlying implemention of the C ABI. If you want to use C++ across library boundaries, you need to ensure that the libraries are compiled with the same C++ ABI version. Note that this can differ between different versions of the same compiler.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM