【发布时间】:2013-11-25 18:00:57
【问题描述】:
我正在开发一个需要支持插件架构的应用程序。这是我第一次这样做,所以我不完全确定我需要如何去做。
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() 所以我可以循环插件的命令一一对应。有没有其他方法可以安全地将结构数组作为返回值而不违反规则?
另外,我不确定我是否正确定义了结构,无论是在拥有wchar_t 数组(我是否仅限于单个wchar_ts?)和对齐值方面。
我也不完全确定插件 DLL 应该如何实现这一点。我想它只需要#includeinterface-definition 头然后创建一个继承自该接口的类,对吧?
#include "EmergeInternalCommandInterface.h"
class EmergeInternalCommands : public IEmergeInternalCommandProvider
//class definition goes here
我也不确定我是否需要向 COM 注册这个接口,或者我是否可以使用它。我用作模板的接口是一个成熟的 COM 接口,并已注册,但我不知道我是否需要任何高级插件系统。
最后但同样重要的是 - 我是否让这种方式变得比它需要的更复杂?
【问题讨论】:
-
您可以考虑的另一种方法是根本不使用 C++ 实现插件,而是使用像 lua 这样的嵌入式脚本语言。这个想法是您将绑定并公开基本构建块或原语到 vm,以便插件可以访问它们。插件本身可以是纯 lua 脚本,也可以是用 C 或 C++ 编写的 lua 扩展模块。通常要求不要求您的应用程序和插件必须用相同的语言编写。
-
虽然我一直在玩弄使用 Lua 的想法,但我最终将不得不在 C++ 中实现一个插件系统,因为我的应用程序及其未来的插件大量使用了 Windows API .我不确定 Lua 能否在这里站稳脚跟。
-
看看 LuaJIT 的 ffi——它允许您直接从 lua 轻松绑定 winapi 函数。通常您只需要提供包含感兴趣的 win32 函数声明的预处理标头。
-
看起来令人印象深刻;我得更深入地看看。我仍然希望能回答我最初的问题,但我为你 +1 了一个有趣的、显然比我最初想象的更可行的替代方案。