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/7/24 12:04:02 阅读更多 →
C# CAD二次开发中如何退出窗口

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

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

2026/7/24 12:04:02 阅读更多 →
Ubuntu 20.04系统卡死诊断与解决方案

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

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

2026/7/24 12:04:02 阅读更多 →

最新新闻

考试复习录音整理不同使用场景实用选择建议

考试复习录音整理不同使用场景实用选择建议

2026年针对考试复习、新人学新岗位知识的录音整理,我整理了最新的实用选择建议,核心逻辑是按你自己的需求选,不用追贵的追热门的,不同场景匹配不同工具,刚好解决很多人只会用基础转写、不知道怎么用AI提高复习效率的痛…

2026/7/24 12:10:03 阅读更多 →
谷歌Gemini定制芯片揭秘:十倍能效背后的AI算力革命

谷歌Gemini定制芯片揭秘:十倍能效背后的AI算力革命

最近在关注 AI 芯片的朋友,可能都看到了一个消息:谷歌似乎正在为它的 Gemini 大模型“量身定制”一款神秘芯片。消息称,这款芯片的能效表现,相比谷歌自家的 TPU 能有十倍级别的提升。 十倍能效是什么概念?这不仅仅是实…

2026/7/24 12:10:03 阅读更多 →
基于YOLOv5改进的肉鸡健康监测系统开发实践

基于YOLOv5改进的肉鸡健康监测系统开发实践

1. 项目背景与核心价值 在现代化养殖场中,肉鸡的健康状态监测一直是个劳动密集型工作。传统的人工巡检方式不仅效率低下,而且容易因疲劳导致误判。我们团队开发的这套基于深度学习的肉鸡目标检测系统,能够实现鸡群数量统计、个体行为识别和健…

2026/7/24 12:10:03 阅读更多 →
西安低空经济飞手接单系统开发实战:本地适配与报备接口指南

西安低空经济飞手接单系统开发实战:本地适配与报备接口指南

# 西安低空经济飞手接单系统开发实战:本地适配与报备接口指南在低空经济政策全面放开、本地政府鼓励无人机商业应用的背景下,开发一套针对西安本地的飞手接单系统,核心任务就是解决空中飞行安全、任务调度高效与政府监管合规三者间的矛盾。本…

2026/7/24 12:10:03 阅读更多 →
同步带频繁跳齿打滑?别只调张力,问题出在同步轮齿宽选型

同步带频繁跳齿打滑?别只调张力,问题出在同步轮齿宽选型

同步带频繁跳齿打滑?别只调张力,问题出在同步轮齿宽选型很多设备遇到同步带频繁跳齿打滑的问题时,第一反应就是反复调整张力,却忽略了最容易被遗漏的选型细节,导致故障反复出现始终无法根治,以下是基于齿宽…

2026/7/24 12:10:03 阅读更多 →
智慧校园系统落地避坑指南

智慧校园系统落地避坑指南

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

2026/7/24 12:09:03 阅读更多 →

日新闻

用Highcharts 创建可拖拽三维散点立方体3D图表

用Highcharts 创建可拖拽三维散点立方体3D图表

该案例基于Highcharts scatter3d 三维散点图实现空间立方体散点可视化,核心特色:三维 X/Y/Z 三轴空间,所有散点分布在 0~10 立方体空间内;散点使用径向渐变实现立体 3D 圆球质感;支持鼠标 / 触屏拖拽画布,…

2026/7/24 0:00:29 阅读更多 →
AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口

AppCertDlls:进程创建路径上的 DLL 入口 AppCertDlls 位于 HKLM\System\CurrentControlSet\Control\Session Manager\AppCertDlls。本文的程序功能是只读列出这个键在 64 位和 32 位注册表视图中的全部值,并显示每条值的来源、名称、类型和可安全显示的数…

2026/7/24 0:00:29 阅读更多 →
我的编程之路:第一篇博客

我的编程之路:第一篇博客

大家好,我是一名编程初学者,同时这也是我编程学习之路上的第一篇博客。在这里,我想要向大家介绍我的一些想法和规划。a.自我介绍我是一个刚刚接触编程的新手,目前在学习c语言,我对编程世界充满了强烈的好奇。当然&…

2026/7/24 0:00:29 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/23 17:49:47 阅读更多 →

月新闻