Reputation: 1921
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:
API.dll
loads directory of plugins (e.g. plugin/plugin1.dll
and
plugin/plugin2.dll
) API.dll
creates singleton instance of the plugin through the abstract class' getPlugin()
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
Upvotes: 1
Views: 365
Reputation: 5138
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.
Upvotes: 1
Reputation: 16256
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.
Upvotes: 1