Кнопка "Установить" запускает не просто копирование файлов. Внутри 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, которые всплывают и на экране, и в конце журнала, выглядят так:

  1. 1603 – неустранимая ошибка установки, чаще всего за ней стоит сбой пользовательского действия, занятый файл или нехватка прав;
  2. 1612 – источник установки недоступен, обычно это след повреждённого кэша в C:\Windows\Installer;
  3. 1618 – уже выполняется другая установка, служба не ведёт несколько транзакций одновременно;
  4. 1638 – на компьютере стоит другая версия того же продукта, и коды продуктов не совпали;
  5. 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 или программа откажется удаляться, виновника теперь получится найти по журналу за минуты, а не за вечер.