Paravis USB installer 2.0 (C++ port)
This commit is contained in:
commit
ce8b49d0ec
18 files changed
+4119
No files matched your search
+694
@@ -0,0 +1,694 @@
|
||||
#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;
|
||||
}
|
||||
|
||||
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";
|
||||
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) {
|
||||
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;
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
Reference in new issue
Block a user