1. 先搞清楚CHM到底是个什么东西
CHM这三个字母,全称是Compiled HTML Help,翻译过来就是“编译后的HTML帮助文件”。微软在九十年代末推出这个格式,本意是给Windows应用程序做离线帮助文档用的。它把一堆HTML页面、图片、CSS样式表、甚至JavaScript脚本,全部打包压缩进一个单一文件里,再用一个索引系统把它们串起来。你可以把它理解成一本自带目录和搜索功能的电子书,只不过这本书的排版语言是网页那一套。
我第一次接触CHM大概是十几年前,当时下载了一个开发工具的离线文档,双击打开之后左边是树形目录,右边是内容区域,顶部还有搜索框和前进后退按钮。那个体验在当时来说相当惊艳,因为整个文件才几兆大小,却装了几百页的内容,而且不需要联网就能全文检索。后来我才知道,CHM内部用的是LZX压缩算法,压缩率相当高,再加上HTML本身是文本格式,所以体积控制得非常好。
这个格式最大的优势有三个。第一是单文件便携,不管你是拷到U盘里还是通过邮件发给别人,一个文件就搞定,不会出现“图片丢失”“样式错乱”这种问题。第二是离线可用,所有内容都在本地,没有网络也能正常浏览和搜索。第三是制作成本低,只要你懂一点HTML,就能用工具把网页打包成CHM。正因为这些特点,CHM在软件文档、技术手册、电子书、教程合集这些场景里被广泛使用。
不过CHM也有它的问题。它是微软的专有格式,在Windows上原生支持,但到了macOS或者Linux上就没那么方便了。而且CHM文件本身可以内嵌脚本,这就带来了一些安全隐患,所以很多邮件系统会直接拦截CHM附件。另外,随着在线文档和PDF的普及,CHM的使用频率确实在下降,但存量文件依然巨大,尤其是在一些传统行业和旧版软件的文档包里,你经常能碰到它。
提示:如果你手头有一个CHM文件但不知道怎么处理,先别急着删。它的内容往往比你在网上能找到的版本更完整,尤其是那些已经停止维护的软件文档。
2. 打开CHM文件的几种靠谱方式
2.1 Windows系统下的原生打开方法
在Windows上打开CHM是最省事的,因为系统本身就内置了支持。你只需要双击文件,系统就会调用hh.exe这个程序来渲染内容。这个程序从Windows 98时代就存在了,一直保留到现在。打开之后你会看到一个标准的帮助窗口,左侧是目录树,右侧是内容区,顶部有隐藏/显示目录的按钮、前进后退、主页、打印、搜索等常用功能。
但这里有一个非常常见的坑:很多从网上下载的CHM文件,双击之后右侧内容区显示“已取消到该网页的导航”或者一片空白。这不是文件损坏,而是Windows的安全机制在作祟。因为CHM文件可能包含恶意脚本,所以系统会对来自网络的文件加一个“锁定”标记。解决办法很简单:右键点击CHM文件,选择“属性”,在“常规”选项卡底部你会看到一个“解除锁定”的复选框,勾上它,再点确定,重新打开就能正常显示了。
还有一个情况是打开之后左侧目录正常,但点击任何条目右侧都提示“无法显示此页”。这通常是因为文件路径里包含了中文或者特殊字符,或者文件被放在了网络共享路径下。把CHM文件复制到本地硬盘的一个纯英文路径下,比如D:\docs\,再打开基本就能解决。
2.2 macOS和Linux上的替代方案
macOS从很早的版本开始就不再原生支持CHM了。如果你在Mac上双击一个CHM文件,系统可能会提示你选择打开方式,但找不到合适的程序。这时候你需要借助第三方工具。我试过几个,比较推荐的是CHM Reader和Clearview。前者是一个轻量级的阅读器,界面简洁,支持目录导航和全文搜索;后者功能更全一些,支持标注和书签,但界面稍微老旧一点。
安装方式也很简单,下载dmg文件拖到Applications文件夹就行。打开之后如果遇到乱码,大概率是编码问题。CHM内部的HTML页面可能用的是GBK或者GB2312编码,而阅读器默认用UTF-8去解析,就会显示成乱码。你可以在阅读器的偏好设置里手动指定编码为GBK试试。
Linux上的选择就更少了。有一个叫xCHM的工具,可以通过包管理器安装,比如在Ubuntu上执行sudo apt install xchm。它的功能比较基础,但应付日常阅读足够了。如果你不想装额外软件,还有一个办法是用7z命令把CHM解压出来。CHM本质上是一个压缩包,7z x filename.chm就能把里面的HTML文件全部提取到一个文件夹里,然后用浏览器直接打开index.html或者toc.html。这个方法虽然原始,但胜在通用,任何系统上只要有7z就能用。
2.3 手机端能不能看CHM
手机上原生支持CHM的应用非常少。Android平台有一个叫CHM Reader的App,界面比较简陋但能用。iOS上就更少了,我试过几个都不太理想,要么是广告太多,要么是打开大文件就崩溃。如果你真的需要在手机上看CHM,我的建议是先在电脑上把它转成PDF或者EPUB,然后再传到手机上看。转换方法后面会详细讲。
注意:不管用什么工具打开CHM,如果文件来源不明,最好先在虚拟机或者沙箱环境里打开,确认安全之后再在主力机器上操作。CHM内嵌脚本的能力是一把双刃剑。
3. 把CHM转换成其他格式的完整实操
3.1 为什么需要转换
CHM虽然方便,但它的局限性也很明显。首先是跨平台支持差,你发给一个用Mac的同事,他可能折腾半天也打不开。其次是编辑困难,CHM是编译后的格式,你想改里面一个错别字,不能直接编辑,得先反编译再重新编译。再次是打印和批注不方便,虽然CHM阅读器一般都有打印功能,但排版效果往往不尽如人意。最后是长期保存的考虑,CHM是专有格式,未来某一天微软彻底放弃支持也不是不可能,而PDF和EPUB是开放标准,保存年限更长。
所以把CHM转换成PDF、EPUB或者纯HTML,是一个很实际的需求。下面我分场景来讲具体的操作方法。
3.2 转换成PDF的两种实用方案
方案一:用虚拟打印机。这是最简单的方法,不需要安装任何额外软件。在Windows上打开CHM文件,然后按Ctrl+P调出打印对话框,在打印机列表里选择Microsoft Print to PDF,然后点击打印。系统会让你选择保存路径,确认之后就会生成一个PDF文件。这个方法的优点是操作极其简单,缺点是它只能打印当前页面,不能一次性把整个CHM的所有页面都打印出来。如果你要转换的CHM页面不多,比如就十几页,那这个方法完全够用。但如果是有几百页的大文件,一页一页打印会让人崩溃。
方案二:先反编译再合并。这个方法稍微复杂一点,但可以一次性处理整个CHM。首先用7z把CHM解压到一个文件夹里,你会看到一堆.html文件和资源文件夹。然后你需要一个工具把这些HTML按顺序合并成一个PDF。我常用的是wkhtmltopdf这个命令行工具,它可以把HTML渲染成PDF,而且支持目录和页码。基本用法是wkhtmltopdf --enable-local-file-access toc.html output.pdf,其中toc.html是CHM的目录页。不过实际操作中你会发现,CHM的HTML文件之间的链接关系比较复杂,直接合并可能会丢失导航结构。更稳妥的做法是先用一个脚本把HTML文件按目录顺序整理好,生成一个总目录页,再用wkhtmltopdf转换。
如果你不想碰命令行,还有一个图形化工具叫CHM to PDF Converter,界面很直观,选好输入文件和输出路径,点一下转换就行。不过这类工具大多是收费的,免费版会有页数限制或者水印。我个人的建议是,如果只是偶尔转一两个文件,用虚拟打印机就够了;如果经常需要处理CHM,花点时间学一下wkhtmltopdf的命令行用法会更高效。
3.3 转换成EPUB的流程
EPUB是电子书的标准格式,在手机和平板上阅读体验很好。把CHM转成EPUB,核心思路是先把CHM反编译成HTML,再把HTML重新打包成EPUB。这里推荐一个工具叫Calibre,它是一个功能非常强大的电子书管理软件,内置了格式转换功能。
具体操作步骤是这样的:先用7z把CHM解压到一个文件夹,然后在Calibre里点击“添加书籍”,选择解压出来的index.html或者toc.html。Calibre会把它识别为一本书,然后你点击“转换书籍”,在输出格式里选择EPUB,再点确定。Calibre会自动处理章节划分、目录生成、图片嵌入这些事情。转换完成之后,你可以用Calibre自带的阅读器预览效果,如果目录层级不对,可以在转换设置里手动调整“目录”选项。
这里有一个细节需要注意:CHM里的HTML文件可能引用了外部CSS和图片,解压之后这些资源的相对路径可能会变。Calibre在转换时一般能自动处理,但如果发现样式丢失,你需要检查一下HTML文件里的<link>和<img>标签的路径是否正确。有时候CHM里的路径用的是反斜杠\,而Calibre期望的是正斜杠/,手动替换一下就能解决。
3.4 直接提取为纯HTML网站
如果你想把CHM的内容放到网上或者内网服务器上供多人访问,那直接提取为HTML是最合适的。用7z解压之后,你会得到一个完整的静态网站结构。但直接打开index.html可能会发现左侧目录树不工作,因为CHM的目录树通常是用一个特殊的ActiveX控件或者JavaScript实现的,脱离CHM环境之后这些功能会失效。
解决办法是找一个替代的目录导航方案。最简单的是用iframe做一个左右分栏的页面,左边放一个自己生成的目录列表,右边用iframe加载内容页。你可以写一个简单的脚本,遍历所有HTML文件,提取它们的标题,生成一个嵌套的<ul>列表。这个脚本用Python写也就二三十行的事情。如果你不想写代码,也有一些现成的工具,比如CHM Decoder,它可以在解压的同时生成一个兼容现代浏览器的目录导航。
提示:提取出来的HTML文件里可能包含一些CHM特有的
<object>标签或者ms-its协议链接,这些在普通浏览器里是无法识别的。你可以用文本编辑器的批量替换功能,把这些标签清理掉,或者替换成普通的<a>链接。
4. 自己动手制作一个CHM文件
4.1 制作CHM的准备工作
制作CHM其实比很多人想象的要简单。你不需要什么昂贵的软件,只需要准备好HTML内容和一个编译工具。HTML内容可以是手写的,也可以是用工具从Markdown或者Word转换过来的。关键是要有一个清晰的目录结构,因为CHM的导航树就是根据这个结构生成的。
你需要准备的东西包括:一个文件夹,里面放所有的HTML文件、图片、CSS文件;一个.hhp项目文件,用来告诉编译器哪些文件要打包、首页是哪个、目录结构是怎样的;一个.hhc目录文件,定义左侧导航树的层级;还有一个.hhk索引文件,定义关键词索引。这三个文件都是纯文本格式,用记事本就能编辑。
编译工具方面,微软官方提供了一个叫HTML Help Workshop的免费工具,虽然界面老旧,但功能完整。下载安装之后,打开它,新建一个项目,按照向导一步步操作就行。如果你不想用这个老古董,也有一些第三方的命令行工具,比如chmcmd,它是Free Pascal项目的一部分,跨平台支持比较好。
4.2 编写HHP项目文件的关键参数
.hhp文件是整个项目的核心配置文件,它决定了编译出来的CHM长什么样。我拿一个实际用过的配置来举例说明:
[OPTIONS] Compatibility=1.1 or later Compiled file=myhelp.chm Contents file=myhelp.hhc Index file=myhelp.hhk Default topic=index.html Display compile progress=Yes Full-text search=Yes Language=0x804 中文(简体) Title=我的帮助文档 [FILES] index.html chapter1.html chapter2.html images/logo.png styles/main.css [INFOTYPES]这里有几个参数值得展开讲。Compiled file指定输出的CHM文件名。Default topic是打开CHM时默认显示的页面。Full-text search=Yes开启全文搜索功能,这个非常实用,建议一定要打开。Language指定语言代码,0x804代表简体中文,设置正确之后搜索和排序才会符合中文习惯。[FILES]段列出所有需要打包的文件,注意路径要用相对路径,而且分隔符用反斜杠。
一个常见的坑是文件路径里包含空格或者中文。虽然CHM规范理论上支持,但实际编译时经常出问题。我的经验是,所有文件名和路径都用纯英文小写字母加数字,不要有空格,用下划线或者短横线代替。这样能避免百分之九十的编译错误。
4.3 生成目录树和索引
.hhc文件定义了左侧的目录树结构。它的格式是HTML的<ul>和<li>嵌套,但用<object>标签来指定每个条目的名称和链接。比如:
<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN"> <HTML> <HEAD> <meta name="GENERATOR" content="Microsoft HTML Help Workshop"> </HEAD> <BODY> <UL> <LI><OBJECT type="text/sitemap"> <param name="Name" value="第一章 概述"> <param name="Local" value="chapter1.html"> </OBJECT> <UL> <LI><OBJECT type="text/sitemap"> <param name="Name" value="1.1 背景介绍"> <param name="Local" value="chapter1.html#section1"> </OBJECT> </UL> </UL> </BODY> </HTML>这个结构看起来有点啰嗦,但逻辑很清晰。每个<OBJECT>代表一个目录条目,Name是显示的文字,Local是链接的目标。嵌套的<UL>表示子层级。你可以手动写这个文件,也可以用工具自动生成。如果HTML文件很多,手动写会非常痛苦,建议写一个脚本从HTML的<h1>到<h3>标签里提取标题和锚点,自动生成.hhc文件。
.hhk索引文件的格式类似,但它是用来做关键词索引的。每个条目包含一个关键词和对应的链接。用户可以在搜索框里输入关键词,然后从索引列表里跳转到对应页面。这个功能对于技术文档特别有用,因为用户往往想直接查某个函数名或者参数名。
4.4 编译与调试
配置文件和内容都准备好之后,就可以编译了。如果用HTML Help Workshop,点击“编译”按钮就行。如果用命令行工具,执行hhc myhelp.hhp。编译过程中如果出错,工具会给出具体的错误信息,比如“文件未找到”“链接无效”之类的。根据提示逐个修复就行。
编译成功之后,双击生成的CHM文件,检查以下几个方面:目录树是否正常显示,点击每个条目是否能正确跳转,全文搜索是否能返回结果,图片和样式是否正常加载。如果发现某个页面显示空白,大概率是路径问题,检查.hhp文件里的[FILES]段是否包含了那个文件。
还有一个容易忽略的点是CHM的窗口样式。你可以在.hhp文件的[WINDOWS]段里定义窗口的大小、位置、是否显示导航面板等。比如:
[WINDOWS] main="我的帮助文档","myhelp.hhc","myhelp.hhk","index.html","index.html",,,,,0x23520,,0x384e,,,,,,,,0这一长串参数看起来吓人,但常用的就前面几个:窗口标题、目录文件、索引文件、默认页面、主页。后面的参数控制按钮显示和窗口样式,不设置也能用默认值。
注意:编译CHM时如果HTML文件里引用了外部网络资源(比如CDN上的CSS或者字体),编译出来的CHM在离线环境下会加载失败。所有资源都要本地化,这是制作CHM的铁律。
5. 常见问题与排查技巧实录
5.1 打开CHM时提示“导航已取消”或内容空白
这是最高频的问题,没有之一。根本原因是Windows对来自网络的文件加了安全锁定。解决办法就是右键属性解除锁定。但有时候你解除了锁定,换一台电脑又出现同样的问题。这是因为锁定信息是存在文件本身的备用数据流里的,拷贝到其他电脑时如果用了不支持备用数据流的文件系统(比如FAT32),锁定信息可能会丢失,但也可能被重新加上。
如果你经常需要分发CHM文件,可以在编译时就在.hhp文件里加上Binary Index=No,这样编译出来的CHM不会包含索引二进制数据,减少被安全软件误判的概率。另外,把CHM文件打包成ZIP再分发,用户解压之后一般就不会被锁定了。
5.2 目录树显示乱码
这个问题通常出现在非中文Windows系统上打开中文CHM时。原因是CHM内部的目录树数据用了特定的编码,而系统默认的语言设置不匹配。解决办法是在控制面板的“区域和语言选项”里,把“非Unicode程序的语言”设置为中文(简体)。这个设置需要重启才能生效。
如果你不想改系统设置,也可以用7z把CHM解压出来,找到.hhc文件,用记事本打开,另存为UTF-8编码,然后再重新编译成CHM。不过这个方法比较绕,适合对CHM结构比较熟悉的人。
5.3 全文搜索失效
CHM的全文搜索依赖于编译时生成的索引文件。如果编译时没有勾选Full-text search,或者索引文件损坏,搜索功能就会失效。检查.hhp文件里是否有Full-text search=Yes这一行。另外,如果CHM文件被部分损坏,搜索索引也可能读不出来。这时候只能重新编译或者找原始文件重新下载。
还有一个情况是搜索能返回结果,但点击结果跳转不到正确页面。这通常是因为HTML文件里的锚点名称和索引里记录的不一致。检查一下.hhk文件里的Local参数是否包含了正确的锚点名称。
5.4 制作CHM时编译报错“文件未找到”
这个错误的根源几乎总是路径问题。CHM编译器对路径的容忍度很低,不支持绝对路径,不支持..上级目录引用,不支持中文路径,不支持空格。所有文件必须放在.hhp文件所在目录或者其子目录下,路径用反斜杠分隔,文件名用纯英文。
我踩过的一个坑是:在.hhp的[FILES]段里写了images\logo.png,但实际文件夹名字是Images,大小写不一致。Windows文件系统不区分大小写,但CHM编译器区分。改成完全一致之后问题解决。所以建议所有文件夹和文件名统一用小写,避免这种低级错误。
5.5 CHM文件太大导致打开缓慢
CHM的LZX压缩率很高,但解压需要时间。如果一个CHM文件超过50MB,打开时可能会有明显的卡顿。优化方法有几个:一是压缩图片,把BMP转成JPG或者PNG,把PNG用工具再压缩一遍;二是合并CSS和JavaScript文件,减少HTTP请求数(虽然CHM是本地文件,但每个外部文件都要单独解压);三是拆分CHM,把一个大文件拆成几个小文件,比如按章节拆分。
还有一个技巧是在.hhp文件里设置Compress=Yes,这是默认开启的。如果你发现编译出来的CHM比预期大很多,检查一下是不是有些没用的文件也被打包进去了。[FILES]段里只列出真正需要的文件,不要用通配符把整个文件夹都包进去。
5.6 快速排查表
| 问题现象 | 最可能原因 | 解决方法 |
|---|---|---|
| 打开后内容区空白 | 文件被锁定 | 右键属性解除锁定 |
| 目录树乱码 | 系统语言不匹配 | 修改非Unicode程序语言设置 |
| 搜索无结果 | 未开启全文搜索 | 重新编译并勾选Full-text search |
| 编译报错文件未找到 | 路径大小写或分隔符错误 | 统一小写,用反斜杠 |
| 图片不显示 | 资源未打包或路径错误 | 检查[FILES]段是否包含图片 |
| 打开速度慢 | 文件过大 | 压缩图片,拆分文件 |
| 跳转链接失效 | 锚点名称不匹配 | 检查hhc/hhk文件中的Local参数 |
提示:遇到任何CHM相关的问题,第一步永远是用
7z把它解压出来看看内部结构。大部分问题在解压之后都能一目了然。
6. 一些进阶玩法和个人经验
6.1 用脚本批量处理CHM文件
如果你手头有几十个CHM文件需要转换或者提取内容,一个个手动操作太慢了。我写过一个Python脚本,用subprocess调用7z批量解压,然后用BeautifulSoup解析HTML提取正文,最后输出成Markdown或者纯文本。核心代码大概是这样:
import subprocess import os from bs4 import BeautifulSoup def extract_chm(chm_path, output_dir): subprocess.run(['7z', 'x', chm_path, f'-o{output_dir}', '-y']) for root, dirs, files in os.walk(output_dir): for f in files: if f.endswith('.html') or f.endswith('.htm'): filepath = os.path.join(root, f) with open(filepath, 'r', encoding='gbk', errors='ignore') as fh: soup = BeautifulSoup(fh.read(), 'html.parser') text = soup.get_text() # 保存为txt或者进一步处理这个脚本的关键点在于编码处理。CHM里的HTML文件编码五花八门,有GBK、GB2312、UTF-8,甚至还有ISO-8859-1。用errors='ignore'可以跳过无法解码的字符,避免脚本崩溃。如果你对提取质量要求高,可以先检测文件的meta charset标签,根据它来决定用哪种编码打开。
6.2 把CHM内容迁移到现代文档系统
现在很多团队用Confluence、Notion或者GitBook来管理文档。如果你想把旧的CHM内容迁移过去,直接复制粘贴往往格式全乱。我的做法是先用上面的脚本提取纯文本,保留标题层级(通过<h1>到<h6>标签判断),然后生成Markdown文件。Markdown的标题语法和CHM的目录树结构天然对应,迁移起来很顺畅。
图片的处理稍微麻烦一点。CHM里的图片解压出来之后是散落在各个文件夹里的,你需要把它们统一放到一个assets文件夹,然后把Markdown里的图片链接改成相对路径。如果图片很多,可以写个脚本自动重命名和移动。
6.3 制作CHM时的一些个人心得
做了这么多年的CHM,我最大的体会是:内容质量永远比格式重要。我见过太多CHM文件,界面花哨、目录复杂,但内容空洞、错误百出。反过来,有些CHM就是简单的HTML页面,目录树也很朴素,但内容扎实、条理清晰,读起来非常舒服。
另外,CHM的全文搜索功能是它最大的杀手锏。你在制作时一定要确保每个页面的<title>标签和<h1>标签写得准确,因为搜索结果的排序和这些标签的权重有关。关键词索引也要认真做,把用户可能搜索的同义词、缩写、错误拼写都加进去。比如一个函数叫getUserName,索引里除了这个词,还要加上get username、获取用户名、用户名获取这些变体。
最后,CHM虽然是个老格式,但它的设计理念——单文件、离线、全文检索——在今天依然有生命力。如果你在做内部知识库或者离线文档,不妨考虑用CHM的思路来组织内容,哪怕最终输出的是PDF或者静态网站,这种“编译后分发”的模式依然高效可靠。