Windows上工程组织方式 --- dll插件式
一个工程需要分模块开发,各个模块以动态链接库或者共享库的形式存在是一种比较好的选择
使用动态库开发,软件编译后的体积比静态库较小,运行时占用内存也会相对较少。
对于软件的线上更新动态库会更加方便,特别是客户端软件,模块的更新只要下载新的动态库即可。
下面是一个简单的示例
main.cpp
#include <list>
#include <iostream>
#include <tuple>
#include <cassert>
#include <Windows.h>
#include "plugin_interface.h"
int main()
{
std::list<std::pair<HMODULE, iplugin*>> plugins;
GlobalEnv env;
const char* plugin_paths[] = { "Plugin1.dll","Plugin2.dll" };
for (const auto* path : plugin_paths)
{
HMODULE handle = LoadLibraryA(path);
assert(handle != nullptr);
plugin_reg_init_t reg_init = (plugin_reg_init_t)GetProcAddress(handle, kFNamePluginRegInit);
auto* plugin = reg_init(&env);
plugins.emplace_back(handle, plugin);
}
//...
for (auto& [handle, plugin] : plugins)
{
plugin_reg_release_t reg_release = (plugin_reg_release_t)GetProcAddress(handle, kFNamePluginRegRelease);
reg_release(plugin);
}
exit(EXIT_SUCCESS);
}
通过LoadLibraryA 将动态库加载进来,约定每个库都有一个注册函数,取消注册函数,注册函数进行一些初始化操作,可以把全局对象传入到模块中,各个模块之间可以能通过一个全局对象进行通信。
Plugin1的dllmain.cpp
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain(HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
#include "plugin_interface.h"
#include "MainObj.h"
extern "C" _declspec(dllexport)
iplugin* plugin_reg_init(GlobalEnv* env)
{
return new MainObj(env);
}
extern "C" _declspec(dllexport)
void plugin_reg_release(iplugin* plugin)
{
delete plugin;
}
在MainObj中编写各个模块的业务功能代码。
plugin_interface.h 是接口定义
#ifndef __PLUGIN_INTERFACE_H__
#define __PLUGIN_INTERFACE_H__
#include "global_env.h"
class iplugin {
public:
explicit iplugin(GlobalEnv* env) :envPtr_(env) {}
virtual ~iplugin() {}
protected:
GlobalEnv* envPtr_;
};
#endif // !__PLUGIN_INTERFACE_H__
GlobalEnv 是全局类
#ifndef __GLOBAL_ENV_H__
#define __GLOBAL_ENV_H__
class iplugin;
class GlobalEnv
{
};
constexpr const char* kFNamePluginRegInit = "plugin_reg_init";
constexpr const char* kFNamePluginRegRelease = "plugin_reg_release";
typedef iplugin* (*plugin_reg_init_t)(GlobalEnv*);
typedef void (*plugin_reg_release_t)(iplugin*);
#endif // !__GLOBAL_ENV_H__
运行后输出如下
1 MainObj init
Plugin2 mainobj init
1 MainObj release
Plugin2 mainobj release