发布时间:2026/7/24 12:03:54
CppMicroServices 样例编译与 Bundle 机制笔记 CppMicroServices 样例编译与 Bundle 机制笔记整理日期2026-07-23工程路径D:/CppMicroServices分支 development构建目录D:/CppMicroServices/buildVS 生成器Debug1. 工程自带的样例程序样例默认不编译由 CMake 开关US_BUILD_EXAMPLES控制默认 OFF定义在顶层CMakeLists.txt:259。1.1 入门示例 —doc/src/examples/getting_started/目标说明ServiceTime定义时钟服务接口的纯接口库不是 bundle无嵌入资源ServiceTime_SystemClock服务实现 bundleServiceTime_Consumer服务消费者 bundleGettingStarted.exe启动 Framework 并安装命令行传入的 bundle1.2 教程示例OSGi 经典字典教程—doc/src/tutorial/配套文档Example1.rst~Example7.rstBundle说明eventlistener服务事件监听dictionaryservice/frenchdictionary字典服务及第二个实现dictionaryclient/ 2 / 3三种服务消费方式直接查询 / 监听器 / ServiceTrackerspellcheckservice/spellcheckclient组合多个服务的拼写检查usTutorialDriver.exe交互式驱动命令行动态 start/stop 各 bundle2. 编译样例cdD:/CppMicroServices/build cmake-DUS_BUILD_EXAMPLESON.cmake--build.--configDebug--parallel4产物全部输出到build/bin/Debug/可执行usTutorialDriver.exe、GettingStarted.exe教程 bundle DLLeventlistener、dictionaryservice、frenchdictionary、dictionaryclient/2/3、spellcheckservice、spellcheckclient入门 bundle DLLServiceTimed、ServiceTime_SystemClockd、ServiceTime_Consumerd3. 运行验证3.1 usTutorialDriver.exe支持的命令h帮助、start id|name、stop id|name、status、shutdown。注意坑驱动按调试后缀找eventlistenerd.dll但教程 bundle 编译出来不带d后缀。解决在bin/Debug下复制一份带后缀的副本fornineventlistener dictionaryservice frenchdictionary dictionaryclient\dictionaryclient2 dictionaryclient3 spellcheckservice spellcheckclient;docp-n$n.dll${n}d.dlldone验证结果框架启动后正确安装全部 8 个 bundlestatus显示 INSTALLED依次start eventlistener→start dictionaryservice→start dictionaryclient后Starting to listen for service events. Ex1: Service of type IDictionaryService registered. Enter word:服务注册、事件监听、服务查询链路全部正常。3.2 GettingStarted.exe./GettingStarted.exe ServiceTime_SystemClockd.dll ServiceTime_Consumerd.dll# 输出: Elapsed: 1784713689727ms注意ServiceTimed.dll是纯接口库不是 bundle传给它会报Could not init zip archive for bundle at ...只传两个真正的 bundle 即可。4. manifest.json 的嵌入机制manifest.json及其他 bundle 资源会被嵌入到 DLL 文件本身CMake 的usFunctionEmbedResources(TARGET ... FILES manifest.json)调用资源编译器usResourceCompiler3资源编译器把资源文件打成一个zip 压缩包链接完成后POST_BUILDzip 直接追加到 DLL 文件末尾构建日志中可见Appending zip file .../res_0.zip to Tutorial-eventlistener为什么可行PE/ELF/Mach-O 加载器只按文件头部结构解析忽略尾部多余数据而 zip 的目录索引在文件末尾、从后往前解析——同一文件既是合法 DLL 又是合法 zip。静态构建时没有独立 DLL用usFunctionEmbedResources的ZIP_ARCHIVES参数把各静态bundle 的资源 zip 合并进最终可执行文件。5. 从 DLL 中解压嵌入资源DLL 本身就是合法 zip任何 zip 工具可直接解压# 查看内容unzip-leventlistener.dll# 80 eventlistener/manifest.json# 解压unzip-oeventlistener.dll-d输出目录警告98304 extra bytes at beginning or within zipfile属正常——这些多余字节是 DLL 的 PE 代码部分unzip 自动跳过其他工具7z x xxx.dll、Pythonzipfile.ZipFile(xxx.dll).extractall(...)、改名.zip后用图形工具打开资源带 bundle 名前缀目录如eventlistener/对应运行时BundleResourceAPI 的路径根解压出的文件数取决于该 bundle 嵌入的资源数。eventlistener 只嵌入了 manifest故只有 1 个文件{bundle.symbolic_name:eventlistener,bundle.activator:true}文件布局与切割eventlistener.dll ├── [0 ~ 98303] PE 格式 DLL 本体机器码unzip 跳过 └── [98304 ~ 末尾] zip 数据资源unzip 解压PE 部分不是压缩数据无法被解压如需单独切出纯 DLLhead-c98304eventlistener.dlleventlistener_pure.dll切割点因 DLL 而异可用 Python 精确获取importzipfile zzipfile.ZipFile(xxx.dll)print(z.infolist()[0].header_offset)# PE 部分长度一般无需切割——尾部 zip 数据不影响 DLL 的加载和执行。6. 可执行文件格式速查缩写全称中文平台PEPortable Executable可移植可执行格式Windows.exe/.dll/.sysELFExecutable and Linkable Format可执行与可链接格式Linux/BSD/Android可执行、.so、.oMach-OMach ObjectMach 对象格式macOS/iOS可执行、.dylibPE 由 COFF 演化而来也称 PE/COFF“Portable” 指跨 CPU 架构不是跨操作系统ELF 一个格式统一三种用途目标文件、可执行文件、共享库Mach-O 得名于苹果内核基于的 Mach 微内核CMU 开发共同点加载器从头部解析、忽略尾部数据——这是资源追加方案的基础7. InstallBundles 实现过程源码追踪核心思想安装阶段只读 DLL 尾部的 zip 元数据不加载 DLL 代码真正的 LoadLibrary 发生在Start()时。7.1 入口 — BundleContext::InstallBundlesframework/src/bundle/BundleContext.cpp:465只做 context 合法性检查转发给bundleRegistry.Install(location, ...)。7.2 并发控制 — BundleRegistry::Installframework/src/bundle/BundleRegistry.cpp:153注册表用multimaplocation, BundlePrivate记录已装 bundle分三种情况该路径已装过复用已有BundleResourceContainer不重新解析 zip已装 bundle 直接进结果只对新增条目走 Install0首次安装在initialBundleInstallMap登记占位 → 创建新容器 → 安装完成后notify_all唤醒等待线程他线程正在装同一路径引用计数 1条件变量等待wait_for100μs 轮询规避通知丢失竞态醒后按已安装路径处理7.3 打开 zip — BundleResourceContainerframework/src/bundle/BundleResourceContainer.cpp:43构造、:214InitMiniz构造时先检查文件存在不存在抛xxx does not existInitMiniz()两级尝试优先BundleObjFactory解析 PE/ELF/Mach-O若资源被链接进数据段则mz_zip_reader_init_mem从内存初始化回退mz_zip_reader_init_file把整个 DLL 文件当 zip 打开miniz 从尾部找 zip 目录自动跳过 PE 部分两者都失败 → 抛Could not init zip archiveInitSortedEntries()遍历 zip 条目按第一级目录名收集m_SortedToplevelDirs——每个顶层目录 一个 bundle 符号名7.4 创建 bundle — Install0framework/src/bundle/BundleRegistry.cpp:312对 zip 里每个顶层目录storage-CreateAndInsertArchive()创建BundleArchive分配 bundle id构造BundlePrivate包装成Bundle加锁插入(location, bundle)到注册表 multimap广播BUNDLE_INSTALLED事件异常时Purge()回滚已创建的 archive7.5 解析 manifest — BundlePrivate 构造函数framework/src/bundle/BundlePrivate.cpp:702从 archive 取/manifest.json资源流JSON 解析进BundleManifest校验bundle.symbolic_name必须存在且非空bundle.version若存在必须合法状态置STATE_INSTALLED优化若 zip 里只有 manifest 一个资源解析完立即关闭 zip 句柄时序总结路径检查 → 并发去重 → miniz 打开 DLL 尾部 zip → 按顶层目录逐个建档、解析 manifest、注册 → 广播 INSTALLED 事件8. 一个 DLL 可包含多个 Bundle判定依据框架只看尾部 zip 的顶层目录数Install0对每个顶层目录各创建一个独立的BundleArchiveBundlePrivate——这也是 API 叫复数 InstallBundles、返回std::vectorBundle的原因。TestBundleB.dll 尾部的 zip ├── TestBundleB/manifest.json ← bundle 1 │ dynamic.ptxt └── TestBundleImportedByB/manifest.json ← bundle 2 static.ptxt典型场景静态链接 bundle测试用例framework/test/bundles/libBWithStatic/CMakeLists.txtTestBundleImportedByB编译为静态库自己的资源 zip 先生成TestBundleB是共享库链接该静态库并用usFunctionEmbedResources的ZIP_ARCHIVES参数把静态 bundle 的 zip 合并进自己的 zip代码层面每个 bundle 各有usFunctionGenerateBundleInit生成的初始化代码和 Activator符号按US_BUNDLE_NAME区分如_us_create_activator_TestBundleB互不冲突Start()时框架按各 bundle 符号名分别查找入口。极端情况全静态构建静态编译时整个程序只有一个 exe所有bundle 的资源 zip 都合并进 exegetting_started CMakeLists 末尾的ZIP_ARCHIVES ${_static_bundles}。对 exe 自身路径调用一次InstallBundles即可装出全部 bundle——测试framework/test/gtest/BundleManifestTest.cpp:296DirectManifestInstallMulti验证了一个 location 装出 2 个 bundle。结论bundle 边界由 zip 顶层目录划分与 DLL 文件是一对多关系多 bundle 单 DLL 主要服务于静态链接和资源合并场景常规动态构建惯例仍是一个 DLL 一个 bundle。9. CPPMICROSERVICES_INITIALIZE_BUNDLE 宏旧版 2.x 时代的宏叫US_INITIALIZE_MODULEModule 时代3.0 更名后在当前代码中已不存在迁移时直接替换为CPPMICROSERVICES_INITIALIZE_BUNDLE编译定义US_MODULE_NAME改为US_BUNDLE_NAME。定义在framework/include/cppmicroservices/BundleInitialization.h:70。作用为 bundle 生成两个按US_BUNDLE_NAME命名的 C 导出函数用于框架与 bundle 间传递 BundleContextstd::atomicBundleContextPrivate*上下文变量;externCBundleContextPrivate*_us_get_bundle_context_instance_bundle名();// 框架读取externCvoid_us_set_bundle_context_instance_bundle名(BundleContextPrivate*);// 框架 Start 时注入Start()加载 DLL 后框架通过GetProcAddress找到 set 函数把 context 注入 bundle。使用位置核心 3 处位置说明cmake/BundleInit.cpp:25最主要入口——usFunctionGenerateBundleInit的模板被 configure 成每个 bundle 的cppmicroservices_init.cpp自动编入教程 bundle 都走这条路framework/src/bundle/CoreBundleContext.cpp:48框架自身system_bundle的初始化getting_started 的ServiceTimeImpl.cpp:42、ServiceTimeConsumer.cpp:60手写调用示例带参数形式CPPMICROSERVICES_INITIALIZE_BUNDLE(service_time_systemclock)另有文档/代码片段示范framework/doc/snippets/下 3 个 main.cpp、doc/src/examples/makefile/的 main.cpp 与 bundle.cpp及 getting_started.rst、Example1.rst、build_instructions.rst。两种使用方式CMake 自动生成推荐usFunctionGenerateBundleInit(TARGET xxx OUT srcs)宏从编译定义US_BUNDLE_NAMExxx取名手动调用非 CMake 构建如 makefile 示例在 bundle 某源文件末尾直接写CPPMICROSERVICES_INITIALIZE_BUNDLE10. 版本演进史共3 个大版本截至 2026-07上游最新正式版为v3.8.12本地VERSION文件与上游最新 tag 一致development 分支为 3.8.12 之后的开发版尚无 4.0 tag。大版本tag 范围核心特征1.xv1.0.0初代核心概念叫Module基础服务注册/查询2.xv2.0.0 ~ v2.1.1仍是 Module 时代US_INITIALIZE_MODULE宏属于此期完善资源嵌入、ServiceTracker3.xv3.0.0 → v3.8.12重大重构全面对齐 OSGi 规范Module 更名Bundle引入 Framework 生命周期FrameworkFactory/Init/Start、BundleContext3.4 起陆续加入 OSGi Compendium 服务声明式服务 DS、ConfigAdmin、LogService2.x → 3.x 是不兼容的破坏性升级API、宏、概念全部改名3.x 系列内部保持 API 兼容主要是增量功能和修复US_GLOBAL_VERSION_SUFFIX基于主版本号生成用于库文件命名隔离不同大版本11. 与 CTK Plugin Framework 的对比CppMicroServices 3.x 的 Framework 生命周期与 CTK非常类似——两者的祖师爷都是OSGi 规范CTK 的 ctkPluginFramework 是 OSGi R4 的 Qt/C 移植CppMicroServices 3.x 重构目标同样是对齐 OSGi。概念对照OSGi (Java)CTKCppMicroServices 3.xFrameworkctkPluginFrameworkFrameworkFrameworkFactoryctkPluginFrameworkFactoryFrameworkFactoryBundlectkPlugin叫 PluginBundleBundleContextctkPluginContextBundleContextBundleActivatorctkPluginActivatorBundleActivatorinstallBundle()installPlugin()InstallBundles()状态机INSTALLED→RESOLVED→STARTING→ACTIVE→STOPPING完全相同的状态集事件ctkPluginEvent / ctkServiceEventBundleEvent / ServiceEvent元数据MANIFEST.MF键值对manifest.json启动流程同构工厂创建 Framework → init()framework 自身成为 0 号 system bundle→拿 context 安装插件/bundle → start() → 各 Activator 的 start(context) 被回调。主要差异Qt 依赖CTK 深度绑定 Qt——插件必须是 QObject/Qt 插件服务接口用Q_DECLARE_INTERFACE事件走 signal/slot元数据为 Java 风格 MANIFEST.MF。CppMicroServices 是纯标准 C零框架依赖服务接口是普通抽象类用模板做类型安全的注册/查询打包方式CTK 插件也是共享库 内嵌 zip 资源思路类似但格式不同CppMicroServices 用 manifest.json且支持静态链接 bundleCTK 基本只支持动态插件规范覆盖CTK 移植了不少 OSGi 服务EventAdmin、ConfigAdmin、Metatype 等但近年维护放缓CppMicroServices 3.4 也在补 Compendium 服务社区更活跃主要由 The MathWorks 驱动粒度CppMicroServices 2.x 刻意轻量化无完整 Framework 概念3.x 才补齐生命周期向 CTK/OSGi 完整度靠拢结论有 CTK 经验如 MITK/医学影像开发可直接复用心智模型——把 Plugin 换成 Bundle、去掉 Qt 层即可。

相关新闻

2026/7/24 12:03:54

API Fuzzer 完整专业释义

目录 一、基础定义 二、核心适用接口类型 三、API Fuzzer 核心工作流程 四、关键专属术语(API Fuzz 特有) 五、主流 API Fuzzer 工具分类 1. 开源专用 API 模糊工具 2. 企业级接口安全平台 3. RPC 专用 Fuzzer 六、API Fuzzer 与普通二进制 Fuz…

2026/7/24 12:03:54

C# CAD二次开发中如何退出窗口

在 C# AutoCAD 二次开发中,退出已加载的 Windows 窗口(如 WinForms 或 WPF 窗体)的核心方法是调用窗体的 Close() 方法或设置 DialogResult 属性。具体实现取决于窗口的显示方式(模态或非模态)以及是否需要与 AutoCAD …

2026/7/24 12:03:54

Ubuntu 20.04系统卡死诊断与解决方案

1. 问题现象与初步诊断遇到Ubuntu 20.04系统完全卡死的情况,连鼠标指针都无法移动,这是典型的系统僵死(hard hang)现象。与普通的程序无响应不同,这种全局性冻结通常意味着内核层或硬件交互出现了严重问题。根据我处理…

2026/7/24 13:39:01

大模型评估新视角:超越Benchmark的实战价值

1. 从benchmark争议看大模型评价体系的局限性 当小米最新AI模型在部分benchmark测试中超越DeepSeek V4时,技术社区的反应耐人寻味。这场讨论揭示了一个关键问题:当前AI领域的评价体系是否真正反映了模型的实际价值? 1.1 benchmark指标的先天…

2026/7/24 13:39:01

数据驱动科技成果转化:架构设计与智能匹配实践

1. 项目概述:数据驱动的科技成果转化新范式 在科技创新领域,一个长期存在的痛点是大量优质科研成果"沉睡"在实验室,无法有效转化为实际生产力。根据行业调研数据显示,我国高校和科研院所的科技成果转化率长期徘徊在30%以…

2026/7/24 13:39:01

为什么说‘自动洞察‘才是CEO真正需要的AI能力

导语 先给一个可能与直觉相反的判断:作为CEO,你真正需要的AI能力,不是"问一句就能查到数",而是"在你还没开口之前,就主动告诉你应该看什么"。 过去两年,ChatBI(自然语言问答…

2026/7/24 13:39:01

先进制造业BI落地路径:从设备数据到经营决策的执行清单

导语 根据艾瑞咨询《2025年中国BI市场报告》,有一个反直觉的行业现状:80%以上先进制造企业已经完成了核心生产设备的联网改造,实现了设备运行数据的实时采集,但仅不到20%能真正将生产端数据转化为可落地的经营决策。这也是当前先进…

2026/7/24 13:39:01

金融行业BI复盘:风控与营销场景下的方案取舍

导语 很多金融机构在规划BI落地时,都会默认一个共识:一套统一的BI平台,可以同时满足风控和营销两大核心业务场景的分析需求,复用数据底座和工具能力能降低建设成本,何乐而不为?但我们在长期服务金融行业客户…

2026/7/24 13:33:58

Amphenol ICC RJE1Y26D57C42401线束组件解析与线束兼容方案

在智能制造、通信基础设施以及高性能电子设备快速发展的背景下,设备内部连接结构越来越复杂。线束组件作为电子系统中的基础互连部件,虽然不像芯片、处理器等器件受到广泛关注,但其稳定性直接影响整机运行可靠性。 一套成熟的线束组件&#x…

2026/7/23 12:54:51

Unity与Python本地通信:基于Flask的跨语言数据交换实战

1. 项目概述:为什么我们需要一个本地通信服务器?在游戏开发、数字孪生、仿真训练等众多领域,Unity作为强大的实时3D内容创作平台,其核心逻辑通常由C#驱动。然而,当我们需要进行复杂的数据分析、机器学习推理、科学计算…

2026/7/24 0:03:10

不同品牌斜齿行星减速机如何替换?以PX与PAG系列为例

不同品牌斜齿行星减速机如何替换?以 PX 与 PAG 系列为例 一、系列对应不等于型号直接互换 PX 与 PAG 都属于斜齿、方法兰、输出轴式精密行星减速机,结构形式和应用方向具有对应关系。 原设备使用PX系列时,可以优先从PAG系列中寻找替换型号。但…

2026/7/24 0:03:10

jdk8 把list 扁平化成String 多个以逗号分隔

在 JDK 8 中&#xff0c;将 List 扁平化为以逗号分隔的 String&#xff0c;有几种非常简洁且高效的方法。&#x1f680; 推荐方案&#xff1a;使用 Collectors.joining()这是最标准的 Java 8 写法&#xff0c;适用于 List<String>。javaimport java.util.stream.Collecto…

2026/7/24 0:03:10

java 两个 long id 怎么合并成一个long id 并且不重复

“把两个 Long ID 合并成一个唯一的 Long ID&#xff0c;且保证不重复”这个需求&#xff0c;在 Java 里直接做数学上的“完美合并”是不可能的。因为两个 Long&#xff08;各 64 位&#xff09;要合并成一个 Long&#xff08;64 位&#xff09;&#xff0c;在信息论上是有损压…

2026/7/23 23:42:43

3个高效策略:快速掌握Axure中文界面配置

3个高效策略&#xff1a;快速掌握Axure中文界面配置 【免费下载链接】axure-cn Chinese language file for Axure RP. Axure RP 简体中文语言包。支持 Axure 11、10、9。不定期更新。 项目地址: https://gitcode.com/gh_mirrors/ax/axure-cn 还在为Axure RP的英文界面感…