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 层即可。

相关新闻

API Fuzzer 完整专业释义

API Fuzzer 完整专业释义

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

2026/10/28 20:45:36 阅读更多 →
C# CAD二次开发中如何退出窗口

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

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

2026/10/28 18:50:12 阅读更多 →
Ubuntu 20.04系统卡死诊断与解决方案

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

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

2026/10/25 22:20:45 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年,我大部分时间都在跟浏览器标签页打交道,直到第一次把桌面应用跑起来,才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词,最开始是在某段时间频繁出现在同事的讨论里,一个用HTML…

2026/10/28 23:48:14 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介:亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档,主要面向智能锁安装人员、终端用户及售后维护者,帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件,大小4.37MB,内容结构清晰&#xff…

2026/10/28 7:28:00 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统,整车要求空载跑到8m/s,电机转速已经拉到额定点,驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令,转速纹丝不动,三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/28 9:02:11 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年,用它写过嵌入式驱动、做过游戏服务端,也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词,我其实挺感慨的——这门语言的难点从来不是语法多,而是知识点之间的耦合太深…

2026/10/28 22:10:30 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介:亚太天能T109滑盖智能锁产品说明书以PDF格式发布,适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开,先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能,再给出完整安装步骤&…

2026/10/28 9:00:54 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历:翻开源码注释、论文摘要或者一份专业报告,每个字都认识,连在一起却像天书。想找人聊聊,身边要么没人懂,要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型,几秒钟之后&a…

2026/10/28 7:29:02 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 16:58:40 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 9:01:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/28 19:25:09 阅读更多 →