I'm working on an application that will need to support a plugin architecture. This is the first time I've done this, so I'm not entirely sure how I need to go about it.
How to create some class from dll(constructor in dll)?(с++) suggests I just need to create a class consisting of entirely virtual functions and let the DLL implement that in a custom class and return that custom object via a GetPluginObject()
method or the like. However, C++ DLL plugin interface says that won't be enough, and that a proper (compatible across multiple compilers) approach will require the following:
- Only basic datatypes are usable
- Something like COM's QueryInterface must be exposed so the plugin DLL can properly identify which interface(s) it implements
- Some form of reference counting is required
- All methods are preferably to be marked as stdcall
- Any structs must be given fixed alignment
What I need a plugin to do is fairly simple: I just need one array of structs returned from one function.
struct InternalCommand
{
int commandValue;
std::wstring commandName;
std::wstring commandHandlerFunctionName; //I'm planning on using GetProcAddress with the provided function name to get the individual command handler
}
std::vector<InternalCommand> GetEmergeInternalCommands();
Given the restrictions and requirements in the list above, and using another interface from this project as a template, it seems I need to define this in the following way:
#define MAX_LINE_LENGTH 4096
#ifdef __GNUC__
#define ALIGNOF(type) __alignof__(type)
#else
#define ALIGNOF(type) __alignof(type)
#endif
#ifdef __GNUC__
#define ALIGNED(size) __attribute__((aligned (size)))
#else
#define ALIGNED(size) __declspec(align(size))
#endif
#include <windows.h>
// {b78285af-c62f-4cff-9e15-f790a4a219ee}
const IID IID_IEmergeInternalCommand = {0xB78285AF, 0xC62F, 0x4CFF, {0x9E, 0x15, 0xF7, 0x90, 0xA4, 0xA2, 0x19, 0xEE}};
#ifdef __cplusplus
extern "C"
{
#endif
struct ALIGNED((ALIGNOF(int) + ALIGNOF(wchar_t) + ALIGNOF(wchar_t))) EmergeInternalCommandInformation
{
int commandValue;
wchar_t commandName[MAX_LINE_LENGTH];
wchar_t commandHandlerFunctionName[MAX_LINE_LENGTH];
};
#undef INTERFACE
#define INTERFACE IEmergeInternalCommandProvider
DECLARE_INTERFACE_(IEmergeInternalCommandProvider, IUnknown)
{
STDMETHOD(QueryInterface)(THIS_ REFIID, LPVOID*) PURE;
STDMETHOD_(ULONG, AddRef)(THIS) PURE;
STDMETHOD_(ULONG, Release)(THIS) PURE;
STDMETHOD_(int, GetEmergeInternalCommandCount)(THIS) PURE;
STDMETHOD_(EmergeInternalCommandInformation, GetEmergeInternalCommandInformation)(THIS_ int) PURE;
};
#undef INTERFACE
typedef IEmergeInternalCommandProvider* LPEMERGEINTERNALCOMMANDPROVIDER;
#ifdef __cplusplus
}
#endif
And then, on the host side, I'd use GetProcAddress
on the plugin DLL to call the DLL's QueryInterface
, then use the pointer QueryInterface
returns to work with the plugin.
This seems like a lot of overkill and a lot of ugly, though. For example, I don't think I can properly pass a std::vector in or out, so I'm stuck using a single-item return for GetEmergeInternalCommandInformation()
and a total-count function GetEmergeInternalCommandCount()
so I can loop through the plugin's commands one by one. Is there a different way I can safely get a struct array as a return value without breaking the rules?
Also, I'm not at all sure I defined the struct correctly, both in terms of having the wchar_t
arrays (am I restricted to single wchar_t
s?) and in terms of the alignment value.
I'm also not entirely certain how the plugin DLL is supposed to implement this. I think it just needs to #include
the interface-definition header and then create a class inheriting from the interface, right?
#include "EmergeInternalCommandInterface.h"
class EmergeInternalCommands : public IEmergeInternalCommandProvider
//class definition goes here
I'm also not certain if I need to register this interface with COM or if I can just use it. The interface I used as a template is a full-fledged COM interface and is registered as such, but I don't know if I need anything that advanced for a basic plugin system.
And last but definitely not least - am I making this way more complicated than it needs to be?
Take a look at my project cppcomponents at https://github.com/jbandela/cppcomponents. I created this library specifically for scenarios such as yours as I found the currently available solutions lacking.
It is a header-only c++11 library that works on Windows and Linux.
It requires a fairly compliant C++11 compiler like MSVC 2013, Gcc 4.7.2, or Clang 3.2
Here is the easiest way to write what you want
First define the interface and plugin in CommandProvider.h
Then in ImplementCommandProvider.cpp that will be compiled into CommandProviderDll.dll
Here is how you would use it
Here is how you would build it from the command line I am assuming you are in the directory with the 3 files and that the MSVC compiler is in your path
This is how to build the main program
This is how to build the Dll
Then when you run the program, Enter in
CommandProviderDll
for your dllnameIf you want to define a custom struct it is possible, and I can help you with it.
The library is lacking documentation currently (working on it :( ), but I can help you with any questions you have about the library. The library is released under the Boost License so you can use it for commercial applications if you want.