Об утилите InCtrl, регистрирующей изменения в процессе инсталляции
PC Magazine/RE logo
(С) СК Пресс 1S/96
PC Magazine, September 26, 1995, p. 269

Инсталляция с возможностью восстановления

Нил Дж. Рубенкинг


При инсталляции программ Windows создается множество новых файлов и меняется настройка системы: все это должно быть зарегистрировано.

Программа ICTRL2 отслеживает процесс инсталляции и создает файл отчета обо всех изменениях, происходящих в системе. Благодаря этому в дальнейшем можно вернуться к исходному состоянию. InCtrl2 - это значительно улучшенная версия исходной утилиты InCtrl, опубликованной в PC Magazine.

Когда-то для инсталляции программы было достаточно переписать на жесткий диск ее единственный EXE-файл. Теперь все стало гораздо сложнее. В состав современных прикладных пакетов для Windows входят специальные программы инсталляции, которые самостоятельно создают каталоги, переписывают нужные файлы, изменяют содержимое INI-файлов и всю новую информацию сохраняют на жестком диске, как правило в нескольких местах. Если необходимо удалить такую программу из системы, то встает вопрос - какие именно файлы удалять, и выяснить это очень трудно. Программа InCtrl2 позволяет смягчить остроту подобного рода проблем. Она создает файл отсчета, в котором регистрирует все изменения, произведенные в процессе инсталляции.

Первая версия InCtrl была создана в 1993 г. и даже, несмотря на некоторые ограничения, неоднократно входила в список десятки лучших утилит журнала PC Magazine. В версии InCtrl2 эти недостатки устранены, а также создан совершенно новый интерфейс; новая версия подготовлена средствами пакета Delphi фирмы Borland. Исходная верси InCtrl могла отслеживать изменения в файлах, число которых не превышало 16 384; в версии InCtrl2 вс отслеживаемая информация записывается на жесткий диск, поэтому единственное ограничение - объем его свободного пространства. Кроме того, здесь предлагаетс использовать вместо сравнительно медленной процедуры просмотра файлов на жестком диске альтернативное средство - механизм быстрого извещения об изменении файлов. В первоначальном варианте программы InCtrl ее работа прекращалась, если при инсталляции производилс перезапуск Windows, версия InCtrl2 допускает неполную инициализацию компьютера. Среди прочих новшеств можно упомянуть о возможности задавать параметры командной строки для программы инсталляции, а также базовые каталоги, по которым будет отслеживаться содержимое жесткого диска.

Исполнимый модуль InCtrl2 и исходный текст этой программы можно получить через службы CompuServe или Internet (ftp.сервер ftp.pcmag.ziff.com/pcmag/utilities/v14_1995/v14n16_inctrl2), а также по почте.

Применение InCtrl2

Как и стоило ожидать от программы, умеющей отслеживать сложные процедуры инсталляций, установка самой InCtrl2 выполняется очень просто: достаточно переписать в отдельный каталог на жестком диске файлы INCTRL2.EXE, INCTRL2.HLP и FILECDRL.DLL. Их следует разместить в отдельном каталоге, так как по умолчанию именно в нем будут сохраняться файлы отчетов. Создайте пиктограмму для программы InCtrl2 в любой группе Program Manager (Диспетчера Программ). Теперь все подготовлено, и можно приступить к отслеживанию инсталляций. Если придется удалить InCtrl2 из системы, то нужно будет удалить три исходных файла, а также INCTRL2.INI, который формируется в том же каталоге, где находится INCTRL2.EXE. Также можно удалить любой созданный файл RPT.

Поскольку пользоваться программой InCtrl2 придетс лишь от случая к случаю, ее интерфейс выполнен в привычном стиле многостраничных "wizards" ("Мастеров") и "experts" ("Экспертов"). Это освобождает от необходимости вспоминать при очередном обращении к этой программе, как с ней работать. На каждой странице задается только один вопрос, и переход к следующей выполняется лишь после получения надлежащего ответа. На любой странице в случае затруднений можно обратиться к справке, если нажать соответствующую кнопку или клавишу F1.

На первой странице в InCtrl2 требуется задать им программы инсталляции. Если данная операци производится с гибких дисков, то обычно указываетс следующее: A:SETUP.EXE или B:INSTALL.EXE. Если инсталляция производится с накопителя CD-ROM, то им программы, вероятно, останется SETUP или INSTALL, а им диска должно быть соответственно изменено. Нужное им можно набрать прямо с клавиатуры или нажать кнопку Browse (Пролистать) и выбрать нужный файл из стандартного диалогового окна Windows File Open (Открыть файл). После этого можно ввести все необходимые переключатели командной строки.

Если выделенную под имя строку оставить незаполненной, то программа InCtrl2 будет выполняться в два этапа. Сначала производится запись всей информации, необходимой для выявления последующих изменений, после чего выполнение программы прекращается. Вы производите инсталляцию и вновь запускаете программу InCtrl2. На этой второй стадии формируется отчет обо всех изменениях.

Если вы набрали полное имя действительно существующего файла (с учетом трехсимвольного расширения), то под строкой с именем появляетс наименование типа данного файла - например, "Windows executable" (исполнимый модуль Windows) или "DOS batch file" (командный файл DOS). Кнопка Next (Вперед...) активизируется только в том случае, если рассматриваемое поле оставить незаполненным или указать в нем имя исполнимого файла, удовлетворяющее принятым правилам.

Следующая страница посвящена вводу строки описания, включаемой в текст отчета. Помните, что, прежде чем созданный файл отчета будет прочитан вновь, может пройти несколько месяцев. Поэтому не поленитесь записать в него, скажем, "Полная инсталляция пакета Microsoft Word for Windows 7.0". Кнопка Next на этой странице доступна всегда.

Теперь пришло время указать имя файла для выходного отчета. По умолчанию предлагается следующее: расширение RPT, размещение в одном каталоге с модулем InCtrl2 и имя, представляющее собой первое из не использованных ранее четырехзначных чисел (например, C:\PCMAG\INCTRL2\0003.RPT). Можно принять предлагаемое имя и сразу перейти к следующей странице либо нажать кнопку Select, чтобы заменить имя или каталог. Кнопка Next будет доступна при условии, что в качестве выходного устройства для файла отчета не указан накопитель на гибких дисках или CD-ROM.

Следующая страница посвящена механизму быстрого извещения об изменении файлов. Это средство предназначено для выявления изменений в файловой системе. Вероятно, вам уже приходилось замечать сверхъестественные возможности модуля File Manager (Диспетчера Файлов). При работе системы Windows в режиме Enhanced (Расширенный) он умеет моментально узнавать о выполняемых другими прикладными программами операциях, связанных с удалением или созданием файлов. Достигается это за счет обращения File Manager к одной недокументированной функции Windows, благодаря которой этот модуль "узнает" о важных событиях, происходящих с файлами. Используемый в программе InCtrl2 механизм быстрого извещения об изменении файлов берет управление этой недокументированной функцией на себя. Однако учтите, что при работе InCtrl2 в режиме быстрого оповещения эти сообщения не будут доходить до программы File Manager; поэтому, чтобы обновить экран, придется, находясь в File Manager, нажимать клавишу F5. Правда, обычно в половине инсталляций прикладных программ File Manager не загружается.

На странице, относящейся к использованию механизма быстрого извещения об изменении файлов, имеется флажок, указывающий на его подключение. Обычно по умолчанию он отмечен. Если он недоступен, можно нажать кнопку Explain... (Объяснить...) и узнать почему. Существует ряд причин, из-за которых программа InCtrl2 может запретить обращение к этому средству:

Совместимость программы InCtrl2 с OS/2 не была апробирована, однако все должно протекать нормально, если, конечно, вы не напоретесь на один из "вывихов" эмуляции Windows в OS/2.

При использовании Windows NT флажок механизма быстрого извещения снимается, поскольку это средство регистрирует изменения в файловой системе только дл дисков с DOS-совместимой таблицей FAT. Если изменени происходят в файловой системе NTFS (NT File System) и на сетевых дисках под управлением NT, то извещения о них не поступают. Однако вполне возможно работать с Windows NT без обращения к NTFS или сетевой среде; в этом случае в программе InCtrl2 разрешаетс принудительно подключить механизм быстрого извещения. Нужно нажать кнопку Explain..., чтобы узнать, как это сделать.

Если же флажок снят, потому что в текущий момент механизм быстрого извещения используется в другой программе, либо из-за того, что в файле SYSTEM.INI отсутствует нужная установка, то еще не все потеряно. При выполнении инсталляции особой необходимости держать в активном состоянии программу File Manager нет; поэтому если она выполняется, следует завершить ее и запустить InCtrl2 заново. Если причина кроется в файле SYSTEM.INI, то следует в разделе [386Enh] задать установку FileSysChange=ON. Если данный параметр отсутствует, его нужно добавить. Теперь придетс перезапустить Windows и загрузить InCtrl2 вновь. (Установка FileSysChange=ON иногда приводит к замедлению операций взаимодействия с жестким диском в сеансах DOS. Поэтому в других ситуациях, за исключением инсталляций в режиме DOS, возможно, данному параметру вы захотите присвоить значение OFF.)

Когда механизм быстрого извещения недоступен или вы решите не использовать его (например, из нежелани обращаться к любым недокументированным средствам Windows), все изменения, происходящие в файловой системе, будут отслеживаться в программе InCtrl2 путем сравнения содержимого логических дисков до инсталляции и после ее окончания. Это - медленное, но надежное решение, позволяющее регистрировать все изменения, происходящие в файлах и каталогах. В исходной версии InCtrl был использован именно данный способ. Обращений к недокументированным функциям Windows здесь не происходит; более того, нет даже необходимости держать программу InCtrl2 активной в ходе самой инсталляции. При отключенном режиме быстрого извещения InCtrl2 создает в собственном каталоге файлы INCTRL$$.$$1 и INCTRL$$.$$2 - раскладка дисков до и после инсталляции. Учтите, что для сохранения этих файлов необходимо предусмотреть достаточный объем свободного пространства на жестком диске. На каждый рассматриваемый файл или каталог требуется по 18 байт. На первый взгляд - немного, однако при объеме диска свыше 500 Мбайт на нем легко может оказаться несколько десятков тысяч файлов. Учтите, что файлы с раскладкой дисков не предназначены для ручной обработки. Их нельзя изменять при работе InCtrl2, иначе можно внести ошибки в итоговый отчет и, возможно, привести программу InCtrl2 к краху.

Если флажок механизма быстрого извещения отмечен, то после нажатия кнопки Next (Вперед...) появляетс завершающая страница. Если же флажок недоступен или снят, то в этом случае вы попадаете на страницу, где можно указать конкретные диски и каталоги дл отслеживания. Однако на сохранение информации о раскладке диска тратится много времени, поэтому стоит обрабатывать только те диски, которые могут оказатьс затронутыми при инсталляции. Просмотр конкретного диска можно также сузить еще более, если указать базовый каталог, начиная с уровня которого будут братьс файлы - например, C:\WINDOWS. Если инсталляци производится в системе с сетевым доступом, то данна проблема приобретает особую важность. Необходимо ограничить просмотр только теми дисками, которые реально могут оказаться затронутыми при инсталляции. И конечно же, следует отслеживать только те сетевые диски, для которых вам разрешены как чтение, так и запись. Хотя программа InCtrl2 была успешно апробирована в работе сети с использованием обоих вариантов слежения за изменением файлов, ее совместимость для произвольной конфигурации сети еще, возможно, требует доказательств.

Если последовательно нажимать кнопку, соответствующую выбранному диску, то находящаяся рядом с ней надпись начинает меняться: с NO на YES, с YES на DIR> и с DIR> на NO. При появлении надписи DIR> открывается список каталогов текущего диска, в котором можно выбрать базовый. Сначала следует указать на него, дважды щелкнув на названии этого каталога, а затем щелкнуть на кнопке OK. При работе с клавиатуры нужно добраться до кнопки нужного диска, нажимая <TAB>, а затем нажать <SPACE> для ее активизации. Для выбора с помощью клавиатуры базового каталога следует найти его, используя клавиши управления курсором и нажимая <ENTER> для раскрытия или для выбора выделенного цветом элемента. Выбрав нужный каталог, следует перейти с помощью <TAB> на кнопку OK и нажать <SPACE>. Кнопки, относящиеся к устройствам CD-ROM вашей системы, недоступны, поскольку запись на них не производится. По умолчанию для съемных (обычно гибких) и сетевых дисков устанавливается значение NO; для жестких дисков - YES.

Покажем сказанное на простом примере. Допустим, в системе имеются логические диски C:, D: и E: дл жесткого диска и F: для устройства CD-ROM. Основной каталог Windows - C:\WINDOWS, а инсталляция будет производиться на диск E:. Укажите для диска D: значение NO, а для диска E: оставьте YES. Нажмите кнопку диска C:, укажите C:\WINDOWS и щелкните OK. Это все, что нужно сделать!

Если выделить для сканирования хотя бы один диск, то находящаяся на этой странице кнопка Next становитс доступной. Нажав ее, вы попадаете на заключительную страницу, где сообщается обо всех выбранных установках. Тщательно проверьте их и в случае обнаружения ошибок нажмите кнопку Prev (Назад...) для возврата назад и внесения исправлений. На последней странице кнопка Next заменена на DO IT! Если ее нажать, то начинаетс процесс инсталляции и окно программы InCtrl2 сжимаетс до размеров маленького окошка, всегда располагаемого поверх других. Действие, выполняемое программой InCtrl2 в текущий момент, отражается в его заголовке - например, чтение файлов на диске C: или копирование SYSTEM.INI. Если нажать на маленькую кнопку со знаком вопроса, можно получить пояснения о текущей операции InCtrl2.

Сначала программа InCtrl2 копирует файлы WIN.INI и SYSTEM.INI и при необходимости сохраняет раскладку диска. Дальнейшее выполнение идет по одному из двух вариантов. Если имя программы инсталляции было указано ранее, то загружается данная программа. Следует выполнить обычную инсталляцию, а после ее завершени нажать кнопку Install Done: Report (Инсталляци завершена: отчет), присутствующую в окне InCtrl2. Если имя программы инсталляции не было указано на самой первой странице, то InCtrl2 выполняется в два этапа, а сейчас появляется уведомление об этом. Следует завершить выполнение InCtrl2, произвести инсталляцию, а затем загрузить InCtrl2 вновь. Она появится на экране в виде маленького окошка, абсолютно идентичного тому, какое возникает, когда программа инсталляции производит инициализацию системы. Нажмите кнопку Install Done: Report.

Первоначально на средней кнопке в сжатом окне InCtrl2 присутствует надпись "Abort: No Install" (Прекратить: без инсталляции). Если ее нажать, InCtrl2 очистит свои временные файлы и завершит работу, не вызывая программу инсталляции. Если же все приготовления завершены и инсталляция запущена, то вернуться назад уже нельзя. Поэтому надпись на этой кнопке меняется на: "Abort: No Report" (Прекратить: без выдачи отчета). Если теперь ее нажать, то выполнение InCtrl2 прекратится без выдачи отчета об инсталляции; завершить работу самой программы инсталляции придетс уже оператору.

Когда инсталляция завершена и вы нажали кнопку "Install Done: Report", программа InCtrl2 начинает подготовку отчета об инсталляции. Прежде всего сравнивается содержимое системных файлов INI с их сохраненными копиями и формируется отчет обо всех изменениях. Если изменения файлов регистрировались через механизм быстрого извещения, то вся полученна информация преобразуется в понятную форму и записывается в конец отчета. Если же использовалс метод сравнения диска по его содержимому, то повторно создается файл раскладки, который затем сравнивается с первоначальным вариантом. Все изменения заносятся в отчет. Когда все готово, отчет выводится на экран в окне просмотра. Изучив его текст, можете завершить работу программы; все сделано!

Иногда при выполнении отдельных программ инсталляции добавляются новые драйверы или изменяются установки в INI-файлах. Для того чтобы новые установки вступили в силу, требуется перезапустить Windows. Если это происходит, InCtrl2 просто добавляет вызов самой себя, внося его в строку RUN= в разделе [windows] файла WIN.INI, а также записывает надлежащую информацию о выполняемой инсталляции в собственный INI-файл. Когда Windows стартует повторно, запускается и InCtrl2.

Программа InCtrl2 была апробирована на совместимость с Windows 3.1, Windows 3.11, Windows for Workgroups 3.11, последней бета-версией Windows 95 и Windows NT 3.5. Она не должна вызывать конфликтов при работе в сети, совместимой с Windows. Следует отметить, что InCtrl2 является 16-разрядной программой, поэтому отслеживание работы 32-разрядной программы инсталляции может оказаться для нее непосильной задачей. Кроме того, она не умеет отслеживать изменения длинных имен файлов, которые используются в Windows 95 и Windows NT; об этих файлах будет сообщаться в отчете по их DOS-эквивалентам. Что же касается Windows 95, то пока это - "темная лошадка", поскольку на момент написания статьи коммерческа версия еще не была выпущена. Представляетс маловероятным, но возможно, что ее окончательный вариант будет несовместим с исходной версией InCtrl2; тогда в эту программу придется внести поправки.

Деинсталляция программ

Программа InCtrl2 не предназначена дл автоматического удаления инсталлированных программ. Вместо этого она предоставляет информацию, необходимую для самостоятельного, "осознанного" выполнения этой операции. В создаваемом программой InCtrl2 отчете содержатся: список всех файлов и каталогов, добавленных при инсталляции, и список всех новых разделов и установок в основных INI-файлах. Аналогичные списки представлены по удаленным или измененным файлам, а также по измененным параметрам из INI-файлов.

Однако проведение деинсталляции не означает автоматического стирания каждого добавленного при инсталляции файла. Возможно, в дальнейшем проводились инсталляции других программ, которые теперь могут обращаться к одним и тем же модулям. Допустим, например, что программа Foo добавила в каталог SYSTEM системы Windows файл SNAFU.DLL. Затем была инсталлирована программа Bar, которая обращается к тому же модулю DLL. Однако при инсталляции никаких изменений не произошло, поскольку модуль SNAFU.DLL уже существует. Если теперь при удалении программы Foo стереть этот модуль DLL, то программа Bar не сможет работать.

Как правило, безошибочному удалению подлежат все файлы, записанные в созданный программой инсталляции каталог. Однако с файлами, добавленными в каталоги общего пользования - например, WINDOWS или WINDOWS\SYSTEM, - следует поступать с особой осторожностью. Не удаляйте их сразу; лучше переименуйте их или переместите в другой каталог, не указанный в переменной PATH. После этого следует проверить работоспособность всех оставшихся прикладных пакетов. Или с помощью какой-либо программы, например утилиты FDDLLS (PC Magazine, December 6, 1994), постаратьс убедиться, что в других программах отсутствуют обращения к указанным файлам из общих каталогов.

Подобные рассуждения относятся также к файлам INI. Например, если программа инсталляции добавила в WIN.INI целый раздел, то, вероятно, после его удаления проблем не будет. Если же новые строки появились в ранее существовавших разделах, то, как поступать дальше, пока не ясно. Лучше вместо удаления новых строк превратить их в комментарии, поставив в начале строки точку с запятой. Если теперь в результате отсутствия строки появятся проблемы, ее можно восстановить, просто удалив знак комментария.

Если же программа инсталляции поменяла значение некоторого параметра в файле INI, не стоит слепо восстанавливать старое значение. Скопируйте данную строку INI-файла, сделайте из копии строки комментарий, а затем в исходной строке восстановите прежнее значение. Вполне возможно, что программа Foo изменила данное значение, а программа Bar учитывает уже новое.


Лист. 1. Создаваемый программой InCtrl2 отчет содержит максимум возможной информации об инсталляции. Он позволяет осознанно осуществлять деинсталляцию программ. Правда, при удалении добавленных файлов и новых строк из INI-файлов следует соблюдать особую осторожность.
Installation report: Example report (generated by INCTRL 2, version 1.02) Wednesday, January 24 02:40 PM Windows for Workgroups 3.11 Notification by fast file notification FILES AND DIRECTORIES ADDED: (3) C:\PCMAG\INCTRL2\NEWDIR C:\PCMAG\INCTRL2\NEWDIR\FOO.DAT C:\PCMAG\INCTRL2\NEWDIR\BAR. DAT FILES AND DIRECTORIES DELETED: (1) C:\PCMAG\INCTRL2\ ????????.~?? FILES AND DIRECTORIES RENAMED: (1) C:\1\HAMMILL\PH.TXT TO OVER.TXT NO CHANGES MADE TO SYSTEM.INI... CHANGE MADE TO WIN.INI... KEYS DELETED FROM WIN.INI: (2) [Extensions] trm=terminal.exe ^.trm [Extensions] rec=recorder.exe ^.rec KEYS CHANGED IN WIN.INI: (1) [windows]DoubleClickSpeed=330 to 452

Построение программы InCtrl2

InCtrl2 является не просто новой версией программы InCtrl, которая была составлена средствами компилятора Borland Pascal и библиотеки OWL. Для нее использована совершенно новая архитектура, а исходный текст полностью переписан на базе пакета визуального программирования Delphi фирмы Borland.

Однако если немного разобраться, то здесь можно встретить приемы и программные модули из других, написанных мною ранее утилит и статей. Окно About (О Программе) почти полностью совпадает с тем, что использовано в предыдущих утилитах COA и IconJack. Способ распознавания значимых изменений в файле INI взят из утилиты INIBAK (PC Magazine, May 17, 1994). Детальное изучение работы функции FileCdr было описано в моей прошлой статье (PC Magazine, November 9, 1993). И конечно же, основная идея, относящаяся к сравнению раскладки диска до и после проведения инсталляции, взята из исходной версии InCtrl. Остальная часть этой статьи будет посвящена рассмотрению некоторых дополнительных приемов, используемых в программе InCtrl2.

Создание визуальных объектов в ходе выполнения программы

Для большинства программ, создаваемых с помощью Delphi, размещение визуальных объектов в форме осуществляется на стадии проектирования. Однако бывают ситуации, когда создавать их приходится в ходе выполнения самой программы - например, когда заранее не известно, сколько объектов необходимо. Прекрасным примером может служить задача генерации набора кнопок и меток в прокручиваемой части диалогового окна InCtrl2, служащего для выбора дисков/каталогов.

В процедуре обработки события OnCreate для главной формы программы InCtrl2 создается по одной кнопке и по две метки для каждого доступного логического диска. В большинстве случаев создание компонент в ходе выполнения самой программы является рутинной и очень утомительной задачей. Вместо того чтобы визуально задавать их размеры и расположение, а затем устанавливать значения остальных свойств с помощью Object Inspector, вам придется написать всего по одной строчке программы для задания значения каждой компоненты. Есть среди них, правда, одно свойство, которое обязательно должно быть определено; если забыть об этом, то созданная в ходе выполнения программы компонента не будет отображена в форме. Это - свойство Parent, значение которого обязательно должно быть задано. Оно должно указывать либо на саму форму, либо на одну из содержащихся в ней компонент (панель кнопок, окно редактирования, окно прокрутки), в подчинении которой будет находиться вводимая компонента. Дл примера в InCtrl2 каждая компонента добавляется в состав одной общей компоненты ScrollBox1. В программе формируется временное окно DriveComboBox, которое получает перечень всех имеющихся дисков и заимствует изображения для их кнопок из их общей компоненты.


Лист. 2. Создание средствами Delphi визуальных объектов в ходе выполнения программы не представляет особых трудностей; конечно, при условии что задано значение для свойства Parent.
PROCEDURE SetupDriveButtons; (* FIG06.TXT *) VAR N : Word; BEGIN {создать компонентные списки (поля данных типа TForm1)} DriveButtons := TList.Create; WatchLabels := TList. Create; DirLabels := TList. Create; {создать временный составной список для хранени информации по дискам} WITH TDriveComboBox.Create(Self) DO try Parent := Self; FOR N := 0 TO Items.Count-1 DO BEGIN DriveButtons.Add(TBitBtn.Create(Self)); WITH TBitBtn(DriveButtons[N]) DO BEGIN Parent := ScrollBox1; Left := 8; Top := 8 + N*28; Width := 49; Height := 25; Caption := Items[N][1] + ':\'; Margin : = 4; Glyph := Items.Objects[N] AS TBitmap; OnClick := DriveBtnClick; Tag : = N; END; WatchLabels.Add(TLabel.Create(Self)); WITH TLabel(WatchLabels[N]) DO BEGIN Parent := ScrollBox1; Left := 64; Top := 12 + N*28; Width := 40; Height := 20; Font.Size := 12; Font.Style := [fsBold]; Font.Color := cIRed; Tag : = 0; Caption := 'NO'; {для устройств CD-ROM кнопки делаютс недоступными для логических дисков, относящихся к жесткому диску, начальное значение - YES, для съемных и сетевых дисков - NO} CASE GetDriveType(Ord(Items[N][1]) - Ord ('a')) OF DRIVE REMOVABLE : ; {дополнительных команд не требуется; все подготовлено} DRIVE REMOTE : BEGIN {сетевой диск или CD-ROM} IF IsCDRom(Ord(Items[N][1]) - Ord('a')) THEN TBitBtn(DriveButtons[N]).Enabled := False; END; DRIVE FIXED : BEGIN Caption := 'YES'; Font.Color := clGreen; Tag := 1; END; END; END; DirLabels.Add(TLabel.Create(Self)); WITH TLabel(DirLabels[N]) DO BEGIN Parent := ScrollBox1; Left := 112; Top := 12 + 28*N; Caption := Items[N][1] + ':\'; Tag := N; END; END; finally Free; end; END;

Как сравнивается содержимое диска

Используемый в программе InCtrl2 алгоритм сравнени по содержимому диска создан из расчета достижени компромисса между скоростью выполнения и затратами свободного пространства на жестком диске. Если бы пришлось сохранять для каждого файла полное имя вместе с маршрутом, то расход места на диске оказался бы чрезмерным. Вместо этого в InCtrl2 записываются только имена файлов, причем в том порядке, как они располагаются внутри каждого каталога, а содержание каждого следующего вложенного каталога вставляетс сразу же после указания его имени. Перед каждым названием стоит один символ, который указывает глубину вложенности текущего элемента. Это позволяет легко управлять данным списком и получать в случае необходимости полное имя для каждого элемента. Первый следующий за именем файла символ имеет следующий смысл; если это 0, то текущее имя относится к файлу, если 1 - к каталогу. В последующих четырех символах кодируетс информация, отражающая спецификацию даты и времени последнего обращения к этому файлу и его размер. При любом изменении данного файла эта часть строки будет меняться. В итоге, на сохранение имени каждого файла отводится по 18 символов. Например,

"1386SPART.PAR0xxxx".

Стоящая в самом начале цифра "1" указывает, что данный файл находится в стартовом каталоге, а его имя - 386SPART.PAR. Стоящая после имени цифра "0" обозначает, что данный элемент является названием файла, а не каталога. В последних четырех позициях могут содержаться любые символы, за исключением пустого символа, символа возврата каретки, символа перевода строки, символа конца файла. Их приходится избегать, потому что программа InCtrl2 выводит информацию о содержимом диска в виде текстовых файлов.

Если бы стояла задача, чтобы программа InCtrl2 отслеживала абсолютно любое изменение в любом файле, тогда для сохранения полной информации о дате/времени и размере файла пришлось бы выделить 10 позиций. Потребовалась бы следующая раскладка: первые четыре символа для хранения значения дата/время длиной 32 бита; пятая позиция для указания, какой из четырех предшествующих символов изменился (если такое изменение было), так как среди них могли встречаться названные выше запрещенные к выводу символы; а также еще пять позиций, отражающие аналогичным образом значение размера файла длиной 32 бита.

Однако если над значениями дата/время и размер файла выполнить операцию XOR (логическое "исключающее ИЛИ"), то количество байт, необходимое для отслеживани характеристик каждого файла, сокращается на треть. Очень маловероятно, что в результате изменения файла появится новое значение параметров даты/времени и размера, объединенных операцией XOR, которое будет в точности совпадать со старым. Поскольку прежде всего нас интересует факт появления новых файлов, нежели их изменения, то данный компромисс представляетс разумным.

На листинге 3 дается часть текста программы InCtrl2, где производится сохранение информации о файлах, находящихся в конкретном базовом каталоге и в ему подчиненных. Текстовый файл OutF предварительно открыт, и в нем записана одна строка с названием стартового каталога, которому предшествует значение 0 (ноль). При первом вызове процедуры FindFiles ей передаютс название стартового каталога и символ "1", указывающий, что используется первый подуровень каталогов. FindFiles обращается к хорошо известным функциям FindFirst и FindNext для извлечения имен файлов со следующими атрибутами - обычных, каталогов, скрытых и системных. Полученная информация о файлах сохраняется в объекте нового типа - сортируемом списке строковых данных. Учтите, что при таком поиске не учитываются элементы каталога, имеющие атрибут метки тома. Поэтому здесь не сохраняется информация о дополнительных элементах каталога, используемых для хранения длинных имен файлов в Windows NT и Windows 95.

Далее процедура FindFiles последовательно просматривает все элементы в этом отсортированном списке и записывает информацию о каждом из них в выходной файл. Если текущий элемент является названием подкаталога, то производится рекурсивный вызов процедуры FindFiles. В ее параметрах теперь указываетс значение Dir, дополненное названием найденного подкаталога, а значение параметра текущего уровня level увеличивается на 1. Процедура FindFiles являетс рекурсивной, а так как выводимый ею список должен быть упорядочен по каждому каталогу, приходится на каждом очередном уровне рекурсии сохранять в стеке список файлов. По умолчанию для программ Delphi размер стека принимается равным 8192 байт. Для работы FindFiles этого недостаточно, поэтому в InCtrl2 размер стека увеличен до 16384 байт.

Операция поиска добавленных, удаленных или измененных файлов выполняется очень просто: сравнивается содержимое двух вариантов отчетных файлов - до и после инсталляции. К сожалению, текст программы CompareDisk занимает слишком много места, чтобы его можно было опубликовать здесь полностью; однако для понимания происходящего достаточно одного его описания. Прежде всего отметим, что содержание обоих файлов упорядочено. Сначала из каждого из них считывается по одной строке и они сравниваются между собой. Если строка из Файла 1 (состояние до инсталляции) меньше, значит, она относится к элементу, который был удален. Эта строка записывается в список удаленных файлов, а из Файла 1 считывается следующа строка. Если же меньше строка из Файла 2, значит, она относится к файлу, который был добавлен. Эта строка записывается в список добавленных файлов, а из Файла 2 считывается следующая по очереди строка. Если названи в обеих строках одинаковые, то сравнивается информация, относящаяся к полю дата/время/размер. При обнаружении несоответствия название данного файла записывается как измененного и считывается по новой строке из каждого файла. Таким образом, попеременно продолжается чтение то из одного, то из другого файла, то сразу из обоих, пока не будет достигнут конец обоих файлов.

Сравнение двух строк не является обычной операцией типа IF x>y. Из каждой строки должны быть выделены части, относящиеся к значению текущего подуровн каталогов, имени и параметру дата/время/размер, а само имя должно быть наращено и содержать полный маршрут текущего файла. Функция сравнения сначала проверяет, относятся ли к одному подуровню сравниваемые элементы; если один из них относится к более глубокому подуровню вложенности, чем другой, значит, этот элемент меньше другого. Если уровень вложенности одинаковый, результатом функции является текстовое (в кодах ANSI) сравнение двух строк. Следует использовать именно этот тип сравнения, поскольку именно он используется дл упорядочивания имен при создании отсортированного списка TList.


Лист. 3. В программе InCtrl2 применяются как давно известные функции FindFirst и FindNext, так и последние новшества - сортируемые списки строковых данных. Этот единый механизм предназначен для создани упорядоченного листинга названий всех файлов, которые относятся к данному каталогу или ему подчиненным.
function DirWMask(const Dir : TFileName) : TFileName; {Убрать из названия каталога концевые пробелы и добавить маску \*.*.} VAR P : Byte; BEGIN P := Length(Dir); WHILE (P > 0) AND (Dir[P] = ' ') DO Dec ( P); Result := Copy(Dir,1,P) + '\*.*'; END; function StringFor(var vTS :TSearchRec; vLevel : Char) : String18; {Создать строку длиной 18 символов для хранени информации по одному файлу.} conct vTimeSize : RECORD Len : Byte; Valu : LongInt; END = (Len: 4; Valu: 0); VAR sTimeSize : String[4] ABSOLUTE vTimeSize; N : Word; BEGIN vTimeSize.valu := vTS.Time XOR vTS.Size; FOR N : = 1 TO 4 DO CASE sTimeSize[N] OF #0, #10, #13, #26 : lnc(sTimeSize[N], $20); END; Result := Format('%.1s%-12.12s%.1x%s',[vLevel, vTS.Name, (vTS.Attr AND faDirectory) SHR 4, sTimeSize]) END; function AddDir(const Dir: TFileName; const Line: String18): TFileName; VAR P : Byte; BEGIN P := 13; WHILE (P > 0) AND (line[P] = ' ') DO Dec(P); AddDir := Dir + '\' + Copy(line, 2, P-1); END; procedure FindFiles(const Dir : TFileName; Level : Char); VAR TS : TSearchRec; I : Integer; TL : TStringList; (local TStringList) BEGIN {Дать Windows возможность обработать сообщения. Если программа закончила работу, то Exit.} IF ProcMsgTerminated THEN Exit; TL := TStringList.Create; try try TL.Sorted : = True; {Добавить строки с информацией о файлах в объект, создающий упорядоченный список.} I := FindFirst(DirwMask(Dir), faDirectory OR faHidden OR faSysFile, TS); WHILE I = 0 DO BEGIN IF TS.Name[1] <> '.' THEN TL.Add(StringFor(TS, Level)); I := FindNext(TS); END; FindClose (TS); {Вывести на печать информацию о файлах в отсортированном порядке; если встречено название каталога, то рекурсивный вызов процедуры FindFiles.} FOR I := 0 TO TL.Count-1 DO BEGIN WriteLn(OutF, TL.Strings[I]); Result := Result + 1; IF TL.Strings[I][14] = '1' THEN FindFiles(AddDir(Dir, TL.Strings[I]), Succ(Level)); END; finally TL.Free; END; except ON E: EInOutError DO IF E.ErrorCode <> 6 THEN Raise; end; END;

Механизм быстрого извещения об изменении файлов

И наконец, коснемся самого интересного - механизма использования великолепной недокументированной функции для быстрого извещения об изменении файлов. Здесь все очень просто! В лист. 4 приводится полный исходный текст FILECDRL.DLL - модуля, обеспечивающего подсоединение программы InCtrl2 к названному механизму через функцию FileCdr. FileCdr - это недокументированная функция Windows, хранящаяся в библиотечном модуле Kernel. Ей передаются дескриптор окна и адрес подпрограммы "обратного вызова" (callback); результат имеет размер LongInt, в младшем слове которого отражается, было ли успешным подсоединение InCtrl2.

Если в разделе [386Enh] файла SYSTEM.INI содержитс установка FileSysChange=ON, то это говорит о том, что разрешается отслеживать изменения в файловой системе, происходящие в сеансах DOS. В этом случае Windows направляет сообщения WM_FILESYSCHANGE непосредственно в то окно, которое было указано при обращении к функции FileCdr. При изменениях в файловой системе, производимых прикладными программами Windows, управление сразу же передается функции "обратного вызова", которая в этом случае просто подытоживает информацию и отправляет ее далее как сообщение WM_FILESYSCHANGE. На лист. 5 дается текст процедуры, которая принимает эти сообщения. Здесь также приведен текст процедуры, которая подключается на этапе создани отчета. Из имеющейся информации она формирует отдельные списки удаленных, добавленных и измененных файлов. Все очень просто - но только для систем, где действует механизм быстрого извещения.

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


Лист. 4. Программа InCtrl2 обращается к этому несложному модулю DLL для подключения и отключения механизма быстрого извещения об изменении файлов.
($K+} LIBRARY FileCdrL; (* FIG08.TXT *) USES WinTypes, WinProcs; CONST WM_FILESYSCHANGE = $0034; hWndInUse : hWnd = 0; FUNCTION FileCdr(FILECDRPROC : TFarProc) : LongInt; FAR; EXTERNAL 'KERNEL' Index 130; PROCEDURE FileCdrProc(wActionCode : Word; lpszPath : PChar); Export; BEGIN SendMessage (hWndInUse, WM FILESYSCHANGE, Hi (wActionCode), LongInt(lpszpath)); END; FUNCTION FileCdrInstall(H : hWnd) : Bool; Export; BEGIN FileCdrInstall := FALSE; IF hWndInUse <> 0 THEN Exit; IF NOT Bool(LoWord(FileCdr(@FileCdrProc))) THEN Exit; hWndInUse := H; FileCdrInstall := TRUE; END; FUNCTION FileCdrUninstall(H : hWnd) : Bool; Export; BEGIN FileCdrUninstall := FALSE; IF hWndInUse = 0 THEN Exit; IF hWndInUse <> H THEN Exit; FileCdr(NIL); hWndInUse := 0; FileCdrUninstall := TRUE; END; EXPORTS FileCdrInstall INDEX 1, FileCdrUnInstall INDEX 2; BEGIN END.
Лист. 5. Метод WmFileSysChange служит для сохранения в отдельном файле информации о каждом изменении в файловой системе. При подготовке отчета подключаетс метод ProcessFiles. Используя список всех зарегистрированных событий, он формирует списки удаленных, добавленных и измененных файлов.
procedure TForm1.WMFileSysChange(VAR Msg : TMessage); {Записать информацию об изменениях в файловой системе во временный файл.} BEGIN WITH Msg DO try IF (wParam = 2) OR (wParam = $56) THEN WriteLn(FileCdrFile, Format('%.2x%s TO %s', [wParam, StrUpper(PChar(lparam)), StrUpper(StrEnd(PChar(LParam)) + 1)])) ELSE WriteLn(FileCdrFile, Format('%.2x%s', [wParam, StrUpper(PChar(lparam))])); except ON E:EInOutError DO IF E.ErrorCode <> 6 THEN Raise; end; END; procedure ProcessLists; VAR N : Word; I : Integer; S : String; Code : Word; BEGIN TAddS.LoadFromFile(OutPath+TempName+TempExt1); IF TAddS.Count = 0 THEN Exit; FOR N := 0 TO TAddS.Count-1 DO BEGIN S := TAddS[N]; Code := StrToIntDef('$'+Copy(S,1,2), 0); S := Copy(S, 3, 255); CASE Code OF {DOS, WIN) 0, $3C, {Создать файл.} $5A, {Создать файл с уникальным именем.} {из сеанса DOS эта функция поступает равной 0.} $5B, {Создать новый файл.} {из сеанса DOS эта функция поступает равной 0.} 7, $39, {Создать каталог.} $6C : {Открыть в расширенном режиме} {Из сеанса DOS эта функци игнорируется.} TAdd.Add(s); 1, $41, {Удалить файл.} 8, $3A : {Удалить каталог.} BEGIN I : = TAdd. IndexOf(S); {Если удаляется файл, добавленный в этом же сеансе...} IF I >= 0 THEN {...тогда сообщать о нем как о добавленном, нет необходимости.} TAdd.Delete(I) ELSE TDel.Add(S); END; 2, $56 : TCha.Add(S); {Переименовать файл/каталог.} ;} 3, $43 : ;{Получить/изменить атрибуты файла} {На запрос атрибутов файла из прикладной программы Windows сообщение не создается.} $57 : ;{Установить значения даты/времени для файла} {Вероятность, что такая операция будет выполняться, невелика.} END; END; TAddS.Clear; END;
Нил Дж. Рубенкинг - научный редактор журнала PC Magazine.