From 3ab65cff040609bb3dbcd3b5b1a5341a5460b1a7 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Fri, 4 Apr 2025 18:38:53 +0200 Subject: [PATCH] Windows: show popup after crash Implements a crash dialog for Windows. The crash popup provides the following actions: - Restart: reopen Blender from the last saved or auto-saved time - Report a Bug: forward to Blender bug tracker - View Crash Log: open the .txt file with the crash log - Close: Closes without any further action Pull Request: https://projects.blender.org/blender/blender/pulls/129974 --- .../cmake/platform/platform_win32.cmake | 4 +- intern/CMakeLists.txt | 6 +- intern/uriconvert/CMakeLists.txt | 22 ++ intern/uriconvert/uri_convert.cc | 42 +++ intern/uriconvert/uri_convert.hh | 25 ++ source/blender/blendthumb/CMakeLists.txt | 7 +- source/blender/blenkernel/BKE_blender.hh | 2 + source/blender/blenkernel/BKE_global.hh | 6 + source/blender/blenkernel/intern/blender.cc | 16 + source/blender/blenkernel/intern/blendfile.cc | 3 + source/blender/blenlib/BLI_system.h | 7 +- source/blender/blenlib/CMakeLists.txt | 2 + source/blender/blenlib/intern/system_win32.cc | 293 +++++++++++++++++- source/blender/blenloader/intern/writefile.cc | 5 +- .../blentranslation/msgfmt/CMakeLists.txt | 12 + source/creator/creator_signals.cc | 59 ++-- 16 files changed, 465 insertions(+), 46 deletions(-) create mode 100644 intern/uriconvert/CMakeLists.txt create mode 100644 intern/uriconvert/uri_convert.cc create mode 100644 intern/uriconvert/uri_convert.hh diff --git a/build_files/cmake/platform/platform_win32.cmake b/build_files/cmake/platform/platform_win32.cmake index da0e24c8761..12ac9b899e4 100644 --- a/build_files/cmake/platform/platform_win32.cmake +++ b/build_files/cmake/platform/platform_win32.cmake @@ -174,8 +174,8 @@ remove_cc_flag( ) if(MSVC_CLANG) # Clangs version of cl doesn't support all flags - string(APPEND CMAKE_CXX_FLAGS " ${CXX_WARN_FLAGS} /MP /nologo /J /Gd /showFilenames /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference /clang:-funsigned-char /clang:-fno-strict-aliasing /clang:-ffp-contract=off") - string(APPEND CMAKE_C_FLAGS " /MP /nologo /J /Gd /showFilenames -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference /clang:-funsigned-char /clang:-fno-strict-aliasing /clang:-ffp-contract=off") + string(APPEND CMAKE_CXX_FLAGS " ${CXX_WARN_FLAGS} /Gy /MP /nologo /J /Gd /showFilenames /EHsc -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference /clang:-funsigned-char /clang:-fno-strict-aliasing /clang:-ffp-contract=off") + string(APPEND CMAKE_C_FLAGS " /MP /nologo /J /Gy /Gd /showFilenames -Wno-unused-command-line-argument -Wno-microsoft-enum-forward-reference /clang:-funsigned-char /clang:-fno-strict-aliasing /clang:-ffp-contract=off") else() string(APPEND CMAKE_CXX_FLAGS " /nologo /J /Gd /MP /EHsc /bigobj") string(APPEND CMAKE_C_FLAGS " /nologo /J /Gd /MP /bigobj") diff --git a/intern/CMakeLists.txt b/intern/CMakeLists.txt index bc003ef827f..0d6c22d5be9 100644 --- a/intern/CMakeLists.txt +++ b/intern/CMakeLists.txt @@ -39,9 +39,13 @@ if(WITH_BULLET) add_subdirectory(rigidbody) endif() -# only windows needs utf16 converter + if(WIN32) + # Only windows needs utf16 converter. add_subdirectory(utfconv) + + # Only used for Windows for now. + add_subdirectory(uriconvert) endif() if(WITH_MOD_FLUID) diff --git a/intern/uriconvert/CMakeLists.txt b/intern/uriconvert/CMakeLists.txt new file mode 100644 index 00000000000..9eeccd10937 --- /dev/null +++ b/intern/uriconvert/CMakeLists.txt @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2024 Blender Authors +# +# SPDX-License-Identifier: GPL-2.0-or-later + +set(INC + . +) + +set(INC_SYS + +) + +set(SRC + uri_convert.cc + + uri_convert.hh +) + +set(LIB +) + +blender_add_lib(bf_intern_uriconvert "${SRC}" "${INC}" "${INC_SYS}" "${LIB}") diff --git a/intern/uriconvert/uri_convert.cc b/intern/uriconvert/uri_convert.cc new file mode 100644 index 00000000000..6b29158c5b1 --- /dev/null +++ b/intern/uriconvert/uri_convert.cc @@ -0,0 +1,42 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#include +#include + +#include "uri_convert.hh" /* Own include. */ + +bool url_encode(const char *str, char *dst, size_t dst_size) +{ + size_t i = 0; + + while (*str && i < dst_size - 1) { + char c = char(*str); + + if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') { + dst[i++] = *str; + } + else if (c == ' ') { + dst[i++] = '+'; + } + else { + if (i + 3 >= dst_size) { + /* There is not enough space for %XX. */ + dst[i] = '\0'; + return false; + } + sprintf(&dst[i], "%%%02X", c); + i += 3; + } + ++str; + } + + dst[i] = '\0'; + + if (*str != '\0') { + /* Output buffer was too small. */ + return false; + } + return true; +} diff --git a/intern/uriconvert/uri_convert.hh b/intern/uriconvert/uri_convert.hh new file mode 100644 index 00000000000..858dc92721f --- /dev/null +++ b/intern/uriconvert/uri_convert.hh @@ -0,0 +1,25 @@ +/* SPDX-FileCopyrightText: 2024 Blender Authors + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +/** \file + * \ingroup intern_uri + */ + +/** + * \brief Encodes a string into URL format by converting special characters into percent-encoded + * sequences. + * + * This function iterates over the provided C-string and replaces non-alphanumeric characters + * (except for '-', '_', '.', '~') with their hexadecimal representations prefixed with '%'. + * Spaces are converted into '+', following the conventions of URL encoding for forms + * (application/x-www-form-urlencoded). + * + * \param str: The input C-string to be URL-encoded. + * \param dst: The output buffer where the URL-encoded string will be stored. + * \param dst_size: The size of the output buffer `dst`. + * \return: `true` if encoding was successful, or `false` if the output buffer was insufficient. + */ +bool url_encode(const char *str, char *dst, size_t dst_size); diff --git a/source/blender/blendthumb/CMakeLists.txt b/source/blender/blendthumb/CMakeLists.txt index 06d8b71bd4f..90b1be8674b 100644 --- a/source/blender/blendthumb/CMakeLists.txt +++ b/source/blender/blendthumb/CMakeLists.txt @@ -37,7 +37,12 @@ if(WIN32) add_library(BlendThumb SHARED ${SRC} ${SRC_WIN32}) - target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib) + target_link_libraries(BlendThumb bf_blenlib dbghelp.lib Version.lib Comctl32.lib) + # Blenlib drags in a whole bunch of dependencies on shared libs, none of which are used by + # blenthumb, but will cause load issues since the debug linker will not eleminate them. + # Link with /OPT:ref to force elmination of those unused dependencies this is already + # enabled by default on the release mode flags. + set_target_properties(BlendThumb PROPERTIES LINK_FLAGS "/OPT:ref") set_target_properties(BlendThumb PROPERTIES LINK_FLAGS_DEBUG "/NODEFAULTLIB:msvcrt") set_target_properties(BlendThumb PROPERTIES VS_GLOBAL_VcpkgEnabled "false") diff --git a/source/blender/blenkernel/BKE_blender.hh b/source/blender/blenkernel/BKE_blender.hh index 43404629548..00cc8c49952 100644 --- a/source/blender/blenkernel/BKE_blender.hh +++ b/source/blender/blenkernel/BKE_blender.hh @@ -29,6 +29,8 @@ void BKE_blender_globals_main_replace(Main *bmain); */ Main *BKE_blender_globals_main_swap(Main *new_gmain); +void BKE_blender_globals_crash_path_get(char *filepath); + void BKE_blender_userdef_data_swap(UserDef *userdef_a, UserDef *userdef_b); void BKE_blender_userdef_data_set(UserDef *userdef); void BKE_blender_userdef_data_set_and_free(UserDef *userdef); diff --git a/source/blender/blenkernel/BKE_global.hh b/source/blender/blenkernel/BKE_global.hh index 17198bd8989..fc0d780d2de 100644 --- a/source/blender/blenkernel/BKE_global.hh +++ b/source/blender/blenkernel/BKE_global.hh @@ -42,6 +42,12 @@ struct Global { char filepath_last_image[/*FILE_MAX*/ 1024]; /** Last used location for library link/append. */ char filepath_last_library[/*FILE_MAX*/ 1024]; + /** + * Last saved location for .blend files. + * This is used for recovery in case of a crash. + * It is set when a .blend file is loaded or when saving (manually or through autosave). + */ + char filepath_last_blend[/*FILE_MAX*/ 1024]; /** * Strings of recently opened files to show in the file menu. diff --git a/source/blender/blenkernel/intern/blender.cc b/source/blender/blenkernel/intern/blender.cc index 207530a57f1..37e2bd97215 100644 --- a/source/blender/blenkernel/intern/blender.cc +++ b/source/blender/blenkernel/intern/blender.cc @@ -28,6 +28,7 @@ #include "MOV_util.hh" #include "BKE_addon.h" +#include "BKE_appdir.hh" #include "BKE_asset.hh" #include "BKE_blender.hh" /* own include */ #include "BKE_blender_user_menu.hh" /* own include */ @@ -199,6 +200,7 @@ void BKE_blender_globals_init() BKE_blender_globals_main_replace(BKE_main_new()); STRNCPY(G.filepath_last_image, "//"); + G.filepath_last_blend[0] = '\0'; #ifndef WITH_PYTHON_SECURITY /* default */ G.f |= G_FLAG_SCRIPT_AUTOEXEC; @@ -241,6 +243,20 @@ Main *BKE_blender_globals_main_swap(Main *new_gmain) return old_gmain; } +void BKE_blender_globals_crash_path_get(char filepath[FILE_MAX]) +{ + /* Might be called after WM/Main exit, so needs to be careful about nullptr-checking before + * de-referencing. */ + + if (!(G_MAIN && G_MAIN->filepath[0])) { + BLI_path_join(filepath, FILE_MAX, BKE_tempdir_base(), "blender.crash.txt"); + } + else { + BLI_path_join(filepath, FILE_MAX, BKE_tempdir_base(), BLI_path_basename(G_MAIN->filepath)); + BLI_path_extension_replace(filepath, FILE_MAX, ".crash.txt"); + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/blenkernel/intern/blendfile.cc b/source/blender/blenkernel/intern/blendfile.cc index f7f7d4cc7a6..9df81b6d9f4 100644 --- a/source/blender/blenkernel/intern/blendfile.cc +++ b/source/blender/blenkernel/intern/blendfile.cc @@ -1202,6 +1202,9 @@ static void setup_app_data(bContext *C, STRNCPY(bmain->filepath, bfd->filepath); } + /* Set the loaded .blend file path for crash recovery. */ + STRNCPY(G.filepath_last_blend, bmain->filepath); + /* Base-flags, groups, make depsgraph, etc. */ /* first handle case if other windows have different scenes visible. */ if (mode == LOAD_UI) { diff --git a/source/blender/blenlib/BLI_system.h b/source/blender/blenlib/BLI_system.h index 5b9df49de67..f891698749c 100644 --- a/source/blender/blenlib/BLI_system.h +++ b/source/blender/blenlib/BLI_system.h @@ -43,7 +43,12 @@ int BLI_system_memory_max_in_megabytes_int(void); * \note Use `void *` for `exception` since we really do not want to drag Windows.h * in to get the proper `typedef`. */ -void BLI_windows_handle_exception(void *exception); +void BLI_windows_exception_capture(void *exception); +void BLI_windows_exception_show_dialog(const void *exception, + const char *filepath_crashlog, + const char *filepath_relaunch, + const char *gpu_name, + const char *build_version); #else # define BLI_SYSTEM_PID_H diff --git a/source/blender/blenlib/CMakeLists.txt b/source/blender/blenlib/CMakeLists.txt index ba2ca75ffd0..57dcb473f51 100644 --- a/source/blender/blenlib/CMakeLists.txt +++ b/source/blender/blenlib/CMakeLists.txt @@ -466,9 +466,11 @@ if(WIN32) add_definitions(-DWITH_BLENDER_THUMBNAILER) endif() list(APPEND INC + ../../../intern/uriconvert ../../../intern/utfconv ) list(APPEND LIB + bf_intern_uriconvert bf_intern_utfconv dxgi ) diff --git a/source/blender/blenlib/intern/system_win32.cc b/source/blender/blenlib/intern/system_win32.cc index a718a8db6d7..610475f0057 100644 --- a/source/blender/blenlib/intern/system_win32.cc +++ b/source/blender/blenlib/intern/system_win32.cc @@ -5,8 +5,10 @@ /** \file * \ingroup bli */ + #include -#include +#include +#include #include #include @@ -14,6 +16,9 @@ #include "MEM_guardedalloc.h" +#include "uri_convert.hh" +#include "utfconv.hh" + #include "BLI_string.h" #include "BLI_system.h" /* Own include. */ @@ -397,22 +402,276 @@ void BLI_system_backtrace_with_os_info(FILE *fp, const void *os_info) bli_windows_system_backtrace_modules(fp); } -void BLI_windows_handle_exception(void *exception) +static void bli_windows_exception_message_get(const EXCEPTION_POINTERS *exception, + char r_message[512]) { - const EXCEPTION_POINTERS *exception_info = static_cast(exception); - if (exception_info) { - fprintf(stderr, - "Error : %s\n", - bli_windows_get_exception_description(exception_info->ExceptionRecord->ExceptionCode)); - fflush(stderr); - - LPVOID address = exception_info->ExceptionRecord->ExceptionAddress; - fprintf(stderr, "Address : 0x%p\n", address); - - CHAR modulename[MAX_PATH]; - bli_windows_get_module_name(address, modulename, sizeof(modulename)); - fprintf(stderr, "Module : %s\n", modulename); - fprintf(stderr, "Thread : %.8x\n", GetCurrentThreadId()); + if (!exception) { + r_message[0] = '\0'; + return; } - fflush(stderr); + + const char *exception_name = bli_windows_get_exception_description( + exception->ExceptionRecord->ExceptionCode); + LPVOID address = exception->ExceptionRecord->ExceptionAddress; + CHAR modulename[MAX_PATH]; + bli_windows_get_module_name(address, modulename, sizeof(modulename)); + DWORD threadId = GetCurrentThreadId(); + + snprintf(r_message, + 512, + "Error : %s\n" + "Address : 0x%p\n" + "Module : %s\n" + "Thread : %.8x\n", + exception_name, + address, + modulename, + threadId); } + +/* -------------------------------------------------------------------- */ +/** \name bli_show_message_box + * \{ */ + +static std::string get_os_info() +{ + OSVERSIONINFOEX osvi; + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); + if (!GetVersionEx((OSVERSIONINFO *)&osvi)) { + return "Unknown System"; + } + + std::string version = std::to_string(osvi.dwMajorVersion) + "-" + + std::to_string(osvi.dwMajorVersion) + "." + + std::to_string(osvi.dwMinorVersion) + "." + + std::to_string(osvi.dwBuildNumber) + "-SP" + + std::to_string(osvi.wServicePackMajor); + + SYSTEM_INFO si; + GetSystemInfo(&si); + std::string architecture; + switch (si.wProcessorArchitecture) { + case PROCESSOR_ARCHITECTURE_AMD64: + architecture = "64 Bits"; + break; + case PROCESSOR_ARCHITECTURE_INTEL: + architecture = "32 Bits"; + break; + case PROCESSOR_ARCHITECTURE_ARM: + architecture = "ARM Architecture"; + break; + case PROCESSOR_ARCHITECTURE_ARM64: + architecture = "ARM64 Architecture"; + break; + case PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64: + architecture = "ARM32 on Windows 64-bit"; + break; + case PROCESSOR_ARCHITECTURE_IA32_ON_ARM64: + architecture = "IA32 on ARM64"; + break; + default: + architecture = "Unknown Architecture"; + } + + return "Windows-" + version + " " + architecture; +} + +/** + * Retrieve the path to "blender-launcher.exe" if it exists; otherwise, return the current + * executable path. + */ +static bool bli_executable_path_get(LPWSTR path, DWORD size) +{ + wchar_t executable_path[MAX_PATH]; + DWORD nSize = GetModuleFileNameW(nullptr, executable_path, MAX_PATH); + if (nSize == 0 || nSize == MAX_PATH) { + return false; + } + + if (size <= nSize) { + return false; + } + + /* Copy the path to the output buffer. */ + if (wcscpy_s(path, size, executable_path) != 0) { + return false; + } + + /* Replace the filename "blender.exe" with "blender-launcher.exe". */ + if (!PathRemoveFileSpecW(executable_path)) { + /* Failed to remove the file spec. Use the original path. */ + return true; + } + if (!PathAppendW(executable_path, L"blender-launcher.exe")) { + /* Failed to append the new filename. Use the original path. */ + return true; + } + + /* Check if "blender-launcher.exe" exists at this path. */ + DWORD attributes = GetFileAttributesW(executable_path); + if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + /* "blender-launcher.exe" does not exist. Use the original executable path. */ + return true; + } + + if (wcslen(executable_path) + 1 > size) { + /* The output buffer is not large enough for the new path. Use the original path. */ + return true; + } + + /* The file exists. Copy the path to the output buffer. */ + if (wcscpy_s(path, size, executable_path) != 0) { + /* Error: It's not supposed to happen. Return false since the buffer has been modified. */ + return false; + } + + return true; +} + +/* Wrapper function for url_encode. */ +static std::wstring url_encode_wstring(const std::string &str) +{ + size_t len = str.length(); + + /* Maximum encoded length is 3 times the original length +1 for null terminator. */ + size_t encoded_len_max = len * 3 + 1; + + char *encoded_str = new char[encoded_len_max]; + url_encode(str.c_str(), encoded_str, encoded_len_max); + + /* Convert the encoded char *to a std::wstring (assuming the encoded string is ASCII). */ + std::wstring result(encoded_str, encoded_str + strlen(encoded_str)); + + delete[] encoded_str; + + return result; +} + +/** + * Displays a crash report dialog with options to open the crash log, restart the application, and + * report a bug. This is based on the `showMessageBox` function in `GHOST_SystemWin32.cc`. + */ +static void bli_show_crash_report_dialog(const char *filepath_crashlog, + const char *filepath_relaunch, + const char *gpu_name, + const char *build_version) +{ + /* Redundant: InitCommonControls is already called during GHOST System initialization. */ + // InitCommonControls(); + + /* Convert file paths to UTF-16 to handle non-ASCII characters. */ + wchar_t *filepath_crashlog_utf16 = alloc_utf16_from_8(filepath_crashlog, 0); + wchar_t *filepath_relaunch_utf16 = filepath_relaunch[0] ? + alloc_utf16_from_8(filepath_relaunch, 0) : + nullptr; + + std::wstring full_message_16 = + L"A problem has caused the program to stop functioning correctly. If you know the steps to " + L"reproduce this issue, please submit a bug report.\n" + "\n" + L"The crash log can be found at:\n" + + std::wstring(filepath_crashlog_utf16); + + TASKDIALOGCONFIG config = {0}; + const TASKDIALOG_BUTTON buttons[] = {{IDRETRY, L"Restart"}, + {IDOK, L"Report a Bug"}, + {IDHELP, L"View Crash Log"}, + {IDCLOSE, L"Close"}}; + + config.cbSize = sizeof(config); + config.hwndParent = GetActiveWindow(); + config.hInstance = 0; + config.dwCommonButtons = 0; + config.pszMainIcon = TD_ERROR_ICON; + config.pszWindowTitle = L"Blender"; + config.pszMainInstruction = L"Blender has stopped working"; + config.pszContent = full_message_16.c_str(); + config.pButtons = buttons; + config.cButtons = ARRAY_SIZE(buttons); + + /* Data passed to the callback function for handling button events. */ + const struct Data { + const wchar_t *filepath_crashlog_utf16; + const wchar_t *filepath_relaunch_utf16; + const char *gpu_name; + const char *build_version; + } data = {filepath_crashlog_utf16, filepath_relaunch_utf16, gpu_name, build_version}; + config.lpCallbackData = reinterpret_cast(&data); + + /* Callback for handling button events. */ + config.pfCallback = [](HWND /*hwnd*/, + UINT uNotification, + WPARAM wParam, + LPARAM /*lParam*/, + LONG_PTR dwRefData) -> HRESULT { + const Data *data_ptr = reinterpret_cast(dwRefData); + if (uNotification != TDN_BUTTON_CLICKED) { + return S_OK; + } + int pnButton = static_cast(wParam); + switch (pnButton) { + case IDCLOSE: + return S_OK; + case IDRETRY: { + /* Relaunch the application. */ + wchar_t executable_path[MAX_PATH]; + if (bli_executable_path_get(executable_path, ARRAYSIZE(executable_path))) { + std::wstring parameters; + if (data_ptr->filepath_relaunch_utf16) { + /* Properly quote the argument to handle spaces and special characters. */ + parameters = L"\"" + std::wstring(data_ptr->filepath_relaunch_utf16) + L"\""; + } + else { + /* Proceeding without parameters. */ + parameters = L""; + } + ShellExecuteW( + nullptr, L"open", executable_path, parameters.c_str(), nullptr, SW_SHOWNORMAL); + } + return S_OK; + } + case IDHELP: + /* Open the crash log. */ + ShellExecuteW( + nullptr, L"open", data_ptr->filepath_crashlog_utf16, nullptr, nullptr, SW_SHOWNORMAL); + return S_FALSE; + case IDOK: { + /* Open the bug report form with pre-filled data. */ + /* clang-format off */ + std::wstring link = + L"https://redirect.blender.org/" + L"?type=bug_report" + L"&project=blender" + L"&os=" + url_encode_wstring(get_os_info()) + + L"&gpu=" + url_encode_wstring(data_ptr->gpu_name) + + L"&broken_version=" + url_encode_wstring(data_ptr->build_version); + /* clang-format on */ + ShellExecuteW(nullptr, L"open", link.c_str(), nullptr, nullptr, SW_SHOWNORMAL); + return S_FALSE; + } + default: + return S_FALSE; + } + }; + + TaskDialogIndirect(&config, nullptr, nullptr, nullptr); + free((void *)filepath_crashlog_utf16); + free((void *)filepath_relaunch_utf16); +} + +void BLI_windows_exception_show_dialog(const void *exception, + const char *filepath_crashlog, + const char *filepath_relaunch, + const char *gpu_name, + const char *build_version) +{ + char message[512]; + bli_windows_exception_message_get(static_cast(exception), message); + fprintf(stderr, message); + fflush(stderr); + + bli_show_crash_report_dialog(filepath_crashlog, filepath_relaunch, gpu_name, build_version); +} + +/** \} */ diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc index 0a600fcf7b4..1a83d7320ba 100644 --- a/source/blender/blenloader/intern/writefile.cc +++ b/source/blender/blenloader/intern/writefile.cc @@ -1771,7 +1771,10 @@ static bool BLO_write_file_impl(Main *mainvar, } write_file_main_validate_post(mainvar, reports); - + if (mainvar->is_global_main && !params->use_save_as_copy) { + /* It is used to reload Blender after a crash on Windows OS. */ + STRNCPY(G.filepath_last_blend, filepath); + } return true; } diff --git a/source/blender/blentranslation/msgfmt/CMakeLists.txt b/source/blender/blentranslation/msgfmt/CMakeLists.txt index 22b9e46740c..8300f1bfcf0 100644 --- a/source/blender/blentranslation/msgfmt/CMakeLists.txt +++ b/source/blender/blentranslation/msgfmt/CMakeLists.txt @@ -12,6 +12,11 @@ set(SRC msgfmt.cc ) +# Manifest required for selecting comctl32 v6 required by the crash popup. +if(WIN32 AND NOT WITH_WINDOWS_EXTERNAL_MANIFEST) + LIST(APPEND SRC "${CMAKE_BINARY_DIR}/tests.exe.manifest") +endif() + set(LIB PRIVATE PRIVATE bf::blenlib PRIVATE bf::intern::guardedalloc @@ -29,3 +34,10 @@ add_executable(msgfmt ${SRC}) setup_platform_linker_flags(msgfmt) blender_target_include_dirs(msgfmt ${INC}) target_link_libraries(msgfmt ${LIB}) + +# If using an external manifest, copy it to the output directory after building. +if(WITH_WINDOWS_EXTERNAL_MANIFEST) + add_custom_command(TARGET msgfmt POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_BINARY_DIR}/tests.exe.manifest $/$.manifest + ) +endif() diff --git a/source/creator/creator_signals.cc b/source/creator/creator_signals.cc index ede76679ad9..744caa098a2 100644 --- a/source/creator/creator_signals.cc +++ b/source/creator/creator_signals.cc @@ -8,6 +8,9 @@ #ifndef WITH_PYTHON_MODULE +# include +# include + # if defined(__linux__) && defined(__GNUC__) # ifndef _GNU_SOURCE # define _GNU_SOURCE @@ -23,23 +26,20 @@ # ifdef WIN32 # include # include -# endif -# include -# include -# include - -# ifdef WIN32 # include "BLI_winstuff.h" + +# include "GPU_platform.hh" # endif + # include "BLI_fileops.h" # include "BLI_path_utils.hh" # include "BLI_string.h" # include "BLI_system.h" -# include "BLI_utildefines.h" # include BLI_SYSTEM_PID_H -# include "BKE_appdir.hh" /* #BKE_tempdir_base. */ +# include "BKE_appdir.hh" /* #BKE_tempdir_session_purge. */ +# include "BKE_blender.hh" # include "BKE_blender_version.h" # include "BKE_global.hh" # include "BKE_main.hh" @@ -81,7 +81,7 @@ static void sig_handle_blender_esc(int sig) } } -static void sig_handle_crash_backtrace(int signum, const void *os_info) +static void crashlog_file_generate(const char *filepath, const void *os_info) { /* Might be called after WM/Main exit, so needs to be careful about nullptr-checking before * de-referencing. */ @@ -91,17 +91,6 @@ static void sig_handle_crash_backtrace(int signum, const void *os_info) FILE *fp; char header[512]; - char filepath[FILE_MAX]; - - if (!(G_MAIN && G_MAIN->filepath[0])) { - BLI_path_join(filepath, sizeof(filepath), BKE_tempdir_base(), "blender.crash.txt"); - } - else { - BLI_path_join( - filepath, sizeof(filepath), BKE_tempdir_base(), BLI_path_basename(G_MAIN->filepath)); - BLI_path_extension_replace(filepath, sizeof(filepath), ".crash.txt"); - } - printf("Writing: %s\n", filepath); fflush(stdout); @@ -140,7 +129,10 @@ static void sig_handle_crash_backtrace(int signum, const void *os_info) fclose(fp); } +} +static void sig_cleanup_and_terminate(int signum) +{ /* Delete content of temp directory. */ BKE_tempdir_session_purge(); @@ -155,7 +147,10 @@ static void sig_handle_crash_backtrace(int signum, const void *os_info) static void sig_handle_crash_fn(int signum) { - sig_handle_crash_backtrace(signum, nullptr); + char filepath_crashlog[FILE_MAX]; + BKE_blender_globals_crash_path_get(filepath_crashlog); + crashlog_file_generate(filepath_crashlog, nullptr); + sig_cleanup_and_terminate(signum); } # ifdef WIN32 @@ -176,8 +171,26 @@ extern LONG WINAPI windows_exception_handler(EXCEPTION_POINTERS *ExceptionInfo) } } else { - BLI_windows_handle_exception(ExceptionInfo); - sig_handle_crash_backtrace(SIGSEGV, ExceptionInfo); + std::string version; +# ifndef BUILD_DATE + const char *build_hash = G_MAIN ? G_MAIN->build_hash : "unknown"; + version = std::string("version: ") + BKE_blender_version_string() + ", hash: `" + build_hash + + "`"; +# else + version = std::string("version: ") + BKE_blender_version_string() + + ", Commit date: " + build_commit_date + " " + build_commit_time + ", hash: `" + + build_hash + "`"; +# endif + + char filepath_crashlog[FILE_MAX]; + BKE_blender_globals_crash_path_get(filepath_crashlog); + crashlog_file_generate(filepath_crashlog, ExceptionInfo); + BLI_windows_exception_show_dialog(ExceptionInfo, + filepath_crashlog, + G.filepath_last_blend, + GPU_platform_gpu_name(), + version.c_str()); + sig_cleanup_and_terminate(SIGSEGV); } return EXCEPTION_EXECUTE_HANDLER;