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:
- COM/ATL stuff
- An article in C# similar to what I'm trying to do in C++ using reflections
- This article on why C++ doesn't have reflections
- and tons of similarly titled articles asking about calling functions from a DLL
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
andplugin/plugin2.dll
)API.dll
creates singleton instance of the plugin through the abstract class'getPlugin()
- 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