Базы данныхИнтернетКомпьютерыОперационные системыПрограммированиеСетиСвязьРазное
Поиск по сайту:
Подпишись на рассылку:

Назад в раздел

Управление процессом установки приложения и его поддержка посредством новой программы инсталляции Wi

Управление процессом установки приложения и его поддержка посредством новой программы инсталляции Windows

Майк Келли

В сравнении с некоторыми темами, рассматриваемыми ранее в журнале Microsoft Systems Journal, например DirectX или Microsoft Transaction Server, статья о процессе установки (инсталляции) может показаться весьма скучной. Однако новый инсталлятор Windows предлагает вашему приложению несколько замечательных качеств, значительно превосходящих все известное раньше. Ваше приложение сможет автоматически восстанавливать само себя или утраченные файлы, ссылки на которые находятся в записях этапов установки. Иконка вашего приложения добавится в меню Start на тысячах компьютеров, установленных на предприятии, причем без записи реальных файлов на какую-либо машину до того момента, когда пользователь обратится к этой иконке. Вы также сможете разрекламировать все СОМ-серверы вашего приложения на тех же машинах без реальной записи файлов на них, причем система автоматически установит их по первому требованию любого клиента.
Новый инсталлятор Windows добавит вашему приложению несколько замечательных качеств, превосходящих все известное раньше. Ваше приложение сможет автоматически восстанавливать само себя или утраченные файлы, ссылки на которые находятся в записях этапов установки. Пора ознакомиться с подробностями.
В сравнении с некоторыми темами, рассматриваемыми ранее в журнале Microsoft Systems Journal, например DirectX или Microsoft Transaction Server, статья о процессе установки (инсталляции) может показаться весьма скучной. Однако новый инсталлятор Windows предлагает вашему приложению несколько замечательных качеств, значительно превосходящих все известное раньше. Ваше приложение сможет автоматически восстанавливать само себя или утраченные файлы, ссылки на которые находятся в записях этапов установки. Иконка вашего приложения добавится в меню Start на тысячах компьютеров, установленных на предприятии, причем без записи реальных файлов на какую-либо машину до того момента, когда пользователь обратится к этой иконке. Вы также сможете разрекламировать все СОМ-серверы вашего приложения на тех же машинах без реальной записи файлов на них, причем система автоматически установит их по первому требованию любого клиента.

Инсталлятор Windows базируется на нескольких фундаментальных принципах. Установка приложений и их последующая поддержка должны стать частью базовых системных сервисов Windows. Это дает системе возможность отслеживать процесс установки и более корректно управлять компонентами, которые используются приложениями. Инсталлятор должен заставить разработчиков думать об установке приложения не как о разовом процессе, а как о составной части приложения. Он должен поддерживать установку программного обеспечения на “закрытых” компьютерах, где обычные пользователи лишены возможности делать то, что необходимо для многих программ установки приложений. Инсталлятор Windows является основной частью инициативы Zero Administration Windows. Наконец, инсталлятор должен использовать интегрированные сервисы каталогов (Directory Service; появление этих сервисов ожидается в Windows NT 5.0) для ввода двух новых схем внедрения приложений в крупных организациях — “Назначения” и “Опубликования”.

Все это я опишу в деталях в предлагаемой статье. Для начала, однако, давайте рассмотрим различные подходы к установке приложений под Windows, практикуемые в настоящее время.

Современные способы установки программ

В настоящее время приложения используют различные технологии установки. Программы InstallShield, WinInstall фирмы Seagate или Wise Installation System фирмы Great Lakes Software — наиболее часто используемые средства для создания программ установки приложений. Компоненты ActiveX и другие Web-компоненты пользуются пакетами IЕxpress, которые в основном представляют собой саморазворачивающиеся программы, выполняющие простые функции копирования файлов и изменения содержимого реестра. Большие комплексные приложения, такие как Microsoft Office или Corel PerfectOffice, часто используют собственные технологии установки. Некоторые простые компоненты, такие как драйверы, могут использовать INF-файлы, которые по своим возможностям подобны пакетам IExpress.

В настоящее время Microsoft Windows предоставляет администраторам сетей и пользователям весьма рудиментарные способы для получения информации о приложениях, устанавливаемых на их машины, и для последующего управления этими приложениями. Процесс работы утилиты Add/Remove Programs Control Panel (“Установка/удаление программ” Панели Управления) определяется стандартным набором записей реестра. Этот набор по идее формируется самими устанавливаемыми приложениями. Такая регистрация задает последовательность команд, которую Windows может вызвать для удаления установленной программы. Современные программные продукты, такие как Microsoft System Management Server, могут действовать в централизованно управляемых сетях, но они ограничены тем набором информации и средствами управления, которые предоставляют конкретные приложения, и совершенно бесполезны для малого бизнеса и пользователей домашних компьютеров, каждый из которых фактически является собственным системным администратором.

Как работает инсталлятор Windows

Новый инсталлятор Windows предназначен для исправления вышеописанной ситуации путем использования стандартного механизма установки Windows-приложений и их компонентов. Такие средства, как WinInstall и InstallShield (так же, как и собственные продукты, используемые некоторыми производителями программного обеспечения), будут существовать и дальше, но они станут лишь средствами для разработки сценариев установки. Новый же инсталлятор Windows является “исполняемым модулем” для любых сценариев установки, которые создаются различными средствами.

Инсталлятор также делает доступным API (интерфейс прикладных программ), который может быть использован приложениями для того, чтобы выяснить, какой выбор сделал пользователь из вариантов действий, предлагаемых ему при установке, или даже для установки отсутствующих компонентов приложения. Больше не будет никаких сообщений типа: “Пожалуйста, покиньте программу и запустите утилиту установки, чтобы решить проблему”; в таких случаях приложение будет просто устанавливаться при помощи API-инсталлятора.

Так как инсталлятор представляет собой базовый системный модуль и отслеживает информацию об устанавливаемых программах, он предоставляет системным администраторам в централизованных сетях комплект мощных утилит для определения типа устанавливаемых приложений, а в Windows NT 5.0 — дистанционное управление процессами установки на машины пользователей.

Рис.1. Архитектура инсталлятора Windows

Рис. 1 иллюстрирует архитектуру инсталлятора Windows. В системе Windows NT инсталлятор состоит из двух исполняемых компонентов: Client Install Engine (инсталляционное ядро клиента), имеющего привилегии пользователя, и Install Service (сервис установки), который может обладать расширенными административными привилегиями, так как выполняется в качестве сервиса Windows NT. Все изменения в конфигурации системы производятся как единая установочная транзакция сервисом Install Service. Такая транзакция дает возможность отката при ошибочной либо прерванной установке. Последовательность отката (то есть возвращения к какой-либо фиксированной точке процесса. — Прим. перев.) включает в себя восстановление первоначального содержимого перемещенных или удаленных в ходе установки файлов, а также восстановление удаленных либо измененных записей системного реестра (таких как регистрационные записи СОМ-классов). Поскольку необходимая для отката информация может занять много места на диске, администратор или пользователь вправе отказаться от этой возможности в ходе установки.
В системах Windows 95 и Windows 98 Client Install Engine и Install Service выполняются в виде отдельного процесса. Они взаимодействуют между собой через защищенный механизм вызова удаленных процедур (remote procedure call).

В дополнение к обычному установочному пакету для устанавливаемого программного продукта инсталлятор может предложить специальный метод настройки продукта для определенной группы пользователей, называемый transform. Такие настройки могут использоваться для того, чтобы сделать доступными или недоступными какие-либо возможности установки, предоставляемые в ходе процесса, или даже дать дополнительные возможности.

Продукты, свойства, компоненты

Инсталлятор вводит три уровня внутренней иерархии приложений. Рис. 2 показывает подобную классификацию для гипотетического приложения EasyMail, которое описывается в этой статье ниже.

На верхнем уровне находится сам продукт — некая программа, которая может быть установлена пользователем. Продукт состоит из набора подпрограмм. При этом каждая подпрограмма является наименьшей отдельно устанавливаемой частью общей совокупности. Примерами подпрограмм для EasyMail будут модули MailReader, MailEditor и SpellChecker. Если вы проведете аналогию с традиционными программами установки, то эти подпрограммы отмечаются кнопками с независимой фиксацией при “выборочном” или “расширенном” варианте установки в ходе диалога утилиты с пользователем. Это те компоненты, которые пользователь может не устанавливать.

Подпрограммы представляют собой набор компонентов. Под термином “компонент” здесь понимается наименьший программный модуль, который может быть совместно использован продуктом и его подпрограммами. В то время как подпрограммы специфичны для продукта и имеют собственные имена, уникальные внутри продукта (как, например, MailReader), компоненты являются общими для всех программных продуктов, установленных на данной машине, и идентифицируются кодом GUID. Хотя COM-компонент может быть инкапсулирован в качестве части инсталлятора (и с большинством COM-компонентов, вероятно, так и поступят), — не путайте термины. Компонент инсталлятора не имеет ничего общего с COM-компонентом.

Компоненты представляют собой действительное содержимое вашего продукта. Один и тот же компонент может состоять из файлов, записей системного реестра, регистрационной информации для СОМ, библиотек типов Visual Basic, “горячих клавиш” быстрого доступа для меню Windows Start и т.д. (см. рис. 2). Компонент же инсталлятора является неразделяемым; он либо устанавливается на компьютер, либо нет.

Рис. 2. Иерархия функций EasyMail

Так как идентификаторы компонента инсталлятора являются глобальными, они используются всеми продуктами. Для примера — множество продуктов поставляется с runtime-модулями Microsoft Visual Basic for Applications. Определение этих модулей в качестве компонента инсталлятора имеет несколько преимуществ. Поясним для начинающих. Логика установки Microsoft Visual Basic for Applications может быть заложена внутри такого компонента, таким образом все продукты будут устанавливать и удалять эти модули абсолютно одинаково. После того как модули Microsoft Visual Basic for Applications единожды установлены на машине, инсталлятор об этом “знает”, и тогда установка всех последующих продуктов, которые используют эти модули, просто увеличит количество соответствующих ссылок на них. Все эти продукты будут использовать одни и те же копии файлов. Это и так возможно сегодня, однако требует тонкой координации продуктов между собой — включая лицензиатов некоторых не Microsoft-продуктов. Новый инсталлятор делает такую координацию необязательной, потому что модули Visual Basic for Applications инкапсулированы в него в качестве компонента.

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

Запомните, что компоненты используются подпрограммами совместно. Как показано на рис. 2, Компонент1 одновременно используется подпрограммами Reader и Editor. В действительности установлена только одна копия этого файла, причем она будет устанавливаться при инсталляции любой из этих подпрограмм.

База данных установки

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

Таблица Назначение
Class Информация о СОМ-серверах, установленных данным продуктом. Ссылается “один к одному” на таблицу Component
Component Информация о компонентах, установленных данным продуктом
Feature Информация о подпрограммах, установленных данным продуктом
FeatureComponent Отображение таблицы Feature на таблицу Component. Это многосторонние перекрестные ссылки, возникающие, например, когда один компонент используется множеством подпрограмм и одна подпрограмма состоит из множества компонентов
File Информация о файлах, установленных данным продуктом. Ссылается “один ко многим” на таблицу Component. Каждый файл является частью только одного компонента
InstallExecute Sequence Список действий, которые надо выполнить для установки программы
InstallUISequence Список интерфейсных элементов, отображаемых перед непосредственной установкой продукта. Они могут использоваться для сбора информации о пользователе, задания опций установки и т.п.
Property Перечень характеристик продукта. Характеристики могут быть использованы в качестве параметров установки и изменяться в ее ходе для фиксации каких-либо событий
PublishComponent Используется для компонентов, определенных системой. Ссылается на таблицу Component
Registry Информация о записях системного реестра, сделанных при установке компонента. Ссылается “многие к одному” на таблицу Component. Каждая запись реестра представляет собой часть только одного компонента
RemoveFile Файлы, которые должны быть удалены при установке компонента. Ссылается “многие к одному” на таблицу Component
RemoveRegistry Записи реестра, которые должны быть удалены при установке компонента. Ссылается “многие к одному” на таблицу Component
SelfReg Используется для обеспечения обратной совместимости с компонентами, не предусматривающими собственной СОМ-регистрации в разделе Class (что является более предпочтительным). Ссылается “многие к одному” на таблицу Component
Shortcut Быстрый доступ для компонента. Ссылается “многие к одному” на таблицу Component
TypeLib Регистрация библиотек типов Visual Basic. Ссылается “многие к одному” на таблицу Component

Рис. 3. Основные таблицы базы данных установки

Файлы самих продуктов могут быть сохранены в сжатых САВ-файлах, находящихся внутри файла базы установочных данных в виде потоков, что облегчает загрузку одного файла для установки какого-нибудь небольшого продукта через Internet. Файлы более объемных продуктов могут находиться вне файла базы установочных данных, в каталогах компакт-диска или на сетевом сервере.

Программа инсталлятора Windows является зарегистрированным сервером для файлов с расширением .MSI, так что, когда MSI-файл открывается пользователем, Windows Shell автоматически вызывает инсталлятор. Инсталлятор, вызванный таким образом, считывает информацию о продукте из файла базы установочных данных и определяет, установлен продукт или нет. Если нет, инсталлятор начинает процесс установки, описанный в базе данных. В случае если продукт был установлен ранее, инсталлятор предлагает другие варианты, такие как добавление или удаление каких-нибудь подпрограмм либо переустановку продукта.

Инсталлятор также предоставляет интерфейс OLE Automation, позволяющий сетевым администраторам или разработчикам создавать код на Visual Basic или VBScript, управляющий процессом установки продукта.

Наконец, пакетная утилита MsiExec может быть использована для установки или удаления продукта, изменения его установочных опций из пакетных (BAT) файлов.

Вы можете спросить, в чем инсталлятор видит различие между информацией для регистрации COM-серверов (таблица Class) и записями реестра (таблица Registry); между файлами (таблица File) и быстрым доступом к меню Start (таблица Shortcut). Ко всему прочему регистрация COM-сервера всего лишь приводит к записи серии параметров в системный реестр (раздел HKEY_CLASSES_ROOT), а поддержка быстрого доступа в действительности является всего лишь файлом. Одна из причин скрывается в поддержке одной из ключевых возможностей Windows NT 5.0 — представления возможностей программы без их установки.

Инсталлятор допускает существование любой подпрограммы в одном из четырех состояний: установленная локально, установленная на сервере, отсутствующая, рекламируемая. Рис. 4 иллюстрирует каждое из этих состояний, имеющих символьные имена, которые создаются API-вызовами при установке.

 
Состояние Описание
INSTALLSTATE_ LOCAL Подпрограмма установлена на локальном компьютере; соответственно ее файлы располагаются в каталоге C:Program Files MyProduct
INSTALLSTATE_ SOURCE Подпрограмма установлена и работает централизованно (с компакт-диска или с сетевого сервера)
INSTALLSTATE_ ABSENT Подпрограмма не установлена
INSTALLSTATE_ ADVERTISED Программа рекламируется. Это значит, что она доступна для использования, но еще не установлена

Рис. 4. Возможные состояния установленной подпрограммы

Состояние INSTALLSTATE_ABSENT используется для тех подпрограмм, которые еще не установлены. Подпрограммы с состояниями INSTALLSTATE_LOCAL или INSTALLSTATE_SOURCE установлены; различие только в том, где реально находятся программные файлы данной подпрограммы. Как правило, продукт имеет некие предпочтения при установке, задаваемые его автором. Например, подпрограмма, представляющая собой большой набор графических изображений, вероятно, предпочтет запуск с централизованного источника (сервера, CD), дабы не занимать место на диске локального компьютера. В то же время наиболее часто используемые подпрограммы будут устанавливаться на машинах пользователей, так что клиентам сети не понадобится иметь установочные файлы при выполнении программы или поддерживать связь с сервером, откуда производилась установка. Однако пользователи и администраторы могут не принимать во внимание эти предпочтения при установке любой программы. В дополнение к вышесказанному отметим, что, используя API, ваше приложение может изменить параметры установки любой подпрограммы; об этом речь пойдет ниже. К примеру, вы можете снабдить программу меню Prepare for Road Trip, которое позволит установить все необходимые подпрограммы на переносной компьютер.

Состояние INSTALLSTATE_ADVERTISED — это наиболее интересная модификация установки. Рекламируемая подпрограмма только кажется установленной. COM-серверы, наборы расширений, данные MIME и т.д. зарегистрированы. При этом рекламируемое приложение не имеет доступа к исполняемым файлами или библиотекам (DLL), которые относятся к серверу. Вместо этого системный реестр содержит дескриптор MSI — бинарные данные, которые ссылаются на установленные программы и подпрограммы и снабжают сервер всей необходимой для запуска информацией. Функции быстрого доступа к меню Start существуют для подпрограммы (если она является исполняемым модулем приложения), но они также не обращаются к файлам напрямую, а содержат дескриптор MSI. Таким образом, на машине пользователя создается только видимость присутствия программы. И оболочка Windows, и OLE32.DLL модифицированы таким образом, чтобы распознавать эти новые дескрипторы MSI и вызывать инсталлятор для определения реального местонахождения файлов. Чтобы вернуть эту информацию, инсталлятор должен установить саму подпрограмму. Когда вы вызываете COM-сервер (скажем, двойным щелчком мыши по ярлыку присоединенного к почтовому отправлению файла) или щелкаете по иконке быстрого доступа рекламируемого приложения в меню Start, инсталлятор автоматически устанавливает все компоненты этого приложения.

Рекламирование с использованием утилиты Application Assign and Publish должно поддерживаться в полном объеме только в среде Windows NT 5.0, хотя в усеченном виде (на уровне поддержки Windows Shell) эта возможность будет доступна и для Windows NT 4.0, Windows 95 и Windows 98, если у вас установлено обновление Microsoft Internet Explorer 4.01 Service Pack 1 и включена опция Active Desktop. (Данное обновление устанавливает модифицированную оболочку (shell), включающую поддержку рекламирования.) Поддержка для рекламных COM-серверов предоставляется только системой Windows NT 5.0.

Поскольку реклама с помощью Assign and Publish требует очень небольших ресурсов на машине пользователя (только место для записей реестра и ярлыков быстрого доступа меню Start), это самый легкий путь к большому количеству СОМ-серверов или приложений, которые большинству пользователей могут оказаться не нужны. Если случилось так, что они вам понадобились, система сможет установить их без участия пользователя. Более того, такое рекламирование использует службу каталогов (Active Directory) Windows NT 5.0, делая рекламу установочного пакета легко доступной большим группам пользователей. Все программы этого пакета появятся на компьютерах этих пользователей сразу же после загрузки системы, однако ни одна программа не будет реально записана до того, как пользователь сам не вызовет одно из приложений либо СОМ-сервер.

Поскольку реклама с помощью Assign and Publish требует очень небольших ресурсов на машине пользователя (только место для записей реестра и ярлыков быстрого доступа меню Start) — это самый легкий путь к большому количеству СОМ-серверов или приложений, которые большинству пользователей могут оказаться ненужными.
Необходимо отметить, что рекламирование будет поддерживаться только для СОМ-серверов, зарегистрированных в таблице Class инсталлятора; запись данных реестра из таблицы Registry или использование таблицы SelfReg не поддерживает эту функцию. Это единственная причина того, что саморегистрация (с использованием функции DllRegisterServer либо опцией командной строки /regserver) теперь не рекомендуется — для регистрации СОМ-сервера предпочтение должно отдаваться использованию таблицы Class инсталлятора. Именно поэтому инсталлятор отделяет регистрацию в таблице Class от других записей реестра. Заметьте, что СОМ-сервер еще может поддержать саморегистрацию, но только путем вызова инсталлятора через интерфейс API для того, чтобы тот сделал записи в Реестре (вместо того, чтобы делать это напрямую).Программа EasyMail

Для того чтобы внести ясность во все вышесказанное, давайте рассмотрим в качестве примера какое-нибудь простое приложение: скажем, ваш босс просит вас взять бестселлер фирмы — программу электронной почты EasyMail — и заставить ее работать с помощью нового инсталлятора Windows. С чего вы начнете?

Первым делом вам нужен уникальный код продукта — GUID, которым инсталлятор пользуется для идентификации вашего продукта. Если продукт представляет собой COM-сервер, вы можете для кода продукта взять тот же GUID, которым вы пользуетесь для вашего CLSID (однако связь между этими двумя GUID отсутствует). Название вашего продукта — EasyMail, и я просто генерирую его код GUID, используя модуль GuidGen. Код GUID будет выглядеть так:

{FC5A660-479C-11D1-883B-0080C7A271D8}

EasyMail состоит из редактора электронной почты, подпрограммы чтения, поддержки разных протоколов передачи данных (SMTP, IMAP и т.д.) и файловых конверторов, делающих возможным отображение и отсылку присоединенных файлов разных форматов. Внутри программы эти подпрограммы имеют короткие названия, такие как MailEditor, MailReader и IMAP. Для пользователей имеются более полные имена, такие как EasyMailEditor. Запомните, что подпрограмма — это наименьшая устанавливаемая отдельно часть общей функциональной совокупности; с точки зрения пользователя, она отображается в отдельную отмечаемую экранную кнопку, показываемую в диалоговой панели инсталлятора.

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

Предположим, что ваш редактор и программа чтения почты совместно используют файл EASYSYS.DLL, который предоставляет используемый обеими подпрограммами набор общих функций. Так как вы поместили редактор и программу чтения в отдельные подпрограммы, может быть установлена одна из них или обе. Однако если установлена хотя бы одна, вам, наверное, захочется иметь общую библиотеку DLL. А учитывая, что вы весьма предусмотрительный разработчик, вам, вероятно, хочется, чтобы ваши будущие продукты также могли использовать эту удобную DLL. Сделайте файл EASYSYS.DLL компонентом, и обе подпрограммы смогут его использовать. Совместное с EasyMail использование общей DLL, если она будет уже установлена, усовершенствует также и ваши будущие продукты. И если необходимо сделать запись в системный реестр или зарегистрировать COM-серверы, вы также присоедините их к вашему компоненту. При установке компонента инсталлятор автоматически сделает эти записи в реестр и зарегистрирует COM-серверы. Более того, при удалении компонента инсталлятор автоматически удалит эти записи и отменит регистрацию СОМ-серверов. При этом учтите, что компоненты используются всеми установленными продуктами, так что в ходе проектирования внимательно отнеситесь к составным элементам.

Будучи предусмотрительным разработчиком, вы, вероятно, также хотите, чтобы ваша EasyMail была многоязычной. Вы разместили все сообщения и интерфейсные элементы в ресурсах и аккуратно использовали функции Windows National Language System API вместо того, чтобы пользоваться фиксированными форматами представления дат и чисел. Единственное, что вы сделали неверно — это размещение ваших локальных ресурсов в главном исполняемом файле. Пользователю никак не установить поддержку мультиязычной среды в EasyMail без копирования массы нелокализованных частей программы, таких как кодовые страницы в вашем исполняемом файле и библиотеках DLL. А для многих стран Европы и Юго-Восточного региона обычной является поддержка более чем одного языка при установке.

При таком раскладе правильно будет поместить все ваши локализованные ресурсы в отдельный языковой файл DLL. В этой библиотеке не будет программного кода — только ресурсы. Вы можете загружать этот файл на определенном этапе выполнения программы, используя функцию LoadLibrary с флагом LOAD_LIBRARY_AS_DATAFILE. Когда вы выполняете такие функции, как LoadResourse или LoadString, вы обращаетесь к HINSTANCE библиотеки, а не к вашей основной DLL. Используйте локальный идентификатор (LCID) как часть имени этой библиотеки ресурсов, и тогда вы сможете осуществить поддержку любого необходимого вам количества языков (не большего, конечно, чем количество одновременно подключенных языковых DLL). Например, ваш ресурс DLL американского варианта английского языка будет называться EASY-1033.DLL. Для японского языка — EASY-1041.DLL. Квалифицированные таким образом компоненты помогут вам легко найти DLL определенного языка в ходе выполнения программы.

Произвести одноуровневое перенаправление можно, используя квалифицированный компонент. Обычные компоненты имеют единственный идентификатор — GUID. У квалифицированных компонентов таких идентификаторов два — идентификатор категории (GUID) и квалификатор, который является всего лишь строкой текста. Квалифицированные компоненты пользуются идентификатором для отображения самих себя на настоящий, неквалифицированный компонент. По-другому это можно представить таким образом: квалифицированный компонент является динамическим массивом компонентов, где имя массива описано идентификатором категории (GUID), а индекс массива представляет собой строку.

В итоге я решил использовать квалифицированный компонент для представления языковых ресурсов EasyMail, а LCID языка использовать как квалификатор. При этом я легко могу “наложить” LCID на любой интерфейсный элемент, используя функцию GetLocaleInfo. Используя функцию инсталлятора MsiProvideQualifiedComponent, я к тому же имею возможность установить маршрут к специальной языковой библиотеке ресурсов, а при необходимости автоматически установить ее на диск с помощью инсталлятора. Можно и перебрать все квалификаторы для имеющихся квалифицированных компонентов. Это даст нам список всех доступных LCID (то есть все доступные языки, для которых EasyMail может предоставить пользовательский интерфейс). Даже если мне в настоящий момент нужно поставить поддержку только американского английского, я могу написать программный код, использующий квалифицированные компоненты. Если я потом решу поставить, к примеру, набор языковых дополнений с поддержкой других языков, он задействует квалифицированные компоненты и EasyMail сразу “научится” разговаривать по-французски и по-немецки.

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

Вы и сами можете сделать нечто подобное с квалифицированным компонентом; можно использовать реестр для регистрации ваших языковых ресурсов и искать маршрут к языковому ресурсу в вашем программном коде, используя при этом реестр. Инсталлятор лишь сделает это проще и быстрее и предоставит эту возможность (установки по запросу) всем пользователям.
Поскольку компоненты являются глобальными, глобальными будут и квалифицированные компоненты. Например, из нескольких продуктов может быть доступен квалифицированный компонент File Converters (обладающий известным идентификатором категории GUID), который предоставляет конверторы файлов в разных форматах. В соответствии с квалификатором (возможно, какое-то кодирование на основе типа файла) большое количество продуктов может устанавливать и использовать эти конверторы. Более того, можно сделать рекламу преобразователя без реальной записи его кода на диск.Программный интерфейс инсталлятора

В предыдущих разделах я ссылался на некий программный интерфейс инсталлятора. Настоящая глава даст более развернутую информацию по программным интерфейсам, которые предоставляет новый инсталлятор. Все они содержатся в библиотеке MSI.DLL. Доступ к OLE Automation для Visual Basic обеспечивается через объектную модель MsiApi. В Windows 95 Unicode (W)-версии функций будут преобразовывать аргументы в коды ANSI и вызывать их ANSI (A)-версии. В Windows NT предусмотрены как Unicode (W)-, так и ANSI (A)-версии данных функций. Вы, вероятно, захотите загрузить самую последнюю версию Microsoft Windows Platform SDK, которая содержит файлы и инструментальные средства, необходимые для использования в вашей программе функций API-инсталлятора. Эти функции определены в файле include/msi.h.

Давайте начнем с основных функций, которые потребуются EasyMail при запуске. Есть несколько последовательных шагов, которые должны быть выполнены в начале работы приложением, установленным новым инсталлятором. Во-первых, оно должно вызвать функцию MsiGetProductCode, чтобы приложение могло идентифицировать себя в качестве инсталлятора. Это обеспечивает код продукта, необходимый при использовании других функций инсталлятора. Во-вторых, вы должны вызвать функцию MsiGetUserInfo для получения информации о зарегистрированном пользователе. Например, ваше приложение могло бы показывать эту информацию в режиме информационного экрана для защиты от пиратского использования EasyMail. Функция MsiGetUserInfo даст сбой при отсутствии этой информации; это произойдет при первом же после установки вызове приложения, если такая информация не была задана при установке. Далее вы должны вызвать функцию MsiCollectUserInfo, которая отобразит диалоговую панель для получения информации о пользователе. Таким образом, вы “привязываете” свой программный продукт к зарегистрированному пользователю и компании. В-третьих, при построении основного интерфейса пользователя для вашего приложения (то есть набора меню и панелей инструментов, которые вы показываете на экране) вы имеете возможность при помощи кода проверять, установлены ли определенные подпрограммы. Например, ваша подпрограмма чтения почты не покажет кнопку редактора в панели инструментов (или сделает ее неактивной), если редактор EasyMail не установлен. Поскольку вы можете устанавливать любую подпрограмму по своему желанию, то вместо использования установочного API допустимо самим определить, какие подпрограммы должны быть доступны в вашем приложении. Перечислять эти характеристики можно несколькими способами: Попросить инсталлятор показать все подпрограммы по очереди. Например, прежде чем приложение отобразит кнопку или элемент меню, оно может вызвать функцию MsiQueryFeaturesState для проверки наличия данной подпрограммы. Перечислить все доступные подпрограммы сразу при вызове функции MsiEnumFeatures. Перечислить квалификаторы для квалифицированных компонентов, используя функцию MsiEnumComponentQualifiers. Вы могли бы пойти этим путем в EasуMail, чтобы определить, какие конверторы файлов установлены или какие языки вы можете поддерживать на уровне пользовательского интерфейса.

Есть несколько способов, которыми Windows-приложения вызывают внешние функции, реализованные в исполняемых файлах или DLL. Вы можете использовать среду COM (через функцию CoCreateInstance); кроме того, вы можете искать маршрут к DLL, используя системный реестр, или предположить, что данный файл находится в определенном каталоге относительно вашего исполняемого файла. Функция CoCreateInstance остается неизменной, но для двух других способов вам необходимо использовать функции инсталлятора.

Сначала давайте рассмотрим вариант с COM и функцией CoCreateInstance. Так как новый инсталлятор — это часть ядра Windows, ядро COM специально модифицировано для работы с ним. Когда вы вызываете функцию CoCreateInstance, вы передаете CLSID (идентификатор класса) COM-класса, экземпляр которого вы хотите создать. В предыдущих версиях ядро COM просматривало реестр, чтобы найти полный маршрут к исполняемому файлу или DLL, которые являются сервером для данного класса, и запускало (для EXE-серверов) или загружало (для внутрипроцессных (inprocess) серверов) этот файл. Начиная с Windows NT 5.0 ядро COM (через библиотеку OLE32.DLL) сначала ищет дескриптор MSI в секции CLSID в реестре. Если он найден, ядро COM знает, что этот класс был установлен инсталлятором Windows, и тогда оно вызывает инсталлятор для получения полного маршрута к COM-серверу. Это позволяет инсталлятору реально устанавливать сервер, если он был только разрекламирован. Если сервер уже был установлен, но пользователь случайно удалил DLL или EXE-файлы его поддержки, инсталлятор обнаружит это и автоматически переустановит их, обеспечивая тем самым регенерацию для COM-серверов.

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

 
Колонка Тип Описание
ComponentID GUID GUID категории, с которой связан этот публикуемый компонент
Qualifier Текст Строка текста, которая определяет значение в колонке ComponentID. Квалификатор используется, чтобы различать многочисленные формы одного и того же компонента, такие как компонент, выполненный на многих языках. На этот квалификатор ссылаются функции MsiProvideQualifiedComponent и MsiEnum ComponentQualifiers
AppData Текст Необязательная строка, которую можно локализовать. Она может быть получена с помощью функции MsiEnum ComponentQualifiers (возвращается в буфере LpApplicationDataBuf)
Feature_ Component_ Идентификатор Две данные колонки вместе предоставляют информацию для установки компонента, на который ссылается этот квалификатор. И Feature и Component необходимы, потому что компонент не может быть установлен без установки подпрограммы. Идентификатор компонента задает специальный файл или полный путь в пределах данной подпрограммы

Рис. 5. Таблица PublishComponent

Вы должны найти другие пути обнаружения внешних компонентов, чтобы использовать интерфейсы инсталлятора. Вы могли бы, конечно, в подражание COM написать что-то вроде идентификатора реестра в разделе, описывающем маршруты, заставить ваше приложение искать эти идентификаторы в реестре и транслировать идентификатор в путь, используя программный интерфейс инсталлятора. Но для чего вообще писать что-нибудь в реестр? Большинство приложений делает это для того, чтобы их программа настройки могла связываться с работающим приложением через реестр — программа настройки пишет полный маршрут в реестр, приложение считывает его оттуда. Вы можете достигнуть этого и без использования реестра, просто применяя компонент инсталлятора. EasyMail может вызывать функцию MsiProvideComponent, чтобы найти полный маршрут к любому компоненту, заданному его идентификатором (ID). Инсталлятор автоматически установит компонент, если вы передаете флаг INSTALLMODE_DEFAULT как значение параметра dwInstallMode, или просто вернет маршрут к уже доступному компоненту (или код ошибки, если компонент не установлен), если вы передаете флаг INSTALLMODE_EXISTING. Тем самым вы получаете три больших преимущества. Во-первых, это уменьшает зависимость основанной на реестре связи между вашей программой настройки и самим приложением. Это также упрощает саму настройку, так как вам не нужно больше делать записи в реестре. Во-вторых, это делает ваше приложение более способным к восстановлению: даже если пользователь неосторожно удалит необходимый файл, инсталлятор может автоматически переустановить его, когда ваше приложение за ним обратится. В-третьих, это позволяет пользователям делать любую подпрограмму “загружаемой прямо из установочного пакета” без какой-либо дополнительной работы самого приложения. Пользователь (или администратор) может определять эффективное соотношение между использованием локального диска и быстродействием.

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

Программный интерфейс квалифицированных компонентов

Квалифицированные компоненты могут использоваться для конверторов и переводчиков, словарей, шаблонов или стандартных форм либо любого языкового ресурса.

Квалифицированные компоненты также полезны для создания дополнительных “Мастеров” или дополнительных файлов, которые ваше приложение использует во время работы для настройки своих параметров. Более того, это можно сделать и через СOM-интерфейс, который вы можете найти, используя стандартные COM-методы. Давайте рассмотрим таблицу PublishComponent (рис. 5) для того, чтобы уяснить, как работают квалифицированные компоненты. Рис. 6 содержит уточнения по установочным API, которые связаны с квалифицированными компонентами.

Когда вы создаете свою программу установки, вы решаете, какие значения квалификатора (Qualifier) и AppData будут у ваших квалифицированных компонентов. Квалификатор должен быть уникальным идентификатором в пределах данной категории. Это должна быть строка, которую вы сможете легко сгенерировать при поиске специфического квалифицированного компонента. Например, если ваш компонент определен языком, использование LCID в качестве такой строки является естественным выбором. Поле AppData является необязательным (и локализуемым). Это может быть строка, которую вы отображаете в вашей программе для описания квалификатора. Если вы используете LCID как квалификатор, то, скорее всего, не хотите отображать для вашего пользователя значение LCID. Тогда, вероятно, лучше отображать на экране строки типа “Французский словарь”, “Немецкий словарь” или что-то подобное. Вы можете использовать поле AppData в качестве хранилища для этих строк. Когда вы переберете квалификаторы для квалифицированного компонента, функция MsiEnumComponentQualifiers возвратит вам и значение поля Qualifier, и значение поля AppData.

UINT MsiEnumComponentQualifiers(

LPTSTR szComponent, // GUID компонент (строка)

DWORD iIndex, // Индекс квалификаторов

LPTSTR lpQualifierBuf, // Буфер для квалификатора

DWORD *pcchQualifierBuf, // Число символов в буфере

LPTSTR lpApplicationDataBuf, // Буфер для данных приложения

DWORD *pcchApplicationDataBuf // Число символов в буфере

)

Рис. 6. Квалифицированные компоненты APIs

Рис. 7 показывает некий код, который находит компонент, чей квалификатор в формате LCID выражен строкой (“1033” для американского английского), или возвращает по умолчанию. Он также пытается использовать первоначально установленный язык, если нужный язык не установлен (например, если нужен бразильский португальский, но установлен только португальский, я найду его). Это только одна из обычных возможностей использования квалифицированных компонентов.Функции установки и конфигурации

Как вы, наверное, ожидали, несколько функций инсталлятора имеет дело с реально устанавливаемыми продуктами и индивидуальными подпрограммами. Маловероятно, что вы будете нуждаться в программной установке полного продукта — пользователи сами выполнят программу настройки для установки вашего продукта. Однако, если вам это необходимо, вы можете использовать функцию MsiInstallProduct (например, в случае когда подпрограмма поставляется в комплекте с вашим основным продуктом). Помните, тем не менее, что, запуская программу установки, не обязательно копировать все файлы вашего приложения на локальную машину.

Если вы обнаружили, что установка вашего продукта прервана на каком-нибудь критическом этапе, вы можете переустановить его, используя функцию MsiReinstallProduct. В этом случае также перепишется вся относящаяся к вашему продукту информация — данные для реестра, включая регистрацию COM-классов. Приложения часто выдают сообщения об ошибках, которые советуют пользователю повторить установку заново, если ожидаемый COM-сервер не может быть создан или когда происходят события, которые не должны произойти. Функция MsiReinstallProduct позволяет делать эту переустановку автоматически. Чтобы более четко контролировать ситуацию, вы можете повторно устанавливать только определенную подпрограмму — возможно, как раз поврежденную.

Функции MsiConfigureProduct и MsiConfigureFeature можно использовать, чтобы изменить параметры установки определенной подпрограммы — например, сделать подпрограмму, которая запускается из централизованного установочного пакета, вместо этого запускаемой локально. Это можно сделать путем копирования файлов всех компонентов, связанных с данной подпрограммой, на локальный компьютер.

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

Большую часть времени вы будете использовать функции инсталлятора для выяснения, установлена ли подпрограмма (тогда вы узнаете, стоит ли разрешить пользовательский интерфейс для этой подпрограммы), и выяснения маршрутов к файлам, в которых вы нуждаетесь при запуске ваших приложений. Компонент имеет ключевой файл. При проверке правильности установки компонента инсталлятор использует факт существования ключевого файла как функцию быстрого доступа. Есть возможность заставить инсталлятор делать более обширную проверку, но обычно так поступают, только если сталкиваются с какой-то проблемой. Инсталлятор требует, чтобы все файлы, связанные с отдельным компонентом, копировались в один каталог так, чтобы маршрут к ключевому файлу также показывал, где расположены все остальные файлы.

#include <windows.h>

#include <msi.h>

//——————————————————————————————

// FindPerLangComponent()

//

// Выполняет поиск компонента, используя функцию

// MsiProvideQualifiedComponent. Сначала функция пытается

// использовать LCID, переданный в качестве квалификатора.

// Если это не получается, она пытается

// использовать первоначально установленный язык, если

// нужный язык не установлен. Если ни то, ни другое не

// получается, возвращается код ошибки.

// Маршрут к компоненту возвращается в параметре

// szPath, если компонент обнаружен. Параметр szComponentCode

// содержит GUID квалификатора компонента как строку:

// TCHAR szComponent[]=

// TEXT(“{5CB2D5F5-19DD-11d1-9A9D-006097C4E489}”);

UINT FindPerLangComponent(

LPCTSTR szComponentCode,

LCID lcid,

LPTSTR szPath,

DWORD *pcchPath)

{

DWORD cchРathInitial = *pcchPath;

TCHAR szQualifier[MAX_PATH];

UINT iResult;

*szPath = 0;

for (UINT i=0; i < 2; i++)

{

wsprintf(szQualifier, TEXT(“%4.4x”), i == 0 ?

LANGIDFROMLCID(lcid) :

PRIMARYLANGID(LANDGIDFROMLCID(lcid)));

*pccPath = cchPathInitial;

iResult = MsiProvideQualifiedComponent(szComponentCode,

szQualifier, INSTALLMODE_DEFAULT, szPath, pccPath);

if (iResult == ERROR_SUCCESS ||

iResult == ERROR_INSTALL_USEREXIT ||

iResult == ERROR_INSTALL_FAILURE)

break;

}

return iResult;

}

Рис. 7. Поиск компонента

Используйте функцию MsiQueryFeatureState для определения состояния установки конкретной подпрограммы. Функция MsiQueryFeatureState работает быстро, поэтому не проверяет наличие компонентов подпрограммы. Она проверяет, была ли подпрограмма помечена как установленная или нет. Функция MsiQueryFeatureState возвращает INSTALLSTATE для подпрограммы (см. рис. 4).

Чтобы получить маршрут к ключевому файлу компонента, сначала проверяют состояние связанной с компонентом подпрограммы. Если подпрограмма не установлена, вы не сможете узнать маршрут к ее компонентам. Для установленных компонентов используйте функцию MsiGetComponentPath, чтобы получить маршрут к ключевому файлу. Вы можете использовать этот маршрут, чтобы найти другие файлы компонента (рис. 8).

#include <windows.h>

#include <msi.h>

// FGetFilePath()

// Возвращает маршрут к файлу, используя интерфейс инсталлятора

// Windows. Поддерживает ключевые и неключевые файлы компонента.

// Если файл не установлен, устанавливает его, если параметр

// fInstall равен TRUE. Параметр szComponentCode является GUID

// компонент в виде строки:

// TCHAR szComponent[]=

// TEXT(“{5CB2D5F5-19DD-11d1-9A9D-006097C4E489}”);

//

// Код продукта для компонента. Инициализируется при

// запуске приложения используя функцию MsiGetProductCode.

//

TCHAR vszProductCode[39] = {0}; // Место для строки GUID

UINT FGetFilePath (

LPCTSTR szFeature, // Подпрограмма

LPCTSTR szComponentCode, // GUID компонент

LPCTSTR szFileName, // Имя файла

BOOL fInstall, // Устанавливать или нет

BOOL fFix, // Исправлять или нет

LPTSTR szPath, // Маршрут

DWORD *pcchPath) // Размер буфера

{

UINT cchPath = *pcchpath; // Запомним размер буфера

UINT is; // Состояние компонента

DWODR er = ERROR_SUCCESS;

*szPath = 0;

is = MsiGetComponentPath(vszProductCode,

szComponentCode, szPath, pcchPath);

switch (is)

{

case INSTALLSTATE_DEFAULT:

case INSTALLSTATE_LOCAL:

case INSTALLSTATE_SOURCE:

// Компонент установлен

if (!fFix)

break;

// Исправить компонент

case INSTALLSTATE_UNKNOWN:

// Компонент не установлен

// Определим его состояние

// или подпрограмму

is = MsiQueryFeatureState(vszProductCode, szFeature);

// Подпрограмма отменена, мы не можем

// ее установить

if (is == INSTALLSTATE_ABSENT)

return INSTALLSTATE_ABSENT;

if (fInstall || fFix)

er = MsiConfigureFeature(vszProductCode, szFeature,

INSTALLSTATE_DEFAULT);

if (!fFix && er == ERROR_SUCCESS)

{

// Определим маршрут к компоненту. Если не

// получится, необходимо исправить

is = MsiGetComponentPath(vszProductCode,

szComponentCode, szPath, pcchPath);

if (is == INSTALLSTATE_LOCAL ||

is == INSTALLSTATE_DEFAULT ||

is == INSTALLSTATE_SOURCE)

break;

}

// Исправить компонент

case INSTALLSTATE_BROKEN:

case INSTALLSATE_INCOMPLETE:

// Попытаться повторно установить

if(fInstall || fFix)

er = MsiReinstallFeature(vszProductCode,

vszFeature,

(REINSTALLMODE_FILEVERIFY |

REINSTALLMODE_REPAIR |

REINSTALLMODE_USERDATA |

REINSTALLMODE_MACHINEDATA));

if (er == ERROR_SUCCESS)

{

// Еще раз получим маршрут к компоненту...

is = MsiGetComponentPath(vszProductCode,

szComponentCode, szPath, pcchPath);

if (is == INSTALLSTATE_LOCAL ||

== INSTALLSTATE_DEFAULT ||

is == INSTALLSTATE_SOURCE)

break;

else

return is;

}

// Мы можем установить или переустановить компонент,

// получить маршрут к нему. Также мы имеем доступ к

// ключевому файлу

if (er == ERROR_SUCCESS && szFileName != NULL)

{

TCHAR *pchSlash = NULL;

TCHAR *pch;

for (pch = szPath; *pch !- 0; pch++)

if (*pch == ‘\’)

pchSlash = pch;

if (pchSlash== &&

(pchSlash-szPath)+lstrlen(szFileName) < cchPath)

{

lstrcpy(pchSlah, szFileName);

}

else

{

er = ERROR_MORE_DATA;

}

}

SetLastError(er);

return er == ERROR_SUCCESS ? is : INSTALLSTATE_BROKEN;

}

Рис. 8

Другие функции инсталлятора

У инсталлятора имеется много других функций. Он содержит пользовательский интерфейс, созданный специальными средствами. Этот интерфейс пользователя поддерживает все интерфейсные элементы Windows и облегчает локализацию вашего инсталлятора для других языков. Если ваше приложение устанавливает отдельную подпрограмму, вы можете решить, позволять ли инсталлятору в это время показывать пользовательский интерфейс или пренебречь этим и обеспечить интерфейс в рамках вашего приложения. С помощью функции MsiSetExternalUI вы обеспечиваете инсталлятор функцией, которую он будет вызывать для отображения процесса установки. Вы можете использовать эту косвенно-вызываемую функцию для отображения собственного пользовательского интерфейса. Имеется также целый набор функций для доступа к внутренним базам данных инсталлятора; они используются либо специальными средствами, либо при создании нестандартных инсталляторов. Для получения дополнительной информации загрузите Platform SDK и просмотрите справочный файл MSI.HLP.

Заключение

Скорее всего, программа установки и настройки приложения является последней в списке необходимых вещей для разрабатываемого вами приложения. Вы надеетесь, что установка нового продукта будет довольно простой: копируете несколько файлов, записываете несколько элементов реестра, и все готово. Если вы готовите новую версию старого приложения, то знаете, как сложно сделать надлежащую программу установки для сегодняшних приложений: регистрация COM-серверов, создание ярлыков быстрого доступа, манипулирование различными, не совместимыми с друг другом версиями DLL и различия в программных платформах. Вы рассчитываете просто взять установочную программу последней версии, немного изменить ее и работать. В любом случае установка программ — это последний раунд в создании приложения.

С новым инсталлятором Windows вы можете еще в начале проекта, когда определяете ключевые компоненты вашего продукта и разрабатываете интерфейс пользователя, продумать, как ваш продукт будет устанавливаться. Если вы поразмыслите об этом заранее, ваше приложение настроит режим использования локального диска согласно предпочтениям каждого пользователя и автоматически переустановит отсутствующие файлы. Вы можете также стать участником новой инициативы Zero Administration for Windows, поддерживая “назначение и публикацию” приложения.

Так как продавцы популярных инсталляторов типа InstallShield и Wise представляют поддержку для инсталлятора Windows, вы можете использовать знакомые инструменты для написания более разумных установочных программ. Инсталлятор Windows помогает замкнуть цикл между начальной установкой и выполняемым приложением, вселяя надежду на уменьшение количества просьб о помощи по вопросам, связанным с вашим продуктом. С его помощью многие проблемы, например автоматическая замена отсутствующих файлов, решаются самостоятельно.

Чтобы получить полный листинг исходных кодов, обратитесь на Web-страницу MSJ по адресу http: // www.microsoft.com/msj.



  • Главная
  • Новости
  • Новинки
  • Скрипты
  • Форум
  • Ссылки
  • О сайте




  • Emanual.ru – это сайт, посвящённый всем значимым событиям в IT-индустрии: новейшие разработки, уникальные методы и горячие новости! Тонны информации, полезной как для обычных пользователей, так и для самых продвинутых программистов! Интересные обсуждения на актуальные темы и огромная аудитория, которая может быть интересна широкому кругу рекламодателей. У нас вы узнаете всё о компьютерах, базах данных, операционных системах, сетях, инфраструктурах, связях и программированию на популярных языках!
     Copyright © 2001-2024
    Реклама на сайте