# NetLeaf 扩展库开发指南 ## 概述 NetLeaf v2.4.0 支持动态扩展库,允许第三方开发者创建遵循与内置模块相同规范的扩展模块。这些扩展库可以在运行时动态加载,并正确注册到主库的模块系统中。 **重要变更 (v2.4.0):** - ⚠️ 静态库已不再支持,扩展库必须动态链接 - 扩展库使用新的 `NL_EXTENSION_DEFINE` 宏定义 - 支持自动加载机制(扫描 extensions/ 目录) - 完整的扩展生命周期管理 API - 扩展搜索、过滤、元数据管理 API ## 扩展库定义字段 每个扩展库必须包含以下字段: | 字段 | 必需 | 说明 | 限制 | |------|------|------|------| | `library_name` | ✅ | 库显示名称 | 64字符 | | `library_id` | ✅ | 唯一标识符 | 32字符 | | `version` | ✅ | 版本字符串 | 16字符 | | `author` | ✅ | 作者名称 | 64字符 | | `description` | ❌ | 简短描述 | 50中文字符(约150字节) | | `platforms` | ✅ | 支持的平台 | "Windows,Linux,MacOS" 或 "all" | **重要**: `library_value` 是由主库在加载时自动分配的值,用于内部标识。用户不应在定义时设置此字段。 ## 扩展库架构 ### 与内置模块的一致性 扩展库遵循与内置模块(如 linkagg、vue、ipc 等)相同的接口规范: - 使用 `NL_EXTENSION_DEFINE` 宏定义扩展信息 - 支持懒加载和模块能力标志 - 通过自动加载机制注册到扩展系统 - 链接主库的共享库(netleaf.dll/netleaf.so)以共享全局状态 ### 关键设计要点 **重要**: 扩展库必须链接主库的**共享库**(netleaf.dll),而不是静态库。这样扩展库和主程序都使用同一个 netleaf.dll,共享全局变量(如模块列表),确保模块注册能正确工作。 ## 开发步骤 ### 第一步:创建扩展库头文件 ```c #ifndef NETLEAF_MY_EXTENSION_H #define NETLEAF_MY_EXTENSION_H #ifdef __cplusplus extern "C" { #endif #include #include #include "netleaf_module.h" #ifdef _WIN32 #ifdef NL_EXTENSION_EXPORTS #define NL_EXT_API __declspec(dllexport) #else #define NL_EXT_API __declspec(dllimport) #endif #else #define NL_EXT_API #endif #define NL_MY_EXTENSION_VERSION "1.0.0" // Extension API - 使用新的扩展库定义 NL_EXT_API nl_extension_info_t* nl_my_extension_get_info(void); NL_EXT_API int nl_my_extension_is_available(void); NL_EXT_API const char* nl_my_extension_version(void); NL_EXT_API int nl_my_extension_init(void); NL_EXT_API void nl_my_extension_shutdown(void); // 扩展功能 API NL_EXT_API int nl_my_extension_process(const char* input, char* output, size_t output_size); #ifdef __cplusplus } #endif #endif // NETLEAF_MY_EXTENSION_H ``` ### 第二步:创建扩展库实现文件 使用新的 `NL_EXTENSION_DEFINE` 宏: ```c #include "netleaf_my_extension.h" #include "netleaf.h" #include "netleaf_module.h" #include #include #include static int g_extension_initialized = 0; // 使用 NL_EXTENSION_DEFINE 宏定义扩展库 // 必需字段:library_id, library_name, version, author // 可选字段:description(限制50个中文字符) // platforms: "Windows,Linux,MacOS" (大小写不限,顺序不限,或使用 "all") NL_EXTENSION_DEFINE( my_ext, // library_id(唯一标识符) "My Extension", // library_name(显示名称) NL_MY_EXTENSION_VERSION, // version "Author Name", // author "这是一个示例扩展库", // description(可选,最多50个中文字符) "Windows,Linux,MacOS", // platforms(支持大小写不限、顺序不限,或 "all") NL_CAP_THREAD_SAFE, // capabilities nl_my_extension_init, // init nl_my_extension_shutdown, // shutdown nl_my_extension_is_available, // is_available nl_my_extension_version // get_version ) NL_EXT_API nl_extension_info_t* nl_my_extension_get_info(void) { return NL_EXTENSION_GET_INFO(my_ext); } NL_EXT_API int nl_my_extension_is_available(void) { return 1; } NL_EXT_API const char* nl_my_extension_version(void) { return NL_MY_EXTENSION_VERSION; } NL_EXT_API int nl_my_extension_init(void) { if (g_extension_initialized) return 0; g_extension_initialized = 1; printf("[My Extension] Initialized\n"); return 0; } NL_EXT_API void nl_my_extension_shutdown(void) { if (!g_extension_initialized) return; g_extension_initialized = 0; printf("[My Extension] Shutdown\n"); } NL_EXT_API int nl_my_extension_process(const char* input, char* output, size_t output_size) { if (!input || !output || output_size == 0) return -1; snprintf(output, output_size, "Processed: %s", input); return 0; } ``` ### 第三步:创建 CMakeLists.txt ```cmake cmake_minimum_required(VERSION 3.10) project(netleaf_my_extension) include_directories( ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../../include ) # 创建扩展库(必须为 SHARED) add_library(netleaf_my_extension SHARED my_extension.c) # 关键:链接主库的共享库(netleaf.dll) # 这确保扩展库和主程序共享同一个全局状态 if(WIN32) target_compile_definitions(netleaf_my_extension PRIVATE NL_EXTENSION_EXPORTS) target_link_libraries(netleaf_my_extension netleaf_shared ws2_32) else() target_link_libraries(netleaf_my_extension -L${CMAKE_RUNTIME_OUTPUT_DIRECTORY} -lnetleaf -ldl -lpthread) endif() set_target_properties(netleaf_my_extension PROPERTIES OUTPUT_NAME "netleaf_my_extension" PREFIX "" ) install(TARGETS netleaf_my_extension DESTINATION extensions) ``` ### 第四步:编译扩展库 扩展库应该在主库构建完成后编译: ```bash # Windows: 在 Visual Studio Developer Command Prompt 中运行 build_all.bat # Linux: 直接运行脚本 ./build_all.sh ``` ### 第五步:放置扩展库 将编译好的扩展库放到 `extensions/` 目录: ``` NetLeaf/ ├── bin/Release/ │ ├── netleaf.dll # 主库 │ └── extensions/ # 扩展库目录 │ └── netleaf_my_extension.dll # 你的扩展库 ``` ## 自动加载机制 v2.4.0 支持扩展库自动加载: 1. **目录结构**: 将扩展库放入 `extensions/` 子目录(与 netleaf.dll 同级) 2. **自动识别**: 主库初始化时自动扫描并加载所有扩展库 3. **标准符号命名**: 支持多种符号命名约定(`my_ext_get_info`、`get_my_ext_info`、`my_ext_extension_info`) 4. **无需手动调用**: `nl_modules_init()` 会自动触发加载 ```c // 主库初始化会自动加载 extensions/ 目录中的所有扩展库 nl_modules_init(); // 检查已加载的扩展库 int ext_count = nl_extension_get_count(); printf("已加载 %d 个扩展库\n", ext_count); // 获取特定扩展库信息 nl_extension_info_t* info = nl_extension_get_info("my_ext"); if (info) { printf("扩展库: %s v%s\n", info->library_name, info->version); } ``` ## 使用扩展库 API(v2.4.0 新增) ### 生命周期管理 ```c // 初始化扩展库 int ret = nl_extension_init("my_ext"); if (ret < 0) { printf("初始化失败\n"); } // 关闭扩展库 nl_extension_shutdown("my_ext"); // 强制关闭(清理资源) nl_extension_force_shutdown("my_ext"); // 批量操作 nl_extension_init_all(); // 初始化所有扩展库 nl_extension_shutdown_all(); // 关闭所有扩展库 nl_extension_force_shutdown_all(); // 强制关闭所有扩展库 ``` ### 状态查询 ```c // 检查是否已初始化 int initialized = nl_extension_is_initialized("my_ext"); // 检查是否正在运行 int running = nl_extension_is_running("my_ext"); // 获取状态 nl_module_status_t status = nl_extension_get_state("my_ext"); ``` ### 信息查询 ```c // 获取基本信息(便捷函数) const char* name = nl_extension_get_name("my_ext"); const char* author = nl_extension_get_author("my_ext"); const char* desc = nl_extension_get_description("my_ext"); uint32_t caps = nl_extension_get_caps("my_ext"); // 检查平台支持 int supports_win = nl_extension_supports_platform("my_ext", "windows"); int supports_linux = nl_extension_supports_platform("my_ext", "linux"); int supports_macos = nl_extension_supports_platform("my_ext", "macos"); ``` ### 搜索过滤 ```c // 按能力搜索 nl_extension_info_t* results[32]; int count = nl_extension_find_by_capability(NL_CAP_THREAD_SAFE, results, 32); for (int i = 0; i < count; i++) { printf("线程安全扩展: %s\n", results[i]->library_name); } // 按平台搜索 count = nl_extension_find_by_platform("windows", results, 32); // 按名称模式搜索 count = nl_extension_find_by_name_pattern("*chat*", results, 32); ``` ### 元数据管理 ```c // 设置元数据 nl_extension_set_metadata("my_ext", "github_url", "https://github.com/user/repo"); nl_extension_set_metadata("my_ext", "license", "MIT"); // 获取元数据 char value[256]; nl_extension_get_metadata("my_ext", "github_url", value, sizeof(value)); printf("GitHub: %s\n", value); ``` ### 热重载 ```c // 重载单个扩展库 nl_extension_reload("my_ext"); // 重载所有扩展库 nl_extension_reload_all(); ``` ## API 参考 ### 基础管理 API | 函数 | 说明 | |------|------| | `nl_extension_get_info(library_id)` | 按 ID 获取扩展库信息 | | `nl_extension_register(info)` | 注册扩展库(自动分配 library_value) | | `nl_extension_unregister(library_id)` | 注销扩展库 | | `nl_extension_get_count()` | 获取已注册扩展库数量 | | `nl_extension_get_all(count)` | 获取所有扩展库数组 | | `nl_extension_validate_description(desc)` | 验证描述长度 | ### 生命周期 API(v2.4.0) | 函数 | 说明 | |------|------| | `nl_extension_init(library_id)` | 初始化扩展库 | | `nl_extension_shutdown(library_id)` | 关闭扩展库 | | `nl_extension_force_shutdown(library_id)` | 强制关闭扩展库 | | `nl_extension_init_all()` | 初始化所有扩展库 | | `nl_extension_shutdown_all()` | 关闭所有扩展库 | | `nl_extension_force_shutdown_all()` | 强制关闭所有扩展库 | ### 状态查询 API(v2.4.0) | 函数 | 说明 | |------|------| | `nl_extension_is_initialized(library_id)` | 检查是否已初始化 | | `nl_extension_is_running(library_id)` | 检查是否正在运行 | | `nl_extension_get_state(library_id)` | 获取模块状态 | ### 信息查询 API(v2.4.0) | 函数 | 说明 | |------|------| | `nl_extension_get_name(library_id)` | 获取扩展库名称 | | `nl_extension_get_author(library_id)` | 获取作者名称 | | `nl_extension_get_description(library_id)` | 获取描述 | | `nl_extension_get_caps(library_id)` | 获取能力标志 | | `nl_extension_supports_platform(library_id, platform)` | 检查平台支持 | ### 搜索过滤 API(v2.4.0) | 函数 | 说明 | |------|------| | `nl_extension_find_by_capability(cap, results, max_count)` | 按能力搜索 | | `nl_extension_find_by_platform(platform, results, max_count)` | 按平台搜索 | | `nl_extension_find_by_name_pattern(pattern, results, max_count)` | 按名称模式搜索 | ### 元数据 API(v2.4.0) | 函数 | 说明 | |------|------| | `nl_extension_get_metadata(library_id, key, value, val_size)` | 获取元数据 | | `nl_extension_set_metadata(library_id, key, value)` | 设置元数据 | ### 热重载 API(v2.4.0) | 函数 | 说明 | |------|------| | `nl_extension_reload(library_id)` | 重载单个扩展库 | | `nl_extension_reload_all()` | 重载所有扩展库 | ### 扩展库查询 API | 函数 | 说明 | |------|------| | `nl_extension_get_value_by_id(library_id)` | 通过标识符获取值 | | `nl_extension_get_id_by_value(library_value)` | 通过值获取标识符 | **示例**: ```c // 注册后获取自动分配的值 nl_extension_register(info); int32_t value = nl_extension_get_value_by_id("my_ext"); printf("my_ext's value: %d\n", value); // 通过值反向查询标识符 const char* id = nl_extension_get_id_by_value(1); printf("value 1's id: %s\n", id); ``` ### 自动加载 API | 函数 | 说明 | |------|------| | `nl_extension_auto_load()` | 从 extensions/ 目录自动加载 | | `nl_extension_auto_load_from_dir(dir)` | 从指定目录自动加载 | | `nl_extension_set_auto_load_dir(dir)` | 设置自动加载目录 | | `nl_extension_get_auto_load_dir()` | 获取自动加载目录 | ### 模块管理 API | 函数 | 说明 | |------|------| | `nl_module_register(info)` | 注册模块 | | `nl_module_unregister(type)` | 注销模块 | | `nl_module_get_info(type)` | 按类型获取模块 | | `nl_module_get_info_by_name(name)` | 按名称获取模块 | | `nl_module_get_count()` | 获取模块数量 | | `nl_print_modules()` | 打印所有模块 | ## 模块能力标志 | 标志 | 值 | 说明 | |------|-----|------| | `NL_CAP_NONE` | 0 | 无能力 | | `NL_CAP_SERVER` | 1 << 0 | 支持创建服务器 | | `NL_CAP_CLIENT` | 1 << 1 | 支持创建客户端 | | `NL_CAP_ASYNC` | 1 << 2 | 支持异步操作 | | `NL_CAP_THREAD_SAFE` | 1 << 3 | 线程安全 | | `NL_CAP_PLATFORM_WIN` | 1 << 4 | Windows 支持 | | `NL_CAP_PLATFORM_LINUX` | 1 << 5 | Linux 支持 | | `NL_CAP_PLATFORM_MACOS` | 1 << 6 | macOS 支持 | | `NL_CAP_LAZY_LOAD` | 1 << 7 | 支持懒加载 | | `NL_CAP_DYNAMIC` | 1 << 8 | 可动态加载 | | `NL_CAP_PLUGIN` | 1 << 9 | 插件模块 | | `NL_CAP_EXT_SYSTEM` | 1 << 10 | NL 扩展系统组件 | ## 最佳实践 ### 1. 链接共享库 必须链接 `netleaf_shared`(动态库),而不是 `netleaf_static`: ```cmake # 正确 target_link_libraries(my_extension netleaf_core) # 错误 - 会导致全局状态隔离 target_link_libraries(my_extension ${CMAKE_CURRENT_SOURCE_DIR}/path/to/netleaf_static) ``` ### 2. 使用正确的导出宏 Windows 上使用 `__declspec(dllexport)`: ```c #ifdef _WIN32 #ifdef NL_EXTENSION_EXPORTS #define NL_EXT_API __declspec(dllexport) #else #define NL_EXT_API __declspec(dllimport) #endif #else #define NL_EXT_API #endif ``` ### 3. 错误处理 始终检查加载结果: ```c nl_plugin_handle_t ext = nl_plugin_load(path); if (!ext) { printf("Error: %s\n", nl_plugin_get_error()); return -1; } ``` ### 4. 平台兼容性 确保扩展库支持所有目标平台: ```c .platforms = "Windows,Linux,MacOS", // 或使用 "all" ``` ## 示例项目 完整示例位于 `examples/plugin_template/` 目录: - `netleaf_plugin_template.h` - 头文件模板 - `plugin.c` - 实现文件 - `CMakeLists.txt` - 构建配置 - `README.md` - 使用说明 ## 故障排除 ### 问题:模块注册后数量不变 **原因**: 扩展库链接了静态库而非共享库 **解决**: 确保 CMakeLists.txt 中链接 `netleaf_shared` ### 问题:找不到符号 **原因**: 导出宏未正确配置 **解决**: 检查 `NL_EXTENSION_EXPORTS` 定义是否在编译时设置 ### 问题:运行时崩溃 **原因**: 扩展库和主库使用不同的 netleaf.dll 实例 **解决**: 确保扩展库输出目录与主程序相同,或使用相同的 PATH 环境变量