CppMicroServices 样例编译与 Bundle 机制笔记

发布时间:2026/9/14 14:51:39

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/9/13 12:45:33

API Fuzzer 完整专业释义

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

2026/9/14 7:13:24

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

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

2026/9/12 5:31:06

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

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

2026/9/14 14:49:53

基于ASP.NET的体检信息管理系统设计与实现全解析

简介:这是一款基于ASP.NET与SQL Server的医院体检信息管理系统源码,面向需要完成毕业设计或课程设计的计算机相关专业学生。系统包含预约用户、医生、管理员三种角色,管理员可进行套餐管理、医生管理、体检人员管理、体检管理及通讯录管理&am…

2026/9/14 14:49:53

C语言职工信息管理系统:链表实现与文件持久化全解析

简介:面向C语言学习者与计算机专业学生的职工信息管理系统设计与实现资源包,定位为课程设计、期末项目或实训的完整对照参考。资源不仅包含可运行的工程源码,还配套系统设计文档、环境配置与项目导入讲解,帮助读者从零搭建开发环境…

2026/9/14 14:49:53

AI文献综述工具:原理、应用与学术伦理探讨

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

2026/9/14 14:49:53

18650锂电池热失控原理与安全防护技术

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

2026/9/14 14:49:53

基于Python Flask的身份证识别系统:PaddleOCR与OpenCV完整实现

简介:一份基于 Python Flask 的身份证识别系统毕业设计源码包,面向计算机相关专业毕业生与 Flask 入门开发者。项目采用前后端分离思路,后端以 Flask 提供数据接口,前端使用 HTML5、CSS3、JavaScript 及 jQuery、Bootstrap 构建页…

2026/9/14 14:44:52

Fortran 77 文件读取:逻辑单元号与 OPEN/READ/CLOSE 完整流程解析

简介:本资源是一份面向Fortran初学者与科学计算实践者的文件读取教学实践包,聚焦Fortran 77标准下结构化数据的可靠读取,解决科研建模、工程仿真中常见的文本/数值文件解析痛点。压缩包共12个文件,涵盖核心源码(.f90&a…

2026/9/14 2:17:50

拯救者Y7000黑屏故障排查与维修实战指南

1. 项目概述:一台黑屏的拯救者Y7000,到底卡在哪一步? 联想拯救者Y7000系列笔记本,从2018年第一代搭载i5-8300H开始,到后来的i7-9750H、i7-10750H、i5-11400H,再到2023年款的R7-7840HS,它始终是学…

2026/9/14 0:03:22

KCF目标跟踪算法与OTB工程实现:毕业设计实战解析

简介:这是一份基于KCF核相关滤波算法、融合尺度池与抗遮挡处理的目标检测跟踪MATLAB完整源码,主要面向计算机相关专业准备毕业设计、课程设计或期末大作业的学生,也适合需要项目实战练习的初学者。源码在OTB数据集上完成验证,能够…

2026/9/14 0:03:22

语音情感识别实战:Keras实现LSTM、CNN、SVM与MLP多模型对比

简介:面向语音情感识别入门与进阶开发者,这份基于Keras的项目源码完整实现了LSTM、CNN、SVM、MLP四种模型,兼容Python3.8与Keras/TensorFlow2环境。压缩包内含49个文件,大小约70.31MB,主体包括Python脚本、yaml/json配…

2026/9/14 11:59:31

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/14 13:53:59

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/14 11:22:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…

还想了解更多?直接咨询顾问

免费诊断 + 免费方案 + 透明报价。

全国咨询热线400-8866-253
免费获取方案
咨询二维码