创建插件界面

时间:2013-11-13 08:15:43

标签: c++ plugins dll interface

我正在开发一个需要支持插件架构的应用程序。这是我第一次这样做,所以我不完全确定我需要怎么做。

How to create some class from dll(constructor in dll)?(с++)建议我只需要创建一个由完全虚函数组成的类,让DLL在自定义类中实现它,并通过GetPluginObject()方法等返回该自定义对象。但是,C++ DLL plugin interface表示这还不够,并且正确的(跨多个编译器兼容)方法需要以下内容:

  • 只有基本数据类型可用
  • 必须公开类似COM的QueryInterface,以便插件DLL可以正确识别它实现的接口
  • 需要某种形式的引用计数
  • 所有方法最好标记为stdcall
  • 任何结构都必须固定对齐

我需要一个插件做的事情很简单:我只需要从一个函数返回一个结构数组。

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();

鉴于上面列表中的限制和要求,并使用此项目中的另一个接口作为模板,我似乎需要通过以下方式定义它:

#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

然后,在主机端,我在插件DLL上使用GetProcAddress来调用DLL的QueryInterface,然后使用指针QueryInterface返回以使用插件。

这看起来像是很多的过度杀戮和很多的丑陋。例如,我认为我不能正确地传入或传出std :: vector,所以我坚持使用GetEmergeInternalCommandInformation()的单项返回和总计数函数GetEmergeInternalCommandCount()所以我可以逐个遍历插件的命令。有没有不同的方法我可以安全地将struct数组作为返回值而不违反规则?

另外,我完全不确定我是否正确地定义了结构,包括wchar_t数组(我是否限制为单wchar_t s?)和对齐方面值。

我也不完全确定插件DLL应该如何实现它。我认为它只需要#include接口定义头,然后创建一个继承自接口的类,对吗?

#include "EmergeInternalCommandInterface.h"
class EmergeInternalCommands : public IEmergeInternalCommandProvider
//class definition goes here

我也不确定是否需要向COM注册此接口,或者我是否可以使用它。我用作模板的接口是一个成熟的COM接口,并且已经注册,但我不知道是否需要任何基本插件系统的高级功能。

最后但绝对不是最不重要的 - 我是否比这更复杂?

1 个答案:

答案 0 :(得分:4)

https://github.com/jbandela/cppcomponents查看我的项目cppcomponents。我专门为你的场景创建了这个库,因为我发现当前可用的解决方案缺乏。

它是一个仅适用于Windows和Linux的头文件c ++ 11库。

它需要一个相当合规的C ++ 11编译器,如MSVC 2013,Gcc 4.7.2或Clang 3.2

  • 它将自动处理QueryInterface,AddRef和Release。
  • 的实现
  • 使用时,会自动处理引用计数
  • 它允许您返回std :: string,vector,tuple以及其他标准类型
  • 它可以处理异常
  • 您可以使用多个编译器,例如您可以用Visual C ++编写程序并在GCC中编写插件

这是写出你想要的最简单方法

首先在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;

然后在ImplementCommandProvider.cpp中将编译成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()

以下是如何使用它

#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";

    }


}

以下是如何从命令行构建它 我假设你在3个文件的目录中,并且MSVC编译器在你的路径中

这是如何构建主程序

cl MainProgram.cpp /I c:\Users\jrb\Source\Repos\cppcomponents /EHsc

这是如何构建Dll

cl ImplementCommandProvider.cpp  /I c:\Users\jrb\Source\Repos\cppcomponents /EHsc /link /dll /OUT:CommandProviderDll.dll

然后,当您运行该程序时,输入CommandProviderDll作为您的dllname

如果你想定义一个自定义结构,那么我可以帮助你。

该库目前缺少文档(正在处理它:(),但我可以帮助您解决有关库的任何问题。该库是根据Boost许可证发布的,因此您可以将它用于商业应用程序