From ecb6a040440c7351f6bb8f3555d184ceba49bc8c Mon Sep 17 00:00:00 2001 From: George Tokmaji Date: Wed, 7 Aug 2024 09:51:07 +0200 Subject: [PATCH 1/3] Windows: Use Unicode APIs wherever possible and use Common Controls Version 6 This gets us a more modern control look. Note that window-related functions never use the ANSI versions, the arguments are converted manually, making the behavior more consistent with true Unicode applications. --- CMakeLists.txt | 7 +++ src/C4Application.cpp | 6 +-- src/C4ComponentHost.cpp | 20 +++---- src/C4Config.cpp | 14 ++--- src/C4Console.cpp | 112 +++++++++++++++++++++++----------------- src/C4Console.h | 4 ++ src/C4EditCursor.cpp | 5 +- src/C4FileMonitor.cpp | 2 +- src/C4FileSelDlg.cpp | 6 +-- src/C4Fonts.cpp | 2 +- src/C4FullScreen.cpp | 4 +- src/C4Group.cpp | 6 +-- src/C4GuiDialogs.cpp | 2 +- src/C4Network2Res.cpp | 2 +- src/C4PropertyDlg.cpp | 34 +++++++----- src/C4Scenario.cpp | 2 +- src/C4Thread.cpp | 2 +- src/C4ToolsDlg.cpp | 54 +++++++++++-------- src/C4Update.cpp | 4 +- src/C4UpdateDlg.cpp | 4 +- src/C4Viewport.cpp | 4 +- src/C4WinMain.cpp | 4 +- src/OpenURL.cpp | 2 +- src/StdFile.cpp | 24 +++++---- src/StdFile.h | 3 +- src/StdRegistry.cpp | 46 ++++++++--------- src/StdWindow.cpp | 5 +- src/c4group_cmdl.cpp | 10 ++-- 28 files changed, 219 insertions(+), 171 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 34d84ec9a..99c78139e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -302,6 +302,10 @@ endif () add_library(standard STATIC ${LIBSTANDARD_SOURCES}) target_compile_definitions(standard PRIVATE C4ENGINE) +if (WIN32) + target_compile_definitions(standard PUBLIC UNICODE _UNICODE) +endif () + # Set C4_OS macro if (APPLE) set(C4_OS "mac") @@ -433,6 +437,9 @@ if (WIN32) if (USE_SDL_MIXER) target_link_libraries(clonk imm32.lib setupapi.lib version.lib) endif () + + target_link_libraries(clonk comctl32) + target_link_options(clonk PRIVATE "/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'") endif () # Link C++/WinRT diff --git a/src/C4Application.cpp b/src/C4Application.cpp index 820ac5e92..7862142aa 100644 --- a/src/C4Application.cpp +++ b/src/C4Application.cpp @@ -66,10 +66,10 @@ C4Application::~C4Application() { #ifdef _WIN32 char strCommandLine[_MAX_PATH + 1]; SCopy(Config.AtExePath(C4CFN_Editor), strCommandLine); - STARTUPINFO StartupInfo{}; + STARTUPINFOA StartupInfo{}; StartupInfo.cb = sizeof(StartupInfo); PROCESS_INFORMATION ProcessInfo{}; - CreateProcess(nullptr, strCommandLine, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &StartupInfo, &ProcessInfo); + CreateProcessA(nullptr, strCommandLine, nullptr, nullptr, TRUE, 0, nullptr, nullptr, &StartupInfo, &ProcessInfo); #endif } } @@ -209,7 +209,7 @@ void C4Application::DoInit() #if defined(_WIN32) && !defined(USE_CONSOLE) // Register clonk file classes - notice: this will only work if we have administrator rights - char szModule[_MAX_PATH + 1]; GetModuleFileName(nullptr, szModule, _MAX_PATH); + char szModule[_MAX_PATH + 1]; GetModuleFileNameA(nullptr, szModule, _MAX_PATH); SetC4FileClasses(szModule); #endif diff --git a/src/C4ComponentHost.cpp b/src/C4ComponentHost.cpp index 8a2394a66..a618b060c 100644 --- a/src/C4ComponentHost.cpp +++ b/src/C4ComponentHost.cpp @@ -22,6 +22,7 @@ #include #ifdef _WIN32 +#include "C4Console.h" #include "StdRegistry.h" #include "res/engine_resource.h" #endif @@ -279,13 +280,13 @@ INT_PTR CALLBACK C4ComponentHost::ComponentDlgProc(const HWND hDlg, const UINT m const auto &componentHost = *reinterpret_cast(lParam); SetWindowLongPtr(hDlg, DWLP_USER, lParam); - SetWindowText(hDlg, componentHost.Name); - SetDlgItemText(hDlg, IDOK, LoadResStr(C4ResStrTableKey::IDS_BTN_OK)); - SetDlgItemText(hDlg, IDCANCEL, LoadResStr(C4ResStrTableKey::IDS_BTN_CANCEL)); + SetWindowText(hDlg, StdStringEncodingConverter::WinAcpToUtf16(componentHost.Name).c_str()); + SetDlgItemText(hDlg, IDOK, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_BTN_OK)).c_str()); + SetDlgItemText(hDlg, IDCANCEL, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_BTN_CANCEL)).c_str()); if (const char *const data{componentHost.GetData()}; data) { - SetDlgItemText(hDlg, IDC_EDITDATA, data); + SetDlgItemText(hDlg, IDC_EDITDATA, StdStringEncodingConverter::WinAcpToUtf16(data).c_str()); } RestoreWindowPosition(hDlg, "Component", Config.GetSubkeyPath("Console")); @@ -320,15 +321,8 @@ INT_PTR CALLBACK C4ComponentHost::ComponentDlgProc(const HWND hDlg, const UINT m } else { - StdStrBuf &data{componentHost.Data}; - data.SetLength(size); - GetDlgItemText(hDlg, IDC_EDITDATA, data.getMData(), static_cast(size + 1)); - - const std::size_t actualSize{std::strlen(data.getData())}; - if (actualSize != size) - { - data.SetLength(size); - } + const std::string text{StdStringEncodingConverter::Utf16ToWinAcp(C4Console::GetDialogItemText(hDlg, IDC_EDITDATA))}; + componentHost.Data.Copy(text.c_str(), text.size()); } componentHost.Modified = true; diff --git a/src/C4Config.cpp b/src/C4Config.cpp index d496916ca..5f736771d 100644 --- a/src/C4Config.cpp +++ b/src/C4Config.cpp @@ -465,7 +465,7 @@ bool C4Config::Load(bool forceWorkingDirectory, const char *szConfigFile) StdStrBuf filename(getenv("HOME"), false); if (filename) { filename += "/"; } filename += ".legacyclonk"; - CreateDirectory(filename.getData()); + MakeDirectory(filename.getData()); } #endif // Buggy StdCompiler crashes when compiling a Null-StdStrBuf @@ -588,12 +588,12 @@ void C4ConfigGeneral::DeterminePaths(bool forceWorkingDirectory) { #ifdef _WIN32 // Exe path - if (GetModuleFileName(nullptr, ExePath, CFG_MaxString)) + if (GetModuleFileNameA(nullptr, ExePath, CFG_MaxString)) { TruncatePath(ExePath); AppendBackslash(ExePath); } // Temp path - GetTempPath(CFG_MaxString, TempPath); + GetTempPathA(CFG_MaxString, TempPath); if (TempPath[0]) AppendBackslash(TempPath); #elif defined(__linux__) #ifdef C4ENGINE @@ -635,7 +635,7 @@ void C4ConfigGeneral::DeterminePaths(bool forceWorkingDirectory) #ifdef C4ENGINE // Create user path if it doesn't already exist if (!DirectoryExists(Config.AtUserPath(""))) - CreateDirectory(Config.AtUserPath(""), nullptr); // currently no error handling here; also: no recursive directory creation + MakeDirectory(Config.AtUserPath(""), nullptr); // currently no error handling here; also: no recursive directory creation #endif } @@ -681,7 +681,7 @@ const char *C4Config::AtScreenshotPath(const char *szFilename) if (const auto len = SLen(AtPathFilename); len > 0) if (AtPathFilename[len - 1] == DirectorySeparator) AtPathFilename[len - 1] = '\0'; - if (!DirectoryExists(AtPathFilename) && !CreateDirectory(AtPathFilename, nullptr)) + if (!DirectoryExists(AtPathFilename) && !MakeDirectory(AtPathFilename, nullptr)) { SCopy(General.ExePath, General.ScreenshotPath, CFG_MaxString - 1); SCopy(General.ScreenshotPath, AtPathFilename, _MAX_PATH); @@ -698,7 +698,7 @@ bool C4ConfigGeneral::CreateSaveFolder(const char *strDirectory, const char *str { // Create directory if needed if (!DirectoryExists(strDirectory)) - if (!CreateDirectory(strDirectory, nullptr)) + if (!MakeDirectory(strDirectory, nullptr)) return false; // Create title component if needed char lang[3]; SCopy(Config.General.Language, lang, 2); @@ -912,7 +912,7 @@ void C4Config::ExpandEnvironmentVariables(char *strPath, int iMaxLen) { #ifdef _WIN32 char buf[_MAX_PATH + 1]; - ExpandEnvironmentStrings(strPath, buf, _MAX_PATH); + ExpandEnvironmentStringsA(strPath, buf, _MAX_PATH); SCopy(buf, strPath, iMaxLen); #else // __linux__ or __APPLE___ StdStrBuf home(getenv("HOME"), false); diff --git a/src/C4Console.cpp b/src/C4Console.cpp index c60fd4133..9349291f8 100644 --- a/src/C4Console.cpp +++ b/src/C4Console.cpp @@ -183,12 +183,14 @@ INT_PTR CALLBACK ConsoleDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPara switch (LOWORD(wParam)) { case IDOK: - // IDC_COMBOINPUT to Console.In() - char buffer[16000]; - GetDlgItemText(hDlg, IDC_COMBOINPUT, buffer, 16000); - if (buffer[0]) - Console.In(buffer); + { + const std::wstring text{C4Console::GetDialogItemText(hDlg, IDC_COMBOINPUT)}; + if (!text.empty()) + { + Console.In(StdStringEncodingConverter::Utf16ToWinAcp(text).c_str()); + } return TRUE; + } case IDC_BUTTONHALT: Console.DoHalt(); @@ -294,25 +296,20 @@ bool C4Console::Init(CStdApp *const app, const char *const title, const C4Rect & Active = true; // Editing (enable even if network) Editing = true; - // Create dialog window #ifdef _WIN32 + // Init common controls + INITCOMMONCONTROLSEX controls{.dwSize = sizeof(controls), .dwICC = ICC_STANDARD_CLASSES}; + if (!InitCommonControlsEx(&controls)) + { + spdlog::critical("Error initializing common controls: {}", winrt::to_string(winrt::hresult_error{HRESULT_FROM_WIN32(GetLastError())}.message())); + return false; + } + + // Create dialog window hWindow = CreateDialog(app->hInstance, MAKEINTRESOURCE(IDD_CONSOLE), nullptr, ConsoleDlgProc); if (!hWindow) { - char *lpMsgBuf; - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - nullptr, - GetLastError(), - 0, - (LPTSTR)&lpMsgBuf, - 0, - nullptr); - spdlog::critical("Error creating dialog window: {}", lpMsgBuf); - // Free the buffer. - LocalFree(lpMsgBuf); + spdlog::critical("Error creating dialog window: {}", winrt::to_string(winrt::hresult_error{HRESULT_FROM_WIN32(GetLastError())}.message())); return false; } // Restore window position @@ -602,14 +599,16 @@ bool C4Console::Out(std::string_view text) if (!Active) return false; if (text.empty()) return true; - const bool hasNewline{text.ends_with("\r\n")}; + const std::wstring textWide{StdStringEncodingConverter::WinAcpToUtf16(text)}; - std::string buffer; + const bool hasNewline{textWide.ends_with(L"\r\n")}; + + std::wstring buffer; const LRESULT dlgItemTextSize{SendDlgItemMessage(hWindow, IDC_EDITOUTPUT, WM_GETTEXTLENGTH, 0, 0)}; - buffer.resize_and_overwrite(dlgItemTextSize + text.size() + (hasNewline ? 0 : 2), [dlgItemTextSize, hasNewline,&text, this](char *const ptr, std::size_t size) + buffer.resize_and_overwrite(dlgItemTextSize + textWide.size() + (hasNewline ? 0 : 2), [dlgItemTextSize, hasNewline, &textWide, this](wchar_t *const ptr, std::size_t size) { const UINT textSize{GetDlgItemText(hWindow, IDC_EDITOUTPUT, ptr, dlgItemTextSize + 1)}; - char *newlinePtr{ptr + textSize + text.copy(ptr + textSize, text.size())}; + wchar_t *newlinePtr{ptr + textSize + textWide.copy(ptr + textSize, textWide.size())}; if (!hasNewline) { @@ -620,7 +619,7 @@ bool C4Console::Out(std::string_view text) return newlinePtr - ptr; }); - const char *const newText{buffer.size() <= 60000 ? buffer.c_str() : buffer.c_str() + buffer.size() - 60000}; // max log length: Otherwise, discard beginning + const wchar_t *const newText{buffer.size() <= 60000 ? buffer.c_str() : buffer.c_str() + buffer.size() - 60000}; // max log length: Otherwise, discard beginning SetDlgItemText(hWindow, IDC_EDITOUTPUT, newText); const auto lines = SendDlgItemMessage(hWindow, IDC_EDITOUTPUT, EM_GETLINECOUNT, 0, 0); @@ -653,7 +652,7 @@ bool C4Console::Out(std::string_view text) bool C4Console::ClearLog() { #ifdef _WIN32 - SetDlgItemText(hWindow, IDC_EDITOUTPUT, ""); + SetDlgItemText(hWindow, IDC_EDITOUTPUT, L""); SendDlgItemMessage(hWindow, IDC_EDITOUTPUT, EM_LINESCROLL, 0, 0); UpdateWindow(hWindow); #elif WITH_DEVELOPER_MODE @@ -679,11 +678,12 @@ bool C4Console::UpdateStatusBars() if (Game.FrameCounter != FrameCounter) { FrameCounter = Game.FrameCounter; - const std::string text{std::format("Frame: {}", FrameCounter)}; #ifdef _WIN32 + const std::wstring text{std::format(L"Frame: {}", FrameCounter)}; SetDlgItemText(hWindow, IDC_STATICFRAME, text.c_str()); UpdateWindow(GetDlgItem(hWindow, IDC_STATICFRAME)); #elif WITH_DEVELOPER_MODE + const std::string text{std::format("Frame: {}", FrameCounter)}; gtk_label_set_label(GTK_LABEL(lblFrame), text.c_str()); #endif // WITH_DEVELOPER_MODE / _WIN32 } @@ -691,11 +691,12 @@ bool C4Console::UpdateStatusBars() if (Game.Script.Counter != ScriptCounter) { ScriptCounter = Game.Script.Counter; - const std::string text{std::format("Script: {}", ScriptCounter)}; #ifdef _WIN32 + const std::wstring text{std::format(L"Script: {}", ScriptCounter)}; SetDlgItemText(hWindow, IDC_STATICSCRIPT, text.c_str()); UpdateWindow(GetDlgItem(hWindow, IDC_STATICSCRIPT)); #elif WITH_DEVELOPER_MODE + const std::string text{std::format("Script: {}", ScriptCounter)}; gtk_label_set_label(GTK_LABEL(lblScript), text.c_str()); #endif // WITH_DEVELOPER_MODE / _WIN32 } @@ -704,11 +705,12 @@ bool C4Console::UpdateStatusBars() { Time = Game.Time; FPS = Game.FPS; - const std::string text{std::format("{:02}:{:02}:{:02} ({} FPS)", Time / 3600, (Time % 3600) / 60, Time % 60, FPS)}; #ifdef _WIN32 + const std::wstring text{std::format(L"{:02}:{:02}:{:02} ({} FPS)", Time / 3600, (Time % 3600) / 60, Time % 60, FPS)}; SetDlgItemText(hWindow, IDC_STATICTIME, text.c_str()); UpdateWindow(GetDlgItem(hWindow, IDC_STATICTIME)); #elif WITH_DEVELOPER_MODE + const std::string text{std::format("{:02}:{:02}:{:02} ({} FPS)", Time / 3600, (Time % 3600) / 60, Time % 60, FPS)}; gtk_label_set_label(GTK_LABEL(lblTime), text.c_str()); #endif // WITH_DEVELOPER_MODE } @@ -851,7 +853,7 @@ bool C4Console::Message(const char *szMessage, bool fQuery) { if (!Active) return false; #ifdef _WIN32 - return (IDOK == MessageBox(hWindow, szMessage, C4ENGINECAPTION, fQuery ? (MB_OKCANCEL | MB_ICONEXCLAMATION) : MB_ICONEXCLAMATION)); + return (IDOK == MessageBox(hWindow, StdStringEncodingConverter::WinAcpToUtf16(szMessage).c_str(), _CRT_WIDE(C4ENGINECAPTION), fQuery ? (MB_OKCANCEL | MB_ICONEXCLAMATION) : MB_ICONEXCLAMATION)); #elif WITH_DEVELOPER_MODE GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window), GTK_DIALOG_MODAL, GTK_MESSAGE_INFO, fQuery ? (GTK_BUTTONS_OK_CANCEL) : (GTK_BUTTONS_OK), "%s", szMessage); int response = gtk_dialog_run(GTK_DIALOG(dialog)); @@ -1008,7 +1010,7 @@ bool C4Console::FileSelect(char *sFilename, int iSize, const char *szFilter, uin #endif { #ifdef _WIN32 - OPENFILENAME ofn{}; + OPENFILENAMEA ofn{}; ofn.lStructSize = sizeof(ofn); ofn.hwndOwner = hWindow; ofn.lpstrFilter = szFilter; @@ -1018,12 +1020,12 @@ bool C4Console::FileSelect(char *sFilename, int iSize, const char *szFilter, uin bool fResult; if (fSave) - fResult = GetSaveFileName(&ofn); + fResult = GetSaveFileNameA(&ofn); else - fResult = GetOpenFileName(&ofn); + fResult = GetOpenFileNameA(&ofn); // Reset working directory to exe path as Windows file dialog might have changed it - SetCurrentDirectory(Config.General.ExePath); + SetCurrentDirectoryA(Config.General.ExePath); return fResult; #elif WITH_DEVELOPER_MODE GtkWidget *dialog = gtk_file_chooser_dialog_new(fSave ? "Save file..." : "Load file...", GTK_WINDOW(window), fSave ? GTK_FILE_CHOOSER_ACTION_SAVE : GTK_FILE_CHOOSER_ACTION_OPEN, "format-text-bold", GTK_RESPONSE_CANCEL, fSave ? "document-save" : "document-open", GTK_RESPONSE_ACCEPT, nullptr); @@ -1202,8 +1204,8 @@ bool C4Console::FileQuit() void C4Console::HelpAbout() { #ifdef _WIN32 - static constexpr auto Message = C4ENGINECAPTION " " C4VERSION "\n\nCopyright (c) " C4COPYRIGHT_YEAR " " C4COPYRIGHT_COMPANY; - MessageBox(nullptr, Message, C4ENGINECAPTION, MB_ICONINFORMATION | MB_TASKMODAL); + static constexpr auto Message = _CRT_WIDE(C4ENGINECAPTION) L" " _CRT_WIDE(C4VERSION) L"\n\nCopyright (c) " _CRT_WIDE(C4COPYRIGHT_YEAR) L" " _CRT_WIDE(C4COPYRIGHT_COMPANY); + MessageBox(nullptr, Message, _CRT_WIDE(C4ENGINECAPTION), MB_ICONINFORMATION | MB_TASKMODAL); #elif WITH_DEVELOPER_MODE gtk_show_about_dialog(GTK_WINDOW(window), "name", C4ENGINECAPTION, "version", C4VERSION, "copyright", "Copyright (c) " C4COPYRIGHT_YEAR " " C4COPYRIGHT_COMPANY, nullptr); #endif // WITH_DEVELOPER_MODE / _WIN32 @@ -1219,7 +1221,7 @@ bool C4Console::UpdateCursorBar(const char *szCursor) if (!Active) return false; #ifdef _WIN32 // Cursor - SetDlgItemText(hWindow, IDC_STATICCURSOR, szCursor); + SetDlgItemText(hWindow, IDC_STATICCURSOR, StdStringEncodingConverter::WinAcpToUtf16(szCursor).c_str()); UpdateWindow(GetDlgItem(hWindow, IDC_STATICCURSOR)); #elif WITH_DEVELOPER_MODE gtk_label_set_label(GTK_LABEL(lblCursor), Languages.IconvUtf8(szCursor).getData()); @@ -1270,13 +1272,16 @@ void C4Console::ClearViewportMenu() bool C4Console::AddMenuItem(HMENU hMenu, DWORD dwID, const char *szString, bool fEnabled) { if (!Active) return false; + + std::wstring text{StdStringEncodingConverter::WinAcpToUtf16(szString)}; + MENUITEMINFO minfo{}; minfo.cbSize = sizeof(minfo); minfo.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA | MIIM_STATE; minfo.fType = MFT_STRING; minfo.wID = dwID; - minfo.dwTypeData = const_cast(szString); - minfo.cch = SLen(szString); + minfo.dwTypeData = text.data(); + minfo.cch = text.size(); if (!fEnabled) minfo.fState |= MFS_GRAYED; return InsertMenuItem(hMenu, 0, FALSE, &minfo); } @@ -1288,6 +1293,19 @@ bool C4Console::GetPositionData(std::string &id, std::string &subKey, bool &stor storeSize = false; return true; } + +std::wstring C4Console::GetDialogItemText(const HWND dlg, const int item) +{ + std::wstring result; + const LRESULT textSize{SendDlgItemMessage(dlg, item, WM_GETTEXTLENGTH, 0, 0)}; + + result.resize_and_overwrite(textSize, [dlg, item, textSize](wchar_t *const ptr, const std::size_t size) + { + return GetDlgItemText(dlg, item, ptr, textSize + 1); + }); + + return result; +} #endif // _WIN32 bool C4Console::UpdateModeCtrls(int iMode) @@ -1378,7 +1396,7 @@ void C4Console::UpdateInputCtrl() if (pFn->GetPublic()) { #ifdef _WIN32 - SendMessage(hCombo, CB_ADDSTRING, 0, reinterpret_cast((std::string{pFn->Name} + "()").c_str())); + SendMessage(hCombo, CB_ADDSTRING, 0, reinterpret_cast(std::format(L"{}()", StdStringEncodingConverter::WinAcpToUtf16(pFn->Name)).c_str())); #elif WITH_DEVELOPER_MODE gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, pFn->Name, -1); @@ -1387,12 +1405,12 @@ void C4Console::UpdateInputCtrl() // Add scenario script functions #ifdef _WIN32 if (pRef = Game.Script.GetSFunc(0)) - SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast("----------")); + SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast(L"----------")); #endif for (cnt = 0; pRef = Game.Script.GetSFunc(cnt); cnt++) { #ifdef _WIN32 - SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast((std::string{pRef->Name} + "()").c_str())); + SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast(std::format(L"{}()", StdStringEncodingConverter::WinAcpToUtf16(pRef->Name)).c_str())); #elif WITH_DEVELOPER_MODE gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, pRef->Name, -1); @@ -1499,7 +1517,7 @@ void C4Console::UpdateMenuText(HMENU hMenu) HMENU hSubMenu; if (!Active) return; // File - ModifyMenu(hMenu, MenuIndexFile, MF_BYPOSITION | MF_STRING, 0, LoadResStr(C4ResStrTableKey::IDS_MNU_FILE)); + ModifyMenu(hMenu, MenuIndexFile, MF_BYPOSITION | MF_STRING, 0, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_MNU_FILE)).c_str()); hSubMenu = GetSubMenu(hMenu, MenuIndexFile); SetMenuItemText(hSubMenu, IDM_FILE_OPEN, LoadResStr(C4ResStrTableKey::IDS_MNU_OPEN)); SetMenuItemText(hSubMenu, IDM_FILE_OPENWPLRS, LoadResStr(C4ResStrTableKey::IDS_MNU_OPENWPLRS)); @@ -1511,17 +1529,17 @@ void C4Console::UpdateMenuText(HMENU hMenu) SetMenuItemText(hSubMenu, IDM_FILE_CLOSE, LoadResStr(C4ResStrTableKey::IDS_MNU_CLOSE)); SetMenuItemText(hSubMenu, IDM_FILE_QUIT, LoadResStr(C4ResStrTableKey::IDS_MNU_QUIT)); // Components - ModifyMenu(hMenu, MenuIndexComponents, MF_BYPOSITION | MF_STRING, 0, LoadResStr(C4ResStrTableKey::IDS_MNU_COMPONENTS)); + ModifyMenu(hMenu, MenuIndexComponents, MF_BYPOSITION | MF_STRING, 0, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_MNU_COMPONENTS)).c_str()); hSubMenu = GetSubMenu(hMenu, MenuIndexComponents); SetMenuItemText(hSubMenu, IDM_COMPONENTS_SCRIPT, LoadResStr(C4ResStrTableKey::IDS_MNU_SCRIPT)); SetMenuItemText(hSubMenu, IDM_COMPONENTS_TITLE, LoadResStr(C4ResStrTableKey::IDS_MNU_TITLE)); SetMenuItemText(hSubMenu, IDM_COMPONENTS_INFO, LoadResStr(C4ResStrTableKey::IDS_MNU_INFO)); // Player - ModifyMenu(hMenu, MenuIndexPlayer, MF_BYPOSITION | MF_STRING, 0, LoadResStr(C4ResStrTableKey::IDS_MNU_PLAYER)); + ModifyMenu(hMenu, MenuIndexPlayer, MF_BYPOSITION | MF_STRING, 0, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_MNU_PLAYER)).c_str()); hSubMenu = GetSubMenu(hMenu, MenuIndexPlayer); SetMenuItemText(hSubMenu, IDM_PLAYER_JOIN, LoadResStr(C4ResStrTableKey::IDS_MNU_JOIN)); // Viewport - ModifyMenu(hMenu, MenuIndexViewport, MF_BYPOSITION | MF_STRING, 0, LoadResStr(C4ResStrTableKey::IDS_MNU_VIEWPORT)); + ModifyMenu(hMenu, MenuIndexViewport, MF_BYPOSITION | MF_STRING, 0, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_MNU_VIEWPORT)).c_str()); hSubMenu = GetSubMenu(hMenu, MenuIndexViewport); SetMenuItemText(hSubMenu, IDM_VIEWPORT_NEW, LoadResStr(C4ResStrTableKey::IDS_MNU_NEW)); // Help @@ -1539,7 +1557,7 @@ void C4Console::UpdateNetMenu() ClearNetMenu(); // Insert menu #ifdef _WIN32 - if (!InsertMenu(GetMenu(hWindow), MenuIndexHelp, MF_BYPOSITION | MF_POPUP, reinterpret_cast(CreateMenu()), LoadResStr(C4ResStrTableKey::IDS_MNU_NET))) return; + if (!InsertMenu(GetMenu(hWindow), MenuIndexHelp, MF_BYPOSITION | MF_POPUP, reinterpret_cast(CreateMenu()), StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_MNU_NET)).c_str())) return; #elif WITH_DEVELOPER_MODE itemNet = gtk_menu_item_new_with_label(LoadResStrUtf8I(C4ResStrTableKey::IDS_MNU_NET)); GtkWidget *menuNet = gtk_menu_new(); diff --git a/src/C4Console.h b/src/C4Console.h index 8f40a3e57..cd01c1da2 100644 --- a/src/C4Console.h +++ b/src/C4Console.h @@ -80,6 +80,10 @@ class C4Console : public C4ConsoleBase bool OpenGame(const char *szCmdLine = nullptr); bool TogglePause(); // key callpack: pause +#ifdef _WIN32 + static std::wstring GetDialogItemText(HWND dlg, int item); +#endif + protected: bool CloseGame(); bool fGameOpen; diff --git a/src/C4EditCursor.cpp b/src/C4EditCursor.cpp index eb78f9fe4..54b607533 100644 --- a/src/C4EditCursor.cpp +++ b/src/C4EditCursor.cpp @@ -317,13 +317,14 @@ bool SetMenuItemEnable(HMENU hMenu, WORD id, bool fEnable) bool SetMenuItemText(HMENU hMenu, WORD id, const char *szText) { + std::wstring text{StdStringEncodingConverter::WinAcpToUtf16(szText)}; MENUITEMINFO minfo{}; minfo.cbSize = sizeof(minfo); minfo.fMask = MIIM_ID | MIIM_TYPE | MIIM_DATA; minfo.fType = MFT_STRING; minfo.wID = id; - minfo.dwTypeData = (char *)szText; - minfo.cch = checked_cast(SLen(szText)); + minfo.dwTypeData = text.data(); + minfo.cch = checked_cast(text.size()); return SetMenuItemInfo(hMenu, id, FALSE, &minfo); } diff --git a/src/C4FileMonitor.cpp b/src/C4FileMonitor.cpp index 41426de09..7d7a13552 100644 --- a/src/C4FileMonitor.cpp +++ b/src/C4FileMonitor.cpp @@ -207,7 +207,7 @@ void C4FileMonitor::StartMonitoring() void C4FileMonitor::AddDirectory(const char *const path) { - winrt::file_handle directory{CreateFile( + winrt::file_handle directory{CreateFileA( path, FILE_LIST_DIRECTORY, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, diff --git a/src/C4FileSelDlg.cpp b/src/C4FileSelDlg.cpp index e6f6a4636..301a586c6 100644 --- a/src/C4FileSelDlg.cpp +++ b/src/C4FileSelDlg.cpp @@ -542,9 +542,9 @@ C4PortraitSelDlg::C4PortraitSelDlg(C4FileSel_BaseCB *pSelCallback, bool fSetPict AddLocation(std::format(C4ENGINECAPTION " {}", LoadResStr(C4ResStrTableKey::IDS_TEXT_USERPATH)).c_str(), path); AddCheckedLocation(std::format(C4ENGINECAPTION " {}", LoadResStr(C4ResStrTableKey::IDS_TEXT_PROGRAMDIRECTORY)).c_str(), Config.General.ExePath); #ifdef _WIN32 - if (SHGetSpecialFolderPath(nullptr, path, CSIDL_PERSONAL, FALSE)) AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_MYDOCUMENTS), path); - if (SHGetSpecialFolderPath(nullptr, path, CSIDL_MYPICTURES, FALSE)) AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_MYPICTURES), path); - if (SHGetSpecialFolderPath(nullptr, path, CSIDL_DESKTOPDIRECTORY, FALSE)) AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_DESKTOP), path); + if (SHGetSpecialFolderPathA(nullptr, path, CSIDL_PERSONAL, FALSE)) AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_MYDOCUMENTS), path); + if (SHGetSpecialFolderPathA(nullptr, path, CSIDL_MYPICTURES, FALSE)) AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_MYPICTURES), path); + if (SHGetSpecialFolderPathA(nullptr, path, CSIDL_DESKTOPDIRECTORY, FALSE)) AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_DESKTOP), path); #endif #ifdef __APPLE__ AddCheckedLocation(LoadResStr(C4ResStrTableKey::IDS_TEXT_HOME), getenv("HOME")); diff --git a/src/C4Fonts.cpp b/src/C4Fonts.cpp index 492c4eced..308ac3fae 100644 --- a/src/C4Fonts.cpp +++ b/src/C4Fonts.cpp @@ -70,7 +70,7 @@ bool C4VectorFont::Init(const char *szFacename, int32_t iSize, uint32_t dwWeight HDC hDC = ::CreateCompatibleDC(nullptr); if (hDC) { - HFONT hFont = ::CreateFont(iSize, 0, 0, 0, dwWeight, FALSE, + HFONT hFont = ::CreateFontA(iSize, 0, 0, 0, dwWeight, FALSE, FALSE, FALSE, C4Config::GetCharsetCode(szCharSet), OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, 5, VARIABLE_PITCH, szFacename); diff --git a/src/C4FullScreen.cpp b/src/C4FullScreen.cpp index e51f05911..d1ab68a62 100644 --- a/src/C4FullScreen.cpp +++ b/src/C4FullScreen.cpp @@ -161,7 +161,7 @@ bool C4FullScreen::Init(CStdApp *const app, const char *const title, const C4Rec hRenderWindow = CreateWindowEx( 0, - "STATIC", + L"STATIC", nullptr, WS_CHILD, 0, 0, rect.right - rect.left, rect.bottom - rect.top, @@ -206,7 +206,7 @@ WNDCLASSEX C4FullScreen::GetWindowClass(const HINSTANCE instance) const .hCursor = nullptr, .hbrBackground = reinterpret_cast(COLOR_BACKGROUND), .lpszMenuName = nullptr, - .lpszClassName = "C4FullScreen", + .lpszClassName = L"C4FullScreen", .hIconSm = LoadIcon(instance, MAKEINTRESOURCE(IDI_00_C4X)) }; } diff --git a/src/C4Group.cpp b/src/C4Group.cpp index fef754dcd..8a5686642 100644 --- a/src/C4Group.cpp +++ b/src/C4Group.cpp @@ -363,7 +363,7 @@ bool C4Group_UnpackDirectory(const char *szFilename) char szFoldername[_MAX_PATH + 1]; SCopy(szFilename, szFoldername, _MAX_PATH); MakeTempFilename(szFoldername); - if (!CreateDirectory(szFoldername, nullptr)) { hGroup.Close(); return false; } + if (!MakeDirectory(szFoldername, nullptr)) { hGroup.Close(); return false; } // Extract files to folder if (!hGroup.Extract("*", szFoldername)) { hGroup.Close(); return false; } @@ -1571,7 +1571,7 @@ bool EraseItemSafe(const char *szFilename) char Filename[_MAX_PATH + 1]; SCopy(szFilename, Filename, _MAX_PATH); Filename[SLen(Filename) + 1] = 0; - SHFILEOPSTRUCT shs; + SHFILEOPSTRUCTA shs; shs.hwnd = 0; shs.wFunc = FO_DELETE; shs.pFrom = Filename; @@ -1580,7 +1580,7 @@ bool EraseItemSafe(const char *szFilename) shs.fAnyOperationsAborted = false; shs.hNameMappings = 0; shs.lpszProgressTitle = nullptr; - return !SHFileOperation(&shs); + return !SHFileOperationA(&shs); #elif defined(USE_SDL_MAINLOOP) && defined(C4ENGINE) && defined(__APPLE__) bool sendFileToTrash(const char *filename); return sendFileToTrash(szFilename); diff --git a/src/C4GuiDialogs.cpp b/src/C4GuiDialogs.cpp index 9da863841..576cd1af3 100644 --- a/src/C4GuiDialogs.cpp +++ b/src/C4GuiDialogs.cpp @@ -277,7 +277,7 @@ WNDCLASSEX DialogWindow::GetWindowClass(const HINSTANCE instance) const .hCursor = LoadCursor(nullptr, IDC_ARROW), .hbrBackground = reinterpret_cast(COLOR_BACKGROUND), .lpszMenuName = nullptr, - .lpszClassName = "C4GUIdlg", // keep for backwards compatibility + .lpszClassName = L"C4GUIdlg", // keep for backwards compatibility .hIconSm = LoadIcon(instance, MAKEINTRESOURCE(IDI_00_C4X)) }; } diff --git a/src/C4Network2Res.cpp b/src/C4Network2Res.cpp index efe59007b..6fa2b1fe3 100644 --- a/src/C4Network2Res.cpp +++ b/src/C4Network2Res.cpp @@ -1713,7 +1713,7 @@ bool C4Network2ResList::CreateNetworkFolder() // does not exist? if (access(szNetworkPath, 00)) { - if (!CreateDirectory(szNetworkPath, nullptr)) + if (!MakeDirectory(szNetworkPath, nullptr)) { LogFatalNTr("could not create network path!"); return false; } diff --git a/src/C4PropertyDlg.cpp b/src/C4PropertyDlg.cpp index dcf5c1aba..d300cd99c 100644 --- a/src/C4PropertyDlg.cpp +++ b/src/C4PropertyDlg.cpp @@ -60,12 +60,16 @@ INT_PTR CALLBACK PropertyDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lPar switch (LOWORD(wParam)) { case IDOK: + { // IDC_COMBOINPUT to Console.EditCursor.In() - char buffer[16000]; - GetDlgItemText(hDlg, IDC_COMBOINPUT, buffer, 16000); - if (buffer[0]) - Console.EditCursor.In(buffer); + const std::wstring text{C4Console::GetDialogItemText(hDlg, IDC_COMBOINPUT)}; + if (!text.empty()) + { + Console.EditCursor.In(StdStringEncodingConverter::Utf16ToWinAcp(text).c_str()); + } + return TRUE; + } case IDC_BUTTONRELOADDEF: Game.ReloadDef(Console.PropertyDlg.idSelectedDef); @@ -106,7 +110,7 @@ bool C4PropertyDlg::Open() PropertyDlgProc); if (!hDialog) return false; // Set text - SetWindowText(hDialog, LoadResStr(C4ResStrTableKey::IDS_DLG_PROPERTIES)); + SetWindowText(hDialog, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_DLG_PROPERTIES)).c_str()); // Enable controls EnableWindow(GetDlgItem(hDialog, IDOK), Console.Editing); EnableWindow(GetDlgItem(hDialog, IDC_COMBOINPUT), Console.Editing); @@ -253,7 +257,7 @@ bool C4PropertyDlg::Update() // Update info edit control #ifdef _WIN32 const auto iLine = SendDlgItemMessage(hDialog, IDC_EDITOUTPUT, EM_GETFIRSTVISIBLELINE, 0, 0); - SetDlgItemText(hDialog, IDC_EDITOUTPUT, output.c_str()); + SetDlgItemText(hDialog, IDC_EDITOUTPUT, StdStringEncodingConverter::WinAcpToUtf16(output).c_str()); SendDlgItemMessage(hDialog, IDC_EDITOUTPUT, EM_LINESCROLL, 0, iLine); UpdateWindow(GetDlgItem(hDialog, IDC_EDITOUTPUT)); #elif defined(WITH_DEVELOPER_MODE) @@ -290,8 +294,14 @@ void C4PropertyDlg::UpdateInputCtrl(C4Object *pObj) #ifdef _WIN32 HWND hCombo = GetDlgItem(hDialog, IDC_COMBOINPUT); // Remember old window text - char szLastText[500 + 1]; - GetWindowText(hCombo, szLastText, 500); + std::wstring lastText; + const LRESULT textSize{SendMessage(hCombo, WM_GETTEXTLENGTH, 0, 0)}; + + lastText.resize_and_overwrite(textSize, [hCombo, textSize](wchar_t *const ptr, const std::size_t size) + { + return GetWindowText(hCombo, ptr, textSize + 1); + }); + // Clear SendMessage(hCombo, CB_RESETCONTENT, 0, 0); #else // _WIN32 @@ -329,7 +339,7 @@ void C4PropertyDlg::UpdateInputCtrl(C4Object *pObj) if (pFn->GetPublic()) { #ifdef _WIN32 - SendMessage(hCombo, CB_ADDSTRING, 0, reinterpret_cast((std::string{pFn->Name} + "()").c_str())); + SendMessage(hCombo, CB_ADDSTRING, 0, reinterpret_cast(std::format(L"{}()", StdStringEncodingConverter::WinAcpToUtf16(pFn->Name)).c_str())); #elif defined(WITH_DEVELOPER_MODE) gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, pFn->Name, -1); @@ -349,11 +359,11 @@ void C4PropertyDlg::UpdateInputCtrl(C4Object *pObj) { #ifdef _WIN32 // Insert divider if necessary - if (!fDivider) { SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast("----------")); fDivider = true; } + if (!fDivider) { SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast(L"----------")); fDivider = true; } #endif // Add function #ifdef _WIN32 - SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast((std::string{pRef->Name} + "()").c_str())); + SendMessage(hCombo, CB_INSERTSTRING, 0, reinterpret_cast(std::format(L"{}()", StdStringEncodingConverter::WinAcpToUtf16(pRef->Name)).c_str())); #elif defined(WITH_DEVELOPER_MODE) gtk_list_store_append(store, &iter); gtk_list_store_set(store, &iter, 0, pRef->Name, -1); @@ -362,7 +372,7 @@ void C4PropertyDlg::UpdateInputCtrl(C4Object *pObj) #ifdef _WIN32 // Restore old text - SetWindowText(hCombo, szLastText); + SetWindowText(hCombo, lastText.c_str()); #elif WITH_DEVELOPER_MODE // Reassociate list store with completion gtk_entry_completion_set_model(completion, GTK_TREE_MODEL(store)); diff --git a/src/C4Scenario.cpp b/src/C4Scenario.cpp index 5b2be627e..31d6d483b 100644 --- a/src/C4Scenario.cpp +++ b/src/C4Scenario.cpp @@ -613,7 +613,7 @@ bool C4ScenarioSection::EnsureTempStore(bool fExtractLandscape, bool fExtractObj // main section: extract section files from main scenario group (create group as open dir) if (Filename.empty()) { - if (!CreateDirectory(tmp.getData(), nullptr)) return false; + if (!MakeDirectory(tmp.getData(), nullptr)) return false; C4Group hGroup; if (!hGroup.Open(tmp.getData(), true)) { EraseItem(tmp.getData()); return false; } // extract all desired section files diff --git a/src/C4Thread.cpp b/src/C4Thread.cpp index 5a0ff2efb..ed9cf0aff 100644 --- a/src/C4Thread.cpp +++ b/src/C4Thread.cpp @@ -27,7 +27,7 @@ void C4Thread::SetCurrentThreadName(const std::string_view name) { #ifdef _WIN32 - static auto *const setThreadDescription = reinterpret_cast(GetProcAddress(GetModuleHandle("KernelBase.dll"), "SetThreadDescription")); + static auto *const setThreadDescription = reinterpret_cast(GetProcAddress(GetModuleHandle(L"KernelBase.dll"), "SetThreadDescription")); if (setThreadDescription) { diff --git a/src/C4ToolsDlg.cpp b/src/C4ToolsDlg.cpp index ddc66f926..e5ea605c2 100644 --- a/src/C4ToolsDlg.cpp +++ b/src/C4ToolsDlg.cpp @@ -189,10 +189,15 @@ INT_PTR CALLBACK ToolsDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { case CBN_SELCHANGE: int32_t cursel = SendDlgItemMessage(hDlg, IDC_COMBOMATERIAL, CB_GETCURSEL, 0, 0); - std::string material; - material.resize(SendDlgItemMessage(hDlg, IDC_COMBOMATERIAL, CB_GETLBTEXTLEN, cursel, 0)); - SendDlgItemMessage(hDlg, IDC_COMBOMATERIAL, CB_GETLBTEXT, cursel, reinterpret_cast(material.data())); - Console.ToolsDlg.SetMaterial(material.c_str()); + std::wstring material; + const LRESULT textSize{SendDlgItemMessage(hDlg, IDC_COMBOMATERIAL, CB_GETLBTEXTLEN, cursel, 0)}; + + material.resize_and_overwrite(textSize, [cursel, hDlg, &material](wchar_t *const ptr, const std::size_t size) + { + return SendDlgItemMessage(hDlg, IDC_COMBOMATERIAL, CB_GETLBTEXT, cursel, reinterpret_cast(material.data())); + }); + + Console.ToolsDlg.SetMaterial(StdStringEncodingConverter::Utf16ToWinAcp(material).c_str()); break; } return TRUE; @@ -202,10 +207,15 @@ INT_PTR CALLBACK ToolsDlgProc(HWND hDlg, UINT Msg, WPARAM wParam, LPARAM lParam) { case CBN_SELCHANGE: int32_t cursel = SendDlgItemMessage(hDlg, IDC_COMBOTEXTURE, CB_GETCURSEL, 0, 0); - std::string texture; - texture.resize(SendDlgItemMessage(hDlg, IDC_COMBOTEXTURE, CB_GETLBTEXTLEN, cursel, 0)); - SendDlgItemMessage(hDlg, IDC_COMBOTEXTURE, CB_GETLBTEXT, cursel, reinterpret_cast(texture.data())); - Console.ToolsDlg.SetTexture(texture.c_str()); + std::wstring texture; + const LRESULT textSize{SendDlgItemMessage(hDlg, IDC_COMBOTEXTURE, CB_GETLBTEXTLEN, cursel, 0)}; + + texture.resize_and_overwrite(textSize, [cursel, hDlg, &texture](wchar_t *const ptr, const std::size_t size) + { + return SendDlgItemMessage(hDlg, IDC_COMBOTEXTURE, CB_GETLBTEXT, cursel, reinterpret_cast(texture.data())); + }); + + Console.ToolsDlg.SetTexture(StdStringEncodingConverter::Utf16ToWinAcp(texture).c_str()); break; } return TRUE; @@ -260,9 +270,9 @@ bool C4ToolsDlg::Open() ToolsDlgProc); if (!hDialog) return false; // Set text - SetWindowText(hDialog, LoadResStr(C4ResStrTableKey::IDS_DLG_TOOLS)); - SetDlgItemText(hDialog, IDC_STATICMATERIAL, LoadResStr(C4ResStrTableKey::IDS_CTL_MATERIAL)); - SetDlgItemText(hDialog, IDC_STATICTEXTURE, LoadResStr(C4ResStrTableKey::IDS_CTL_TEXTURE)); + SetWindowText(hDialog, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_DLG_TOOLS)).c_str()); + SetDlgItemText(hDialog, IDC_STATICMATERIAL, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_CTL_MATERIAL)).c_str()); + SetDlgItemText(hDialog, IDC_STATICTEXTURE, StdStringEncodingConverter::WinAcpToUtf16(LoadResStr(C4ResStrTableKey::IDS_CTL_TEXTURE)).c_str()); // Load bitmaps if necessary LoadBitmaps(); // create target ctx for OpenGL rendering @@ -473,10 +483,10 @@ void C4ToolsDlg::InitMaterialCtrls() { // Materials #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_ADDSTRING, 0, reinterpret_cast(C4TLS_MatSky)); + SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_ADDSTRING, 0, reinterpret_cast(_CRT_WIDE(C4TLS_MatSky))); for (int32_t cnt = 0; cnt < Game.Material.Num; cnt++) - SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_ADDSTRING, 0, reinterpret_cast(Game.Material.Map[cnt].Name)); - SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_SELECTSTRING, 0, reinterpret_cast(Material)); + SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_ADDSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(Game.Material.Map[cnt].Name).c_str())); + SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_SELECTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(Material).c_str())); #elif defined(WITH_DEVELOPER_MODE) GtkListStore *list = GTK_LIST_STORE(gtk_combo_box_get_model(GTK_COMBO_BOX(materials))); @@ -513,7 +523,7 @@ void C4ToolsDlg::UpdateTextures() { fAnyEntry = true; #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_INSERTSTRING, 0, reinterpret_cast(szTexture)); + SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_INSERTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(szTexture).c_str())); #elif defined(WITH_DEVELOPER_MODE) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(textures), szTexture); #endif @@ -523,7 +533,7 @@ void C4ToolsDlg::UpdateTextures() if (fAnyEntry) { #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_INSERTSTRING, 0, reinterpret_cast("-------")); + SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_INSERTSTRING, 0, reinterpret_cast(L"-------")); #elif defined(WITH_DEVELOPER_MODE) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(textures), "-------"); #endif @@ -536,7 +546,7 @@ void C4ToolsDlg::UpdateTextures() if (Game.TextureMap.GetIndex(Material, szTexture, false) || Game.Landscape.Mode == C4LSC_Exact) { #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_INSERTSTRING, 0, reinterpret_cast(szTexture)); + SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_INSERTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(szTexture).c_str())); #elif defined(WITH_DEVELOPER_MODE) gtk_combo_box_text_prepend_text(GTK_COMBO_BOX_TEXT(textures), szTexture); #endif @@ -544,7 +554,7 @@ void C4ToolsDlg::UpdateTextures() } // reselect current #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_SELECTSTRING, 0, reinterpret_cast(Texture)); + SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_SELECTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(Texture).c_str())); #elif defined(WITH_DEVELOPER_MODE) g_signal_handler_block(textures, handlerTextures); SelectComboBoxText(GTK_COMBO_BOX(textures), Texture); @@ -568,7 +578,7 @@ void C4ToolsDlg::SetTexture(const char *szTexture) { // ensure correct texture is in dlg #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_SELECTSTRING, 0, reinterpret_cast(Texture)); + SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_SELECTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(Texture).c_str())); #elif defined(WITH_DEVELOPER_MODE) g_signal_handler_block(textures, handlerTextures); SelectComboBoxText(GTK_COMBO_BOX(textures), Texture); @@ -814,7 +824,7 @@ void C4ToolsDlg::UpdateLandscapeModeCtrls() caption = LoadResStr(C4ResStrTableKey::IDS_DLG_EXACT); break; } - SetWindowText(hDialog, caption); + SetWindowText(hDialog, StdStringEncodingConverter::WinAcpToUtf16(caption).c_str()); #elif defined(WITH_DEVELOPER_MODE) g_signal_handler_block(landscape_dynamic, handlerDynamic); g_signal_handler_block(landscape_static, handlerStatic); @@ -975,7 +985,7 @@ void C4ToolsDlg::AssertValidTexture() bool C4ToolsDlg::SelectTexture(const char *szTexture) { #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_SELECTSTRING, 0, reinterpret_cast(szTexture)); + SendDlgItemMessage(hDialog, IDC_COMBOTEXTURE, CB_SELECTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(szTexture).c_str())); #elif defined(WITH_DEVELOPER_MODE) g_signal_handler_block(textures, handlerTextures); SelectComboBoxText(GTK_COMBO_BOX(textures), szTexture); @@ -988,7 +998,7 @@ bool C4ToolsDlg::SelectTexture(const char *szTexture) bool C4ToolsDlg::SelectMaterial(const char *szMaterial) { #ifdef _WIN32 - SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_SELECTSTRING, 0, reinterpret_cast(szMaterial)); + SendDlgItemMessage(hDialog, IDC_COMBOMATERIAL, CB_SELECTSTRING, 0, reinterpret_cast(StdStringEncodingConverter::WinAcpToUtf16(szMaterial).c_str())); #elif defined(WITH_DEVELOPER_MODE) g_signal_handler_block(materials, handlerMaterials); SelectComboBoxText(GTK_COMBO_BOX(materials), szMaterial); diff --git a/src/C4Update.cpp b/src/C4Update.cpp index dec2e086b..ff154eef8 100644 --- a/src/C4Update.cpp +++ b/src/C4Update.cpp @@ -319,7 +319,7 @@ bool C4UpdatePackage::Execute(C4Group *pGroup) // GrpUpdate -> file must exist if (GrpUpdate) return false; // create dir - CreateDirectory(strTarget, nullptr); + MakeDirectory(strTarget, nullptr); } *p = '\\'; lp = p + 1; } @@ -533,7 +533,7 @@ bool C4UpdatePackage::DoUpdate(C4Group *pGrpFrom, C4GroupEx *pGrpTo, const char { const std::string msg{std::format("updating {}\\{}\n", pGrpTo->GetFullName().getData(), strFileName)}; #ifdef _MSC_VER - OutputDebugString(msg.c_str()); + OutputDebugStringA(msg.c_str()); #elif !defined(NDEBUG) std::print("{}", msg); #endif diff --git a/src/C4UpdateDlg.cpp b/src/C4UpdateDlg.cpp index 7bdbe8a73..9c3fe606f 100644 --- a/src/C4UpdateDlg.cpp +++ b/src/C4UpdateDlg.cpp @@ -197,10 +197,10 @@ bool C4UpdateDlg::ApplyUpdate(const char *strUpdateFile, bool fDeleteUpdate, C4G succeeded = true; #ifdef _WIN32 // Close editor if open - HWND hwnd = FindWindow(nullptr, C4EDITORCAPTION); + HWND hwnd = FindWindowA(nullptr, C4EDITORCAPTION); if (hwnd) PostMessage(hwnd, WM_CLOSE, 0, 0); const std::string updateArgs{std::format("\"{}\" /p -w \"" C4ENGINECAPTION "\" -w \"" C4EDITORCAPTION "\" -w 2000 {}", strUpdateFile, fDeleteUpdate ? "-yd" : "-y")}; - const auto iError = ShellExecute(nullptr, "runas", strUpdateProg.getData(), updateArgs.c_str(), Config.General.ExePath, SW_SHOW); + const auto iError = ShellExecuteA(nullptr, "runas", strUpdateProg.getData(), updateArgs.c_str(), Config.General.ExePath, SW_SHOW); if (reinterpret_cast(iError) <= 32) return false; // must quit ourselves for update program to work if (succeeded) Application.Quit(); diff --git a/src/C4Viewport.cpp b/src/C4Viewport.cpp index f01a00ef7..a1cbbda7b 100644 --- a/src/C4Viewport.cpp +++ b/src/C4Viewport.cpp @@ -209,7 +209,7 @@ WNDCLASSEX C4ViewportWindow::GetWindowClass(const HINSTANCE instance) const .hCursor = LoadCursor(nullptr, IDC_ARROW), .hbrBackground = reinterpret_cast(COLOR_BACKGROUND), .lpszMenuName = nullptr, - .lpszClassName = "C4Viewport", + .lpszClassName = L"C4Viewport", .hIconSm = LoadIcon(instance, MAKEINTRESOURCE(IDI_01_C4S)) }; } @@ -231,7 +231,7 @@ bool C4Viewport::DropFiles(HANDLE hDrop) char szFilename[500 + 1]; for (int32_t cnt = 0; cnt < iFileNum; cnt++) { - DragQueryFile((HDROP)hDrop, cnt, szFilename, 500); + DragQueryFileA((HDROP)hDrop, cnt, szFilename, 500); DragQueryPoint((HDROP)hDrop, &pntPoint); Game.DropFile(szFilename, ViewX + pntPoint.x, ViewY + pntPoint.y); } diff --git a/src/C4WinMain.cpp b/src/C4WinMain.cpp index b837200d5..c60155869 100644 --- a/src/C4WinMain.cpp +++ b/src/C4WinMain.cpp @@ -112,7 +112,7 @@ int ClonkMain(const HINSTANCE instance, const int cmdShow, const int argc, char catch (const CStdApp::StartupException &e) { Application.Clear(); - MessageBox(nullptr, e.what(), STD_PRODUCT, MB_ICONERROR); + MessageBoxA(nullptr, e.what(), STD_PRODUCT, MB_ICONERROR); return C4XRV_Failure; } @@ -135,7 +135,7 @@ int WINAPI WinMain(HINSTANCE hInst, int main(const int argc, char **const argv) { // Get command line, go over program name - char *commandLine{GetCommandLine()}; + char *commandLine{GetCommandLineA()}; if (*commandLine == L'"') { ++commandLine; diff --git a/src/OpenURL.cpp b/src/OpenURL.cpp index 45fbdf26c..409da64ca 100644 --- a/src/OpenURL.cpp +++ b/src/OpenURL.cpp @@ -34,7 +34,7 @@ bool OpenURL(const char *szURL) { #ifdef _WIN32 - if (reinterpret_cast(ShellExecute(nullptr, "open", szURL, nullptr, nullptr, SW_SHOW)) > 32) + if (reinterpret_cast(ShellExecuteA(nullptr, "open", szURL, nullptr, nullptr, SW_SHOW)) > 32) return true; #endif #ifdef WITH_GLIB diff --git a/src/StdFile.cpp b/src/StdFile.cpp index 586028cbc..c9350ef83 100644 --- a/src/StdFile.cpp +++ b/src/StdFile.cpp @@ -425,7 +425,7 @@ time_t FileTime(const char *szFilename) bool EraseFile(const char *szFilename) { #ifdef _WIN32 - SetFileAttributes(szFilename, FILE_ATTRIBUTE_NORMAL); + SetFileAttributesA(szFilename, FILE_ATTRIBUTE_NORMAL); #endif // either unlink or remove could be used. Well, stick to ANSI C where possible. if (remove(szFilename)) @@ -441,8 +441,10 @@ bool EraseFile(const char *szFilename) return true; } -#ifndef _WIN32 -bool CopyFile(const char *szSource, const char *szTarget, bool FailIfExists) +#ifdef _WIN32 +#define CopyFileTo CopyFileA +#else +bool CopyFileTo(const char *szSource, const char *szTarget, bool FailIfExists) { int fds = open(szSource, O_RDONLY); if (!fds) return false; @@ -466,7 +468,7 @@ bool RenameFile(const char *szFilename, const char *szNewFilename) { if (rename(szFilename, szNewFilename) < 0) { - if (CopyFile(szFilename, szNewFilename, false)) + if (CopyFileTo(szFilename, szNewFilename, false)) { return EraseFile(szFilename); } @@ -484,7 +486,7 @@ bool MakeOriginalFilename(char *szFilename) if (Inside(SLen(szFilename), 2, 3)) if (szFilename[1] == ':') { szFilename[2] = '\\'; szFilename[3] = 0; - if (GetDriveType(szFilename) == DRIVE_NO_ROOT_DIR) return false; + if (GetDriveTypeA(szFilename) == DRIVE_NO_ROOT_DIR) return false; return true; } struct _finddata_t fdt; intptr_t shnd; @@ -513,15 +515,15 @@ const char *GetWorkingDirectory() bool SetWorkingDirectory(const char *path) { #ifdef _WIN32 - return SetCurrentDirectory(path) != 0; + return SetCurrentDirectoryA(path) != 0; #else return (chdir(path) == 0); #endif } #ifndef _WIN32 -// CreateDirectory: true on success -bool CreateDirectory(const char *pathname, void *) +// MakeDirectory: true on success +bool MakeDirectory(const char *pathname, void *) { // mkdir: false on success return !mkdir(pathname, S_IREAD | S_IWRITE | S_IEXEC); @@ -635,7 +637,7 @@ bool EraseDirectory(const char *szDirName) } // Remove directory #ifdef _WIN32 - return !!RemoveDirectory(szDirName); + return !!RemoveDirectoryA(szDirName); #else return (rmdir(szDirName) == 0 || errno == ENOENT); #endif @@ -675,10 +677,10 @@ bool CopyItem(const char *szSource, const char *szTarget, bool fResetAttributes) if (DirectoryExists(szSource)) return CopyDirectory(szSource, szTarget, fResetAttributes); // Copy file - if (!CopyFile(szSource, szTarget, false)) return false; + if (!CopyFileTo(szSource, szTarget, false)) return false; // Reset any attributes if desired #ifdef _WIN32 - if (fResetAttributes) if (!SetFileAttributes(szTarget, FILE_ATTRIBUTE_NORMAL)) return false; + if (fResetAttributes) if (!SetFileAttributesA(szTarget, FILE_ATTRIBUTE_NORMAL)) return false; #else if (fResetAttributes) if (chmod(szTarget, S_IRWXU)) return false; #endif diff --git a/src/StdFile.h b/src/StdFile.h index 438eef6a5..08e4de8c2 100644 --- a/src/StdFile.h +++ b/src/StdFile.h @@ -26,6 +26,7 @@ #ifdef _WIN32 #include #define F_OK 0 +#define MakeDirectory CreateDirectoryA #else #include #include @@ -33,7 +34,7 @@ #define _MAX_PATH PATH_MAX #define _MAX_FNAME NAME_MAX -bool CreateDirectory(const char *pathname, void * = nullptr); +bool MakeDirectory(const char *pathname, void * = nullptr); bool CopyFile(const char *szSource, const char *szTarget, bool FailIfExists); #endif diff --git a/src/StdRegistry.cpp b/src/StdRegistry.cpp index 77e3ea835..08245bdeb 100644 --- a/src/StdRegistry.cpp +++ b/src/StdRegistry.cpp @@ -29,7 +29,7 @@ bool GetRegistryDWord(HKEY hKey, const char *szSubKey, const char *szValueName, DWORD valsize = sizeof(DWORD); // Open the key - if ((qerr = RegOpenKeyEx(hKey, + if ((qerr = RegOpenKeyExA(hKey, szSubKey, 0, KEY_READ, @@ -37,7 +37,7 @@ bool GetRegistryDWord(HKEY hKey, const char *szSubKey, const char *szValueName, )) != ERROR_SUCCESS) return false; // Get the value - if ((qerr = RegQueryValueEx(ckey, + if ((qerr = RegQueryValueExA(ckey, szValueName, nullptr, &valtype, @@ -65,7 +65,7 @@ bool GetRegistryString(const char *szSubKey, DWORD valtype; // Open the key - if ((qerr = RegOpenKeyEx(HKEY_CURRENT_USER, + if ((qerr = RegOpenKeyExA(HKEY_CURRENT_USER, szSubKey, 0, KEY_READ, @@ -73,7 +73,7 @@ bool GetRegistryString(const char *szSubKey, )) != ERROR_SUCCESS) return false; // Get the value - if ((qerr = RegQueryValueEx(ckey, + if ((qerr = RegQueryValueExA(ckey, szValueName, nullptr, &valtype, @@ -101,7 +101,7 @@ bool SetRegistryString(const char *szSubKey, DWORD disposition; // Open the key - if ((qerr = RegCreateKeyEx(HKEY_CURRENT_USER, + if ((qerr = RegCreateKeyExA(HKEY_CURRENT_USER, szSubKey, 0, nullptr, @@ -113,7 +113,7 @@ bool SetRegistryString(const char *szSubKey, )) != ERROR_SUCCESS) return false; // Set the value - if ((qerr = RegSetValueEx(ckey, + if ((qerr = RegSetValueExA(ckey, szValueName, 0, REG_SZ, @@ -134,17 +134,17 @@ bool DeleteRegistryKey(HKEY hKey, const char *szSubKey) { HKEY ckey; // Open the key - if (RegOpenKeyEx(hKey, szSubKey, 0, KEY_ALL_ACCESS, &ckey) != ERROR_SUCCESS) return false; + if (RegOpenKeyExA(hKey, szSubKey, 0, KEY_ALL_ACCESS, &ckey) != ERROR_SUCCESS) return false; // Delete all subkeys char strChild[1024 + 1]; - while (RegEnumKey(ckey, 0, strChild, 1024) == ERROR_SUCCESS) + while (RegEnumKeyA(ckey, 0, strChild, 1024) == ERROR_SUCCESS) if (!DeleteRegistryKey(ckey, strChild)) return false; // Close the key RegCloseKey(ckey); // Delete the key - if (RegDeleteKey(hKey, szSubKey) != ERROR_SUCCESS) return false; + if (RegDeleteKeyA(hKey, szSubKey) != ERROR_SUCCESS) return false; // Success return true; } @@ -158,7 +158,7 @@ static HKEY ClassKeyForUser(std::string &subKey) static bool CreateClassKey(std::string subKey, HKEY &ckey, DWORD &disposition) { const HKEY key{ClassKeyForUser(subKey)}; - return RegCreateKeyEx(key, subKey.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &ckey, &disposition) == ERROR_SUCCESS; + return RegCreateKeyExA(key, subKey.c_str(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, nullptr, &ckey, &disposition) == ERROR_SUCCESS; } bool SetRegClassesRoot(const char *szSubKey, @@ -174,7 +174,7 @@ bool SetRegClassesRoot(const char *szSubKey, } // Set the value - if (RegSetValueEx(ckey, + if (RegSetValueExA(ckey, szValueName, 0, REG_SZ, @@ -204,7 +204,7 @@ bool SetRegClassesRootString(const char *szSubKey, } // Set the value - if (RegSetValueEx(ckey, + if (RegSetValueExA(ckey, szValueName, 0, REG_SZ, @@ -378,8 +378,8 @@ bool StdCompilerConfigWrite::Default(const char *szName) // Open parent CreateKey(); // Remove key/value (failsafe) - RegDeleteKey(pKey->Handle, szName); - RegDeleteValue(pKey->Handle, szName); + RegDeleteKeyA(pKey->Handle, szName); + RegDeleteValueA(pKey->Handle, szName); // Handled return true; } @@ -471,7 +471,7 @@ void StdCompilerConfigWrite::CreateKey(HKEY hParent) if (pKey->Handle) return; // Open/Create registry key - if (RegCreateKeyEx(hParent ? hParent : pKey->Parent->Handle, + if (RegCreateKeyExA(hParent ? hParent : pKey->Parent->Handle, pKey->Name.getData(), 0, nullptr, REG_OPTION_NON_VOLATILE, KEY_WRITE, nullptr, @@ -486,7 +486,7 @@ void StdCompilerConfigWrite::WriteInteger(T value) DWORD type = sizeof(T) == 8 ? REG_QWORD : REG_DWORD; // Set the value - if (RegSetValueEx(pKey->Parent->Handle, pKey->Name.getData(), + if (RegSetValueExA(pKey->Parent->Handle, pKey->Name.getData(), 0, type, reinterpret_cast(&value), sizeof(value)) != ERROR_SUCCESS) excCorrupt("Could not write key {}!", pKey->Name.getData()); @@ -500,7 +500,7 @@ void StdCompilerConfigWrite::WriteDWord(uint32_t iVal) void StdCompilerConfigWrite::WriteString(const char *szString) { // Set the value - if (RegSetValueEx(pKey->Parent->Handle, pKey->Name.getData(), + if (RegSetValueExA(pKey->Parent->Handle, pKey->Name.getData(), 0, REG_SZ, reinterpret_cast(szString), checked_cast(strlen(szString) + 1)) != ERROR_SUCCESS) excCorrupt("Could not write key {}!", pKey->Name.getData()); @@ -514,7 +514,7 @@ StdCompilerConfigRead::StdCompilerConfigRead(HKEY hRoot, const char *szPath) pKey->Name.Ref(szPath); pKey->Virtual = false; // Open root - if (RegOpenKeyEx(hRoot, szPath, + if (RegOpenKeyExA(hRoot, szPath, 0, KEY_READ, &pKey->Handle) != ERROR_SUCCESS) pKey->Handle = 0; @@ -532,13 +532,13 @@ StdCompiler::NameGuard StdCompilerConfigRead::Name(const char *szName) bool fFound = true; // Try to open registry key HKEY hSubKey; DWORD dwType = 0; - if (RegOpenKeyEx(pKey->Handle, szName, + if (RegOpenKeyExA(pKey->Handle, szName, 0, KEY_READ, &hSubKey) != ERROR_SUCCESS) { hSubKey = 0; // Try to query value (exists?) - if (RegQueryValueEx(pKey->Handle, szName, + if (RegQueryValueExA(pKey->Handle, szName, 0, &dwType, nullptr, nullptr) != ERROR_SUCCESS) fFound = false; } @@ -679,7 +679,7 @@ T StdCompilerConfigRead::ReadInteger(DWORD type, DWORD alternativeType) } // Read T iVal; DWORD iSize = sizeof(iVal); - if (RegQueryValueEx(pKey->Parent->Handle, pKey->Name.getData(), + if (RegQueryValueExA(pKey->Parent->Handle, pKey->Name.getData(), 0, nullptr, reinterpret_cast(&iVal), &iSize) != ERROR_SUCCESS) @@ -714,7 +714,7 @@ StdStrBuf StdCompilerConfigRead::ReadString() } // Get size of string DWORD iSize; - if (RegQueryValueEx(pKey->Parent->Handle, pKey->Name.getData(), + if (RegQueryValueExA(pKey->Parent->Handle, pKey->Name.getData(), 0, nullptr, nullptr, &iSize) != ERROR_SUCCESS) @@ -724,7 +724,7 @@ StdStrBuf StdCompilerConfigRead::ReadString() // Allocate string StdStrBuf Result; Result.SetLength(iSize); // Read - if (RegQueryValueEx(pKey->Parent->Handle, pKey->Name.getData(), + if (RegQueryValueExA(pKey->Parent->Handle, pKey->Name.getData(), 0, nullptr, reinterpret_cast(Result.getMData()), &iSize) != ERROR_SUCCESS) diff --git a/src/StdWindow.cpp b/src/StdWindow.cpp index 637e479b0..6a810a7ef 100644 --- a/src/StdWindow.cpp +++ b/src/StdWindow.cpp @@ -20,6 +20,7 @@ #include #include "StdApp.h" #include +#include "StdStringEncodingConverter.h" #ifndef USE_CONSOLE #include #endif @@ -51,7 +52,7 @@ bool CStdWindow::Init(CStdApp *const app, const char *const title, const C4Rect hWindow = CreateWindowEx( exStyle, windowClass.lpszClassName, - title, + StdStringEncodingConverter::WinAcpToUtf16(title).c_str(), style, bounds.x, bounds.y, bounds.Wdt, bounds.Hgt, parent ? parent->hWindow : nullptr, nullptr, app->hInstance, this); @@ -110,7 +111,7 @@ void CStdWindow::Clear() void CStdWindow::SetTitle(const char *const szToTitle) { - if (hWindow) SetWindowText(hWindow, szToTitle ? szToTitle : ""); + if (hWindow) SetWindowText(hWindow, szToTitle ? StdStringEncodingConverter::WinAcpToUtf16(szToTitle).c_str() : L""); } bool CStdWindow::GetSize(C4Rect &rect) diff --git a/src/c4group_cmdl.cpp b/src/c4group_cmdl.cpp index ed745712c..f9f4e2d0e 100644 --- a/src/c4group_cmdl.cpp +++ b/src/c4group_cmdl.cpp @@ -315,7 +315,7 @@ bool ProcessGroup(const char *szFilename) else { std::print("Waiting for {} to end", argv[iArg + 1]); - for (int i = 0; FindWindow(nullptr, argv[iArg + 1]) && (i < 5); i++) + for (int i = 0; FindWindowA(nullptr, argv[iArg + 1]) && (i < 5); i++) { Sleep(1000); std::print("."); @@ -369,7 +369,7 @@ int RegisterShellExtensions() char strModule[2048]; char strCommand[2048]; char strClass[128]; - GetModuleFileName(nullptr, strModule, 2048); + GetModuleFileNameA(nullptr, strModule, 2048); // Groups const char *strClasses = "Clonk4.Definition;Clonk4.Folder;Clonk4.Group;Clonk4.Player;Clonk4.Scenario;Clonk4.Update;Clonk4.Weblink;Clonk4.Object"; for (int i = 0; SCopySegment(strClasses, i, strClass); i++) @@ -492,7 +492,7 @@ int main(int argc, char *argv[]) if (!fQuiet) { char strWorkingDirectory[_MAX_PATH + 1] = ""; - GetCurrentDirectory(_MAX_PATH, strWorkingDirectory); + GetCurrentDirectoryA(_MAX_PATH, strWorkingDirectory); std::println("Location: {}", strWorkingDirectory); } @@ -565,12 +565,12 @@ int main(int argc, char *argv[]) { std::println("Executing: {}", strExecuteAtEnd); - STARTUPINFO startInfo{}; + STARTUPINFOA startInfo{}; startInfo.cb = sizeof(startInfo); PROCESS_INFORMATION procInfo; - CreateProcess(strExecuteAtEnd, nullptr, nullptr, nullptr, false, 0, nullptr, nullptr, &startInfo, &procInfo); + CreateProcessA(strExecuteAtEnd, nullptr, nullptr, nullptr, false, 0, nullptr, nullptr, &startInfo, &procInfo); } // Done From 05bcd14e4b969a5ec506643b9e38498959952971 Mon Sep 17 00:00:00 2001 From: George Tokmaji Date: Wed, 7 Aug 2024 19:58:57 +0200 Subject: [PATCH 2/3] C4CrashHandlerWin32: Use Unicode --- src/C4Config.cpp | 12 ++ src/C4CrashHandlerWin32.cpp | 281 ++++++++++++++++++++---------------- 2 files changed, 170 insertions(+), 123 deletions(-) diff --git a/src/C4Config.cpp b/src/C4Config.cpp index 5f736771d..22745c16c 100644 --- a/src/C4Config.cpp +++ b/src/C4Config.cpp @@ -584,6 +584,13 @@ bool C4Config::Save() return true; } +#if defined(_WIN32) && defined(C4ENGINE) +namespace C4CrashHandlerWin32 +{ + void SetUserPath(std::string_view); +} +#endif + void C4ConfigGeneral::DeterminePaths(bool forceWorkingDirectory) { #ifdef _WIN32 @@ -595,6 +602,11 @@ void C4ConfigGeneral::DeterminePaths(bool forceWorkingDirectory) // Temp path GetTempPathA(CFG_MaxString, TempPath); if (TempPath[0]) AppendBackslash(TempPath); + +#ifdef C4ENGINE + C4CrashHandlerWin32::SetUserPath(UserPath); +#endif + #elif defined(__linux__) #ifdef C4ENGINE GetParentPath(Application.Location, ExePath); diff --git a/src/C4CrashHandlerWin32.cpp b/src/C4CrashHandlerWin32.cpp index 7828aad6d..8dae9dd22 100644 --- a/src/C4CrashHandlerWin32.cpp +++ b/src/C4CrashHandlerWin32.cpp @@ -21,10 +21,10 @@ #include "C4Include.h" // Dump generation on crash -#include "C4Config.h" #include "C4Log.h" #include "C4Version.h" #include "C4Windows.h" +#include "StdStringEncodingConverter.h" #include #include @@ -47,74 +47,77 @@ namespace #endif constexpr size_t DumpBufferSize = 2048; - char DumpBuffer[DumpBufferSize]; + constexpr size_t DumpBufferSizeInBytes = DumpBufferSize * sizeof(wchar_t); + wchar_t DumpBuffer[DumpBufferSize]; char SymbolBuffer[DumpBufferSize]; + wchar_t UserPathWide[_MAX_PATH] = {L'0'}; + // Dump crash info in a human readable format. Uses a static buffer to avoid heap allocations // from an exception handler. For the same reason, this also doesn't use Log/LogF etc. - void SafeTextDump(LPEXCEPTION_POINTERS exc, int fd, const char *dumpFilename) + void SafeTextDump(LPEXCEPTION_POINTERS exc, int fd, const wchar_t *dumpFilename) { #if defined(_MSC_VER) -# define LOG_SNPRINTF _snprintf +# define LOG_SNPRINTF _snwprintf #else -# define LOG_SNPRINTF snprintf +# define LOG_SNPRINTF snwprintf #endif -#define LOG_STATIC_TEXT(text) write(fd, text, sizeof(text) - 1) -#define LOG_DYNAMIC_TEXT(...) write(fd, DumpBuffer, LOG_SNPRINTF(DumpBuffer, DumpBufferSize-1, __VA_ARGS__)) +#define LOG_STATIC_TEXT(text) write(fd, text, sizeof(text) - sizeof(wchar_t)) +#define LOG_DYNAMIC_TEXT(...) write(fd, DumpBuffer, LOG_SNPRINTF(DumpBuffer, DumpBufferSize-sizeof(wchar_t), __VA_ARGS__)) // Figure out which kind of format string will output a pointer in hex #if defined(PRIdPTR) -# define POINTER_FORMAT_SUFFIX PRIdPTR +# define POINTER_FORMAT_SUFFIX _CRT_WIDE(PRIdPTR) #elif defined(_MSC_VER) -# define POINTER_FORMAT_SUFFIX "Ix" +# define POINTER_FORMAT_SUFFIX L"Ix" #elif defined(__GNUC__) -# define POINTER_FORMAT_SUFFIX "zx" +# define POINTER_FORMAT_SUFFIX L"zx" #else -# define POINTER_FORMAT_SUFFIX "p" +# define POINTER_FORMAT_SUFFIX L"p" #endif #if LC_MACHINE == LC_MACHINE_X64 -# define POINTER_FORMAT "0x%016" POINTER_FORMAT_SUFFIX +# define POINTER_FORMAT L"0x%016" POINTER_FORMAT_SUFFIX #elif LC_MACHINE == LC_MACHINE_X86 -# define POINTER_FORMAT "0x%08" POINTER_FORMAT_SUFFIX +# define POINTER_FORMAT L"0x%08" POINTER_FORMAT_SUFFIX #else -# define POINTER_FORMAT "0x%" POINTER_FORMAT_SUFFIX +# define POINTER_FORMAT L"0x%" POINTER_FORMAT_SUFFIX #endif #ifndef STATUS_ASSERTION_FAILURE # define STATUS_ASSERTION_FAILURE ((DWORD)0xC0000420L) #endif - LOG_STATIC_TEXT("**********************************************************************\n"); - LOG_STATIC_TEXT("* UNHANDLED EXCEPTION\n"); + LOG_STATIC_TEXT(L"**********************************************************************\n"); + LOG_STATIC_TEXT(L"* UNHANDLED EXCEPTION\n"); - if (exc->ExceptionRecord->ExceptionCode != STATUS_ASSERTION_FAILURE && dumpFilename && dumpFilename[0] != '\0') + if (exc->ExceptionRecord->ExceptionCode != STATUS_ASSERTION_FAILURE && dumpFilename && dumpFilename[0] != L'\0') { - LOG_STATIC_TEXT("* A crash dump may have been written to "); - write(fd, dumpFilename, strlen(dumpFilename)); - LOG_STATIC_TEXT("\n"); - LOG_STATIC_TEXT("* If this file exists, please send it to a developer for investigation.\n"); + LOG_STATIC_TEXT(L"* A crash dump may have been written to "); + write(fd, dumpFilename, std::wcslen(dumpFilename)); + LOG_STATIC_TEXT(L"\n"); + LOG_STATIC_TEXT(L"* If this file exists, please send it to a developer for investigation.\n"); } LOG_STATIC_TEXT("**********************************************************************\n"); // Log exception type switch (exc->ExceptionRecord->ExceptionCode) { -#define LOG_EXCEPTION(code, text) case code: LOG_STATIC_TEXT(#code ": " text "\n"); break - LOG_EXCEPTION(EXCEPTION_ACCESS_VIOLATION, "The thread tried to read from or write to a virtual address for which it does not have the appropriate access."); - LOG_EXCEPTION(EXCEPTION_ILLEGAL_INSTRUCTION, "The thread tried to execute an invalid instruction."); - LOG_EXCEPTION(EXCEPTION_IN_PAGE_ERROR, "The thread tried to access a page that was not present, and the system was unable to load the page."); - LOG_EXCEPTION(EXCEPTION_NONCONTINUABLE_EXCEPTION, "The thread tried to continue execution after a noncontinuable exception occurred."); - LOG_EXCEPTION(EXCEPTION_PRIV_INSTRUCTION, "The thread tried to execute an instruction whose operation is not allowed in the current machine mode."); - LOG_EXCEPTION(EXCEPTION_STACK_OVERFLOW, "The thread used up its stack."); - LOG_EXCEPTION(EXCEPTION_GUARD_PAGE, "The thread accessed memory allocated with the PAGE_GUARD modifier."); - LOG_EXCEPTION(STATUS_ASSERTION_FAILURE, "The thread specified a pre- or postcondition that did not hold."); +#define LOG_EXCEPTION(code, text) case code: LOG_STATIC_TEXT(_CRT_WIDE(#code) L": " text L"\n"); break + LOG_EXCEPTION(EXCEPTION_ACCESS_VIOLATION, L"The thread tried to read from or write to a virtual address for which it does not have the appropriate access."); + LOG_EXCEPTION(EXCEPTION_ILLEGAL_INSTRUCTION, L"The thread tried to execute an invalid instruction."); + LOG_EXCEPTION(EXCEPTION_IN_PAGE_ERROR, L"The thread tried to access a page that was not present, and the system was unable to load the page."); + LOG_EXCEPTION(EXCEPTION_NONCONTINUABLE_EXCEPTION, L"The thread tried to continue execution after a noncontinuable exception occurred."); + LOG_EXCEPTION(EXCEPTION_PRIV_INSTRUCTION, L"The thread tried to execute an instruction whose operation is not allowed in the current machine mode."); + LOG_EXCEPTION(EXCEPTION_STACK_OVERFLOW, L"The thread used up its stack."); + LOG_EXCEPTION(EXCEPTION_GUARD_PAGE, L"The thread accessed memory allocated with the PAGE_GUARD modifier."); + LOG_EXCEPTION(STATUS_ASSERTION_FAILURE, L"The thread specified a pre- or postcondition that did not hold."); #undef LOG_EXCEPTION default: - LOG_DYNAMIC_TEXT("%#08x: The thread raised an unknown exception.\n", static_cast(exc->ExceptionRecord->ExceptionCode)); + LOG_DYNAMIC_TEXT(L"%#08x: The thread raised an unknown exception.\n", static_cast(exc->ExceptionRecord->ExceptionCode)); break; } if (exc->ExceptionRecord->ExceptionFlags == EXCEPTION_NONCONTINUABLE) - LOG_STATIC_TEXT("This is a non-continuable exception.\n"); + LOG_STATIC_TEXT(L"This is a non-continuable exception.\n"); else - LOG_STATIC_TEXT("This is a continuable exception.\n"); + LOG_STATIC_TEXT(L"This is a continuable exception.\n"); // For some exceptions, there is a defined meaning to the ExceptionInformation field switch (exc->ExceptionRecord->ExceptionCode) @@ -123,10 +126,10 @@ namespace case EXCEPTION_IN_PAGE_ERROR: if (exc->ExceptionRecord->NumberParameters < 2) { - LOG_STATIC_TEXT("Additional information for the exception was not provided.\n"); + LOG_STATIC_TEXT(L"Additional information for the exception was not provided.\n"); break; } - LOG_STATIC_TEXT("Additional information for the exception: The thread "); + LOG_STATIC_TEXT(L"Additional information for the exception: The thread "); switch (exc->ExceptionRecord->ExceptionInformation[0]) { #ifndef EXCEPTION_READ_FAULT @@ -134,29 +137,29 @@ namespace # define EXCEPTION_WRITE_FAULT 1 # define EXCEPTION_EXECUTE_FAULT 8 #endif - case EXCEPTION_READ_FAULT: LOG_STATIC_TEXT("tried to read from memory"); break; - case EXCEPTION_WRITE_FAULT: LOG_STATIC_TEXT("tried to write to memory"); break; - case EXCEPTION_EXECUTE_FAULT: LOG_STATIC_TEXT("caused an user-mode DEP violation"); break; - default: LOG_DYNAMIC_TEXT("tried to access (%#x) memory", static_cast(exc->ExceptionRecord->ExceptionInformation[0])); break; + case EXCEPTION_READ_FAULT: LOG_STATIC_TEXT(L"tried to read from memory"); break; + case EXCEPTION_WRITE_FAULT: LOG_STATIC_TEXT(L"tried to write to memory"); break; + case EXCEPTION_EXECUTE_FAULT: LOG_STATIC_TEXT(L"caused an user-mode DEP violation"); break; + default: LOG_DYNAMIC_TEXT(L"tried to access (%#x) memory", static_cast(exc->ExceptionRecord->ExceptionInformation[0])); break; } - LOG_DYNAMIC_TEXT(" at address " POINTER_FORMAT ".\n", static_cast(exc->ExceptionRecord->ExceptionInformation[1])); + LOG_DYNAMIC_TEXT(L" at address " POINTER_FORMAT ".\n", static_cast(exc->ExceptionRecord->ExceptionInformation[1])); if (exc->ExceptionRecord->ExceptionCode == EXCEPTION_IN_PAGE_ERROR) { if (exc->ExceptionRecord->NumberParameters >= 3) - LOG_DYNAMIC_TEXT("The NTSTATUS code that resulted in this exception was " POINTER_FORMAT ".\n", static_cast(exc->ExceptionRecord->ExceptionInformation[2])); + LOG_DYNAMIC_TEXT(L"The NTSTATUS code that resulted in this exception was " POINTER_FORMAT ".\n", static_cast(exc->ExceptionRecord->ExceptionInformation[2])); else - LOG_STATIC_TEXT("The NTSTATUS code that resulted in this exception was not provided.\n"); + LOG_STATIC_TEXT(L"The NTSTATUS code that resulted in this exception was not provided.\n"); } break; case STATUS_ASSERTION_FAILURE: if (exc->ExceptionRecord->NumberParameters < 3) { - LOG_STATIC_TEXT("Additional information for the exception was not provided.\n"); + LOG_STATIC_TEXT(L"Additional information for the exception was not provided.\n"); break; } - LOG_DYNAMIC_TEXT("Additional information for the exception:\n Assertion that failed: %ls\n File: %ls\n Line: %d\n", + LOG_DYNAMIC_TEXT(L"Additional information for the exception:\n Assertion that failed: %s\n File: %s\n Line: %d\n", reinterpret_cast(exc->ExceptionRecord->ExceptionInformation[0]), reinterpret_cast(exc->ExceptionRecord->ExceptionInformation[1]), (int) exc->ExceptionRecord->ExceptionInformation[2]); @@ -165,45 +168,45 @@ namespace // Dump registers #if LC_MACHINE == LC_MACHINE_X64 - LOG_STATIC_TEXT("\nProcessor registers (x86_64):\n"); - LOG_DYNAMIC_TEXT("RAX: " POINTER_FORMAT ", RBX: " POINTER_FORMAT ", RCX: " POINTER_FORMAT ", RDX: " POINTER_FORMAT "\n", + LOG_STATIC_TEXT(L"\nProcessor registers (x86_64):\n"); + LOG_DYNAMIC_TEXT(L"RAX: " POINTER_FORMAT L", RBX: " POINTER_FORMAT L", RCX: " POINTER_FORMAT L", RDX: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->Rax), static_cast(exc->ContextRecord->Rbx), static_cast(exc->ContextRecord->Rcx), static_cast(exc->ContextRecord->Rdx)); - LOG_DYNAMIC_TEXT("RBP: " POINTER_FORMAT ", RSI: " POINTER_FORMAT ", RDI: " POINTER_FORMAT ", R8: " POINTER_FORMAT "\n", + LOG_DYNAMIC_TEXT(L"RBP: " POINTER_FORMAT L", RSI: " POINTER_FORMAT L", RDI: " POINTER_FORMAT L", R8: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->Rbp), static_cast(exc->ContextRecord->Rsi), static_cast(exc->ContextRecord->Rdi), static_cast(exc->ContextRecord->R8)); - LOG_DYNAMIC_TEXT(" R9: " POINTER_FORMAT ", R10: " POINTER_FORMAT ", R11: " POINTER_FORMAT ", R12: " POINTER_FORMAT "\n", + LOG_DYNAMIC_TEXT(L" R9: " POINTER_FORMAT L", R10: " POINTER_FORMAT L", R11: " POINTER_FORMAT L", R12: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->R9), static_cast(exc->ContextRecord->R10), static_cast(exc->ContextRecord->R11), static_cast(exc->ContextRecord->R12)); - LOG_DYNAMIC_TEXT("R13: " POINTER_FORMAT ", R14: " POINTER_FORMAT ", R15: " POINTER_FORMAT "\n", + LOG_DYNAMIC_TEXT(L"R13: " POINTER_FORMAT L", R14: " POINTER_FORMAT L", R15: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->R13), static_cast(exc->ContextRecord->R14), static_cast(exc->ContextRecord->R15)); - LOG_DYNAMIC_TEXT("RSP: " POINTER_FORMAT ", RIP: " POINTER_FORMAT "\n", + LOG_DYNAMIC_TEXT(L"RSP: " POINTER_FORMAT L", RIP: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->Rsp), static_cast(exc->ContextRecord->Rip)); #elif LC_MACHINE == LC_MACHINE_X86 - LOG_STATIC_TEXT("\nProcessor registers (x86):\n"); - LOG_DYNAMIC_TEXT("EAX: " POINTER_FORMAT ", EBX: " POINTER_FORMAT ", ECX: " POINTER_FORMAT ", EDX: " POINTER_FORMAT "\n", + LOG_STATIC_TEXT(L"\nProcessor registers (x86):\n"); + LOG_DYNAMIC_TEXT(L"EAX: " POINTER_FORMAT L", EBX: " POINTER_FORMAT L", ECX: " POINTER_FORMAT L", EDX: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->Eax), static_cast(exc->ContextRecord->Ebx), static_cast(exc->ContextRecord->Ecx), static_cast(exc->ContextRecord->Edx)); - LOG_DYNAMIC_TEXT("ESI: " POINTER_FORMAT ", EDI: " POINTER_FORMAT "\n", + LOG_DYNAMIC_TEXT(L"ESI: " POINTER_FORMAT L", EDI: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->Esi), static_cast(exc->ContextRecord->Edi)); - LOG_DYNAMIC_TEXT("EBP: " POINTER_FORMAT ", ESP: " POINTER_FORMAT ", EIP: " POINTER_FORMAT "\n", + LOG_DYNAMIC_TEXT(L"EBP: " POINTER_FORMAT L", ESP: " POINTER_FORMAT L", EIP: " POINTER_FORMAT L"\n", static_cast(exc->ContextRecord->Ebp), static_cast(exc->ContextRecord->Esp), static_cast(exc->ContextRecord->Eip)); #endif #if LC_MACHINE == LC_MACHINE_X64 || LC_MACHINE == LC_MACHINE_X86 - LOG_DYNAMIC_TEXT("EFLAGS: 0x%08x (%c%c%c%c%c%c%c)\n", static_cast(exc->ContextRecord->EFlags), - exc->ContextRecord->EFlags & 0x800 ? 'O' : '.', // overflow - exc->ContextRecord->EFlags & 0x400 ? 'D' : '.', // direction - exc->ContextRecord->EFlags & 0x80 ? 'S' : '.', // sign - exc->ContextRecord->EFlags & 0x40 ? 'Z' : '.', // zero - exc->ContextRecord->EFlags & 0x10 ? 'A' : '.', // auxiliary carry - exc->ContextRecord->EFlags & 0x4 ? 'P' : '.', // parity - exc->ContextRecord->EFlags & 0x1 ? 'C' : '.'); // carry + LOG_DYNAMIC_TEXT(L"EFLAGS: 0x%08x (%c%c%c%c%c%c%c)\n", static_cast(exc->ContextRecord->EFlags), + exc->ContextRecord->EFlags & 0x800 ? L'O' : L'.', // overflow + exc->ContextRecord->EFlags & 0x400 ? L'D' : L'.', // direction + exc->ContextRecord->EFlags & 0x80 ? L'S' : L'.', // sign + exc->ContextRecord->EFlags & 0x40 ? L'Z' : L'.', // zero + exc->ContextRecord->EFlags & 0x10 ? L'A' : L'.', // auxiliary carry + exc->ContextRecord->EFlags & 0x4 ? L'P' : L'.', // parity + exc->ContextRecord->EFlags & 0x1 ? L'C' : L'.'); // carry #endif // Dump stack - LOG_STATIC_TEXT("\nStack contents:\n"); + LOG_STATIC_TEXT(L"\nStack contents:\n"); MEMORY_BASIC_INFORMATION stackInfo; intptr_t stackPointer = #if LC_MACHINE == LC_MACHINE_X64 @@ -220,44 +223,44 @@ namespace for (intptr_t dumpRowBase = dumpMin & ~0xF; dumpRowBase < dumpMax; dumpRowBase += 0x10) { - LOG_DYNAMIC_TEXT(POINTER_FORMAT ": ", dumpRowBase); + LOG_DYNAMIC_TEXT(POINTER_FORMAT L": ", dumpRowBase); // Hex dump for (intptr_t dumpRowCursor = dumpRowBase; dumpRowCursor < dumpRowBase + 16; ++dumpRowCursor) { if (dumpRowCursor < dumpMin || dumpRowCursor > dumpMax) - LOG_STATIC_TEXT(" "); + LOG_STATIC_TEXT(L" "); else - LOG_DYNAMIC_TEXT("%02x ", (unsigned int) *reinterpret_cast(dumpRowCursor)); // Safe, since it's inside the VM of our process + LOG_DYNAMIC_TEXT(L"%02x ", (unsigned int) *reinterpret_cast(dumpRowCursor)); // Safe, since it's inside the VM of our process } - LOG_STATIC_TEXT(" "); + LOG_STATIC_TEXT(L" "); // Text dump for (intptr_t dumpRowCursor = dumpRowBase; dumpRowCursor < dumpRowBase + 16; ++dumpRowCursor) { if (dumpRowCursor < dumpMin || dumpRowCursor > dumpMax) - LOG_STATIC_TEXT(" "); + LOG_STATIC_TEXT(L" "); else { unsigned char c = *reinterpret_cast(dumpRowCursor); // Safe, since it's inside the VM of our process if (c < 0x20 || (c > 0x7e && c < 0xa1)) - LOG_STATIC_TEXT("."); + LOG_STATIC_TEXT(L"."); else - LOG_DYNAMIC_TEXT("%c", static_cast(c)); + LOG_DYNAMIC_TEXT(L"%c", static_cast(c)); } } - LOG_STATIC_TEXT("\n"); + LOG_STATIC_TEXT(L"\n"); } } else { - LOG_STATIC_TEXT("[Failed to access stack memory]\n"); + LOG_STATIC_TEXT(L"[Failed to access stack memory]\n"); } // Initialize DbgHelp.dll symbol functions SymSetOptions(SYMOPT_UNDNAME | SYMOPT_DEFERRED_LOADS | SYMOPT_LOAD_LINES); HANDLE process = GetCurrentProcess(); - if (SymInitialize(process, nullptr, true)) + if (SymInitializeW(process, nullptr, true)) { - LOG_STATIC_TEXT("\nStack trace:\n"); + LOG_STATIC_TEXT(L"\nStack trace:\n"); auto frame = STACKFRAME64(); DWORD imageType; CONTEXT context = *exc->ContextRecord; @@ -278,52 +281,52 @@ namespace frame.AddrFrame.Offset = context.Ebp; #endif // Dump stack trace - SYMBOL_INFO *symbol = reinterpret_cast(SymbolBuffer); - static_assert(DumpBufferSize >= sizeof(*symbol), "SYMBOL_INFO too large to fit into buffer"); - IMAGEHLP_MODULE64 *module = reinterpret_cast(SymbolBuffer); - static_assert(DumpBufferSize >= sizeof(*module), "IMAGEHLP_MODULE64 too large to fit into buffer"); - IMAGEHLP_LINE64 *line = reinterpret_cast(SymbolBuffer); - static_assert(DumpBufferSize >= sizeof(*line), "IMAGEHLP_LINE64 too large to fit into buffer"); + SYMBOL_INFOW *symbol = reinterpret_cast(SymbolBuffer); + static_assert(DumpBufferSizeInBytes >= sizeof(*symbol), "SYMBOL_INFO too large to fit into buffer"); + IMAGEHLP_MODULEW64 *module = reinterpret_cast(SymbolBuffer); + static_assert(DumpBufferSizeInBytes >= sizeof(*module), "IMAGEHLP_MODULE64 too large to fit into buffer"); + IMAGEHLP_LINEW64 *line = reinterpret_cast(SymbolBuffer); + static_assert(DumpBufferSizeInBytes >= sizeof(*line), "IMAGEHLP_LINE64 too large to fit into buffer"); int frameNumber = 0; while (StackWalk64(imageType, process, GetCurrentThread(), &frame, &context, nullptr, SymFunctionTableAccess64, SymGetModuleBase64, nullptr)) { - LOG_DYNAMIC_TEXT("#%3d ", frameNumber); + LOG_DYNAMIC_TEXT(L"#%3d ", frameNumber); module->SizeOfStruct = sizeof(*module); DWORD64 imageBase = 0; - if (SymGetModuleInfo64(process, frame.AddrPC.Offset, module)) + if (SymGetModuleInfoW64(process, frame.AddrPC.Offset, module)) { - LOG_DYNAMIC_TEXT("%s", module->ModuleName); + LOG_DYNAMIC_TEXT(L"%s", module->ModuleName); imageBase = module->BaseOfImage; } DWORD64 disp64; symbol->MaxNameLen = DumpBufferSize - sizeof(*symbol); symbol->SizeOfStruct = sizeof(*symbol); - if (SymFromAddr(process, frame.AddrPC.Offset, &disp64, symbol)) + if (SymFromAddrW(process, frame.AddrPC.Offset, &disp64, symbol)) { - LOG_DYNAMIC_TEXT("!%s+%#lx", symbol->Name, static_cast(disp64)); + LOG_DYNAMIC_TEXT(L"!%s+%#lx", symbol->Name, static_cast(disp64)); } else if (imageBase > 0) { - LOG_DYNAMIC_TEXT("+%#lx", static_cast(frame.AddrPC.Offset - imageBase)); + LOG_DYNAMIC_TEXT(L"+%#lx", static_cast(frame.AddrPC.Offset - imageBase)); } else { - LOG_DYNAMIC_TEXT("%#lx", static_cast(frame.AddrPC.Offset)); + LOG_DYNAMIC_TEXT(L"%#lx", static_cast(frame.AddrPC.Offset)); } DWORD disp; line->SizeOfStruct = sizeof(*line); - if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &disp, line)) + if (SymGetLineFromAddrW64(process, frame.AddrPC.Offset, &disp, line)) { - LOG_DYNAMIC_TEXT(" [%s @ %u]", line->FileName, static_cast(line->LineNumber)); + LOG_DYNAMIC_TEXT(L" [%s @ %u]", line->FileName, static_cast(line->LineNumber)); } - LOG_STATIC_TEXT("\n"); + LOG_STATIC_TEXT(L"\n"); ++frameNumber; } SymCleanup(process); } else { - LOG_STATIC_TEXT("[Stack trace not available: failed to initialize Debugging Help Library]\n"); + LOG_STATIC_TEXT(L"[Stack trace not available: failed to initialize Debugging Help Library]\n"); } // Dump loaded modules @@ -338,13 +341,13 @@ namespace if (snapshot != INVALID_HANDLE_VALUE) { - LOG_STATIC_TEXT("\nLoaded modules:\n"); + LOG_STATIC_TEXT(L"\nLoaded modules:\n"); MODULEENTRY32 *module = reinterpret_cast(SymbolBuffer); - static_assert(DumpBufferSize >= sizeof(*module), "MODULEENTRY32 too large to fit into buffer"); + static_assert(DumpBufferSizeInBytes >= sizeof(*module), "MODULEENTRY32 too large to fit into buffer"); module->dwSize = sizeof(*module); for (BOOL success = Module32First(snapshot, module); success; success = Module32Next(snapshot, module)) { - LOG_DYNAMIC_TEXT("%32hs loaded at " POINTER_FORMAT " - " POINTER_FORMAT " (%hs)\n", module->szModule, + LOG_DYNAMIC_TEXT(L"%32s loaded at " POINTER_FORMAT L" - " POINTER_FORMAT L" (%s)\n", module->szModule, reinterpret_cast(module->modBaseAddr), reinterpret_cast(module->modBaseAddr + module->modBaseSize), module->szExePath); } @@ -370,26 +373,47 @@ LONG WINAPI GenerateDump(EXCEPTION_POINTERS *pExceptionPointers) // Open dump file // Work on the assumption that the config isn't corrupted - char filenameBuffer[_MAX_PATH + sizeof("\\\\?\\")] = {'\0'}; // extra chars for GetFinalPathNameByHandleA, null byte space included - strncpy(filenameBuffer, Config.General.UserPath, strnlen(Config.General.UserPath, sizeof(Config.General.UserPath))); + wchar_t filenameBuffer[_MAX_PATH + sizeof(L"\\\\?\\")] = {L'\0'}; // extra chars for GetFinalPathNameByHandleA, null byte space included + std::wmemcpy(filenameBuffer, UserPathWide, std::wcslen(UserPathWide)); - auto *filename = reinterpret_cast(DumpBuffer); + auto *filename = reinterpret_cast(DumpBuffer); - ExpandEnvironmentStringsA(filenameBuffer, filename, _MAX_PATH); + ExpandEnvironmentStrings(filenameBuffer, filename, _MAX_PATH); - if (!DirectoryExists(filename)) + static constexpr auto directoryExists = [](std::wstring_view filename) -> bool + { + // Ignore trailing slash or backslash + wchar_t bufFilename[_MAX_PATH + 1]; + if (!filename.empty()) + { + if (filename.ends_with(L'\\') || filename.ends_with(L'/')) + { + bufFilename[filename.copy(bufFilename, filename.size() - 1)] = L'\0'; + filename = {bufFilename, filename.size() - 1}; + } + } + + WIN32_FIND_DATA fdt; + HANDLE handle; + if ((handle = FindFirstFile(filename.data(), &fdt)) == INVALID_HANDLE_VALUE) return false; + FindClose(handle); + + return fdt.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY; + }; + + if (!directoryExists(filename)) { // Config corrupted or broken - filename[0] = '\0'; + filename[0] = L'\0'; } HANDLE file = INVALID_HANDLE_VALUE; - if (filename[0] != '\0') + if (filename[0] != L'\0') { // There is some path where we want to store our data - const char tmpl[] = C4ENGINENAME "-crash-YYYY-MM-DD-HH-MM-SS.dmp"; - size_t pathLength = strlen(filename); - if (pathLength + sizeof(tmpl) / sizeof(*tmpl) > DumpBufferSize) + const wchar_t tmpl[] = _CRT_WIDE(C4ENGINENAME) L"-crash-YYYY-MM-DD-HH-MM-SS.dmp"; + size_t pathLength = std::wcslen(filename); + if (pathLength + std::size(tmpl) > DumpBufferSize) { // Somehow the length of the required path is too long to fit in // our buffer. Don't dump anything then. @@ -398,52 +422,54 @@ LONG WINAPI GenerateDump(EXCEPTION_POINTERS *pExceptionPointers) else { // Make sure the path ends in a backslash. - if (filename[pathLength - 1] != '\\') + if (filename[pathLength - 1] != L'\\') { - filename[pathLength] = '\\'; - filename[++pathLength] = '\0'; + filename[pathLength] = L'\\'; + filename[++pathLength] = L'\0'; } SYSTEMTIME st; GetSystemTime(&st); auto *ptr = filename + pathLength; - sprintf_s(ptr, _MAX_PATH - pathLength, "%s-crash-%04d-%02d-%02d-%02d-%02d-%02d.dmp", - C4ENGINENAME, st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); + // For some reason, using just %s ends up reading the argument as an ANSI string, + //even though swprintf should read it as a wide string. + swprintf_s(ptr, _MAX_PATH - pathLength, L"%ls-crash-%04d-%02d-%02d-%02d-%02d-%02d.dmp", + _CRT_WIDE(C4ENGINENAME), st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond); } } - if (filename[0] != '\0') + if (filename[0] != L'\0') { - file = CreateFileA(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); + file = CreateFile(filename, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, nullptr, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, nullptr); // If we can't create a *new* file to dump into, don't dump at all. if (file == INVALID_HANDLE_VALUE) { - filename[0] = '\0'; + filename[0] = L'\0'; } } - char buffer[DumpBufferSize] = {'\0'}; - strcat(buffer, "LegacyClonk crashed. Please report this crash "); + wchar_t buffer[DumpBufferSize] = {L'\0'}; + std::wcscat(buffer, L"LegacyClonk crashed. Please report this crash "); - if (GetLogFD() != -1 || filename[0] != '\0') + if (GetLogFD() != -1 || filename[0] != L'\0') { - strcat(buffer, "together with the following information to the developers:\n"); + std::wcscat(buffer, L"together with the following information to the developers:\n"); if (GetLogFD() != -1) { - strcat(buffer, "\nYou can find detailed information in "); - GetFinalPathNameByHandleA(reinterpret_cast(_get_osfhandle(GetLogFD())), filenameBuffer, sizeof(filenameBuffer), 0); - strcat(strcat(buffer, filenameBuffer), "."); + std::wcscat(buffer, L"\nYou can find detailed information in "); + GetFinalPathNameByHandle(reinterpret_cast(_get_osfhandle(GetLogFD())), filenameBuffer, std::size(filenameBuffer), 0); + std::wcscat(std::wcscat(buffer, filenameBuffer), L"."); } - if (filename[0] != '\0') + if (filename[0] != L'\0') { - strcat(strcat(strcat(buffer, "\nA crash dump has been generated at "), filename), "."); + std::wcscat(std::wcscat(std::wcscat(buffer, L"\nA crash dump has been generated at "), filename), L"."); } } else { - strcat(buffer, "to the developers."); + std::wcscat(buffer, L"to the developers."); } // Write dump (human readable format) @@ -463,7 +489,7 @@ LONG WINAPI GenerateDump(EXCEPTION_POINTERS *pExceptionPointers) CloseHandle(file); } - MessageBoxA(nullptr, buffer, "LegacyClonk crashed", MB_ICONERROR); + MessageBox(nullptr, buffer, L"LegacyClonk crashed", MB_ICONERROR); // Call native exception handler return EXCEPTION_CONTINUE_SEARCH; @@ -650,3 +676,12 @@ void InstallCrashHandler() HookAssert(&assertionHandler); #endif } + +namespace C4CrashHandlerWin32 +{ + void SetUserPath(const std::string_view userPath) + { + const std::wstring wide{StdStringEncodingConverter::WinAcpToUtf16(userPath)}; + UserPathWide[wide.copy(UserPathWide, sizeof(UserPathWide) - 1)] = L'\0'; + } +} From 366940d4aa4f4aaed0dc757b2ff2e457c86d1a31 Mon Sep 17 00:00:00 2001 From: George Tokmaji Date: Thu, 8 Aug 2024 21:52:11 +0200 Subject: [PATCH 3/3] C4ThreadPool::Io: Fix hanging on usage Cancellation wasn't properly registered, the threadpool IO was cancelled for no reason leading to the callback not being called, and waiting for all callbacks to complete with the coroutine having been resumed from the callback caused a deadlock. --- src/C4ThreadPool.cpp | 18 ++++++------------ src/C4ThreadPool.h | 12 +++++++++++- 2 files changed, 17 insertions(+), 13 deletions(-) diff --git a/src/C4ThreadPool.cpp b/src/C4ThreadPool.cpp index efdd47aaa..4dc3210e8 100644 --- a/src/C4ThreadPool.cpp +++ b/src/C4ThreadPool.cpp @@ -67,9 +67,8 @@ C4ThreadPool::Io::Awaiter::~Awaiter() noexcept } } -bool C4ThreadPool::Io::Awaiter::await_suspend(const std::coroutine_handle<> handle) +bool C4ThreadPool::Io::Awaiter::DoSuspend(const std::coroutine_handle<> handle) { - SetCancellablePromise(handle); coroutineHandle.store(handle, std::memory_order_relaxed); io.SetAwaiter(&overlapped, this); @@ -130,12 +129,7 @@ void C4ThreadPool::Io::Awaiter::SetupCancellation(C4Task::CancellablePromise *co [](void *const argument) { auto *const awaiter = reinterpret_cast(argument); - - if (awaiter->state.exchange(State::Cancelled, std::memory_order_acq_rel) == State::Started) - { - awaiter->io.Cancel(); - CancelIoEx(awaiter->io.fileHandle, &awaiter->overlapped); - } + CancelIoEx(awaiter->io.fileHandle, &awaiter->overlapped); }, this ); @@ -166,10 +160,10 @@ void C4ThreadPool::Io::Awaiter::Callback(const PTP_CALLBACK_INSTANCE instance, c if (state.compare_exchange_strong(expected, desired, std::memory_order_acq_rel, std::memory_order_acquire)) { - CallbackMayRunLong(instance); - - const auto handle = coroutineHandle.load(std::memory_order_relaxed); - handle.resume(); + TrySubmitThreadpoolCallback([](const PTP_CALLBACK_INSTANCE, void *const context) + { + std::coroutine_handle<>::from_address(context).resume(); + }, coroutineHandle.load(std::memory_order_relaxed).address(), nullptr); } } diff --git a/src/C4ThreadPool.h b/src/C4ThreadPool.h index f45d2c515..d9cf3f69c 100644 --- a/src/C4ThreadPool.h +++ b/src/C4ThreadPool.h @@ -115,12 +115,22 @@ class C4ThreadPool public: constexpr bool await_ready() const noexcept { return false; } - bool await_suspend(std::coroutine_handle<> handle); + + template + bool await_suspend(const std::coroutine_handle handle) + { + SetCancellablePromise(handle); + return DoSuspend(handle); + } + std::uint64_t await_resume() const; void SetupCancellation(C4Task::CancellablePromise *promise); void Callback(PTP_CALLBACK_INSTANCE instance, ULONG result, ULONG numberOfBytesTransferred); + private: + bool DoSuspend(std::coroutine_handle<> handle); + private: Io &io; IoFunction ioFunction;