Files
USB_installer/src/install.cpp
T

777 lines
27 KiB
C++

#include "app.h"
#include "disks.h"
#include "util.h"
#include <algorithm>
#include <cctype>
#include <cstdlib>
#include <cwchar>
#include <string>
#include <vector>
namespace {
const unsigned long long MB = 1048576ULL;
const int P1_MB = 16384;
const int P2_MB = 32;
const int TAIL_MB = 3;
std::wstring TempName(const std::wstring& run, const std::wstring& suffix)
{
return GetTempDir() + L"paravis_" + run + L"_" + suffix;
}
bool RunHidden(App* app, const std::wstring& commandLine)
{
DebugLog(L"RunHidden: %s", commandLine.c_str());
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_HIDE;
PROCESS_INFORMATION process{};
std::vector<wchar_t> buffer(commandLine.begin(), commandLine.end());
buffer.push_back(L'\0');
if (!CreateProcessW(nullptr, buffer.data(), nullptr, nullptr, FALSE,
CREATE_NO_WINDOW, nullptr, nullptr, &startup, &process)) {
return false;
}
app->WaitForProcess(process.hProcess);
DWORD exitCode = 1;
GetExitCodeProcess(process.hProcess, &exitCode);
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
DebugLog(L"RunHidden done code=%lu", exitCode);
return exitCode == 0;
}
void KillProcessTree(DWORD pid)
{
if (pid == 0) {
return;
}
wchar_t systemDir[MAX_PATH] = {};
GetSystemDirectoryW(systemDir, MAX_PATH);
std::wstring command = L"\"" + std::wstring(systemDir) +
L"\\taskkill.exe\" /T /F /PID " + std::to_wstring(pid);
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_HIDE;
PROCESS_INFORMATION process{};
std::vector<wchar_t> buffer(command.begin(), command.end());
buffer.push_back(L'\0');
if (CreateProcessW(nullptr, buffer.data(), nullptr, nullptr, FALSE,
CREATE_NO_WINDOW, nullptr, nullptr, &startup, &process)) {
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
}
}
bool LaunchLoader(const std::wstring& path)
{
DebugLog(L"LaunchLoader: %s", path.c_str());
std::wstring commandLine = L"\"" + path + L"\"";
std::vector<wchar_t> buffer(commandLine.begin(), commandLine.end());
buffer.push_back(L'\0');
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
PROCESS_INFORMATION process{};
if (!CreateProcessW(nullptr, buffer.data(), nullptr, nullptr, FALSE, 0,
nullptr, nullptr, &startup, &process)) {
DebugLog(L"LaunchLoader failed error=%lu", GetLastError());
return false;
}
DebugLog(L"LaunchLoader started pid=%lu", process.dwProcessId);
CloseHandle(process.hThread);
CloseHandle(process.hProcess);
return true;
}
void UpdateOutputProgress(App* app, const std::string& output,
const std::wstring& caption, double& shown)
{
size_t percent = output.rfind('%');
if (percent == std::string::npos) {
return;
}
size_t index = percent;
while (index > 0 && (output[index - 1] == ' ' || output[index - 1] == '\t' ||
output[index - 1] == '\r' || output[index - 1] == '\n')) {
--index;
}
size_t numberEnd = index;
while (index > 0 && (std::isdigit(static_cast<unsigned char>(output[index - 1])) ||
output[index - 1] == '.' || output[index - 1] == ',')) {
--index;
}
if (index == numberEnd) {
return;
}
std::string number = output.substr(index, numberEnd - index);
std::replace(number.begin(), number.end(), ',', '.');
char* parseEnd = nullptr;
double value = std::strtod(number.c_str(), &parseEnd);
if (parseEnd == number.c_str()) {
return;
}
if (value != shown) {
shown = value;
app->SetWork(caption);
app->ShowProgress(static_cast<int>(value));
}
}
} // namespace
std::wstring App::FindWim()
{
if (!wimPath.empty()) {
if (FileExists(wimPath)) {
return wimPath;
}
ShowMessage(MB_ICONWARNING | MB_OK, L"Архив не найден",
L"Файл не найден:\r\n" + wimPath);
}
std::wstring path = JoinPath(GetExeDir(), L"Data.pf");
if (FileExists(path)) {
return path;
}
return std::wstring();
}
void App::Apply()
{
DebugLog(L"Apply enter busy=%d canInstall=%d testDisk=%d selIdx=%d disks=%zu",
busy ? 1 : 0, canInstall ? 1 : 0, testDisk, selIdx, disks.size());
if (busy) {
return;
}
if (!canInstall && testDisk < 0) {
return;
}
int disk = testDisk;
unsigned long long expectedSize = 0;
if (disk < 0) {
int index = (selIdx >= 0 && selIdx < static_cast<int>(disks.size())) ? selIdx : -1;
if (index < 0) {
ShowMessage(MB_ICONWARNING | MB_OK, L"Устройство не выбрано",
L"Выберите флешку в списке.");
return;
}
disk = disks[index].number;
expectedSize = disks[index].size;
}
std::vector<int> systemDisks = GetSystemDiskIds();
if (std::find(systemDisks.begin(), systemDisks.end(), disk) != systemDisks.end()) {
ShowMessage(MB_ICONERROR | MB_OK, L"Это системный диск",
Format(L"Диск %d содержит Windows. Разметка отменена - выберите другой диск.",
disk));
return;
}
unsigned long long sizeBytes = 0;
if (!GetDiskSizeBytes(disk, sizeBytes) || sizeBytes == 0) {
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка",
Format(L"Не удалось определить размер диска %d.", disk));
return;
}
if (expectedSize > 0) {
unsigned long long difference = sizeBytes > expectedSize
? sizeBytes - expectedSize
: expectedSize - sizeBytes;
if (difference > expectedSize / 100) {
ShowMessage(MB_ICONERROR | MB_OK, L"Диск не совпадает",
Format(L"Размер выбранного диска (%.1f ГБ) не совпадает с тем, "
L"что был в списке (%.1f ГБ).\r\n\r\n"
L"Нажмите «Обновить» и выберите устройство заново.",
static_cast<double>(sizeBytes) / 1073741824.0,
static_cast<double>(expectedSize) / 1073741824.0));
return;
}
}
unsigned long long totalMB = sizeBytes / MB;
long long p3 = static_cast<long long>(totalMB) - 1 - P1_MB - P2_MB - TAIL_MB;
if (p3 < 512) {
ShowMessage(MB_ICONERROR | MB_OK, L"Слишком маленький диск",
Format(L"Нужен диск не меньше %d МБ.\r\nСейчас: %llu МБ.",
P1_MB + P2_MB + TAIL_MB + 512, totalMB));
return;
}
if (esdPfAvailable && !IsWimFile(esdPfPath)) {
ShowMessage(MB_ICONERROR | MB_OK, L"WindowsESD.pf повреждён",
L"Файл WindowsESD.pf не является WIM-архивом:\r\n" + esdPfPath);
return;
}
std::wstring message =
Format(L"Накопитель (%.1f ГБ)\r\n\r\n",
static_cast<double>(sizeBytes) / 1073741824.0);
message += L"Будет создано:\r\n";
message += L" 1) FAT32 16 ГБ метка PARAVIS\r\n";
message += L" 2) FAT 32 МБ метка VTOYEFI\r\n";
message += Format(L" 3) exFAT %.1f ГБ метка DATA\r\n\r\n",
static_cast<double>(p3) / 1024.0);
message += L"Затем на разделы будут распакованы файлы из архива Data.pf\r\n";
message += L"(1 -> PARAVIS, 2 -> VTOYEFI, 3 -> DATA).\r\n\r\n";
if (esdPfAvailable) {
if (noEsd) {
message += L"WindowsESD.pf распаковываться не будет.\r\n\r\n";
} else {
message += L"После Data.pf будет распакован WindowsESD.pf.\r\n\r\n";
}
} else {
message += L"WindowsESD.pf не найден - Windows ESD не будет распакован.\r\n\r\n";
}
message += L"ВНИМАНИЕ! ВСЕ ДАННЫЕ НА ЭТОМ ДИСКЕ БУДУТ УНИЧТОЖЕНЫ.\r\n";
message += L"Продолжить?";
goText = L"Ожидание подтверждения...";
goBg = ColorFromHex(0xAEB6C4);
goFg = ColorFromHex(0xEDEFF3);
InvalidateAll();
if (ShowMessage(MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2,
L"Подтверждение", message) != IDYES) {
GoCaption();
RefreshGo();
return;
}
DebugLog(L"confirmation accepted, disk=%d size=%llu totalMB=%llu p3=%lld",
disk, sizeBytes, totalMB, p3);
BusySet(true);
phase = 1;
DebugLog(L"busy set, phase=1");
std::wstring onlineFile = TempName(runStamp, L"online.txt");
std::wstring onlineScript = Format(L"select disk %d\r\nonline disk\r\nexit\r\n", disk);
if (WriteAnsiFile(onlineFile, onlineScript)) {
std::wstring command = L"\"" + GetComSpec() + L"\" /c diskpart.exe /s \"" +
onlineFile + L"\" > nul 2>&1";
RunHidden(this, command);
DeleteFileW(onlineFile.c_str());
}
std::wstring script = Format(
L"select disk %d\r\n"
L"clean\r\n"
L"convert mbr\r\n"
L"create partition primary size=%d\r\n"
L"format fs=fat32 quick label=\"PARAVIS\"\r\n"
L"active\r\n"
L"assign\r\n"
L"create partition primary size=%d\r\n"
L"format fs=fat quick label=\"VTOYEFI\"\r\n"
L"assign\r\n"
L"create partition primary size=%lld\r\n"
L"format fs=exfat quick label=\"DATA\"\r\n"
L"assign\r\n"
L"exit\r\n",
disk, P1_MB, P2_MB, p3);
std::wstring scriptFile = TempName(runStamp, L"diskpart.txt");
std::wstring logFile = TempName(runStamp, L"diskpart_log.txt");
if (!WriteAnsiFile(scriptFile, script)) {
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка",
L"Не удалось создать файл скрипта.");
BusySet(false);
HideProgress();
return;
}
SetWork(L"Разметка диска…");
std::wstring command = L"\"" + GetComSpec() + L"\" /c diskpart.exe /s \"" +
scriptFile + L"\" > \"" + logFile + L"\" 2>&1";
DebugLog(L"diskpart start");
RunHidden(this, command);
DebugLog(L"diskpart done");
std::string logBytes = ReadFileBytes(logFile);
std::wstring logText = AnsiToWide(logBytes, CP_ACP);
SleepPump(1500);
bool layoutOk = false;
std::wstring check = VerifyLayout(disk, layoutOk);
DebugLog(L"layoutOk=%d check=%s", layoutOk ? 1 : 0, check.c_str());
if (!layoutOk) {
SetWork(L"Раскладка не совпала с ожидаемой");
HideProgress();
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка разметки",
L"РАСКЛАДКА НЕ СОВПАЛА С ОЖИДАЕМОЙ\r\n\r\n" + check +
L"\r\n\r\nЛог: " + logFile + L"\r\n\r\n" + logText.substr(0, 600));
BusySet(false);
HideProgress();
return;
}
if (cancel) {
BusySet(false);
HideProgress();
ShowMessage(MB_ICONINFORMATION | MB_OK, L"Отменено",
L"Операция отменена пользователем.\r\n\r\n"
L"Диск размечен, файлы не скопированы.");
return;
}
std::wstring wim = FindWim();
DebugLog(L"wim=%s", wim.c_str());
if (wim.empty()) {
SetWork(L"Размечено, архив Data.pf не найден");
ShowMessage(MB_ICONWARNING | MB_OK, L"Разметка выполнена",
L"Диск размечен, раскладка верна.\r\n\r\n" + check +
L"\r\n\r\nНо файл Data.pf не найден рядом с программой - "
L"файлы не скопированы.");
BusySet(false);
HideProgress();
return;
}
phase = 2;
int result = ApplyImages(disk, wim);
phase = 0;
DebugLog(L"ApplyImages result=%d applied=%d cancel=%d", result, applied, cancel ? 1 : 0);
if (result == 1) {
if (!noEsd && esdPfAvailable) {
int esdResult = ApplyWindowsEsd(disk);
if (esdResult == 3) {
SetWork(L"Отменено");
HideProgress();
BusySet(false);
HideProgress();
ShowMessage(MB_ICONINFORMATION | MB_OK, L"Отменено",
L"Операция отменена пользователем.\r\n\r\n"
L"Диск размечен, Windows ESD не распакован.");
return;
}
if (esdResult == 2) {
SetWork(L"Ошибка Windows ESD");
HideProgress();
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка Windows ESD",
L"Не удалось распаковать WindowsESD.pf.\r\n\r\n"
L"Проверьте, что файл не повреждён.");
BusySet(false);
HideProgress();
return;
}
}
std::wstring first = GetPartitionLetter(disk, 0);
DebugLog(L"partition0=%s", first.c_str());
// If the C++ bootloader changer sits next to the installer, put it on
// the PARAVIS partition and use it instead of the copy from Data.pf.
if (!first.empty()) {
std::wstring source = JoinPath(GetExeDir(), L"Change_BootLoader.exe");
if (FileExists(source)) {
std::wstring destination = first + L"Change_BootLoader.exe";
if (_wcsicmp(source.c_str(), destination.c_str()) == 0) {
DebugLog(L"bootloader source is already the target: %s", source.c_str());
} else if (CopyFileW(source.c_str(), destination.c_str(), FALSE)) {
DebugLog(L"copied bootloader: %s -> %s", source.c_str(), destination.c_str());
} else {
DebugLog(L"bootloader copy failed error=%lu", GetLastError());
}
} else {
DebugLog(L"no C++ bootloader next to installer: %s", source.c_str());
}
}
HideVentoy(disk);
SetWork(L"Готово");
HideProgress();
DebugLog(L"showing success message");
ShowMessage(MB_ICONINFORMATION | MB_OK, L"Установка завершена",
L"Установка завершена успешно. Сейчас будет запущен установщик загрузчиков. "
L"Выберите Legacy и UEFI загрузчики. Если этого не сделать, то будет "
L"работать только UEFI.");
DebugLog(L"success message closed, partition0=%s", first.c_str());
if (!first.empty()) {
std::wstring loader = first + L"Change_BootLoader.exe";
DebugLog(L"loader path=%s exists=%d", loader.c_str(), FileExists(loader) ? 1 : 0);
if (FileExists(loader)) {
if (LaunchLoader(loader)) {
DebugLog(L"loader launched, ExitProcess");
ExitProcess(0);
}
ShowMessage(MB_ICONWARNING | MB_OK, L"Установщик загрузчиков",
L"Не удалось запустить Change_BootLoader.exe.\r\n\r\n"
L"Путь: " + loader);
} else {
ShowMessage(MB_ICONWARNING | MB_OK, L"Установщик загрузчиков",
L"Файл Change_BootLoader.exe не найден на разделе PARAVIS.\r\n\r\n"
L"Путь: " + loader);
}
} else {
ShowMessage(MB_ICONWARNING | MB_OK, L"Установщик загрузчиков",
L"Не удалось определить букву раздела PARAVIS.");
}
} else if (result == 2) {
SetWork(L"Ошибка записи");
HideProgress();
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка копирования файлов",
L"Диск размечен, но скопировать файлы не удалось.\r\n\r\n"
L"Запустите установку ещё раз.");
} else if (result == 3) {
SetWork(L"Отменено");
HideProgress();
BusySet(false);
HideProgress();
ShowMessage(MB_ICONINFORMATION | MB_OK, L"Отменено",
Format(L"Операция отменена пользователем.\r\n\r\n"
L"Диск размечен, скопировано образов: %d из 3.", applied));
return;
}
BusySet(false);
HideProgress();
}
int App::ApplyImages(int disk, const std::wstring& wim)
{
const wchar_t* names[3] = { L"PARAVIS", L"VTOYEFI", L"DATA" };
for (int i = 0; i < 3; ++i) {
if (cancel) {
return 3;
}
std::wstring dir;
for (int attempt = 0; attempt < 15; ++attempt) {
dir = GetPartitionLetter(disk, i);
if (!dir.empty()) {
break;
}
SleepPump(1000);
}
if (dir.empty()) {
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка",
Format(L"Не удалось определить букву раздела %d (%s).\r\n"
L"Проверьте, что раздел подключён (diskpart: assign).",
i + 1, names[i]));
return 2;
}
if (cancel) {
return 3;
}
std::wstring caption = std::wstring(names[i]) + L" → " + dir;
DebugLog(L"ApplyImages image=%d part=%d dir=%s", i + 1, i, dir.c_str());
ShowProgress(0);
SetWork(caption);
if (!DismApply(wim, i + 1, dir, caption)) {
if (cancel) {
return 3;
}
return 2;
}
++applied;
if (cancel) {
return 3;
}
}
return 1;
}
int App::ApplyWindowsEsd(int disk)
{
std::wstring dir;
for (int attempt = 0; attempt < 15; ++attempt) {
dir = GetPartitionLetter(disk, 2);
if (!dir.empty()) {
break;
}
SleepPump(1000);
}
if (dir.empty()) {
ShowMessage(MB_ICONERROR | MB_OK, L"Ошибка",
L"Не удалось определить букву раздела DATA.");
return 2;
}
if (cancel) {
return 3;
}
if (!IsWimFile(esdPfPath)) {
ShowMessage(MB_ICONERROR | MB_OK, L"WindowsESD.pf повреждён",
L"Файл WindowsESD.pf не является WIM-архивом:\r\n" + esdPfPath);
return 2;
}
std::wstring caption = L"Распаковка Windows ESD";
DebugLog(L"ApplyWindowsEsd %s -> %s", esdPfPath.c_str(), dir.c_str());
phase = 2;
ShowProgress(0);
SetWork(caption);
bool ok = DismApply(esdPfPath, 1, dir, caption);
phase = 0;
if (!ok) {
if (cancel) {
return 3;
}
return 2;
}
return 1;
}
bool App::DismApply(const std::wstring& wim, int index, const std::wstring& dir,
const std::wstring& caption)
{
std::wstring arguments = L"/Apply-Image /ImageFile:\"" + wim +
L"\" /Index:" + std::to_wstring(index) +
L" /ApplyDir:" + dir;
int code = RunCmd(arguments, caption);
if (code == 0) {
return true;
}
SetWork(caption + Format(L": ОШИБКА (код %d)", code));
return false;
}
int App::RunCmd(const std::wstring& arguments, const std::wstring& caption)
{
DebugLog(L"RunCmd start: dism %s", arguments.c_str());
wchar_t systemDir[MAX_PATH] = {};
GetSystemDirectoryW(systemDir, MAX_PATH);
std::wstring dism = std::wstring(systemDir) + L"\\dism.exe";
std::wstring full = L"\"" + dism + L"\" " + arguments;
SECURITY_ATTRIBUTES security{};
security.nLength = sizeof(security);
security.bInheritHandle = TRUE;
HANDLE readPipe = nullptr;
HANDLE writePipe = nullptr;
if (!CreatePipe(&readPipe, &writePipe, &security, 0)) {
return -1;
}
SetHandleInformation(readPipe, HANDLE_FLAG_INHERIT, 0);
HANDLE nullInput = CreateFileW(L"NUL", GENERIC_READ,
FILE_SHARE_READ | FILE_SHARE_WRITE,
&security, OPEN_EXISTING, 0, nullptr);
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
startup.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_HIDE;
startup.hStdOutput = writePipe;
startup.hStdError = writePipe;
startup.hStdInput = nullInput;
PROCESS_INFORMATION process{};
std::vector<wchar_t> buffer(full.begin(), full.end());
buffer.push_back(L'\0');
BOOL created = CreateProcessW(dism.c_str(), buffer.data(), nullptr, nullptr, TRUE,
CREATE_NO_WINDOW | CREATE_SUSPENDED,
nullptr, nullptr, &startup, &process);
CloseHandle(writePipe);
if (nullInput != INVALID_HANDLE_VALUE) {
CloseHandle(nullInput);
}
if (!created) {
CloseHandle(readPipe);
return -1;
}
HANDLE job = CreateJobObjectW(nullptr, nullptr);
if (job) {
JOBOBJECT_EXTENDED_LIMIT_INFORMATION limit{};
limit.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
SetInformationJobObject(job, JobObjectExtendedLimitInformation, &limit, sizeof(limit));
if (!AssignProcessToJobObject(job, process.hProcess)) {
DebugLog(L"AssignProcessToJobObject failed error=%lu", GetLastError());
}
}
runJob = job;
runProcess = process.hProcess;
runPid = process.dwProcessId;
ResumeThread(process.hThread);
CloseHandle(process.hThread);
std::string output;
double shown = -1.0;
bool killed = false;
for (;;) {
for (;;) {
DWORD available = 0;
if (!PeekNamedPipe(readPipe, nullptr, 0, nullptr, &available, nullptr) ||
available == 0) {
break;
}
char chunk[4096];
DWORD toRead = std::min<DWORD>(available, sizeof(chunk));
DWORD read = 0;
if (!ReadFile(readPipe, chunk, toRead, &read, nullptr) || read == 0) {
break;
}
output.append(chunk, read);
}
UpdateOutputProgress(this, output, caption, shown);
DWORD wait = MsgWaitForMultipleObjects(1, &process.hProcess, FALSE, 30, QS_ALLINPUT);
if (wait == WAIT_OBJECT_0) {
for (;;) {
DWORD available = 0;
if (!PeekNamedPipe(readPipe, nullptr, 0, nullptr, &available, nullptr) ||
available == 0) {
break;
}
char chunk[4096];
DWORD toRead = std::min<DWORD>(available, sizeof(chunk));
DWORD read = 0;
if (!ReadFile(readPipe, chunk, toRead, &read, nullptr) || read == 0) {
break;
}
output.append(chunk, read);
}
UpdateOutputProgress(this, output, caption, shown);
break;
}
if (wait != WAIT_OBJECT_0) {
// Pump even on timeout so due WM_TIMER messages run OnPoll and
// the cancel button stays responsive during DISM.
PumpMessages();
}
if (cancel && !killed) {
killed = true;
if (job) {
TerminateJobObject(job, 1);
}
TerminateProcess(process.hProcess, 1);
KillProcessTree(process.dwProcessId);
}
}
WaitForSingleObject(process.hProcess, INFINITE);
DWORD exitCode = 1;
GetExitCodeProcess(process.hProcess, &exitCode);
int code = static_cast<int>(exitCode);
runJob = nullptr;
runProcess = nullptr;
runPid = 0;
if (job) {
CloseHandle(job);
}
CloseHandle(process.hProcess);
CloseHandle(readPipe);
DebugLog(L"RunCmd done code=%d killed=%d cancel=%d", code, killed ? 1 : 0, cancel ? 1 : 0);
return code;
}
void App::HideVentoy(int disk)
{
std::wstring scriptFile = TempName(runStamp, L"hide.txt");
std::wstring script = Format(
L"select disk %d\r\n"
L"select partition 2\r\n"
L"remove\r\n"
L"set id=EF override\r\n"
L"exit\r\n", disk);
if (WriteAnsiFile(scriptFile, script)) {
std::wstring command = L"\"" + GetComSpec() + L"\" /c diskpart.exe /s \"" +
scriptFile + L"\" > nul 2>&1";
RunHidden(this, command);
DeleteFileW(scriptFile.c_str());
}
}
int App::ShowMessage(UINT flags, const wchar_t* title, const std::wstring& text)
{
DebugLog(L"MsgBox enter title=%s busy=%d", title, busy ? 1 : 0);
bool previous = modal;
modal = true;
GoState(false);
DimControls(true);
int result = MessageBoxW(hwnd, text.c_str(), title, flags);
DebugLog(L"MsgBox exit title=%s result=%d", title, result);
modal = previous;
clickGuard = GetTickCount64();
if (!busy) {
DimControls(false);
RefreshGo();
} else {
CancelLook(Hit::None, false);
}
InvalidateAll();
return result;
}
void App::ConfirmCancel()
{
if (cancel) {
return;
}
DebugLog(L"ConfirmCancel phase=%d", phase);
int result = ShowMessage(MB_ICONWARNING | MB_YESNO | MB_DEFBUTTON2,
L"Отмена установки",
L"Вы уверены, что хотите отменить установку?");
if (result == IDYES) {
RequestCancel();
}
}
void App::RequestCancel()
{
DebugLog(L"RequestCancel phase=%d busy=%d", phase, busy ? 1 : 0);
if (cancel) {
return;
}
cancel = true;
goText = L"Отменяю. Ждите...";
goBg = ColorFromHex(0xAEB6C4);
goFg = ColorFromHex(0xEDEFF3);
if (phase == 2) {
if (runJob) {
TerminateJobObject(runJob, 1);
}
if (runProcess) {
TerminateProcess(runProcess, 1);
}
// Fallback for environments where the process could not be assigned
// to a job: kill the whole DISM tree.
KillProcessTree(runPid);
}
InvalidateAll();
}