How can I instantiate a class from a dll in C++?

1

I am writing an API for plugin developers so that they can access the SDK of an application through my API. My API offers a macro and abstract class* in a header file that plugin-devs must include to inherit from the abstract base class and implement the necessary functions (*see code below).

As far as research on the issue at hand, I've read through many MSDN and stackoverflow articles and found these most relevant:

This question is about getting an instance of an inherited abstract class/interface that you're expecting plugin developer to implement when they create their .dll files.

Perhaps my methodology is incorrect and I've gone down a rabbit hole, but something like Reflections and/or COM seems what I should be doing... only COM seems overkill for this operation as the application will be running client side. This article on C++/CLI Reflections seemed promising, but I'm working in Visual Studio with C++17.

At the highest level, the intended behavior is:

  1. API.dll loads directory of plugins (e.g. plugin/plugin1.dll and plugin/plugin2.dll)
  2. API.dll creates singleton instance of the plugin through the abstract class' getPlugin()
  3. Call the other methods implemented by the plugin, of notable interest is load()

So here's some background setup information about the API's usage by the plugin developers. The API offers a header to the devs for the interface/abstract class and the methods to create singletons in a macro.

API's: baseplugin.hpp

#ifdef BUILDINGAPI
#define PLUGINIMPORT 
#define PLUGINEXPORT  __declspec(dllimport)
#else
#define PLUGINIMPORT  __declspec(dllimport)
#define PLUGINEXPORT  __declspec(dllexport)
#endif

// Long macro defined here for creating/deleting singleton
#define PLUGIN(classType)                                           \
static std::shared_ptr<classType> singleton;                        \
extern "C" {                                                        \
    PLUGINEXPORT uintptr_t getPlugin()                                  \
    {                                                               \
        if(!singleton) {                                            \
            singleton = std::shared_ptr<classType>(new classType());\
        }                                                           \
        return reinterpret_cast<std::uintptr_t>(&singleton);        \
    }                                                               \
    PLUGINEXPORT void erasePlugin() {                               \
        if(singleton)                                               \
            singleton = nullptr;                                    \
      }                                                             \
}

// Abstract class defined here:

class PLUGINEXPORT baseplugin
{
public:
    virtual void load() = 0;
    virtual void unload() = 0;
};

So a plugin developer can quickly create a plugin using:

Plugin's: plugin1.hpp

class plugin1: public baseplugin
{
public:
    virtual void load();
    virtual void unload();
// other useful plugin methods/vars
}

Plugin's: plugin1.cpp

PLUGIN(plugin1) // This creates getPlugin() and erasePlugin()
void plugin1::load() {
// do stuff
}
void plugin1::unload() {
// do stuff
}
// other functions

Now I'm left with loading the coding/building the API.dll to load a directory of plugin .dlls. Here's my current code for this, which I realize isn't going to work do to not know the RTTI:

API's: dllmain.cpp

typedef uintptr_t(*pFunc)();
HINSTANCE hinstLib = LoadLibrary(TEXT("plugin1.dll"));
if (hinstLib != NULL) {
    FARPROC ProcAdd = GetProcAddress(hinstLib, "getPlugin"); // address to singeleton function
    // If the function address is valid, call the function.  
    if (NULL != ProcAdd) {
        pFunc pPluginSingleton = (pFunc) ProcAdd;
        baseplugin* plugin1Singleton = (baseplugin*) pPluginSingleton(); // THIS LINE WORKS, I GET A POINTER TO A SINGLETON
        plugin1Singleton->load(); // THIS CRASHES!!!! 

It may be worth noting here that building the API.dll with the code for plugin1.dll works as expected. I'm now testing/figuring out how to have plugins types loaded at runtime. I have verified that I'm able to get the pointer to the singleton with a debugger, but I crash when trying to run the load method after casting it to the abstract class: 0xC0000005: Access violation executing location 0x80B1CCDC

c++
dll
dllimport
asked on Stack Overflow Nov 23, 2019 by jsonV • edited Nov 23, 2019 by jsonV

2 Answers

1

Your application knows nothing about concrete types defined in plugins. The only things it can operate on are classes defined in your application. Each plugin needs to provide a factory method that creates an instance of a concrete type defined in the plugin, and return a pointer to an abstract class defined in your application. Something like this defined in Plugin1.dll:

baseplugin* PLUGINEXPORT create_plugin()
{
    return new plugin1;
}

In your application you dynamically load Plugin1.dll, get address of create_plugin function and call it to get an instance of plugin1 class as a pointer to baseplugin abstract class.

answered on Stack Overflow Nov 23, 2019 by Andriy Tylychko
1

A good way to implement a plugin API is with the help of the boost::dll. It provides some helpfull tools for implementing a plugin mechanism. There is, for example, the Factory method in plugin.

answered on Stack Overflow Nov 23, 2019 by Thomas

User contributions licensed under CC BY-SA 3.0