Как сделать установщик
Готовая программа редко состоит из одного .exe. Обычно нужны папка с файлами, ярлык, запись об установке и возможность удалить всё без ручной охоты по Program Files. Установщик автоматизирует этот сценарий: пользователь запускает один файл — и через минуту может работать.
См. также: Исполняемые файлы (в т.ч. MSI и установочные пакеты), Архивы и установочные пакеты, Распространение десктопных приложений, Microsoft Store и MSIX, Electron — сборка установщика.
Содержание
- Что такое установщик
- Архитектура: что обязан делать установщик
- Inno Setup
- Скрипт на PowerShell
- Скрипт на Python и PyInstaller
- Установщик на C# (WinForms / WPF)
- Сравнение подходов
- Чек-лист перед релизом
Что такое установщик
Установочный файл (часто Setup.exe, MyApp-1.0.0-installer.exe) — программа, которая разворачивает ваше приложение на компьютере пользователя. Внутри неё обычно лежит сжатый набор файлов (как в архиве) и логика: куда копировать, что проверить, что записать в систему.
Установочный пакет — более широкое понятие: сам дистрибутив плюс метаданные (версия, зависимости, права). Примеры форматов пакетов без «своего» GUI: .msi (Windows Installer), .deb / .rpm (Linux), .msix (Store). Примеры собственного мастера установки: Inno Setup, NSIS, WiX, скрипт на PowerShell/Python, приложение на C#.
Главная цель для пользователя и разработчика одна: быстро и предсказуемо получить рабочую копию программы — без ручного копирования десятков файлов и без гадания «куда положить DLL».
Как это выглядит для пользователя
- Скачал один файл с сайта или получил по ссылке.
- Запустил (Windows может спросить права администратора).
- Прошёл мастер: папка → компоненты → «Установить».
- Появился ярлык; приложение запускается.
- При необходимости — «Удалить программу» в параметрах Windows (если установщик зарегистрировал деинсталлятор).
Что такое setup.exe
Имя setup.exe — устоявшееся соглашение, не отдельный формат. Это обычный PE-исполняемый файл (исполняемые файлы), внутри которого может быть:
- оболочка Inno Setup / NSIS / InstallShield;
- bootstrap Visual Studio (
vs_setup.exe); - ваш скрипт, упакованный через PyInstaller;
- MSI, переименованный или обёрнутый в EXE (редко, но встречается).
Антивирусы и SmartScreen смотрят на подпись издателя и репутацию файла, а не на слово setup в имени.
Архив только распаковывает файлы. Установщик ещё проверяет ОС, создаёт ярлыки, пишет ключ в реестр для удаления и может поставить зависимости (runtime). Подробнее — в статье «Архивы и установочные пакеты».
Архитектура: что обязан делать установщик
Минимальная логика любого приличного установщика:
| Этап | Задача |
|---|---|
| До установки | Проверить ОС (64-bit?), свободное место, не занята ли ли уже другая версия |
| Выбор пути | Папка по умолчанию (%ProgramFiles%\Vendor\App) и возможность изменить |
| Копирование | Файлы приложения, ресурсы, конфиги по умолчанию |
| Интеграция | Ярлыки (меню «Пуск», при необходимости рабочий стол), ассоциации файлов |
| Регистрация | Запись в «Приложения и компоненты» — чтобы работало удаление |
| После установки | Опционально: запуск приложения, открытие README |
| Деинсталлятор | Скрипт или unins000.exe, который удаляет скопированное и чистит записи |
Права администратора
Установка в C:\Program Files требует повышения прав (UAC). Если ставите только в профиль пользователя (%LocalAppData%\Programs\MyApp), админ часто не нужен — так делают многие «портативные» и Electron-сборки.
| Куда ставим | Типичные права |
|---|---|
Program Files | Администратор |
%LocalAppData% | Обычный пользователь |
Запись в HKLM (машина) | Администратор |
Запись в HKCU (текущий пользователь) | Обычный пользователь |
Проверка папки
Перед копированием стоит убедиться:
- путь существует или его можно создать;
- на диске достаточно места;
- нет конфликта с запущенным процессом (файлы не заблокированы);
- при обновлении — корректно перезаписать или сначала остановить старую версию.
Деинсталлятор
В Windows список «Установка и удаление программ» питается из реестра:
HKLM\Software\Microsoft\Windows\CurrentVersion\Uninstall\{GUID}— для всех пользователей;HKCU\Software\Microsoft\Windows\CurrentVersion\Uninstall\{GUID}— только для текущего.
Ключевые поля: DisplayName, UninstallString, InstallLocation, DisplayVersion, Publisher.
Без этой записи пользователь будет удалять папку вручную — и часто оставит мусор в реестре и в %AppData%.
Что ещё учесть
- Зависимости: .NET, VC++ Redistributable, WebView2 — либо включить в установщик, либо проверить и скачать (self-contained сборка .NET).
- Обновления: тот же GUID в Uninstall, инкремент версии, опция «удалить старую перед копированием».
- Подпись кода: Authenticode снижает предупреждения SmartScreen (дистрибуция).
- Тихая установка: ключи
/SILENT,/VERYSILENT(Inno),/quiet(MSI) — для IT и CI.
Публикация в Microsoft Store — отдельный путь через MSIX, без классического setup.exe: 117.
Inno Setup
Inno Setup — бесплатный установщик для Windows с декларативным скриптом .iss и визуальным мастером Inno Setup Compiler. Подходит, когда нужен привычный мастер «Далее → Далее → Готово» без написания GUI на C#.
Плюсы: быстрый старт, встроенное сжатие LZMA, деинсталлятор, локализация, тихий режим.
Минусы: только Windows; сложная кастомная логика — через Pascal-скрипты в [Code].
Минимальный пример MyApp.iss:
#define MyAppName "My Demo App"
#define MyAppVersion "1.0.0"
#define MyAppPublisher "IT Universe"
#define MyAppExeName "MyApp.exe"
[Setup]
AppId={{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}
AppName={#MyAppName}
AppVersion={#MyAppVersion}
AppPublisher={#MyAppPublisher}
DefaultDirName={autopf}\{#MyAppName}
DefaultGroupName={#MyAppName}
OutputBaseFilename=MyApp-{#MyAppVersion}-setup
Compression=lzma2
SolidCompression=yes
PrivilegesRequired=admin
[Languages]
Name: "russian"; MessagesFile: "compiler:Languages\Russian.isl"
[Files]
Source: "publish\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs
[Icons]
Name: "{group}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"
Name: "{autodesktop}\{#MyAppName}"; Filename: "{app}\{#MyAppExeName}"; Tasks: desktopicon
[Tasks]
Name: "desktopicon"; Description: "Ярлык на рабочем столе"; GroupDescription: "Дополнительно:"
[Run]
Filename: "{app}\{#MyAppExeName}"; Description: "Запустить {#MyAppName}"; Flags: nowait postinstall skipifsilent
Сборка: положите результат dotnet publish или Release-сборку в папку publish\, откройте .iss в Inno Setup Compiler → Compile. На выходе — один MyApp-1.0.0-setup.exe.
Официальная документация и примеры — на jrsoftware.org.
Скрипт на PowerShell
Скриптовый установщик уместен для внутренних утилит, CI и админов: без GUI, зато прозрачная логика в репозитории. Запуск: правый клик → «Выполнить с PowerShell» или powershell -ExecutionPolicy Bypass -File install.ps1.
Структура проекта:
installer/
install.ps1
payload/ ← сюда кладёте собранное приложение
MyApp.exe
appsettings.json
Пример install.ps1 (упрощённо, без GUI):
#Requires -RunAsAdministrator
$ErrorActionPreference = "Stop"
$AppName = "MyDemoApp"
$Version = "1.0.0"
$InstallRoot = Join-Path $env:ProgramFiles $AppName
$PayloadDir = Join-Path $PSScriptRoot "payload"
$UninstallKey = "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\$AppName"
if (-not (Test-Path $PayloadDir)) {
throw "Не найдена папка payload рядом со скриптом."
}
$requiredMb = 50
$drive = (Split-Path $InstallRoot -Qualifier)
$freeMb = (Get-PSDrive ($drive.TrimEnd(':'))).Free / 1MB
if ($freeMb -lt $requiredMb) {
throw "Недостаточно места на диске (нужно ~$requiredMb МБ)."
}
if (Test-Path $InstallRoot) {
Write-Host "Обновление: удаляем предыдущую копию..."
Remove-Item $InstallRoot -Recurse -Force
}
New-Item -ItemType Directory -Path $InstallRoot -Force | Out-Null
Copy-Item -Path (Join-Path $PayloadDir "*") -Destination $InstallRoot -Recurse -Force
$exePath = Join-Path $InstallRoot "MyApp.exe"
$uninstallScript = Join-Path $InstallRoot "uninstall.ps1"
@'
Remove-Item -LiteralPath $PSScriptRoot -Recurse -Force
Remove-Item -Path "HKLM:\Software\Microsoft\Windows\CurrentVersion\Uninstall\MyDemoApp" -ErrorAction SilentlyContinue
'@ | Set-Content -Path $uninstallScript -Encoding UTF8
New-Item -Path $UninstallKey -Force | Out-Null
Set-ItemProperty -Path $UninstallKey -Name DisplayName -Value $AppName
Set-ItemProperty -Path $UninstallKey -Name DisplayVersion -Value $Version
Set-ItemProperty -Path $UninstallKey -Name Publisher -Value "IT Universe"
Set-ItemProperty -Path $UninstallKey -Name InstallLocation -Value $InstallRoot
Set-ItemProperty -Path $UninstallKey -Name UninstallString -Value "powershell.exe -ExecutionPolicy Bypass -File `"$uninstallScript`""
$WshShell = New-Object -ComObject WScript.Shell
$Shortcut = $WshShell.CreateShortcut("$env:ProgramData\Microsoft\Windows\Start Menu\Programs\$AppName.lnk")
$Shortcut.TargetPath = $exePath
$Shortcut.WorkingDirectory = $InstallRoot
$Shortcut.Save()
Write-Host "Установлено в $InstallRoot"
Ограничения: Execution Policy, предупреждения SmartScreen для неподписанных .ps1, слабее UX, чем у Inno. Для обучения и автоматизации — отличный первый шаг. См. PowerShell в терминале.
Скрипт на Python и PyInstaller
Python удобен, когда установщик должен быть кроссплатформенным (Windows / macOS / Linux): одна логика на pathlib и shutil, разные пути по умолчанию.
Пример installer.py (консольный, без GUI):
#!/usr/bin/env python3
"""Простой установщик: копирует payload/ в каталог приложения."""
from __future__ import annotations
import argparse
import platform
import shutil
import sys
from pathlib import Path
APP_NAME = "MyDemoApp"
VERSION = "1.0.0"
def default_install_dir() -> Path:
system = platform.system()
if system == "Windows":
base = Path.home() / "AppData" / "Local" / "Programs"
elif system == "Darwin":
base = Path.home() / "Applications"
else:
base = Path.home() / ".local" / "share"
return base / APP_NAME
def install(target: Path, payload: Path) -> None:
if not payload.is_dir():
raise FileNotFoundError(f"Нет папки payload: {payload}")
if target.exists():
shutil.rmtree(target)
target.parent.mkdir(parents=True, exist_ok=True)
shutil.copytree(payload, target)
print(f"{APP_NAME} {VERSION} установлен в {target}")
def main() -> int:
parser = argparse.ArgumentParser(description=f"Установщик {APP_NAME}")
parser.add_argument(
"--dir",
type=Path,
default=default_install_dir(),
help="Каталог установки",
)
args = parser.parse_args()
script_dir = Path(__file__).resolve().parent
payload = script_dir / "payload"
try:
install(args.dir.resolve(), payload)
except OSError as exc:
print(f"Ошибка: {exc}", file=sys.stderr)
return 1
return 0
if __name__ == "__main__":
raise SystemExit(main())
Рядом с скриптом — папка payload/ с MyApp.exe или всей сборкой.
Один файл для Windows: PyInstaller
Чтобы раздать пользователю один .exe без установленного Python:
pip install pyinstaller
pyinstaller --onefile --noconsole installer.py
--onefile— всё в одном исполняемом файле (при старте распаковка во временную папку).--noconsole— без чёрного окна (для GUI-установщика; для отладки уберите флаг).
Артефакт: dist/installer.exe. Его можно положить на сайт как «портативный установщик». Для полноценного мастера с шагами позже тот же скрипт оборачивают в tkinter / PyQt или собирают Inno поверх папки payload.
Связь с упаковкой Python-приложений: раздел про PyInstaller в Исполняемых файлах.
Установщик на C# (WinForms / WPF)
Когда нужен свой интерфейс (брендинг, лицензия, выбор компонентов, прогресс-бар с логом) — пишут отдельное desktop-приложение-установщик на .NET. Обычно это WinForms (быстрее для простого мастера) или WPF (гибче вёрстка). См. WinForms, WPF, примеры в Lab.
Типичная архитектура:
- UI — несколько страниц (
UserControl/Page): приветствие → путь → прогресс → готово. - InstallService — копирование, проверки, запись в реестр (класс без привязки к UI).
- Rollback — при ошибке на середине удалить уже скопированное.
Фрагмент сервиса установки (ядро логики):
using System.IO;
using Microsoft.Win32;
public sealed class InstallService
{
private const string UninstallKeyPath =
@"Software\Microsoft\Windows\CurrentVersion\Uninstall\MyDemoApp";
public void Install(string sourceDir, string targetDir, string publisher, string version)
{
if (!Directory.Exists(sourceDir))
throw new DirectoryNotFoundException(sourceDir);
Directory.CreateDirectory(targetDir);
CopyAll(new DirectoryInfo(sourceDir), new DirectoryInfo(targetDir));
using var key = Registry.LocalMachine.CreateSubKey(UninstallKeyPath, true);
key.SetValue("DisplayName", "My Demo App");
key.SetValue("DisplayVersion", version);
key.SetValue("Publisher", publisher);
key.SetValue("InstallLocation", targetDir);
key.SetValue("UninstallString", $"\"{Path.Combine(targetDir, "uninstall.exe")}\"");
}
private static void CopyAll(DirectoryInfo source, DirectoryInfo target)
{
target.Create();
foreach (var file in source.GetFiles())
file.CopyTo(Path.Combine(target.FullName, file.Name), overwrite: true);
foreach (var dir in source.GetDirectories())
CopyAll(dir, target.CreateSubdirectory(dir.Name));
}
}
Деинсталлятор — отдельный маленький проект Uninstall.exe (или тот же код с флагом --uninstall), который читает InstallLocation, удаляет дерево файлов и ветку реестра.
Сборка release:
dotnet publish installer/Installer.csproj -c Release -r win-x64 --self-contained false
Для распространения без установленного .NET на машине пользователя добавьте --self-contained true или положите VC++ / .NET runtime в Inno Setup.
WinForms-мастер: форма с Button «Далее», TextBox для пути (FolderBrowserDialog), ProgressBar + фоновый Task.Run для копирования (не блокировать UI — см. 112).
Повторное использование демо в других статьях
Тот же мастер можно вставить в гайды по установке IDE, runtime или первой программы — с другими текстами через props:
import InstallerWizardPlay from '@site/src/components/InstallerWizardPlay.jsx';
<InstallerWizardPlay
appName="Установка Visual Studio"
productName="Visual Studio Community"
welcomeText="Мастер установит среду разработки и выбранные рабочие нагрузки."
/>
Доступные props: appName, productName, welcomeText, pathDescription, defaultPath, extraOptionsLabel, cardTitle, cardSubtitle, showCard (если false — только окно мастера без обёртки карточки).
Сравнение подходов
| Подход | Сложность | GUI | Кроссплатформа | Типичное применение |
|---|---|---|---|---|
| Inno Setup | Низкая | Готовый мастер | Windows | Релизы Win32/.NET для сайта |
| PowerShell | Низкая | Нет (можно дописать) | В основном Windows | IT, внутренние tools |
| Python + PyInstaller | Средняя | По желанию (tkinter) | Да | Утилиты, прототипы |
| C# WinForms/WPF | Высокая | Полный контроль | Windows (.NET) | Бренд, сложные сценарии |
| MSI / MSIX | Средняя–высокая | Стандарт Windows | Windows | Корпорации, Store (117) |
| electron-builder | Средняя | Зависит от tool | Win/mac/Linux | Electron-приложения (114) |
Чек-лист перед релизом
- Установка на чистой VM / виртуалке без SDK
- Запуск приложения после установки и после перезагрузки
- Удаление через «Приложения» — папка и ярлыки исчезли
- Повторная установка поверх старой версии (обновление)
- Путь с пробелами и кириллицей в имени пользователя
- Подпись установщика (желательно для внешних пользователей)
- README: системные требования и что делать при ошибке