#ifndef UNICODE #define UNICODE #endif #ifndef _UNICODE #define _UNICODE #endif #ifndef NOMINMAX #define NOMINMAX #endif #include #include #include #include #include #include #include #include #include #include #include namespace fs = std::filesystem; namespace { constexpr UINT WM_APPEND_LOG = WM_APP + 1; constexpr UINT WM_PROCESS_DONE = WM_APP + 2; constexpr int LOG_TEXT_LIMIT = 8 * 1024 * 1024; constexpr int LOG_TRIM_KEEP = 6 * 1024 * 1024; constexpr int LOG_APPEND_MARGIN = 32768; constexpr int TOPOLOGY_COUNT = 59; constexpr int ID_CUDA_CHAINS = 1001; constexpr int ID_CUDA_ITERS = 1002; constexpr int ID_MINUTES = 1003; constexpr int ID_TOPOLOGY_FROM = 1004; constexpr int ID_TOPOLOGY_TO = 1005; constexpr int ID_CHECKPOINT_SECONDS = 1006; constexpr int ID_SEARCH_MODE = 1007; constexpr int ID_START = 2001; constexpr int ID_STOP = 2002; enum class SearchState { idle, running, stopping }; HWND g_main_window = nullptr; HWND g_log = nullptr; HWND g_status = nullptr; HWND g_start = nullptr; HWND g_stop = nullptr; HWND g_cuda_chains = nullptr; HWND g_cuda_iters = nullptr; HWND g_minutes = nullptr; HWND g_topology_from = nullptr; HWND g_topology_to = nullptr; HWND g_checkpoint_seconds = nullptr; HWND g_search_mode = nullptr; std::vector g_setting_controls; HFONT g_ui_font = nullptr; HFONT g_title_font = nullptr; HFONT g_mono_font = nullptr; fs::path g_root; fs::path g_stop_file; PROCESS_INFORMATION g_process{}; SearchState g_search_state = SearchState::idle; bool g_close_after_stop = false; std::wstring quote_arg(const std::wstring& value) { if (value.empty()) { return L"\"\""; } if (value.find_first_of(L" \t\n\v\"") == std::wstring::npos) { return value; } std::wstring result; result.push_back(L'"'); size_t backslash_count = 0; for (const wchar_t ch : value) { if (ch == L'\\') { ++backslash_count; continue; } if (ch == L'"') { result.append(backslash_count * 2 + 1, L'\\'); result.push_back(L'"'); } else { result.append(backslash_count, L'\\'); result.push_back(ch); } backslash_count = 0; } result.append(backslash_count * 2, L'\\'); result.push_back(L'"'); return result; } std::wstring join_args(const std::vector& args) { std::wstring result; for (const std::wstring& arg : args) { if (!result.empty()) { result.push_back(L' '); } result += quote_arg(arg); } return result; } std::wstring get_text(HWND hwnd) { const int length = GetWindowTextLengthW(hwnd); std::vector buffer(static_cast(length) + 1, L'\0'); GetWindowTextW(hwnd, buffer.data(), static_cast(buffer.size())); return std::wstring(buffer.data()); } int make_automatic_seed() { LARGE_INTEGER counter{}; QueryPerformanceCounter(&counter); std::uint64_t value = static_cast(counter.QuadPart); value ^= static_cast(GetTickCount64()) << 21; value ^= static_cast(GetCurrentProcessId()) << 37; value ^= value >> 30; value *= 0xbf58476d1ce4e5b9ULL; value ^= value >> 27; return 1 + static_cast(value % 2147483646ULL); } std::wstring now_time() { SYSTEMTIME st{}; GetLocalTime(&st); wchar_t buffer[32]{}; swprintf_s(buffer, L"%02d:%02d:%02d", st.wHour, st.wMinute, st.wSecond); return buffer; } std::wstring win32_error_message(DWORD error) { wchar_t* message = nullptr; const DWORD length = FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, nullptr, error, 0, reinterpret_cast(&message), 0, nullptr); if (length == 0 || message == nullptr) { return L"код " + std::to_wstring(error); } std::wstring result(message, length); LocalFree(message); while (!result.empty() && (result.back() == L'\r' || result.back() == L'\n' || result.back() == L' ')) { result.pop_back(); } return result + L" (код " + std::to_wstring(error) + L")"; } void post_log(const std::wstring& text) { auto* payload = new std::wstring(text); const HWND window = g_main_window; if (window == nullptr || !PostMessageW(window, WM_APPEND_LOG, 0, reinterpret_cast(payload))) { delete payload; } } std::wstring bytes_to_wide(const char* data, int length) { if (length <= 0) { return L""; } int needed = MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, data, length, nullptr, 0); UINT codepage = CP_UTF8; DWORD flags = MB_ERR_INVALID_CHARS; if (needed <= 0) { codepage = CP_ACP; flags = 0; needed = MultiByteToWideChar(codepage, flags, data, length, nullptr, 0); } if (needed <= 0) { return L""; } std::wstring result(static_cast(needed), L'\0'); MultiByteToWideChar(codepage, flags, data, length, result.data(), needed); return result; } void append_log_direct(const std::wstring& text) { if (g_log == nullptr) { return; } const std::wstring line = L"[" + now_time() + L"] " + text + L"\r\n"; const int length = GetWindowTextLengthW(g_log); if (length + static_cast(line.size()) > LOG_TEXT_LIMIT - LOG_APPEND_MARGIN) { const int remove_until = std::max(0, length - LOG_TRIM_KEEP); if (remove_until > 0) { SendMessageW(g_log, WM_SETREDRAW, FALSE, 0); SendMessageW(g_log, EM_SETSEL, 0, remove_until); SendMessageW( g_log, EM_REPLACESEL, FALSE, reinterpret_cast(L"[... старые строки журнала удалены ...]\r\n")); SendMessageW(g_log, WM_SETREDRAW, TRUE, 0); InvalidateRect(g_log, nullptr, TRUE); } } const int end = GetWindowTextLengthW(g_log); SendMessageW(g_log, EM_SETSEL, end, end); SendMessageW(g_log, EM_REPLACESEL, FALSE, reinterpret_cast(line.c_str())); SendMessageW(g_log, EM_SCROLLCARET, 0, 0); } void set_search_state(SearchState state, const std::wstring& status) { g_search_state = state; const BOOL settings_enabled = state == SearchState::idle ? TRUE : FALSE; for (HWND control : g_setting_controls) { EnableWindow(control, settings_enabled); } EnableWindow(g_start, state == SearchState::idle ? TRUE : FALSE); EnableWindow(g_stop, state == SearchState::running ? TRUE : FALSE); if (g_status != nullptr) { SetWindowTextW(g_status, status.c_str()); } } fs::path find_root() { std::vector buffer(32768, L'\0'); const DWORD length = GetModuleFileNameW( nullptr, buffer.data(), static_cast(buffer.size())); fs::path candidate = fs::path(std::wstring(buffer.data(), length)).parent_path(); while (!candidate.empty()) { if (fs::exists(candidate / L"CMakeLists.txt") && fs::exists(candidate / L"data" / L"topologies.txt")) { return candidate; } const fs::path parent = candidate.parent_path(); if (parent == candidate) { break; } candidate = parent; } return fs::path(std::wstring(buffer.data(), length)).parent_path(); } fs::path main_exe() { const fs::path msbuild = g_root / L"build" / L"msbuild" / L"bin" / L"x64" / L"Release" / L"Szilassi.exe"; if (fs::exists(msbuild)) { return msbuild; } return g_root / L"build" / L"vs2026" / L"Release" / L"neighborly_main.exe"; } void close_process_handles() { if (g_process.hProcess != nullptr) { CloseHandle(g_process.hProcess); g_process.hProcess = nullptr; } if (g_process.hThread != nullptr) { CloseHandle(g_process.hThread); g_process.hThread = nullptr; } g_process.dwProcessId = 0; g_process.dwThreadId = 0; } bool parse_integer_setting( HWND control, const wchar_t* name, int minimum, int maximum, int& result ) { const std::wstring text = get_text(control); const wchar_t* first = text.c_str(); while (*first != L'\0' && iswspace(*first)) { ++first; } errno = 0; wchar_t* end = nullptr; const long long value = wcstoll(first, &end, 10); while (end != nullptr && *end != L'\0' && iswspace(*end)) { ++end; } if (first == end || end == nullptr || *end != L'\0' || errno == ERANGE || value < minimum || value > maximum) { const std::wstring message = std::wstring(L"Поле «") + name + L"» должно содержать целое число от " + std::to_wstring(minimum) + L" до " + std::to_wstring(maximum) + L"."; MessageBoxW(g_main_window, message.c_str(), L"Некорректные настройки", MB_OK | MB_ICONWARNING); SetFocus(control); SendMessageW(control, EM_SETSEL, 0, -1); return false; } result = static_cast(value); return true; } fs::path make_stop_path() { SYSTEMTIME st{}; GetLocalTime(&st); wchar_t name[128]{}; swprintf_s( name, L"cuda_stop_%04d%02d%02d_%02d%02d%02d_%03d_pid%lu.flag", st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond, st.wMilliseconds, static_cast(GetCurrentProcessId())); return g_root / L"runtime" / L"reports" / name; } bool start_process( const std::wstring& title, const fs::path& exe, const std::vector& args, const fs::path& stop_file ) { if (g_search_state != SearchState::idle) { return false; } if (!fs::exists(exe)) { const std::wstring message = L"Не найден исполняемый файл:\n" + exe.wstring() + L"\n\nСоберите preset vs2026-release через CMake или отдельно " L"projects\\Szilassi\\Szilassi.vcxproj в конфигурации Release|x64."; MessageBoxW(g_main_window, message.c_str(), L"Файл не найден", MB_OK | MB_ICONWARNING); return false; } std::error_code directory_error; fs::create_directories(stop_file.parent_path(), directory_error); fs::create_directories(g_root / L"results" / L"search", directory_error); std::error_code remove_error; fs::remove(stop_file, remove_error); if (remove_error || fs::exists(stop_file)) { const std::wstring message = L"Не удалось подготовить stop-файл:\n" + stop_file.wstring(); MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR); return false; } SECURITY_ATTRIBUTES sa{}; sa.nLength = sizeof(sa); sa.bInheritHandle = TRUE; HANDLE read_pipe = nullptr; HANDLE write_pipe = nullptr; if (!CreatePipe(&read_pipe, &write_pipe, &sa, 0)) { const DWORD error = GetLastError(); const std::wstring message = L"Не удалось создать канал вывода:\n" + win32_error_message(error); MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR); return false; } if (!SetHandleInformation(read_pipe, HANDLE_FLAG_INHERIT, 0)) { const DWORD error = GetLastError(); CloseHandle(read_pipe); CloseHandle(write_pipe); const std::wstring message = L"Не удалось настроить канал вывода:\n" + win32_error_message(error); MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR); return false; } std::wstring command = quote_arg(exe.wstring()); const std::wstring argument_string = join_args(args); if (!argument_string.empty()) { command += L" " + argument_string; } STARTUPINFOW startup{}; startup.cb = sizeof(startup); startup.dwFlags = STARTF_USESTDHANDLES; startup.hStdOutput = write_pipe; startup.hStdError = write_pipe; startup.hStdInput = GetStdHandle(STD_INPUT_HANDLE); close_process_handles(); PROCESS_INFORMATION process{}; std::vector mutable_command(command.begin(), command.end()); mutable_command.push_back(L'\0'); append_log_direct(L"=== " + title + L" ==="); append_log_direct(command); const BOOL created = CreateProcessW( nullptr, mutable_command.data(), nullptr, nullptr, TRUE, CREATE_NO_WINDOW | BELOW_NORMAL_PRIORITY_CLASS, nullptr, g_root.c_str(), &startup, &process); const DWORD create_error = created ? ERROR_SUCCESS : GetLastError(); CloseHandle(write_pipe); if (!created) { CloseHandle(read_pipe); const std::wstring message = L"Не удалось запустить поиск:\n" + win32_error_message(create_error); MessageBoxW(g_main_window, message.c_str(), L"Ошибка запуска", MB_OK | MB_ICONERROR); return false; } g_process = process; g_stop_file = stop_file; g_close_after_stop = false; set_search_state(SearchState::running, L"Поиск выполняется"); std::thread([read_pipe, process]() { char buffer[4096]; DWORD bytes_read = 0; while (ReadFile(read_pipe, buffer, sizeof(buffer), &bytes_read, nullptr) && bytes_read > 0) { post_log(bytes_to_wide(buffer, static_cast(bytes_read))); } CloseHandle(read_pipe); WaitForSingleObject(process.hProcess, INFINITE); DWORD exit_code = 0; GetExitCodeProcess(process.hProcess, &exit_code); const HWND window = g_main_window; if (window != nullptr) { PostMessageW(window, WM_PROCESS_DONE, static_cast(exit_code), 0); } }).detach(); return true; } bool create_stop_file() { if (g_stop_file.empty()) { return false; } HANDLE file = CreateFileW( g_stop_file.c_str(), GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); if (file == INVALID_HANDLE_VALUE) { const DWORD error = GetLastError(); const std::wstring message = L"Не удалось запросить штатную остановку:\n" + win32_error_message(error) + L"\n\nПоиск продолжает работать; данные не были принудительно прерваны."; MessageBoxW(g_main_window, message.c_str(), L"Ошибка остановки", MB_OK | MB_ICONERROR); append_log_direct(message); return false; } static constexpr char marker[] = "stop\n"; DWORD written = 0; WriteFile(file, marker, static_cast(sizeof(marker) - 1), &written, nullptr); FlushFileBuffers(file); CloseHandle(file); return true; } void request_stop(bool close_after_stop) { if (g_search_state == SearchState::idle) { if (close_after_stop) { DestroyWindow(g_main_window); } return; } if (g_search_state == SearchState::stopping) { g_close_after_stop = g_close_after_stop || close_after_stop; if (g_close_after_stop) { SetWindowTextW(g_status, L"Останавливается, сохраняет checkpoint и закроется..."); } return; } if (!create_stop_file()) { return; } g_close_after_stop = close_after_stop; append_log_direct(L"Запрошена штатная остановка. Ожидаю завершения CUDA-пакета и сохранения checkpoint..."); set_search_state( SearchState::stopping, close_after_stop ? L"Останавливается, сохраняет checkpoint и закроется..." : L"Останавливается и сохраняет checkpoint..."); } HWND make_label(HWND parent, const std::wstring& text, int x, int y, int width, int height) { HWND control = CreateWindowExW( 0, L"STATIC", text.c_str(), WS_CHILD | WS_VISIBLE, x, y, width, height, parent, nullptr, nullptr, nullptr); SendMessageW(control, WM_SETFONT, reinterpret_cast(g_ui_font), TRUE); return control; } HWND make_number_edit( HWND parent, int id, const std::wstring& text, int x, int y, int width, int height ) { HWND control = CreateWindowExW( WS_EX_CLIENTEDGE, L"EDIT", text.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP | ES_AUTOHSCROLL | ES_NUMBER, x, y, width, height, parent, reinterpret_cast(static_cast(id)), nullptr, nullptr); SendMessageW(control, WM_SETFONT, reinterpret_cast(g_ui_font), TRUE); SendMessageW(control, EM_SETLIMITTEXT, 10, 0); g_setting_controls.push_back(control); return control; } HWND make_button( HWND parent, int id, const std::wstring& text, int x, int y, int width, int height ) { HWND control = CreateWindowExW( 0, L"BUTTON", text.c_str(), WS_CHILD | WS_VISIBLE | WS_TABSTOP | BS_PUSHBUTTON, x, y, width, height, parent, reinterpret_cast(static_cast(id)), nullptr, nullptr); SendMessageW(control, WM_SETFONT, reinterpret_cast(g_ui_font), TRUE); return control; } HWND make_search_mode_combo(HWND parent, int x, int y, int width, int height) { HWND control = CreateWindowExW( 0, L"COMBOBOX", L"", WS_CHILD | WS_VISIBLE | WS_TABSTOP | WS_VSCROLL | CBS_DROPDOWNLIST, x, y, width, height, parent, reinterpret_cast(static_cast(ID_SEARCH_MODE)), nullptr, nullptr); SendMessageW(control, WM_SETFONT, reinterpret_cast(g_ui_font), TRUE); SendMessageW(control, CB_ADDSTRING, 0, reinterpret_cast(L"Обычный")); SendMessageW(control, CB_ADDSTRING, 0, reinterpret_cast(L"Проработка худших")); SendMessageW(control, CB_SETCURSEL, 0, 0); g_setting_controls.push_back(control); return control; } void create_controls(HWND window) { g_title_font = CreateFontW( 24, 0, 0, 0, FW_BOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI"); g_ui_font = CreateFontW( 17, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, DEFAULT_PITCH, L"Segoe UI"); g_mono_font = CreateFontW( 15, 0, 0, 0, FW_NORMAL, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, FIXED_PITCH, L"Consolas"); HWND title = make_label(window, L"Szilassi — CUDA-поиск", 18, 14, 430, 32); SendMessageW(title, WM_SETFONT, reinterpret_cast(g_title_font), TRUE); make_label(window, L"Папка проекта: " + g_root.wstring(), 20, 50, 980, 24); make_label(window, L"Цепочек (0 = авто)", 22, 86, 138, 22); g_cuda_chains = make_number_edit(window, ID_CUDA_CHAINS, L"0", 22, 110, 120, 27); make_label(window, L"Итераций / пакет", 160, 86, 150, 22); g_cuda_iters = make_number_edit(window, ID_CUDA_ITERS, L"64", 160, 110, 135, 27); make_label(window, L"Минут", 313, 86, 85, 22); g_minutes = make_number_edit(window, ID_MINUTES, L"480", 313, 110, 85, 27); make_label(window, L"Топологии: от", 416, 86, 125, 22); g_topology_from = make_number_edit(window, ID_TOPOLOGY_FROM, L"0", 416, 110, 70, 27); make_label(window, L"до", 504, 86, 45, 22); g_topology_to = make_number_edit(window, ID_TOPOLOGY_TO, L"58", 504, 110, 70, 27); make_label(window, L"Checkpoint, сек", 592, 86, 135, 22); g_checkpoint_seconds = make_number_edit(window, ID_CHECKPOINT_SECONDS, L"30", 592, 110, 125, 27); make_label(window, L"Режим поиска", 735, 86, 245, 22); g_search_mode = make_search_mode_combo(window, 735, 110, 245, 180); make_label( window, L"CUDA-устройство выбирается автоматически. Поиск: FP32; финальная проверка кандидатов: DD.", 22, 149, 960, 24); g_start = make_button(window, ID_START, L"Запустить поиск", 22, 184, 220, 38); g_stop = make_button(window, ID_STOP, L"Остановить поиск", 254, 184, 220, 38); g_status = make_label(window, L"Готово", 22, 238, 960, 24); g_log = CreateWindowExW( WS_EX_CLIENTEDGE, L"EDIT", L"", WS_CHILD | WS_VISIBLE | WS_VSCROLL | ES_MULTILINE | ES_AUTOVSCROLL | ES_READONLY, 18, 270, 982, 400, window, nullptr, nullptr, nullptr); SendMessageW(g_log, EM_SETLIMITTEXT, static_cast(LOG_TEXT_LIMIT), 0); SendMessageW(g_log, WM_SETFONT, reinterpret_cast(g_mono_font), TRUE); set_search_state(SearchState::idle, L"Готово"); append_log_direct(L"Интерфейс готов. Существующие checkpoint будут подхвачены автоматически."); append_log_direct(L"Исполняемый файл поиска: " + main_exe().wstring()); } void resize_controls(HWND window) { if (g_log == nullptr) { return; } RECT client{}; GetClientRect(window, &client); const int width = client.right - client.left; const int height = client.bottom - client.top; MoveWindow(g_log, 18, 270, std::max(360, width - 36), std::max(140, height - 290), TRUE); } void start_search() { if (g_search_state != SearchState::idle) { return; } int cuda_chains = 0; int cuda_iters = 0; int minutes = 0; int topology_from = 0; int topology_to = 0; int checkpoint_seconds = 0; if (!parse_integer_setting(g_cuda_chains, L"GPU-цепочек", 0, 1048576, cuda_chains) || !parse_integer_setting(g_cuda_iters, L"Итераций / пакет", 1, 64, cuda_iters) || !parse_integer_setting(g_minutes, L"Минут", 0, 525600, minutes) || !parse_integer_setting(g_topology_from, L"Топологии: от", 0, TOPOLOGY_COUNT - 1, topology_from) || !parse_integer_setting(g_topology_to, L"Топологии: до", 0, TOPOLOGY_COUNT - 1, topology_to) || !parse_integer_setting( g_checkpoint_seconds, L"Checkpoint, сек", 5, 3600, checkpoint_seconds)) { return; } if (topology_from > topology_to) { MessageBoxW( g_main_window, L"Начало диапазона топологий не может быть больше конца.", L"Некорректный диапазон", MB_OK | MB_ICONWARNING); SetFocus(g_topology_from); return; } const int seed = make_automatic_seed(); append_log_direct(L"Seed запуска: " + std::to_wstring(seed)); const bool prioritize_worst = SendMessageW(g_search_mode, CB_GETCURSEL, 0, 0) == 1; append_log_direct( prioritize_worst ? L"Режим поиска: проработка худших вариантов." : L"Режим поиска: обычный."); const fs::path stop_path = make_stop_path(); const std::wstring title = L"CUDA-поиск, топологии " + std::to_wstring(topology_from) + L"–" + std::to_wstring(topology_to); std::vector args{ L"--global-search", L"--cuda", L"--seed", std::to_wstring(seed), L"--cuda-chains", std::to_wstring(cuda_chains), L"--cuda-iters", std::to_wstring(cuda_iters), L"--minutes", std::to_wstring(minutes), L"--topology-from", std::to_wstring(topology_from), L"--topology-to", std::to_wstring(topology_to), L"--checkpoint-seconds", std::to_wstring(checkpoint_seconds), L"--stop-file", stop_path.wstring(), L"--global-dir", L"results\\search" }; if (prioritize_worst) { args.push_back(L"--prioritize-worst"); } start_process( title, main_exe(), args, stop_path); } void handle_command(int id) { switch (id) { case ID_START: start_search(); break; case ID_STOP: request_stop(false); break; } } LRESULT CALLBACK window_proc(HWND window, UINT message, WPARAM wparam, LPARAM lparam) { switch (message) { case WM_CREATE: g_main_window = window; create_controls(window); return 0; case WM_SIZE: resize_controls(window); return 0; case WM_COMMAND: if (HIWORD(wparam) == BN_CLICKED) { handle_command(LOWORD(wparam)); } return 0; case WM_APPEND_LOG: { auto* text = reinterpret_cast(lparam); append_log_direct(*text); delete text; return 0; } case WM_PROCESS_DONE: { const DWORD exit_code = static_cast(wparam); const bool stopped_by_user = g_search_state == SearchState::stopping; append_log_direct(L"Процесс завершён, код выхода: " + std::to_wstring(exit_code)); close_process_handles(); if (!g_stop_file.empty()) { if (exit_code == 0) { std::error_code remove_error; fs::remove(g_stop_file, remove_error); } g_stop_file.clear(); } if (exit_code == 0 && stopped_by_user) { set_search_state(SearchState::idle, L"Поиск остановлен, checkpoint сохранён"); } else if (exit_code == 0) { set_search_state(SearchState::idle, L"Поиск завершён"); } else { set_search_state( SearchState::idle, L"Поиск завершился с ошибкой, код " + std::to_wstring(exit_code)); } if (g_close_after_stop) { g_close_after_stop = false; DestroyWindow(window); } return 0; } case WM_QUERYENDSESSION: if (g_search_state != SearchState::idle) { request_stop(true); } return TRUE; case WM_CLOSE: if (g_search_state == SearchState::running) { const int answer = MessageBoxW( window, L"Поиск ещё выполняется. Штатно остановить его, сохранить checkpoint и закрыть окно?", L"Поиск выполняется", MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2); if (answer == IDYES) { request_stop(true); } return 0; } if (g_search_state == SearchState::stopping) { g_close_after_stop = true; SetWindowTextW(g_status, L"Останавливается, сохраняет checkpoint и закроется..."); return 0; } DestroyWindow(window); return 0; case WM_DESTROY: g_main_window = nullptr; if (g_search_state == SearchState::idle) { close_process_handles(); } if (g_ui_font != nullptr) { DeleteObject(g_ui_font); g_ui_font = nullptr; } if (g_title_font != nullptr) { DeleteObject(g_title_font); g_title_font = nullptr; } if (g_mono_font != nullptr) { DeleteObject(g_mono_font); g_mono_font = nullptr; } PostQuitMessage(0); return 0; } return DefWindowProcW(window, message, wparam, lparam); } } // namespace int WINAPI wWinMain(HINSTANCE instance, HINSTANCE, PWSTR, int show_command) { g_root = find_root(); WNDCLASSW window_class{}; window_class.lpfnWndProc = window_proc; window_class.hInstance = instance; window_class.lpszClassName = L"PolyhedronGuiWindow"; window_class.hCursor = LoadCursor(nullptr, IDC_ARROW); window_class.hbrBackground = reinterpret_cast(COLOR_WINDOW + 1); RegisterClassW(&window_class); HWND window = CreateWindowExW( 0, window_class.lpszClassName, L"Szilassi — CUDA-поиск", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 1040, 740, nullptr, nullptr, instance, nullptr); if (window == nullptr) { return static_cast(GetLastError()); } ShowWindow(window, show_command); UpdateWindow(window); MSG message{}; while (GetMessageW(&message, nullptr, 0, 0)) { TranslateMessage(&message); DispatchMessageW(&message); } return static_cast(message.wParam); }