Windows installer 2.0 (C++ port)
This commit is contained in:
commit
45967534aa
32 files changed
+1768
No files matched your search
+627
@@ -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
@@ -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
@@ -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, ...);
|
||||
Reference in new issue
Block a user