跳到主要内容

插件系统 Plugin

插件是软件生命力的延伸。HiEasyX 的插件架构让你能在不重新编译主程序的前提下,给框架注册新的控件、命令、主题和蓝图节点。虽然目前 DLL 加载机制仍在完善中,但插件接口和生命周期已经就绪,你可以先在静态链接或手动加载的场景下使用。

核心设计

插件系统采用接口契约模式:

  1. 你写一个类继承 HXPlugin
  2. 导出 CreatePlugin() 工厂函数(DLL 场景)。
  3. 框架通过 HXPluginManager 管理加载、枚举和卸载。
即使不用 DLL

你可以直接把插件类编译进主程序,在 main() 里手动 new MyPlugin 并调用 OnLoad()。插件接口的价值不仅在于动态加载,更在于代码边界清晰


插件接口

HXPlugin

struct HXPlugin {
HXString Name; // 插件名称
HXString Version; // 版本号,建议 "major.minor.patch"

virtual void OnLoad() {} // 加载时调用
virtual void OnUnload() {} // 卸载时调用

virtual void RegisterControls() {} // 注册自定义控件
virtual void RegisterCommands() {} // 注册命令/菜单项
virtual void RegisterThemes() {} // 注册自定义主题
};
生命周期顺序
LoadPlugin(dllPath)
→ 内部创建插件实例
→ OnLoad()
→ RegisterControls()
→ RegisterCommands()
→ RegisterThemes()

API 详解

LoadPlugin

bool LoadPlugin(const HXString &dllPath);

参数

参数类型说明
dllPathHXString插件 DLL 的路径。

返回值

bool — 加载成功返回 true,失败返回 false

当前状态

LoadPlugin 内部会尝试加载 DLL 并查找 CreatePlugin 导出函数,但跨平台兼容层(LoadLibrary / dlopen 的抽象)仍在完善中。如果你在静态编译环境或嵌入设备上使用,建议直接手动构造插件对象。

UnloadAllPlugins

void UnloadAllPlugins();

卸载所有已加载的插件,依次调用每个插件的 OnUnload(),然后释放实例。

什么时候调用

通常在程序退出前调用,确保插件有机会清理资源、保存状态、注销注册项。

GetLoadedPlugins

const std::vector<HXPlugin*> &GetLoadedPlugins();

返回值

所有已加载插件实例的只读列表。可用于菜单枚举、版本检查、依赖排序等。


如何写一个插件

步骤 1:继承 HXPlugin

// MyPlugin.h
#pragma once
#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>

struct MyPlugin : HX::HXPlugin {
MyPlugin() {
Name = HXStr("MyAwesomePlugin");
Version = HXStr("1.0.0");
}

void OnLoad() override;
void OnUnload() override;
void RegisterControls() override;
void RegisterCommands() override;
void RegisterThemes() override;
};

步骤 2:实现逻辑

// MyPlugin.cpp
#include "MyPlugin.h"

void MyPlugin::OnLoad() {
// 初始化插件内部状态
LoadConfigFile();
}

void MyPlugin::OnUnload() {
SaveConfigFile();
CleanupResources();
}

void MyPlugin::RegisterControls() {
// 可以在这里向框架注册自定义控件工厂
// (具体注册接口取决于框架扩展点)
}

void MyPlugin::RegisterCommands() {
// 注册命令,例如快捷键绑定的动作
HX::RegisterShortcut({true, false, false, 'M'}, []() {
ToggleMyPanel();
});
}

void MyPlugin::RegisterThemes() {
// 注册自定义主题变体
// HX::RegisterTheme(HXStr("NeonNight"), neonTheme);
}

步骤 3:导出工厂函数(DLL 模式)

extern "C" __declspec(dllexport) HX::HXPlugin *CreatePlugin() {
return new MyPlugin();
}
extern "C"

必须加 extern "C" 防止 C++ 名称修饰,否则 LoadPlugin 找不到 CreatePlugin 符号。

步骤 4:加载

// 动态加载(DLL 完善后)
HX::LoadPlugin(HXStr("plugins/MyPlugin.dll"));

// 静态加载(当前推荐)
static MyPlugin plugin;
plugin.OnLoad();
plugin.RegisterControls();
plugin.RegisterCommands();
plugin.RegisterThemes();

插件管理器

class HXPluginManager {
public:
bool Load(const HXString &dllPath);
void Unload(const HXString &pluginName);
void UnloadAll();
const std::vector<HXPlugin*> &GetPlugins() const;
};

// 全局访问
HXPluginManager &GetPluginManager();
插件间依赖

如果插件 A 依赖插件 B,在 OnLoad() 里检查 GetPluginManager().GetPlugins() 中是否存在 B。如果不存在,可以延迟初始化或报错。


完整示例:蓝图节点插件

#include <include/hex.h>
#include <include/impl/EasyX/hex_impl_easyx.h>

struct BlueprintNodesPlugin : HX::HXPlugin {
BlueprintNodesPlugin() {
Name = HXStr("ExtraBlueprintNodes");
Version = HXStr("1.2.0");
}

void OnLoad() override {
// 注册自定义数据节点:Noise
HX::RegisterBlueprintDataNode(HXStr("Noise"), [](
const HX::HXNode &node,
HX::HXBlueprintRuntime &rt
) -> HX::HXBlueprintValue {
float x = std::stof(node.Values[0]);
float y = std::stof(node.Values[1]);
float noise = PerlinNoise(x, y); // 你的噪声实现
return HX::HXBlueprintValue(noise);
});

// 注册节点模板,让它出现在右键菜单
HX::HXBlueprintNodeTemplate tmpl;
tmpl.TypeName = HXStr("Noise");
tmpl.DisplayName = HXStr("Perlin Noise");
tmpl.Category = HXStr("Math");
tmpl.Inputs.push_back({HXStr("x"), HXStr("X"), HX::HXPinType::Float, HX::HXPinDirection::Input});
tmpl.Inputs.push_back({HXStr("y"), HXStr("Y"), HX::HXPinType::Float, HX::HXPinDirection::Input});
tmpl.Outputs.push_back({HXStr("out"), HXStr("Value"), HX::HXPinType::Float, HX::HXPinDirection::Output});
HX::RegisterBlueprintNodeTemplate(tmpl);
}

void OnUnload() override {
// 清理,如果需要注销节点可在此处理
}
};

// 静态嵌入
static BlueprintNodesPlugin g_bpPlugin;

void InitPlugins() {
g_bpPlugin.OnLoad();
g_bpPlugin.RegisterControls();
g_bpPlugin.RegisterCommands();
g_bpPlugin.RegisterThemes();
}

注意事项

ABI 稳定性

插件与主程序共享 C++ 标准库和 HiEasyX 头文件。如果主程序用 Clang 编译,插件也建议用相同编译器和相同 CRT 设置,否则 std::string / std::vector 的内存布局可能不一致,导致崩溃。

版本协商

生产环境的插件应在文档中注明所需框架版本,并在 OnLoad() 中进行功能检测。


总结

需求做法
写插件继承 HXPlugin,重写生命周期和注册函数
导出 DLLextern "C" __declspec(dllexport) HXPlugin *CreatePlugin()
加载插件LoadPlugin(dllPath) 或手动构造
注册蓝图节点RegisterControls()OnLoad() 中调用 RegisterBlueprintDataNode
卸载清理UnloadAllPlugins() 或单个 Unload(name)

插件是生态的起点。把你的自定义控件、主题、蓝图节点打包成插件,分享出去,HiEasyX 的社区就会越来越丰富 🔌