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:
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?
Allows you to pass data between your organization and a specified flow.
Examples include the Adobe Flash Player, a Java virtual machine (for Java applets), QuickTime, Microsoft Silverlight and the Unity Web Player. (Browser extensions, which are a separate type of installable module, are still widely in use.)
plug-in, also called add-on or extension, computer software that adds new functions to a host program without altering the host program itself. Widely used in digital audio, video, and Web browsing, plug-ins enable programmers to update a host program while keeping the user within the program's environment.
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
#include <cppcomponents/cppcomponents.hpp>
#include <tuple>
#include <vector>
typedef std::tuple<int, std::wstring, std::wstring> Command;
struct ICommandProvider:cppcomponents::define_interface<cppcomponents::uuid<0xf4b4056d, 0x37a8, 0x4f32, 0x9eea, 0x03a31ed55dfa>>
{
std::vector<Command>GetEmergeInternalCommands();
CPPCOMPONENTS_CONSTRUCT(ICommandProvider, GetEmergeInternalCommands)
};
inline std::string CommandProviderId(){ return "CommandProvider"; }
typedef cppcomponents::runtime_class<CommandProviderId, cppcomponents::object_interfaces<ICommandProvider>> CommandProvider_t;
typedef cppcomponents::use_runtime_class<CommandProvider_t> CommandProvider;
Then in ImplementCommandProvider.cpp that will be compiled into CommandProviderDll.dll
#include "CommandProvider.h"
struct ImplementCommandProvider :cppcomponents::implement_runtime_class<ImplementCommandProvider, CommandProvider_t>
{
ImplementCommandProvider(){}
std::vector<Command>GetEmergeInternalCommands(){
std::vector<Command> vec;
vec.push_back(std::make_tuple(1, L"Test", L"TestFunction"));
vec.push_back(std::make_tuple(2, L"Test2", L"TestFunction2"));
vec.push_back(std::make_tuple(3, L"Test3", L"TestFunction3"));
return vec;
}
};
CPPCOMPONENTS_REGISTER(ImplementCommandProvider)
CPPCOMPONENTS_DEFINE_FACTORY()
Here is how you would use it
#include "CommandProvider.h"
#include <iostream>
int main(){
std::string dllName;
std::cout << "Enter dll name without the .dll extension\n";
std::cin >> dllName;
auto p = CommandProvider::dynamic_creator(dllName, "CommandProvider")();
for (auto& c : p.GetEmergeInternalCommands()){
std::wcout << L"Value " << std::get<0>(c) << L" Name " << std::get<1>(c) << L" Function " << std::get<2>(c) << L"\n";
}
}
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
cl MainProgram.cpp /I c:\Users\jrb\Source\Repos\cppcomponents /EHsc
This is how to build the Dll
cl ImplementCommandProvider.cpp /I c:\Users\jrb\Source\Repos\cppcomponents /EHsc /link /dll /OUT:CommandProviderDll.dll
Then when you run the program, Enter in CommandProviderDll
for your dllname
If 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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With