Кнопка "Установить" запускает не просто копирование файлов. Внутри Windows в этот момент трудится целый механизм: он читает базу данных из пакета, составляет сценарий, выполняет его одной транзакцией с возможностью отката и фиксирует каждый шаг в журнале. Формат пакетов называется MSI, сам движок – Windows Installer, и работает он с 1999 года. Ниже показано, что происходит между щелчком по кнопке и готовой программой в меню "Пуск".
MSI-файл устроен как контейнер с базой из таблиц
Первый сюрприз ждёт уже на уровне формата: файл с расширением .msi не архив и не исполняемая программа, а составной документ OLE, то есть структурированное хранилище. Внутри лежат потоки, и каждый поток занимает одна таблица реляционной базы: Directory задаёт папки, Property перечисляет свойства, File содержит список файлов, Component описывает компоненты, Feature группирует их в функции, Registry хранит записи реестра, Shortcut отвечает за ярлыки. Есть и таблицы поведения: InstallExecuteSequence и InstallUISequence задают порядок действий, CustomAction описывает собственные действия автора, Upgrade учитывает предыдущие версии. По сути, установщик применяет к системе тот же подход, что и СУБД к данным: читает строки, проверяет условия, выполняет план.
Посмотреть таблицы можно в редакторе Orca из состава Windows SDK. Он открывает пакет и показывает таблицы листами, будто книгу с вкладками; там же работает проверка правил ICE, которая ругается на ошибки в описаниях. Файловых форматов у технологии несколько: .msm называют модулем слияния и поставляют общие компоненты внутрь пакетов, .mst – преобразование таблиц, .msp – обновление с дельтами для уже установленных программ.
Функции и компоненты, на которые раскладывается программа
Каждая программа внутри пакета распадается на функции и компоненты, и это не синонимы. Функция – то, что видит пользователь: её названия появляются на экране выбора компонентов, её можно оставить или снять галочкой. Компонент – минимальная неделимая порция: набор файлов, записей реестра, ярлыков и служб, который ставится и удаляется только целиком. У каждого компонента есть уникальный GUID и опорный элемент KeyPath – файл, папка или запись реестра, по состоянию которых движок решает, установлен ли компонент.
Главное правило авторинга: один ресурс не должен делиться между компонентами. Если два производителя выпускают пакеты с общим файлом и оба описывают его в своих компонентах, Windows Installer заводит на файл счётчик ссылок в реестре и снимает его только после удаления последнего владельца. Именно поэтому программы на одной машине годами не отбирают друг у друга общие библиотеки. Связь между функциями и компонентами фиксирует таблица FeatureComponents, а столбец Level задаёт порог: если свойство INSTALLLEVEL при установке ниже этого числа, функция останется за бортом.
Атомарность приносит и предсказуемый ремонт: при восстановлении движок перепроверяет опорные элементы и перезаписывает только то, что расходится с описанием.
Сценарий установки в таблице InstallExecuteSequence
Установка идёт в два прохода. Сначала движок собирает информацию: оценивает свободное место, проверяет условия, задаёт вопросы пользователю через диалоги, и всем этим управляет таблица InstallUISequence. Затем наступает черёд исполнения: действия из InstallExecuteSequence превращаются в единый сценарий, который выполняется уже без лишних вопросов. Поэтому пакет можно поставить "молча", ключом /qn: интерфейсная фаза просто пропускается.
Столбцы таблицы просты: Action содержит имя действия, Condition – условие в виде выражения, Sequence – порядковый номер. Номер важен до мелочей: нулевое или пустое значение означает, что действие никогда не выполнится, а отрицательные числа привязывают диалоги к исходам, например -1 срабатывает при успехе, -3 при сбое. Стандартные действия идут по номерам в строгом порядке: LaunchConditions проверяет права и версию системы, CostInitialize, FileCost и CostFinalize считают место, InstallValidate ищет конфликты, InstallInitialize открывает транзакцию, InstallFiles копирует файлы, WriteRegistryValues пишет реестр, CreateShortcuts создаёт ярлыки, InstallFinalize закрывает транзакцию.
Свои шаги авторы добавляют через CustomAction: так вызываются библиотеки, программы и скрипты. Немедленные действия срабатывают сразу при сборке сценария, отложенные – уже внутри транзакции, и только отложенные участвуют в откате. Разница на деле огромна: тяжёлая операция в немедленном действии способна нарушить атомарность всей установки.
Транзакция с откатом и точки восстановления
Между InstallInitialize и InstallFinalize движок ведёт двойную бухгалтерию. Пока сценарий меняет систему, параллельно пишется сценарий отката, а каждый удаляемый файл предварительно копируется в скрытую системную папку, обычно это C:\Config.Msi, где он лежит с расширением .rbf рядом со сценарием .rbs. Если установка доходит до конца, вспомогательные файлы исчезают сами. Если случается сбой, движок прокручивает сценарий назад: возвращает перезаписанные файлы, вычищает записи реестра, и система остаётся ровно в том виде, в каком была до щелчка по кнопке.
Откат включён по умолчанию, и отключать его стоит лишь в особых случаях, через свойство DISABLEROLLBACK, например когда пакет огромен, а диск забит. Второй страховкой служит точка восстановления: если защита системы включена, установщик перед изменениями создаёт контрольную точку сам. Проверить это можно по пути Панель управления → Система и безопасность → Система → Защита системы, кнопка "Настроить": здесь видно, включена ли защита для диска C:, а ползунком "Использование дискового пространства" отводится место под точки.
Точку можно поставить и вручную. Откройте PowerShell от имени администратора и введите команду:
Checkpoint-Computer -Description "Перед установкой" -RestorePointType MODIFY_SETTINGS
После выполнения в защите системы появится новая точка с тем описанием, что было в команде. А свойство MSIFASTINSTALL со значением 1 отменяет создание точки восстановления для конкретной установки. В новых сборках Windows частоту точек дополнительно ограничивает параметр SystemRestorePointCreationFrequency в ветке HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\SystemRestore: он задаёт минимальный интервал в минутах, а значение 0 снимает ограничение.
Скрытый кэш пакетов и почему его нельзя чистить руками
Установка заканчивается, но пакет не исчезает. Движок копирует .msi и применённые обновления .msp в системный кэш C:\Windows\Installer – скрытый и защищённый, с файлами под малопонятными именами. Кэш не мусор: из него берутся данные для ремонта, удаления и установки будущих обновлений. Стереть папку руками – верный способ получить программу, которая висит в списке установленных, но не удаляется: деинсталлятор обратится к источнику, не найдёт его и завершится ошибкой 1612.
Размер кэша стоит знать и держать под контролем. В PowerShell выполните:
Get-ChildItem C:\Windows\Installer -Filter *.msi -Force | Measure-Object -Property Length -Sum
В выводе будут количество файлов и их суммарный размер в байтах. Растёт кэш в основном из-за крупных офисных пакетов и постоянных обновлений к ним; штатной очистки для него не существует, поэтому разумный приём – периодически снимать замеры и не пугаться нескольких гигабайт.
Отдельная роль у службы. Установки ведёт процесс msiexec.exe, но за ним стоит служба Windows Installer с внутренним именем msiserver: она стартует по требованию и гаснет после простоя. Список служб открывается через сочетание Win+R и команду services.msc, там находится строка "Windows Installer" с типом запуска "Вручную". Если служба зависла посреди сбойной установки, в командной строке от имени администратора перезапустите её двумя командами:
net stop msiserver
net start msiserver
После перезапуска повторите установку: незавершённая транзакция откатится при следующем обращении к установщику.
Журналы установки и подробная запись через ключ /l*v
Журнал – главный инструмент, когда установка ведёт себя странно. Самый частый приём выглядит так: откройте командную строку и выполните:
msiexec /i C:\Distr\app.msi /l*v C:\Logs\install.log
Ключ /i ставит пакет, /lv включает предельно подробную запись в файл install.log. Полный набор букв режимов таков: i – сообщения о состоянии, w – некритичные предупреждения, e – все ошибки, a – старт действий, r – записи действий, u – запросы пользователя, c – исходные параметры интерфейса, m – нехватка памяти, o – сообщения о нехватке места, p – итоговые свойства, x – дополнительная отладка. Звёздочка включает всё, кроме подробного режима и отладки, поэтому пары /lvx хватает почти всегда. Папка для журнала должна существовать заранее, установщик её не создаёт.
Включить журнал для всех установок сразу, без ключей, можно политикой реестра. Создайте reg-файл со строками:
[HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Windows\Installer]
"Logging"="voicewarmup"
После слияния файла каждый запуск установщика начнёт писать MSI*.LOG во временную папку текущего пользователя, в %TEMP%. Звёздочку и плюс в значении политики использовать нельзя, поэтому строка собирается из букв вручную.
Читать журнал учатся на одном маркере: строка "Return value 3" почти всегда предшествует неустранимой ошибке, и тексты рядом называют виновника – действие, файл или компонент. Частые коды возврата msiexec, которые всплывают и на экране, и в конце журнала, выглядят так:
- 1603 – неустранимая ошибка установки, чаще всего за ней стоит сбой пользовательского действия, занятый файл или нехватка прав;
- 1612 – источник установки недоступен, обычно это след повреждённого кэша в C:\Windows\Installer;
- 1618 – уже выполняется другая установка, служба не ведёт несколько транзакций одновременно;
- 1638 – на компьютере стоит другая версия того же продукта, и коды продуктов не совпали;
- 1719 – служба Windows Installer недоступна, и её стоит перезапустить через services.msc.
Контекст установки и свойства ALLUSERS и MSIINSTALLPERUSER
Один и тот же пакет может ставиться на всю машину или только текущему пользователю, и решают это свойства. Контекст на машину пишет файлы в Program Files, а записи – в HKLM; контекст на пользователя отправляет данные в папку профиля и ветку HKCU. Если свойство ALLUSERS не задано, установка идёт для пользователя; значение 1 означает машину; значение 2 включает согласование: система смотрит на права пользователя и на свойство MSIINSTALLPERUSER.
Схема с двойкой работает начиная с Windows 7, то есть с пятой версии Windows Installer. Пакет, свёрстанный по правилам единой сборки, понимает оба контекста: MSIINSTALLPERUSER=1 даёт пользователю без прав администратора поставить программу себе без запроса UAC, а пустое значение MSIINSTALLPERUSER уводит установку на машину, уже с запросом прав. После установки движок сбрасывает ALLUSERS в единицу для машинного контекста или в пустую строку для пользовательского, поэтому последующие ремонт и обновление идут в том же контексте.
Перечень установленных программ лежит в двух ветках реестра:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
HKEY_CURRENT_USER\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
Внутри находятся подразделы с именами продуктов; параметр UninstallString хранит готовую команду вида MsiExec.exe /X{GUID}, параметр InstallDate – дату установки. Удаление по коду из командной строки выглядит так, где в фигурные скобки подставляется код из UninstallString:
msiexec /x {XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX} /qn
Ключ /x удаляет продукт, /qn убирает интерфейс. Для интерактивного удаления в Windows 11 открывается путь Параметры → Приложения → Установленные приложения, в Windows 10 – Параметры → Приложения → Приложения и возможности. Напоследок предупреждение: запрос к классу Win32_Product через PowerShell запускает переконфигурацию всех установленных пакетов, поэтому для инвентаризации он годится с осторожностью, а надёжнее читать те самые ветки Uninstall.
Полный цикл замыкается: база таблиц становится сценарием, сценарий – транзакцией с журналом, а транзакция – записями реестра и кэшем на диске. Когда установка встанет с ошибкой 1603 или программа откажется удаляться, виновника теперь получится найти по журналу за минуты, а не за вечер.