在线CHM阅读器(1)——CHM文件格式概述

之前开源的WEBOS中有一个在线CHM阅读器,本文将介绍如何开发一个功能类似的在线CHM阅读器。

效果图

相关技术

1.Structured Storage

Structured Storage provides file and data persistence in COM by handling a single file as a structured collection of objects known as storages and streams.

The purpose of Structured Storage is to reduce the performance penalties and overhead associated with storing separate objects in a single file. Structured Storage provides a solution by defining how to handle a single file entity as a structured collection of two types of objects—storages and streams—through a standard implementation called Compound Files. This enables the user to interact with, and manage, a compound file as if it were a single file rather than a nested hierarchy of separate objects.

关于Structured Storage请查阅MSDN,CHM文件本质上就是一个结构化存储格式的文件,因此,如果要读取CHM中的文件,需要知道Structured Storage的相关API。

2.ISAPI筛选器

ISAPI(Internet Server Application Programming Interface)作为一种可用来替代CGI的方法,是由微软和Process软件公司联合提出的Web服务器上的API标准。ISAPI与Web服务器结合紧密,功能强大,能够获得大量的信息,因此利用ISAPI可以开发出灵活高效的Web服务器增强程序。实现CHM在线阅读器(在不反编译的情况下)需要使用ISAPI筛选器来实现URL重定向。

CHM文件格式

1.反编译CHM文件

要阅读CHM文件,首先就必须反编译CHM文件,提取出其中的文件(网页,图片等),反编译CHM文件需要用到WIN32 API的StgOpenStorage函数,.NET反编译CHM文件的方法可阅读这篇文章:

CHM Help File Extractor

2.#SYSTEM文件

反编译CHM文件后,您可以在解压出来的文件中看到这个名称为#SYSTEM的文件,这个文件保存了一些关于CHM文件的信息,例如起始页,标题等等。#SYSTEM是一个二进制文件,其格式也并不复杂,格式如下所示:

ID(2字节)+数据长度(2字节)+数据(字节数由数据长度决定)

#SYSTEM文件就是有多个以上这种数据构成,根据这个规律,可以读取出所有ID对应的数据,并保存到一个Hashtable中,代码如下:

private bool ReadSession(BinaryReader reader)
{
    if (reader.BaseStream.Position >= reader.BaseStream.Length) return false;

    UInt16 id = reader.ReadUInt16();
    UInt16 count = reader.ReadUInt16();
    if (count + reader.BaseStream.Position <= reader.BaseStream.Length)
    {
        if (count > 0)
        {
            _session[id] = reader.ReadBytes(count);
        }
        return true;
    }
    else
    {
        return false;
    }
}
public ChmInfo(Stream stream)
{
    BinaryReader reader = new BinaryReader(stream);

    while (ReadSession(reader)) ;
}

目前可以确定的ID和数据对应关系如下:

0x0002 - 起始页的路径

0x0003 - 标题

0x0004 - 语言

根据这个对应关系,就可以读取出CHM文件的标题,起始页等。

3.目录文件(*.hhc)

如果CHM带有目录的话,反编译CHM文件后,您可以在解压出来的文件中看到一个扩展名为HHC的文件,这个文件保存了CHM的目录结构。

上图是一个HHC文件的内容,大概的规律是,每一个<LI><OBJECT>…<OBJECT>对应着目录树中的一个节点,<OBJECT>…<OBJECT>中的参数记录着该节点的属性(对应的页面,名称等)。如果这个节点有子节点的话,那么<LI>后面会紧跟着一个<UL></UL>,<UL>里面所有的节点都是其子节点。

上文已简单的介绍了如何反编译CHM的文件格式以及关键文件的格式,在下一篇文章中,将介绍如何处理目录文件(*.hhc文件)以及如何利用ISAPI筛选器在没有反编译出CHM内部文件的情况下实现一个在线CHM阅读器。

尽管目前这个系列还没有完成,但是您可以先下载源代码预览一下这个在线CHM阅读器:

CHM在线阅读器源代码下载

  1. 顾客
    2010-11-25 09:38

    太感谢了,真是好东西。

发表评论