简介:chmsee 是面向 Linux 用户的 CHM 文档阅读工具,解决 Linux 系统无法直接打开 Microsoft Compiled HTML Help 文件的痛点,尤其适合需要频繁查阅中文技术文档的开发者与运维人员。资源包共 111 个文件,约 355KB,以 C 源码与头文件为主体,辅以 configure、Makefile.am 等构建脚本,以及 glade 界面描述、po 翻译、desktop 与 mime 配置、图标图片和 readme、changelog 等说明文档,构成一套可编译安装的完整工程。已有 468 人学习下载,说明其在 Linux 中文阅读场景中具有一定参考价值。借助这份源码,读者可以了解 CHM 解析与索引加载的实现思路、Firefox 插件集成方式,以及多语言支持与书签、搜索等功能的组织方式,也便于按发行版依赖自行编译调试,是研究轻量级桌面阅读工具的不错样本。
1. chmsee 在 Linux 下读 CHM:一个被低估的老工具,为什么还值得装
手头有一份几百页的 CHM 手册,双击在 Windows 上能开,换到 Linux 桌面就只剩一个文件图标,这种场景做嵌入式、运维、逆向的同行基本都遇到过。CHM 是微软那套编译过的 HTML 帮助格式,本质是 LZX 压缩的容器加一套索引结构,Linux 原生桌面环境并不认它。chmsee 就是冲着这个缺口来的:一个基于 GTK 的轻量 CHM 阅读器,把解包、渲染、目录树、全文检索这几件事塞进一个窗口里,装完就能直接翻文档,不用先转 PDF 再读。它适合两类人:一是在 Linux 上长期干活、经常收到 CHM 格式资料包的从业者;二是想找一个不依赖浏览器、启动快、能离线查手册的本地阅读器的人。下面按「它怎么工作、怎么装、怎么用、哪里会翻车」的顺序拆开讲,参数和命令都能直接抄。
2. chmsee 的解析链路:从 LZX 容器到 GTK 渲染
2.1 CHM 文件结构决定了阅读器要做什么
要理解 chmsee 为什么这么设计,得先看 CHM 里到底装了什么。一个 CHM 文件由三部分组成:文件头(ITSF 标识)、一系列内容块(Content Section),以及最关键的 ITSP/PMGL 索引结构。内容块里混着 LZX 压缩的 HTML、图片、CSS,索引结构则记录了「哪个路径对应哪块数据」以及「目录树怎么组织」。阅读器要干的事其实就三步:定位并解压内容块、按索引还原出目录层级、把 HTML 交给渲染引擎显示。
chmsee 的做法是先用 libchm 这层库把容器拆开,拿到解压后的字节流,再交给 Gecko 渲染引擎(早期版本基于 XULRunner)去显示。这个选型在今天看有点重,好处是 HTML/CSS 兼容性好,坏处是依赖链长、编译门槛高。常见做法是把它当成「一次性配好、长期用」的工具,而不是天天折腾的玩具。理解这条链路的价值在于:后面遇到乱码、图片不显示、目录树空白,你大概能判断是解压层的问题还是渲染层的问题,排查方向不会跑偏。
2.2 依赖清单与安装路径选择
chmsee 不在主流发行版的默认仓库里,装它基本有两条路:源码编译,或者找现成的包。源码编译前先把依赖理清楚,缺一个就会在 configure 阶段报错。核心依赖是 libchm(提供 CHM 解析)、GTK 开发库(提供界面)、以及 Gecko/XULRunner 相关的开发包(提供渲染)。不同发行版的包名不一样,下面给一份通用的检查脚本,跑一遍就知道缺什么。
# 检查 chmsee 编译所需的核心依赖是否齐全 # 逐个探测头文件与库,缺失的会打印 MISSING check_pkg() { pkg-config --exists "$1" && echo "OK $1" || echo "MISSING $1" } check_pkg chm # libchm,CHM 容器解析 check_pkg gtk+-2.0 # GTK2 界面库 check_pkg xulrunner # Gecko 渲染引擎(老版本依赖) check_pkg glib-2.0 # 基础工具库这段脚本的逻辑很直白:pkg-config --exists只做存在性判断,不打印版本细节,适合快速摸底。参数上要注意gtk+-2.0和gtk+-3.0是两个不同的包名,chmsee 老版本认的是 GTK2,如果你系统里只有 GTK3,编译会卡在界面层。xulrunner这一项是最容易缺的,很多新系统已经不带它了,这时候要么装旧版运行时,要么接受「编译不过」的现实,改用别的方案。我一般会先跑这个脚本,把 MISSING 的项一次性装齐再动手,避免 configure 报一个错装一个、来回折腾。
装齐依赖后,源码编译的标准三步是./configure、make、make install。configure 阶段重点看输出末尾的 summary,确认 chm、gtk、xulrunner 三项都是 yes。如果 xulrunner 显示 no 但你想强行继续,可以试--disable-xulrunner之类的开关(具体开关名以 configure --help 为准),代价是渲染能力退化,部分带复杂 CSS 的 CHM 会显示异常。这一步没有后悔药,编译前想清楚。
2.3 首次启动与界面分区
装完直接命令行敲chmsee就能起界面。第一次打开会看到一个很朴素的三栏布局:左侧目录树、中间内容区、顶部工具栏。目录树来自 CHM 内部的索引结构,如果这份 CHM 制作时没写目录(有些工具打包时会省略),左栏就是空的,但内容区照样能翻,别以为文件坏了。工具栏上有前进后退、缩放、搜索入口,搜索是全文检索,走的是 CHM 自带的索引,速度取决于文件大小。
启动时如果报「无法加载渲染引擎」之类的错,八成是 xulrunner 路径没对上,检查LD_LIBRARY_PATH里有没有运行时目录。另一个常见现象是界面能起但内容区一片白,这时候先换一份确定正常的 CHM 试,排除是文件本身的问题,再回头查渲染层。界面这块没什么可调的,chmsee 的定位就是「打开即读」,不追求花哨。
3. 把 CHM 读顺:打开、检索、导出的实操细节
3.1 命令行打开与批量处理
chmsee 支持直接把文件路径当参数传进去,这在脚本化场景里很有用。比如你有一批 CHM 资料放在某个目录,想快速抽查其中几份,可以写个循环逐个打开,读完关掉再开下一个。下面这段脚本演示了「按关键字筛文件 + 逐个打开」的用法。
# 在当前目录下找出所有 chm 文件,按名称排序后逐个用 chmsee 打开 # 适合人工抽查,读完一个关掉窗口再继续下一个 for f in $(ls *.chm | sort); do echo ">>> 正在打开: $f" chmsee "$f" done逻辑说明:ls *.chm | sort保证顺序稳定,chmsee "$f"是阻塞式调用——窗口不关,循环不往下走,所以这是「人工逐个看」的节奏,不是后台批量。参数上,路径带空格时一定要加引号,否则会被 shell 拆成多个参数,chmsee 会报「文件不存在」。如果你想要非阻塞,可以在命令末尾加&,但多个窗口叠在一起反而乱,我一般不用。这个脚本的价值在于把「找文件」和「开文件」两步合并,资料多的时候省事。
3.2 全文检索的边界与替代方案
chmsee 自带的搜索是全文检索,输入关键词后会在内容区高亮命中位置,并在结果列表里列出所在页面。它的边界在于:只搜当前打开的这一个 CHM,不跨文件;对中文的分词支持一般,长句搜索容易漏;索引是 CHM 打包时生成的,如果原文件没建索引,搜索可能直接不可用。遇到搜不到的情况,先确认这份 CHM 在别的阅读器里能不能搜,能搜就是 chmsee 的索引读取问题,不能搜就是文件本身没索引。
替代方案是先把 CHM 解包成普通 HTML 目录,再用grep -r搜。解包可以用extract_chmLib(libchm 自带的工具)或者7z x,后者对多数 CHM 都能拆。拆完得到一堆 HTML,检索就变成文件系统层面的操作,灵活得多。下面给一个解包后按关键词定位文件的例子。
# 把 CHM 解包到指定目录,再在解包结果里按关键词找文件 mkdir -p ./chm_out extract_chmLib ./manual.chm ./chm_out # libchm 自带解包工具 # 若没有 extract_chmLib,可用 7z 替代:7z x manual.chm -o./chm_out grep -rl "目标关键词" ./chm_out --include="*.html"extract_chmLib的两个参数分别是源文件和输出目录,输出目录要先建好。grep -rl的-r是递归、-l是只列文件名不打印内容,配合--include="*.html"把范围限定在网页文件上,避免在图片二进制里瞎匹配。这套组合适合「我要在一堆 CHM 里找某段话」的场景,比逐个打开快得多。注意解包后的目录结构可能和原目录树不完全一致,路径以实际输出为准。
3.3 显示异常时的定位顺序
内容区显示不对,按「文件 → 解包 → 渲染」三层往下查,别一上来就重装。第一层换文件:拿一份确定正常的 CHM 试,排除源文件损坏。第二层看解包:用 3.2 的方法把文件拆开,直接浏览器打开解出来的 HTML,如果浏览器显示正常而 chmsee 不正常,问题在渲染层;如果浏览器也乱,问题在文件或解包。第三层查渲染:确认 xulrunner 运行时是否完整、版本是否匹配。
编码问题也归在这一层。CHM 里的 HTML 可能声明了 GBK 或 UTF-8,chmsee 按声明去解,声明错了就乱码。这种情况在解包后的 HTML 里能直接看到<meta charset=...>,改对再重新打包不现实,但至少能确认是文件问题不是工具问题。我遇到过一份手册,目录树正常、正文全是方块,最后定位到是打包时编码声明写错,换阅读器也一样,这种就只能认。
4. 避坑与排查:chmsee 用起来最容易翻车的五处
4.1 编译时 xulrunner 找不到
现象:./configure跑到渲染引擎检测时直接失败,提示找不到 xulrunner 或 mozilla 相关头文件。原因:新发行版普遍不再打包 XULRunner 运行时,而 chmsee 老版本硬依赖它。解决:优先找系统里是否还有旧版运行时包可装;装不上就接受「这个版本编不过」,转而用解包 + 浏览器阅读的方案,或者换一个不依赖 Gecko 的轻量阅读器。别在这上面耗太久,工具是拿来用的,不是拿来供的。
4.2 界面能起但内容区空白
现象:窗口正常弹出,目录树也有,点任何条目中间都是白的。原因:多半是渲染引擎加载失败,或者 CHM 内部 HTML 引用了 chmsee 不支持的资源。解决:先换一份简单 CHM 验证工具本身是否正常;正常则说明是这份文件的兼容性问题,用解包方式读。如果换文件也白,检查运行时库路径,ldd一下 chmsee 主程序看有没有 not found 的依赖。
4.3 中文目录或正文乱码
现象:目录树里的中文变成问号或方块,正文同样。原因:CHM 打包时的编码声明与实际字节不符,或系统 locale 不是 UTF-8。解决:先locale确认当前环境是zh_CN.UTF-8或至少en_US.UTF-8;再解包看 HTML 的 charset 声明。locale 问题改环境变量即可,文件声明问题只能换阅读方式,改文件成本太高。
4.4 搜索功能完全没反应
现象:点搜索、输关键词、回车,结果列表空着,也不报错。原因:这份 CHM 打包时没生成全文索引,chmsee 读不到索引就静默返回空。解决:确认文件在别的阅读器里能否搜;不能搜就是文件问题,改用解包后grep。能搜而 chmsee 搜不到,则是索引格式兼容问题,同样走解包路线。
4.5 大文件打开卡死或内存飙升
现象:打开几十兆以上的 CHM,界面长时间无响应,内存占用一路涨。原因:Gecko 渲染整份文档时会把资源都载入,大文件吃不消。解决:不要整份打开,先用解包工具拆开,按需用浏览器看单个 HTML;或者接受「大文件不适合这个工具」的结论,换更轻的解析方案。这条没有巧办法,工具的能力边界就在这。
5. 进阶:把 chmsee 嵌进你的文档工作流
单靠 chmsee 读文件只是起点,真正省事的是把它和解包、检索、格式转换串成一条流水线。我的习惯是:收到 CHM 先不急着开,先解包,解包结果既可以用 chmsee 打开原始文件对照,也可以直接用浏览器读 HTML,还能丢进grep做全文检索。三种读法各有适用面,下面这张表是我实际用下来总结的对照。
| 场景 | 推荐方式 | 理由 |
|---|---|---|
| 快速翻阅、看目录结构 | chmsee 直接打开 | 目录树现成,翻页顺手 |
| 精确找某段话 | 解包后 grep | 跨文件、支持正则、结果可定位 |
| 读带复杂样式的页面 | 解包后浏览器打开 | 渲染兼容性最好 |
| 批量处理多份 CHM | 脚本解包 + 索引 | 可自动化,不依赖 GUI |
流水线的关键一步是解包后的目录整理。不同 CHM 解出来的结构五花八门,有的把 HTML 平铺在根目录,有的按章节建子目录。我一般会在解包后先跑一遍find统计文件类型分布,确认 HTML 占比,再决定检索策略。下面这段脚本做的是「解包 + 统计 + 建一份文件清单」,清单留着后面检索时对照。
# 解包 CHM 并生成一份 HTML 文件清单,便于后续检索定位 src="$1" # 传入的 chm 文件路径 out="./unpacked_$(basename "$src" .chm)" mkdir -p "$out" extract_chmLib "$src" "$out" || 7z x "$src" -o"$out" >/dev/null # 统计各类文件数量,并列出所有 html 的相对路径 find "$out" -type f | sed 's/.*\.//' | sort | uniq -c | sort -rn find "$out" -name "*.html" -o -name "*.htm" | sort > "$out/_index.txt" echo "清单已写入: $out/_index.txt"basename "$src" .chm去掉扩展名,保证输出目录名干净。extract_chmLib ... || 7z x ...是「首选工具失败就换备选」的写法,两条路都试过还不行就说明文件本身有问题。sed 's/.*\.//'取扩展名做统计,能快速看出这份 CHM 是纯网页还是夹了大量图片。最后生成的_index.txt是一份纯路径清单,配合grep或编辑器跳转都好用。参数上注意7z x的-o后面不能有空格,这是 7z 的语法要求,写错会解到当前目录。
验证这套流程是否跑通,看两个信号:解包目录里 HTML 数量是否和 CHM 目录树的条目数大致对得上;_index.txt里的路径能否在浏览器里逐个打开。对不上就回头查解包工具是否完整支持这份文件的压缩方式,个别 CHM 用了非标准压缩,extract_chmLib会静默跳过部分块,这时候7z往往更稳。
从那以后我每次拿到 CHM,都强制先解包再决定用哪种方式读,不再一上来就双击。这个习惯帮我避开了好几次「以为文件坏了、其实是工具不兼容」的误判。希望帮到你。
本文还有配套的精品资源,点击获取