Сообщений 2    Оценка 0        Оценить  
Система Orphus

HHCOLREG

Регистрация HTMLHELP коллекций

Автор: Алексей Кирюшкин
The RSDN Group

Источник: RSDN Magazine #2
Опубликовано: 03.02.2003
Исправлено: 15.04.2009
Версия текста: 1.0
Зачем это надо?
Использование программы
Подробности реализации
Пример коллекции
Поиск hhcolreg.dat
Предварительная обработка файлов
Узнаём номер для новой коллекции
Формируем список файлов новой коллекции
Собственно регистрация
Сохранение внесённых изменений
Пост-обработка файлов
Разрегистрация
Заключение

Исходные тексты (VC7) и демо-коллекция 36кБ
HHCOLREG.exe - Исполняемый файл v 1.0.8.1 26кБ

Зачем это надо?

HTMLHELP коллекция (текстовый файл определенного формата с расширением .col) позволяет объединять для просмотра и поиска несколько справочных CHM-файлов, что бывает очень удобно при написании модульных приложений, когда с каждым модулем одновременно создается отдельный справочный файл. Однако чтобы данную коллекцию можно было использовать, её нужно зарегистрировать – внести записи о коллекции в регистрационный файл hhcolreg.dat.

Использование программы

HHCOLREG.exe – консольная win-программа, работающая с параметрами командной строки. При запуске без параметров, или с неправильным набором параметров выводится краткая справка:

Программа hhcolreg.exe предназначена для регистрации файлов 
HTMLHelp коллекций (*.col) на компьютере пользователя. Использование: hhcolreg.exe [-u] MyCollection.col [путь к hhcolreg.dat] -u ( или /u ) - разрегистрировать, если этого ключа нет,
то зарегистрировать MyCollection.col - файл новой коллекции Если путь к hhcolreg.dat не задан явно, программа пытается
найти его самостоятельно.
Предполагается также, что в узле TitleString задано имя
соответствующего chm файла.

Т.о. в командной строке должен быть, по крайней мере, один параметр - имя файла коллекции. Для разрегистрации коллекции нужно добавить ключ –u. Если есть необходимость, можно потренироваться на копии файла hhcolreg.dat, указав её местоположение в командной строке. Если программа не находит hhcolreg.dat, она его создаёт.

Перед внесением изменений в файл коллекции и hhcolreg.dat они сохраняются с добавлением расширения .orig.

ПРЕДУПРЕЖДЕНИЕ

.orig будут переписываться каждый раз при регистрации коллекции, т.ч. если вы зарегистрируете две коллекции подряд, копия исходного hhcolreg.dat будет утеряна окончательно.

CHM-файлы коллекции должны находится в одном каталоге с регистрируемым COL-файлом, их имена должны совпадать с параметрами, прописанными в TitleString-ах файла коллекции.

Работа программы проверялась в OC Windows 2000, Windows XP и Windows 98. Впрочем, для запуска программы под win98 придется установить MSXML3, а для просмотра коллекции обновить версию HTMLHELP.

Подробности реализации

Программа написана в среде VS7, главным образом из-за наличия в ATL7 класса CAtlRegExp. При компиляции использовались также Microsoft Platform SDK November 2001 и STLPort 4.5, если возникнут проблемы при компиляции, копайте в этом направлении. В части вывода сообщений об ошибках использован класс CErrCodeMsg от Игоря Вартанова.

Пример коллекции

Разбор полетов будет проводиться на примере регистрации следующей коллекции:

<XML>
    <HTMLHelpCollection>
        <masterchm value="=first"/>
        <masterlangid value=1033/>
        <samplelocation value=""/>
        <collectionnum value=10032/>
        <refcount value=0/>
        <version value=0/>
        <findmergedchms value=0/>
        <Folders>
            <Folder>
                <TitleString value="Раздел 1"/>
                <FolderOrder value=0/>
                <Folder>
                    <TitleString value="Часть 1"/>
                    <FolderOrder value=0/>
                    <Folder>
                        <TitleString value="=first"/>
                        <FolderOrder value=0/>
                        <LangId value=1033/>
                    </Folder>
                </Folder>
            </Folder>
            <Folder>
                <TitleString value="=second"/>
                <FolderOrder value=1/>
                <LangId value=1033/>
            </Folder>
        </Folders>
    </HTMLHelpCollection>
</XML>

first и second – это псевдонимы файлов, составляющих нашу коллекцию, по ним программа просмотра HTMLHELP получит из hhcolreg.dat полные пути и имена chm-файлов коллекции. Для упрощения реализации процесса регистрации принято, что эти псевдонимы должны совпадать с именами соответствующих chm-файлов, находящихся в одном каталоге с файлом коллекции, хотя в общем случае это не обязательно. “Раздел 1” и “Часть 1” – просто названия (на что указывает отсутствие знака ‘=’ ) дополнительных папочек, внутрь которой будет помещено содержимое first.chm.

На этом примере видно, что файл коллекции, как собственно и hhcolreg.dat представляет собой своеобразный “недоXML”, т.к. здесь в отличии от “честного” XML числовые значения атрибутов не заключены в кавычки и отсутствует ProcessingInstruction,

<?xml version="1.0" encoding="Windows-1251"?>

указывающая, что русские буквы - в кодировке Windows-1251. Возможны два варианта работы с такими файлами:

  1. Написать свой парсер для извлечения, записи и удаления данных
  2. Использовать какой-либо готовый XML-парсер, предварительно обработав файлы коллекции и hhcolreg.dat – расставив недостающие кавычки и добавив на время обработки ProcessingInstruction. Причем кавычки по окончании регистрации можно и не убирать, т.к. HTMLHELP продолжает работать и при наличии кавычек, а вот от ProcessingInstruction по окончании процесса регистрации придется избавиться. В hhcolreg.exe реализован именно этот путь с использованием MSXML3.

Поиск hhcolreg.dat

Если расположение файла hhcolreg.dat не задано в командной строке, программа пытается найти его самостоятельно. В зависимости от версии Windows возможны два варианта расположения hhcolreg.dat в системе: для семейства NT это каталог %COMMON_APPDATA%\Microsoft\Html Help\ (например C:\Documents and Settings\All Users\Application Data\Microsoft\HTML Help), а для windows 98 – каталог %WINDOWS%\Help (например c:\windows\help).

ПРИМЕЧАНИЕ

В системах семейства NT может одновременно использоваться два файла hhcolreg.dat – один в %COMMON_APPDATA%\Microsoft\Html Help\, а второй в %WINDOWS%\Help\. При попытке просмотреть коллекцию первым проверяется файл %COMMON_APPDATA%\Microsoft\Html Help\hhcolreg.dat, если коллекция с искомым номером будет там найдена, то на этом все и прекращается, если нет, то проверяется файл %WINDOWS%\Help\hhcolreg.dat. Таким образом, если немножко постараться и зарегистрировать часть файлов коллекции в одном файле, а часть – в другом (у меня такое получалось при добавлении своих разделов к MSDN от VS6), то при просмотре коллекции будут отображены только файлы, зарегистрированные в %COMMON_APPDATA%\Microsoft\Html Help\hhcolreg.dat.

Поиск hhcolreg.dat

Если файл hhcolreg.dat не найден там, где он должен находиться (актуально для win98 и свежеустановленной winXP) программа его создаёт:

CreateNewHhcolregDat

Предварительная обработка файлов

Перед началом работы программой сохраняются оригиналы файлов коллекции и hhcolreg.dat в файлах с добавлением расширения .orig, а также создаются временные файлы коллекции и hhcolreg.dat с добавлением расширения .xml. Если в процессе работы программы все действия по регистрации коллекции проходят успешно, эти файлы копируются на место первоначальных файлов коллекции и hhcolreg.dat, а временные файлы удаляются.

Теперь нужно подготовить файл коллекции и hhcolreg.dat для загрузки в XML-парсер. Для этого создается временный файл и в его начало записывается строка ProcessingInstruction. Содержимое исходного файла построчно обрабатывается с помощью регулярного выражения, обеспечивающего замену записей вида 'value=1' на 'value="1"' и также записывается во временный файл. Если обработка успешно завершается, оба файла, исходный и временный закрываются, а затем временный файл копируется на место исходного. Все действия по предобработке файлов вынесены в функцию RepairFileAsXML():

RepairFileAsXML

Далее с нашими файлами можно работать, используя MSXML. Предварительно, для получения текстового сообщения по коду ошибки MSXML необходимо загрузить модуль, содержащий текстовые описания для кодов ошибок, возвращаемых MSXML:

        hmMSXML3R = ::LoadLibraryEx( _T( "msxml3r.dll" ), 
                                     NULL, 
                                     LOAD_LIBRARY_AS_DATAFILE);

Загрузка файлов и получение указателя на интерфейс IXMLDOMDocument2 для каждого файла происходят в LoadNewDocument().

Узнаём номер для новой коллекции

В файле hhcolreg.dat, в узле XML/HTMLHelpDocInfo/NextCollectionId хранится номер для следующей регистрируемой коллекции. Наша задача – узнать его, вписать в файл регистрируемой коллекции, нарастить на 1 и сохранить увеличенный номер в hhcolreg.dat. Функция GetNextCollectionNumber() получает указатель на IXMLDOMDocument2 для hhcolreg.dat и возвращает номер новой коллекции в CComVariant:

GetNextCollectionNumber

Теперь вписываем этот номер в файл новой коллекции, он должен хранится в узле XML/HTMLHelpCollection/collectionnum:

SetNewCollectionNumber

Формируем список файлов новой коллекции

Для формирования списка chm-файлов, входящих в коллекцию нужно обойти и проанализировать значение атрибута value всех узлов TitleString. Если это значение начинается со знака '=' в соответствии с соглашением, описанным в разделе "Пример коллекции" считаем его именем (без пути и расширения) одного из файлов коллекции и сохраняем в своем списке. Если знака '=' в первой позиции нет – это просто название одного из разделов коллекции.

На всякий случай повторю еще раз – то, что для данной программы-регистратора необходимо строгое соответствие псевдонима файла из TitleString коллекции его реальному имени – это не более чем частный случай, упрощающий нам реализацию процесса регистрации, т.к. иначе пришлось бы вводить и анализировать дополнительный файл с соответствиями псевдонимов файлов и их реальных имен. Прежде чем вы попытаетесь зарегистрировать какую-либо коллекцию, созданную не вами, убедитесь, что ее автор придерживался той же логики.

GetDocumentList

Собственно регистрация

Очистка от предыдущих регистраций данной коллекции

Наличие нескольких регистраций одной и той же коллекции под разными номерами и из разных каталогов может быть одной из причин ошибки при попытке открыть коллекцию для просмотра (HTMLHELP сможет открыть для просмотра только первый из нескольких зарегистрированных экземпляров коллекции), поэтому нужно найти все предыдущие регистрации данной коллекции и удалить их.

Правда в этом случае HTMLHELP сможет открыть для просмотра только последний зарегистрированный экземпляр коллекции, но это всё же больше соответствует логике пользователя, который переписывает коллекцию на новое место, регистрирует её и пытается просмотреть.

Для очистки hhcolreg.dat от предыдущих регистраций вызывается функция разрегистрации, UnregisterCollection, принимающая в качестве параметра имя файла коллекции. Если имя файла (+ расширение) разрегистрируемой коллекции встречается в полном имени какой-либо из уже зарегистрированных в hhcolreg.dat коллекций, данные об этой коллекции и её документах удаляются из hhcolreg.dat. Сравнение имен файлов нужно проводить без учета регистра, т.к. предыдущие регистрации могли быть произведены другой программой, с приведением всех символов в имени файла к верхнему или нижнему регистру.

Итак, по очереди обходим в hhcolreg.dat все узлы XML/HTMLHelpDocInfo/Collections/Collection, в которых атрибут value дочернего узла ColName содержит имя файла разрегистрируемой коллекции. Для каждой найденной таким образом коллекции получаем ее номер из атрибута value соседнего с ColName узла ColNum. По каждому полученному номеру выбираем документы, входящие в коллекцию (для каждого документа номер его коллекции находится в атрибуте value узла XML/HTMLHelpDocInfo/DocCompilations/DocCompilation/LocationHistory/ColNum) и удаляем секцию XML/HTMLHelpDocInfo/DocCompilations/DocCompilation содержащую данные этого документа. На последнем этапе удаляются секции XML/HTMLHelpDocInfo/Collections/Collection всех попавших под нашу выборку коллекций.

UnregisterCollection

Регистрация файлов коллекции

Во-первых, нужно занести в hhcolreg.dat информацию о каждом файле коллекции. Для приведенного выше примера коллекции нужно добавить примерно следующее:

            <DocCompilation>
                <DocCompId value="first"/>
                <DocCompLanguage value="1033"/>
                <LocationHistory>
                    <ColNum value="10032"/>
                    <TitleLocation value="E:\PROJECTS\VC7\hhcolreg\test\first.chm"/>
                    <IndexLocation value="E:\PROJECTS\VC7\hhcolreg\test\first.chm"/>
                    <QueryLocation value=""/>
                    <LocationRef value=""/>
                    <Version value="1"/>
                    <LastPromptedVersion value="0"/>
                    <TitleSampleLocation value=""/>
                    <TitleQueryLocation value=""/>
                    <SupportsMerge value="0"/>
                </LocationHistory>
            </DocCompilation>
            <DocCompilation>
                <DocCompId value="second"/>
                <DocCompLanguage value="1033"/>
                <LocationHistory>
                    <ColNum value="10032"/>
                    <TitleLocation value="E:\PROJECTS\VC7\hhcolreg\test\second.chm"/>
                    <IndexLocation value="E:\PROJECTS\VC7\hhcolreg\test\second.chm"/>
                    <QueryLocation value=""/>
                    <LocationRef value=""/>
                    <Version value="1"/>
                    <LastPromptedVersion value="0"/>
                    <TitleSampleLocation value=""/>
                    <TitleQueryLocation value=""/>
                    <SupportsMerge value="0"/>
                </LocationHistory>
            </DocCompilation>

Это происходит в функции RegisterDocuments(). Для получения полного пути к файлу каждого документа к имени, полученному при чтении списка документов добавляется расширение chm и путь. совпадающий с путем к файлу коллекции. Его можно получить либо из полного имени файла коллекции, переданного в командной строке, либо, если передано только имя – как путь к текущему каталогу.

RegisterDocuments
ПРИМЕЧАНИЕ

В данном случае предполагается, что информация об индексах находится в том же chm файле, а не в отдельном chi.

Регистрация файла коллекции

И, во-вторых, занести информацию о файле коллекции:

AddNewCollectionNode

в результате добавляется секция вида:

            <Collection>
                <ColNum value="10032"/>
                <ColName value="E:\PROJECTS\VC7\hhcolreg\test\sample.col"/>
            </Collection>

Сохранение внесённых изменений

Пока все изменения проводились только в памяти, в XML-документах, представляющих наши файлы. Теперь нужно сохранить внесенные изменения на диске. Операция предельно простая, но иррациональное желание видеть при просмотре файла правильное XML-деревце, а не одну строку длиной в 300000 символов заставляет сделать пару финтов. Весь XML-документ выгружается в BSTR-строку, строка обрабатывается – между тэгами вставляются знаки перевода строки, после чего снова загружается в XML-документ. Поскольку в процессе этих преобразований неизбежно теряется ProcessingInstruction с данными о кодировке, восстанавливаем ее, после чего со спокойной душой и чистой совестью вызываем save() и сохраняем документ в файле. Мораль: красота – страшная сила :)

SaveDocument

Пост-обработка файлов

Осталось только вычистить строку ProcessingInstruction из начала файлов. Для этой операции удобно использовать класс CFileMap для работы с проецируемыми в память файлами от Виталия Брусенцева. Проецируем обрабатываемый файл в память, сдвигаем то что следует сразу за ProcessingInstruction в начало файла, устанавливаем новый конец и сохраняем изменения.

PostFix

Уфф-ф, может зря я не начал писать свой парсер? Ладно, если уж мы благополучно добрались до этого момента, осталось только сохранить временные файлы под их настоящими именами и почистить за собой.

Зарегистрированную коллекцию можно открыть для просмотра двойным щелчком, либо из командной строки: hh.exe sample.col:


.

Разрегистрация

При разрегистрации коллекции нужно удалить из hhcolreg.dat все относящиеся к ней записи. Для этого просто вызывается уже известная вам UnregisterCollection.

Заключение

Утилита с таким же назначением входит в состав инсталлятора GhostInstaller, однако, на момент написания данной программы она не вполне корректно регистрировала некоторые виды файлов коллекций, что собственно и стало причиной появления данного опуса.


Эта статья опубликована в журнале RSDN Magazine #2. Информацию о журнале можно найти здесь
    Сообщений 2    Оценка 0        Оценить