Extension - 508364/NetLeaf GitHub Wiki

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,共享全局变量(如模块列表),确保模块注册能正确工作。

开发步骤

第一步:创建扩展库头文件

#ifndef NETLEAF_MY_EXTENSION_H
#define NETLEAF_MY_EXTENSION_H

#ifdef __cplusplus
extern "C" {
#endif

#include <stddef.h>
#include <stdint.h>
#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 宏:

#include "netleaf_my_extension.h"
#include "netleaf.h"
#include "netleaf_module.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

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_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)

第四步:编译扩展库

扩展库应该在主库构建完成后编译:

# 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() 会自动触发加载
// 主库初始化会自动加载 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 新增)

生命周期管理

// 初始化扩展库
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(); // 强制关闭所有扩展库

状态查询

// 检查是否已初始化
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");

信息查询

// 获取基本信息(便捷函数)
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");

搜索过滤

// 按能力搜索
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);

元数据管理

// 设置元数据
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);

热重载

// 重载单个扩展库
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) 通过值获取标识符

示例:

// 注册后获取自动分配的值
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:

# 正确
target_link_libraries(my_extension netleaf_core)

# 错误 - 会导致全局状态隔离
target_link_libraries(my_extension ${CMAKE_CURRENT_SOURCE_DIR}/path/to/netleaf_static)

2. 使用正确的导出宏

Windows 上使用 __declspec(dllexport):

#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. 错误处理

始终检查加载结果:

nl_plugin_handle_t ext = nl_plugin_load(path);
if (!ext) {
    printf("Error: %s\n", nl_plugin_get_error());
    return -1;
}

4. 平台兼容性

确保扩展库支持所有目标平台:

.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 环境变量

⚠️ **GitHub.com Fallback** ⚠️