Windows installer 2.0 (C++ port)

This commit is contained in:
paravis committed 2026-11-07 20:40:49 +03:00
commit 45967534aa
32 files changed
+1768

No files matched your search

+627
View File
@@ -0,0 +1,627 @@
#include "util.h"
#include <windowsx.h>
#include <objbase.h>
#include <gdiplus.h>
#include <algorithm>
#include <cstring>
#include <cwchar>
#include <string>
#include <vector>
namespace {
const wchar_t* kMainClass = L"ParavisWindowsSetupMain";
const wchar_t* kWallClass = L"ParavisWindowsSetupWallpaper";
const COLORREF CLR_KEY = RGB(0xAB, 0xCD, 0xEF);
const int IDI_APP = 1;
const int IDR_FONT_REGULAR = 101;
const int IDR_FONT_BOLD = 102;
const int IDR_WALLPAPER = 201;
HINSTANCE g_hInst = nullptr;
HWND g_wallHwnd = nullptr;
HWND g_mainHwnd = nullptr;
HBITMAP g_wallBitmap = nullptr;
int g_wallWidth = 0;
int g_wallHeight = 0;
HBRUSH g_keyBrush = nullptr;
HFONT g_buttonFont = nullptr;
std::wstring g_fontFace = L"Segoe UI";
std::wstring g_imageRoot;
std::wstring g_bootRoot;
bool g_preview = false;
bool g_is64 = true;
bool g_x86Legacy = false;
bool g_busy = false;
bool g_gdiplusStarted = false;
ULONG_PTR g_gdiplusToken = 0;
enum class Action {
Win11,
Win10x64,
Win7x64,
Win10x86,
Win7x86,
TotalCmd64,
TotalCmd86,
Reboot
};
struct ButtonDef {
int id = 0;
std::wstring text;
int y = 0;
Action action = Action::Reboot;
bool enabled = true;
};
std::vector<ButtonDef> g_buttons;
void FillRectColor(HDC dc, const RECT& rect, COLORREF color)
{
HBRUSH brush = CreateSolidBrush(color);
FillRect(dc, &rect, brush);
DeleteObject(brush);
}
std::wstring Lower(std::wstring value)
{
for (wchar_t& ch : value) {
ch = static_cast<wchar_t>(towlower(ch));
}
return value;
}
bool StartsWith(const std::wstring& value, const std::wstring& prefix)
{
return value.size() >= prefix.size() &&
_wcsnicmp(value.c_str(), prefix.c_str(), prefix.size()) == 0;
}
bool HasPreviewArg()
{
std::wstring name = Lower(GetExeFileName());
if (StartsWith(name, L"setup_preview")) {
return true;
}
std::wstring commandLine = Lower(GetCommandLineW());
return commandLine.find(L"/preview") != std::wstring::npos;
}
std::wstring FirmwareType()
{
wchar_t buffer[64] = {};
DWORD length = GetEnvironmentVariableW(L"firmware_type", buffer,
static_cast<DWORD>(std::size(buffer)));
if (length == 0 || length >= std::size(buffer)) {
return std::wstring();
}
return std::wstring(buffer, length);
}
std::wstring LoadFonts()
{
std::wstring temp = GetTempDir();
std::wstring regular = temp + L"Montserrat-Regular.ttf";
std::wstring bold = temp + L"Montserrat-Bold.ttf";
bool loaded = false;
if (ExtractResourceToFile(g_hInst, IDR_FONT_REGULAR, RT_RCDATA, regular)) {
if (AddFontResourceExW(regular.c_str(), FR_PRIVATE, nullptr) > 0) {
loaded = true;
}
}
if (ExtractResourceToFile(g_hInst, IDR_FONT_BOLD, RT_RCDATA, bold)) {
if (AddFontResourceExW(bold.c_str(), FR_PRIVATE, nullptr) > 0) {
loaded = true;
}
}
DebugLog(L"fonts loaded=%d regular=%s bold=%s", loaded ? 1 : 0,
regular.c_str(), bold.c_str());
return loaded ? L"Montserrat" : L"Segoe UI";
}
bool LoadWallpaper()
{
HRSRC resource = FindResourceW(g_hInst, MAKEINTRESOURCEW(IDR_WALLPAPER),
RT_RCDATA);
if (!resource) {
DebugLog(L"wallpaper PNG resource not found");
return false;
}
DWORD size = SizeofResource(g_hInst, resource);
HGLOBAL loaded = LoadResource(g_hInst, resource);
const void* data = loaded ? LockResource(loaded) : nullptr;
if (!data || size == 0) {
DebugLog(L"wallpaper PNG resource load failed");
return false;
}
HGLOBAL memory = GlobalAlloc(GMEM_MOVEABLE, size);
if (!memory) {
return false;
}
void* destination = GlobalLock(memory);
if (!destination) {
GlobalFree(memory);
return false;
}
memcpy(destination, data, size);
GlobalUnlock(memory);
IStream* stream = nullptr;
if (FAILED(CreateStreamOnHGlobal(memory, TRUE, &stream))) {
GlobalFree(memory);
return false;
}
Gdiplus::Bitmap bitmap(stream, FALSE);
if (bitmap.GetLastStatus() == Gdiplus::Ok) {
HBITMAP handle = nullptr;
if (bitmap.GetHBITMAP(Gdiplus::Color(255, 255, 255), &handle) ==
Gdiplus::Ok && handle) {
g_wallBitmap = handle;
g_wallWidth = static_cast<int>(bitmap.GetWidth());
g_wallHeight = static_cast<int>(bitmap.GetHeight());
}
}
stream->Release();
DebugLog(L"wallpaper PNG %dx%d loaded=%d", g_wallWidth, g_wallHeight,
g_wallBitmap ? 1 : 0);
return g_wallBitmap != nullptr;
}
void PaintWallpaper(HWND hwnd)
{
PAINTSTRUCT paint{};
HDC dc = BeginPaint(hwnd, &paint);
RECT client{};
GetClientRect(hwnd, &client);
if (g_wallBitmap) {
HDC memory = CreateCompatibleDC(dc);
HGDIOBJ old = SelectObject(memory, g_wallBitmap);
SetStretchBltMode(dc, COLORONCOLOR);
StretchBlt(dc, 0, 0, client.right, client.bottom,
memory, 0, 0, g_wallWidth, g_wallHeight, SRCCOPY);
SelectObject(memory, old);
DeleteDC(memory);
} else {
FillRectColor(dc, client, RGB(0xF0, 0xF0, 0xF0));
}
EndPaint(hwnd, &paint);
}
LRESULT CALLBACK WallpaperProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_ERASEBKGND:
return 1;
case WM_PAINT:
PaintWallpaper(hwnd);
return 0;
case WM_CLOSE:
return 0;
}
return DefWindowProcW(hwnd, message, wParam, lParam);
}
bool ContainsMarkerRecursive(const std::wstring& folder, const wchar_t* marker,
int depth)
{
if (depth > 32 || !DirectoryExists(folder)) {
return false;
}
WIN32_FIND_DATAW data{};
HANDLE find = FindFirstFileW((folder + L"\\*").c_str(), &data);
if (find == INVALID_HANDLE_VALUE) {
return false;
}
bool found = false;
do {
if (wcscmp(data.cFileName, L".") == 0 ||
wcscmp(data.cFileName, L"..") == 0) {
continue;
}
if (data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if ((data.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == 0) {
std::wstring child = folder + L"\\" + data.cFileName;
if (ContainsMarkerRecursive(child, marker, depth + 1)) {
found = true;
break;
}
}
} else if (_wcsicmp(data.cFileName, marker) == 0) {
found = true;
break;
}
} while (FindNextFileW(find, &data));
FindClose(find);
return found;
}
void ShowWarning(const std::wstring& text)
{
MessageBoxW(g_wallHwnd, text.c_str(), L"Предупреждение!",
MB_ICONWARNING | MB_OK);
}
bool SearchPaths()
{
int pathCount = 0;
for (wchar_t letter = L'A'; letter <= L'Z'; ++letter) {
std::wstring root{ letter, L':', L'\\' };
if (GetDriveTypeW(root.c_str()) == DRIVE_NO_ROOT_DIR) {
continue;
}
if (ContainsMarkerRecursive(root + L"WindowsESD", L"NOT.DEL", 0)) {
g_imageRoot = root + L"WindowsESD\\";
++pathCount;
break;
}
}
for (wchar_t letter = L'A'; letter <= L'Z'; ++letter) {
std::wstring root{ letter, L':', L'\\' };
if (GetDriveTypeW(root.c_str()) == DRIVE_NO_ROOT_DIR) {
continue;
}
if (ContainsMarkerRecursive(root + L"efi\\Paravis\\Windows", L"NOT.DEL", 0)) {
g_imageRoot = root + L"efi\\Paravis\\Windows\\";
++pathCount;
break;
}
}
if (pathCount > 1) {
ShowWarning(L"В системе больше одной папки с образами. "
L"Проверьте и удалите лишние (читаем ReadMe.txt). "
L"Система будет перезагружена.");
return false;
}
for (wchar_t letter = L'A'; letter <= L'Z'; ++letter) {
std::wstring root{ letter, L':', L'\\' };
if (GetDriveTypeW(root.c_str()) == DRIVE_NO_ROOT_DIR) {
continue;
}
if (ContainsMarkerRecursive(root + L"efi\\Paravis\\Boot", L"NOT.DEL", 0)) {
g_bootRoot = root + L"efi\\Paravis\\Boot";
break;
}
}
DebugLog(L"imageRoot=%s bootRoot=%s paths=%d",
g_imageRoot.c_str(), g_bootRoot.c_str(), pathCount);
return true;
}
bool HasEsd(const wchar_t* name)
{
return FileExists(g_imageRoot + name);
}
std::wstring UnattendArgument(const wchar_t* file)
{
std::wstring path = g_imageRoot + L"autounattend\\" + file;
if (FileExists(path)) {
return L" /unattend:" + path;
}
return std::wstring();
}
std::wstring SetupCommand(const wchar_t* esd, const wchar_t* oem,
const wchar_t* unattend)
{
std::wstring command = L"X:\\sources\\setup.exe /installfrom:" +
g_imageRoot + esd + L" /m:" + g_imageRoot + oem;
command += UnattendArgument(unattend);
return command;
}
std::wstring TotalCmdCommand()
{
const wchar_t* exe = g_is64 ? L"TOTALCMD64.EXE" : L"TOTALCMD.EXE";
return g_bootRoot + L"\\TotalCommander\\" + exe +
L" /I=\"%%COMMANDER_PATH%%\\tcmdmain.ini\"";
}
void RunInstallerCommand(const std::wstring& command)
{
DebugLog(L"run: %s", command.c_str());
g_busy = true;
ShowWindow(g_mainHwnd, SW_HIDE);
RunWait(command, false);
ShowWindow(g_mainHwnd, SW_SHOW);
SetWindowPos(g_mainHwnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_SHOWWINDOW);
g_busy = false;
}
void HandleButton(int id)
{
if (g_busy) {
return;
}
const ButtonDef* selected = nullptr;
for (const ButtonDef& button : g_buttons) {
if (button.id == id) {
selected = &button;
break;
}
}
if (!selected) {
return;
}
switch (selected->action) {
case Action::Win11:
RunInstallerCommand(SetupCommand(L"install_11.esd", L"$OEM11$",
L"unattend11.xml"));
break;
case Action::Win10x64:
RunInstallerCommand(SetupCommand(L"install_10_x64.esd", L"$OEM10x64$",
L"unattend10x64.xml"));
break;
case Action::Win7x64:
RunInstallerCommand(SetupCommand(L"install_7_x64.esd", L"$OEM7x64$",
L"unattend7x64.xml"));
break;
case Action::Win10x86:
RunInstallerCommand(SetupCommand(L"install_10_x86.esd", L"$OEM10x86$",
L"unattend10x86.xml"));
break;
case Action::Win7x86:
RunInstallerCommand(SetupCommand(L"install_7_x86.esd", L"$OEM7x86$",
L"unattend7x86.xml"));
break;
case Action::TotalCmd64:
case Action::TotalCmd86:
RunInstallerCommand(TotalCmdCommand());
break;
case Action::Reboot:
ExitProcess(0);
break;
}
}
void BuildButtonDefs()
{
g_buttons.clear();
int id = 1001;
if (g_is64) {
g_buttons.push_back({ id++, L"Установить Windows 11", 5, Action::Win11,
g_preview || HasEsd(L"install_11.esd") });
g_buttons.push_back({ id++, L"Установить Windows 10", 45, Action::Win10x64,
g_preview || HasEsd(L"install_10_x64.esd") });
g_buttons.push_back({ id++, L"Установить Windows 7", 85, Action::Win7x64,
g_preview || HasEsd(L"install_7_x64.esd") });
g_buttons.push_back({ id++, L"Total Commander", 145, Action::TotalCmd64, true });
g_buttons.push_back({ id++, L"Перезагрузка", 185, Action::Reboot, true });
} else if (g_x86Legacy) {
g_buttons.push_back({ id++, L"Установить Windows 10", 5, Action::Win10x86,
g_preview || HasEsd(L"install_10_x86.esd") });
g_buttons.push_back({ id++, L"Установить Windows 7", 45, Action::Win7x86,
g_preview || HasEsd(L"install_7_x86.esd") });
g_buttons.push_back({ id++, L"Total Commander", 105, Action::TotalCmd86, true });
g_buttons.push_back({ id++, L"Перезагрузка", 145, Action::Reboot, true });
} else {
g_buttons.push_back({ id++, L"Установить Windows 10", 5, Action::Win10x86, true });
g_buttons.push_back({ id++, L"Total Commander", 65, Action::TotalCmd86, true });
g_buttons.push_back({ id++, L"Перезагрузка", 105, Action::Reboot, true });
}
}
void CreateMainWindow()
{
int width = (g_is64 || g_x86Legacy) ? 410 : 360;
int height = (g_is64 || g_x86Legacy) ? 250 : 190;
int screenWidth = GetSystemMetrics(SM_CXSCREEN);
int screenHeight = GetSystemMetrics(SM_CYSCREEN);
int x = (screenWidth - width) / 2;
int y = (screenHeight - height) / 2;
if (g_preview && width == 410) {
x = 307;
y = 259;
}
HDC screen = GetDC(nullptr);
int dpi = GetDeviceCaps(screen, LOGPIXELSY);
ReleaseDC(nullptr, screen);
if (dpi <= 0) {
dpi = 96;
}
int fontHeight = -(13 * dpi / 72);
g_buttonFont = CreateFontW(fontHeight, 0, 0, 0, 500, FALSE, FALSE, FALSE,
DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
DEFAULT_PITCH | FF_DONTCARE, g_fontFace.c_str());
g_mainHwnd = CreateWindowExW(
WS_EX_LAYERED, kMainClass, L"Windows Setup by Paravis",
WS_POPUP | WS_CLIPCHILDREN, x, y, width, height,
nullptr, nullptr, g_hInst, nullptr);
if (!g_mainHwnd) {
DebugLog(L"main window creation failed error=%lu", GetLastError());
return;
}
SetLayeredWindowAttributes(g_mainHwnd, CLR_KEY, 0, LWA_COLORKEY);
for (const ButtonDef& button : g_buttons) {
HWND control = CreateWindowExW(
0, L"BUTTON", button.text.c_str(),
WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON,
5, button.y, 350, 40,
g_mainHwnd, reinterpret_cast<HMENU>(static_cast<INT_PTR>(button.id)),
g_hInst, nullptr);
if (!control) {
continue;
}
if (g_buttonFont) {
SendMessageW(control, WM_SETFONT,
reinterpret_cast<WPARAM>(g_buttonFont), TRUE);
}
if (!button.enabled) {
EnableWindow(control, FALSE);
}
}
SetWindowPos(g_mainHwnd, HWND_TOPMOST, x, y, width, height, SWP_SHOWWINDOW);
UpdateWindow(g_mainHwnd);
SetForegroundWindow(g_mainHwnd);
DebugLog(L"main window %dx%d at %d,%d buttons=%zu", width, height, x, y,
g_buttons.size());
}
void CreateWallpaperWindow()
{
int width = GetSystemMetrics(SM_CXSCREEN);
int height = GetSystemMetrics(SM_CYSCREEN);
DWORD exStyle = WS_EX_TOOLWINDOW;
if (g_preview) {
width = 1024;
height = 768;
exStyle |= WS_EX_TOPMOST;
}
g_wallHwnd = CreateWindowExW(
exStyle, kWallClass, L"", WS_POPUP,
0, 0, width, height, nullptr, nullptr, g_hInst, nullptr);
if (!g_wallHwnd) {
DebugLog(L"wallpaper window creation failed error=%lu", GetLastError());
return;
}
ShowWindow(g_wallHwnd, SW_SHOWNOACTIVATE);
UpdateWindow(g_wallHwnd);
DebugLog(L"wallpaper window %dx%d", width, height);
}
LRESULT CALLBACK MainProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message) {
case WM_ERASEBKGND: {
HDC dc = reinterpret_cast<HDC>(wParam);
RECT rect{};
GetClientRect(hwnd, &rect);
FillRectColor(dc, rect, CLR_KEY);
return 1;
}
case WM_COMMAND:
if (HIWORD(wParam) == BN_CLICKED) {
HandleButton(LOWORD(wParam));
}
return 0;
case WM_CLOSE:
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProcW(hwnd, message, wParam, lParam);
}
} // namespace
int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, LPWSTR, int)
{
g_hInst = hInstance;
g_preview = HasPreviewArg();
Gdiplus::GdiplusStartupInput gdiplusInput;
g_gdiplusStarted =
Gdiplus::GdiplusStartup(&g_gdiplusToken, &gdiplusInput, nullptr) ==
Gdiplus::Ok;
g_keyBrush = CreateSolidBrush(CLR_KEY);
WNDCLASSEXW wallpaperClass{};
wallpaperClass.cbSize = sizeof(wallpaperClass);
wallpaperClass.lpfnWndProc = WallpaperProc;
wallpaperClass.hInstance = hInstance;
wallpaperClass.hCursor = LoadCursorW(nullptr, IDC_ARROW);
wallpaperClass.lpszClassName = kWallClass;
wallpaperClass.hbrBackground = static_cast<HBRUSH>(GetStockObject(BLACK_BRUSH));
if (!RegisterClassExW(&wallpaperClass)) {
return 1;
}
WNDCLASSEXW mainClass{};
mainClass.cbSize = sizeof(mainClass);
mainClass.lpfnWndProc = MainProc;
mainClass.hInstance = hInstance;
mainClass.hCursor = LoadCursorW(nullptr, IDC_ARROW);
mainClass.hIcon = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_APP));
mainClass.hIconSm = LoadIconW(hInstance, MAKEINTRESOURCEW(IDI_APP));
mainClass.lpszClassName = kMainClass;
mainClass.hbrBackground = g_keyBrush;
if (!RegisterClassExW(&mainClass)) {
return 1;
}
g_fontFace = LoadFonts();
LoadWallpaper();
CreateWallpaperWindow();
if (!SearchPaths()) {
return 0;
}
g_is64 = DirectoryExists(GetWindowsDir() + L"\\SYSWOW64");
std::wstring firmware = FirmwareType();
g_x86Legacy = !g_is64 && _wcsicmp(firmware.c_str(), L"Legacy") == 0;
DebugLog(L"is64=%d firmware=%s legacy86=%d", g_is64 ? 1 : 0,
firmware.c_str(), g_x86Legacy ? 1 : 0);
if (!g_preview && !g_bootRoot.empty()) {
std::wstring source = g_bootRoot +
(g_is64 ? L"\\src_x64.exe" : L"\\src_x86.exe");
if (FileExists(source)) {
DebugLog(L"running source helper: %s auto2", source.c_str());
RunWait(source + L" auto2", false);
} else {
DebugLog(L"source helper not found: %s", source.c_str());
}
}
if (!g_is64 && !g_x86Legacy) {
if (!FileExists(g_imageRoot + L"install_10_x86.esd")) {
ShowWarning(L"Не найден образ Windows 10 x86. "
L"Система будет перезагружена.");
return 0;
}
}
BuildButtonDefs();
CreateMainWindow();
if (!g_mainHwnd) {
return 1;
}
MSG message{};
while (GetMessageW(&message, nullptr, 0, 0) > 0) {
TranslateMessage(&message);
DispatchMessageW(&message);
}
if (g_gdiplusStarted) {
Gdiplus::GdiplusShutdown(g_gdiplusToken);
}
return static_cast<int>(message.wParam);
}
+228
View File
@@ -0,0 +1,228 @@
#include "util.h"
#include <cstdarg>
#include <cstdio>
#include <iterator>
#include <vector>
std::wstring Format(const wchar_t* fmt, ...)
{
va_list args;
va_start(args, fmt);
va_list copy;
va_copy(copy, args);
int needed = _vscwprintf(fmt, copy);
va_end(copy);
if (needed < 0) {
va_end(args);
return std::wstring();
}
std::wstring result(static_cast<size_t>(needed), L'\0');
if (needed > 0) {
_vsnwprintf_s(result.data(), result.size() + 1, _TRUNCATE, fmt, args);
}
va_end(args);
return result;
}
std::wstring GetExeDir()
{
wchar_t buffer[MAX_PATH * 4] = {};
DWORD length = GetModuleFileNameW(nullptr, buffer, static_cast<DWORD>(std::size(buffer)));
if (length == 0) {
return std::wstring();
}
std::wstring path(buffer, length);
size_t slash = path.find_last_of(L"\\/");
if (slash != std::wstring::npos) {
path.resize(slash);
}
return path;
}
std::wstring GetExeFileName()
{
wchar_t buffer[MAX_PATH * 4] = {};
DWORD length = GetModuleFileNameW(nullptr, buffer, static_cast<DWORD>(std::size(buffer)));
if (length == 0) {
return std::wstring();
}
std::wstring path(buffer, length);
size_t slash = path.find_last_of(L"\\/");
if (slash != std::wstring::npos) {
path = path.substr(slash + 1);
}
return path;
}
std::wstring GetWindowsDir()
{
wchar_t buffer[MAX_PATH] = {};
UINT length = GetWindowsDirectoryW(buffer, MAX_PATH);
if (length == 0) {
return L"C:\\Windows";
}
return std::wstring(buffer, length);
}
std::wstring GetTempDir()
{
wchar_t buffer[MAX_PATH + 1] = {};
DWORD length = GetTempPathW(MAX_PATH, buffer);
if (length == 0 || length >= MAX_PATH) {
wchar_t windows[MAX_PATH] = {};
GetWindowsDirectoryW(windows, MAX_PATH);
return std::wstring(windows) + L"\\Temp\\";
}
std::wstring path(buffer, length);
if (path.empty() || path.back() != L'\\') {
path.push_back(L'\\');
}
return path;
}
bool FileExists(const std::wstring& path)
{
DWORD attributes = GetFileAttributesW(path.c_str());
return attributes != INVALID_FILE_ATTRIBUTES &&
(attributes & FILE_ATTRIBUTE_DIRECTORY) == 0;
}
bool DirectoryExists(const std::wstring& path)
{
DWORD attributes = GetFileAttributesW(path.c_str());
return attributes != INVALID_FILE_ATTRIBUTES &&
(attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
}
bool ExtractResourceToFile(HINSTANCE instance, int resourceId, const wchar_t* type,
const std::wstring& path)
{
HRSRC resource = FindResourceW(instance, MAKEINTRESOURCEW(resourceId), type);
if (!resource) {
return false;
}
DWORD size = SizeofResource(instance, resource);
HGLOBAL loaded = LoadResource(instance, resource);
if (!loaded) {
return false;
}
const void* data = LockResource(loaded);
if (!data || size == 0) {
return false;
}
HANDLE file = CreateFileW(path.c_str(), GENERIC_WRITE, 0, nullptr,
CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file == INVALID_HANDLE_VALUE) {
return false;
}
DWORD written = 0;
bool ok = WriteFile(file, data, size, &written, nullptr) != FALSE && written == size;
CloseHandle(file);
return ok;
}
void PumpMessages()
{
MSG message{};
while (PeekMessageW(&message, nullptr, 0, 0, PM_REMOVE)) {
if (message.message == WM_QUIT) {
PostQuitMessage(static_cast<int>(message.wParam));
continue;
}
TranslateMessage(&message);
DispatchMessageW(&message);
}
}
int RunWait(const std::wstring& commandLine, bool hidden)
{
STARTUPINFOW startup{};
startup.cb = sizeof(startup);
DWORD creationFlags = 0;
if (hidden) {
startup.dwFlags = STARTF_USESHOWWINDOW;
startup.wShowWindow = SW_HIDE;
creationFlags = CREATE_NO_WINDOW;
}
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,
creationFlags, nullptr, nullptr, &startup, &process)) {
return -1;
}
CloseHandle(process.hThread);
for (;;) {
DWORD result = MsgWaitForMultipleObjects(1, &process.hProcess, FALSE,
30, QS_ALLINPUT);
if (result == WAIT_OBJECT_0) {
break;
}
PumpMessages();
}
DWORD exitCode = 1;
GetExitCodeProcess(process.hProcess, &exitCode);
CloseHandle(process.hProcess);
return static_cast<int>(exitCode);
}
COLORREF ColorFromHex(DWORD rgb)
{
return RGB((rgb >> 16) & 0xFF, (rgb >> 8) & 0xFF, rgb & 0xFF);
}
void DebugLog(const wchar_t* fmt, ...)
{
wchar_t enabled[8] = {};
if (GetEnvironmentVariableW(L"PARAVIS_DEBUG_LOG", enabled,
static_cast<DWORD>(std::size(enabled))) == 0) {
return;
}
va_list args;
va_start(args, fmt);
wchar_t message[4096] = {};
_vsnwprintf_s(message, std::size(message), _TRUNCATE, fmt, args);
va_end(args);
SYSTEMTIME st{};
GetLocalTime(&st);
std::wstring line = Format(L"%02d:%02d:%02d.%03d [%lu] %s\r\n",
st.wHour, st.wMinute, st.wSecond, st.wMilliseconds,
GetCurrentThreadId(), message);
wchar_t temp[MAX_PATH] = {};
DWORD length = GetTempPathW(MAX_PATH, temp);
std::wstring path;
if (length > 0) {
path.assign(temp, length);
}
path += L"winsetup_cpp_debug.log";
int needed = WideCharToMultiByte(CP_UTF8, 0, line.c_str(),
static_cast<int>(line.size()), nullptr, 0,
nullptr, nullptr);
if (needed > 0) {
std::string bytes(static_cast<size_t>(needed), '\0');
WideCharToMultiByte(CP_UTF8, 0, line.c_str(),
static_cast<int>(line.size()), bytes.data(), needed,
nullptr, nullptr);
HANDLE file = CreateFileW(path.c_str(), FILE_APPEND_DATA,
FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr,
OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (file != INVALID_HANDLE_VALUE) {
DWORD written = 0;
WriteFile(file, bytes.data(), static_cast<DWORD>(bytes.size()),
&written, nullptr);
CloseHandle(file);
}
}
}
+18
View File
@@ -0,0 +1,18 @@
#pragma once
#include <windows.h>
#include <string>
std::wstring Format(const wchar_t* fmt, ...);
std::wstring GetExeDir();
std::wstring GetExeFileName();
std::wstring GetWindowsDir();
std::wstring GetTempDir();
bool FileExists(const std::wstring& path);
bool DirectoryExists(const std::wstring& path);
bool ExtractResourceToFile(HINSTANCE instance, int resourceId, const wchar_t* type,
const std::wstring& path);
int RunWait(const std::wstring& commandLine, bool hidden = false);
void PumpMessages();
COLORREF ColorFromHex(DWORD rgb);
void DebugLog(const wchar_t* fmt, ...);