Blender V5.0
system_win32.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include <Windows.h>
10#include <commctrl.h>
11#include <sstream>
12
13#include <dbghelp.h>
14#include <shlwapi.h>
15#include <tlhelp32.h>
16
17#include "MEM_guardedalloc.h"
18
19#include "uri_convert.hh"
20#include "utfconv.hh"
21
22#include "BLI_string.h"
23
24#include "BLI_system.h" /* Own include. */
25
26static const char *bli_windows_get_exception_description(const DWORD exceptioncode)
27{
28 switch (exceptioncode) {
29 case EXCEPTION_ACCESS_VIOLATION:
30 return "EXCEPTION_ACCESS_VIOLATION";
31 case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
32 return "EXCEPTION_ARRAY_BOUNDS_EXCEEDED";
33 case EXCEPTION_BREAKPOINT:
34 return "EXCEPTION_BREAKPOINT";
35 case EXCEPTION_DATATYPE_MISALIGNMENT:
36 return "EXCEPTION_DATATYPE_MISALIGNMENT";
37 case EXCEPTION_FLT_DENORMAL_OPERAND:
38 return "EXCEPTION_FLT_DENORMAL_OPERAND";
39 case EXCEPTION_FLT_DIVIDE_BY_ZERO:
40 return "EXCEPTION_FLT_DIVIDE_BY_ZERO";
41 case EXCEPTION_FLT_INEXACT_RESULT:
42 return "EXCEPTION_FLT_INEXACT_RESULT";
43 case EXCEPTION_FLT_INVALID_OPERATION:
44 return "EXCEPTION_FLT_INVALID_OPERATION";
45 case EXCEPTION_FLT_OVERFLOW:
46 return "EXCEPTION_FLT_OVERFLOW";
47 case EXCEPTION_FLT_STACK_CHECK:
48 return "EXCEPTION_FLT_STACK_CHECK";
49 case EXCEPTION_FLT_UNDERFLOW:
50 return "EXCEPTION_FLT_UNDERFLOW";
51 case EXCEPTION_ILLEGAL_INSTRUCTION:
52 return "EXCEPTION_ILLEGAL_INSTRUCTION";
53 case EXCEPTION_IN_PAGE_ERROR:
54 return "EXCEPTION_IN_PAGE_ERROR";
55 case EXCEPTION_INT_DIVIDE_BY_ZERO:
56 return "EXCEPTION_INT_DIVIDE_BY_ZERO";
57 case EXCEPTION_INT_OVERFLOW:
58 return "EXCEPTION_INT_OVERFLOW";
59 case EXCEPTION_INVALID_DISPOSITION:
60 return "EXCEPTION_INVALID_DISPOSITION";
61 case EXCEPTION_NONCONTINUABLE_EXCEPTION:
62 return "EXCEPTION_NONCONTINUABLE_EXCEPTION";
63 case EXCEPTION_PRIV_INSTRUCTION:
64 return "EXCEPTION_PRIV_INSTRUCTION";
65 case EXCEPTION_SINGLE_STEP:
66 return "EXCEPTION_SINGLE_STEP";
67 case EXCEPTION_STACK_OVERFLOW:
68 return "EXCEPTION_STACK_OVERFLOW";
69 /* This one does not have a known define, but the MSVC runtime raises this for uncaught C++
70 * exceptions. See https://devblogs.microsoft.com/oldnewthing/20100730-00/?p=13273 for
71 * details. */
72 case 0xe06d7363:
73 return "Microsoft C++ Exception";
74 default:
75 return "UNKNOWN EXCEPTION";
76 }
77}
78
79static void bli_windows_get_module_name(LPVOID address, PCHAR buffer, size_t size)
80{
81 HMODULE mod;
82 buffer[0] = 0;
83 if (GetModuleHandleEx(
84 GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, static_cast<LPCSTR>(address), &mod))
85 {
86 if (GetModuleFileName(mod, buffer, size)) {
87 PathStripPath(buffer);
88 }
89 }
90}
91
92static void bli_windows_get_module_version(const char *file, char *buffer, size_t buffersize)
93{
94 buffer[0] = 0;
95 DWORD verHandle = 0;
96 UINT size = 0;
97 LPBYTE lpBuffer = nullptr;
98 DWORD verSize = GetFileVersionInfoSize(file, &verHandle);
99 if (verSize != 0) {
100 LPSTR verData = (LPSTR)MEM_callocN(verSize, "crash module version");
101
102 if (GetFileVersionInfo(file, verHandle, verSize, verData)) {
103 if (VerQueryValue(verData, "\\", (VOID FAR * FAR *)&lpBuffer, &size)) {
104 if (size) {
105 VS_FIXEDFILEINFO *verInfo = (VS_FIXEDFILEINFO *)lpBuffer;
106 /* Magic value from
107 * https://docs.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo
108 */
109 if (verInfo->dwSignature == 0xfeef04bd) {
110 BLI_snprintf(buffer,
111 buffersize,
112 "%d.%d.%d.%d",
113 (verInfo->dwFileVersionMS >> 16) & 0xffff,
114 (verInfo->dwFileVersionMS >> 0) & 0xffff,
115 (verInfo->dwFileVersionLS >> 16) & 0xffff,
116 (verInfo->dwFileVersionLS >> 0) & 0xffff);
117 }
118 }
119 }
120 }
121 MEM_freeN(verData);
122 }
123}
124
125static void bli_windows_system_backtrace_exception_record(FILE *fp, PEXCEPTION_RECORD record)
126{
127 char module[MAX_PATH];
128 fprintf(fp, "Exception Record:\n\n");
129 fprintf(fp,
130 "ExceptionCode : %s (0x%.8x)\n",
131 bli_windows_get_exception_description(record->ExceptionCode),
132 record->ExceptionCode);
133 fprintf(fp, "Exception Address : 0x%p\n", record->ExceptionAddress);
134 bli_windows_get_module_name(record->ExceptionAddress, module, sizeof(module));
135 fprintf(fp, "Exception Module : %s\n", module);
136 fprintf(fp, "Exception Flags : 0x%.8x\n", record->ExceptionFlags);
137 fprintf(fp, "Exception Parameters : 0x%x\n", record->NumberParameters);
138
139 /* Special handling for access violations to make them a little easier to read. */
140 if (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION && record->NumberParameters == 2) {
141 const char *action;
142 switch (record->ExceptionInformation[0]) {
143 case 0:
144 action = "read";
145 break;
146 case 1:
147 action = "write";
148 break;
149 case 8:
150 action = "execute";
151 break;
152 default:
153 action = "unknown";
154 break;
155 }
156 fprintf(fp,
157 "\tParameters[0] (action) : 0x%p (%s)\n",
158 (LPVOID *)record->ExceptionInformation[0],
159 action);
160 fprintf(fp, "\tParameters[1] (address) : 0x%p\n", (LPVOID *)record->ExceptionInformation[1]);
161 }
162 else {
163 for (DWORD idx = 0; idx < record->NumberParameters; idx++) {
164 fprintf(fp, "\tParameters[%d] : 0x%p\n", idx, (LPVOID *)record->ExceptionInformation[idx]);
165 }
166 }
167 if (record->ExceptionRecord) {
168 fprintf(fp, "Nested ");
169 bli_windows_system_backtrace_exception_record(fp, record->ExceptionRecord);
170 }
171 fprintf(fp, "\n\n");
172}
173
174static bool BLI_windows_system_backtrace_run_trace(FILE *fp, HANDLE hThread, PCONTEXT context)
175{
176 const int max_symbol_length = 100;
177
178 bool result = true;
179
180 PSYMBOL_INFO symbolinfo = static_cast<PSYMBOL_INFO>(
181 MEM_callocN(sizeof(SYMBOL_INFO) + max_symbol_length * sizeof(char), "crash Symbol table"));
182 symbolinfo->MaxNameLen = max_symbol_length - 1;
183 symbolinfo->SizeOfStruct = sizeof(SYMBOL_INFO);
184
185 STACKFRAME frame = {0};
186 DWORD machineType = 0;
187#if defined(_M_AMD64)
188 frame.AddrPC.Offset = context->Rip;
189 frame.AddrPC.Mode = AddrModeFlat;
190 frame.AddrFrame.Offset = context->Rsp;
191 frame.AddrFrame.Mode = AddrModeFlat;
192 frame.AddrStack.Offset = context->Rsp;
193 frame.AddrStack.Mode = AddrModeFlat;
194 machineType = IMAGE_FILE_MACHINE_AMD64;
195#elif defined(_M_ARM64)
196 frame.AddrPC.Offset = context->Pc;
197 frame.AddrPC.Mode = AddrModeFlat;
198 frame.AddrFrame.Offset = context->Fp;
199 frame.AddrFrame.Mode = AddrModeFlat;
200 frame.AddrStack.Offset = context->Sp;
201 frame.AddrStack.Mode = AddrModeFlat;
202 machineType = IMAGE_FILE_MACHINE_ARM64;
203#endif
204
205 while (true) {
206 if (StackWalk64(machineType,
207 GetCurrentProcess(),
208 hThread,
209 &frame,
210 context,
211 nullptr,
212 SymFunctionTableAccess64,
213 SymGetModuleBase64,
214 0))
215 {
216 if (frame.AddrPC.Offset) {
217 char module[MAX_PATH];
218
219 bli_windows_get_module_name((LPVOID)frame.AddrPC.Offset, module, sizeof(module));
220
221 if (SymFromAddr(GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), 0, symbolinfo)) {
222 fprintf(fp, "%-20s:0x%p %s", module, (LPVOID)symbolinfo->Address, symbolinfo->Name);
223 IMAGEHLP_LINE lineinfo;
224 lineinfo.SizeOfStruct = sizeof(lineinfo);
225 DWORD displacement = 0;
226 if (SymGetLineFromAddr(
227 GetCurrentProcess(), (DWORD64)(frame.AddrPC.Offset), &displacement, &lineinfo))
228 {
229 fprintf(fp, " %s:%d", lineinfo.FileName, lineinfo.LineNumber);
230 }
231 fprintf(fp, "\n");
232 }
233 else {
234 fprintf(fp,
235 "%-20s:0x%p %s\n",
236 module,
237 (LPVOID)frame.AddrPC.Offset,
238 "Symbols not available");
239 result = false;
240 break;
241 }
242 }
243 else {
244 break;
245 }
246 }
247 else {
248 break;
249 }
250 }
251 MEM_freeN(symbolinfo);
252 fprintf(fp, "\n\n");
253 return result;
254}
255
256static bool bli_windows_system_backtrace_stack_thread(FILE *fp, HANDLE hThread)
257{
258 CONTEXT context = {0};
259 context.ContextFlags = CONTEXT_ALL;
260 /* GetThreadContext requires the thread to be in a suspended state, which is problematic for the
261 * currently running thread, RtlCaptureContext is used as an alternative to sidestep this */
262 if (hThread != GetCurrentThread()) {
263 SuspendThread(hThread);
264 bool success = GetThreadContext(hThread, &context);
265 ResumeThread(hThread);
266 if (!success) {
267 fprintf(fp, "Cannot get thread context : 0x0%.8x\n", GetLastError());
268 return false;
269 }
270 }
271 else {
272 RtlCaptureContext(&context);
273 }
274 return BLI_windows_system_backtrace_run_trace(fp, hThread, &context);
275}
276
278{
279 fprintf(fp, "Loaded Modules :\n");
280 HANDLE hModuleSnap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, 0);
281 if (hModuleSnap == INVALID_HANDLE_VALUE) {
282 return;
283 }
284
285 MODULEENTRY32 me32;
286 me32.dwSize = sizeof(MODULEENTRY32);
287
288 if (!Module32First(hModuleSnap, &me32)) {
289 CloseHandle(hModuleSnap); /* Must clean up the snapshot object! */
290 fprintf(fp, " Error getting module list.\n");
291 return;
292 }
293
294 do {
295 if (me32.th32ProcessID == GetCurrentProcessId()) {
296 char version[MAX_PATH];
297 bli_windows_get_module_version(me32.szExePath, version, sizeof(version));
298
299 IMAGEHLP_MODULE64 m64;
300 m64.SizeOfStruct = sizeof(m64);
301 if (SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)me32.modBaseAddr, &m64)) {
302 fprintf(fp,
303 "0x%p %-20s %s %s %s\n",
304 me32.modBaseAddr,
305 version,
306 me32.szModule,
307 m64.LoadedPdbName,
308 m64.PdbUnmatched ? "[unmatched]" : "");
309 }
310 else {
311 fprintf(fp, "0x%p %-20s %s\n", me32.modBaseAddr, version, me32.szModule);
312 }
313 }
314 } while (Module32Next(hModuleSnap, &me32));
315}
316
318{
319 fprintf(fp, "Threads:\n");
320 HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
321 THREADENTRY32 te32;
322
323 hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
324 if (hThreadSnap == INVALID_HANDLE_VALUE) {
325 fprintf(fp, "Unable to retrieve threads list.\n");
326 return;
327 }
328
329 te32.dwSize = sizeof(THREADENTRY32);
330
331 if (!Thread32First(hThreadSnap, &te32)) {
332 CloseHandle(hThreadSnap);
333 return;
334 }
335 do {
336 if (te32.th32OwnerProcessID == GetCurrentProcessId()) {
337 if (GetCurrentThreadId() != te32.th32ThreadID) {
338 fprintf(fp, "Thread : %.8x\n", te32.th32ThreadID);
339 HANDLE ht = OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID);
341 CloseHandle(ht);
342 }
343 }
344 } while (Thread32Next(hThreadSnap, &te32));
345 CloseHandle(hThreadSnap);
346}
347
348static bool bli_windows_system_backtrace_stack(FILE *fp, const EXCEPTION_POINTERS *exception_info)
349{
350 fprintf(fp, "Stack trace:\n");
351 /* If we are handling an exception use the context record from that. */
352 if (exception_info && exception_info->ExceptionRecord->ExceptionAddress) {
353 /* The back trace code will write to the context record, to protect the original record from
354 * modifications give the backtrace a copy to work on. */
355 CONTEXT TempContext = *exception_info->ContextRecord;
356 return BLI_windows_system_backtrace_run_trace(fp, GetCurrentThread(), &TempContext);
357 }
358 else {
359 /* If there is no current exception or the address is not set, walk the current stack. */
360 return bli_windows_system_backtrace_stack_thread(fp, GetCurrentThread());
361 }
362}
363
365{
366 IMAGEHLP_MODULE64 m64;
367 m64.SizeOfStruct = sizeof(m64);
368 if (SymGetModuleInfo64(GetCurrentProcess(), (DWORD64)GetModuleHandle(nullptr), &m64)) {
369 return m64.GlobalSymbols;
370 }
371 return false;
372}
373
374static void bli_load_symbols()
375{
376 /* If this is a developer station and the private pdb is already loaded leave it be. */
378 return;
379 }
380
381 char pdb_file[MAX_PATH] = {0};
382
383 /* get the currently executing image */
384 if (GetModuleFileNameA(nullptr, pdb_file, sizeof(pdb_file))) {
385 /* remove the filename */
386 PathRemoveFileSpecA(pdb_file);
387 /* append blender.pdb */
388 PathAppendA(pdb_file, "blender.pdb");
389 if (PathFileExistsA(pdb_file)) {
390 HMODULE mod = GetModuleHandle(nullptr);
391 if (mod) {
392 WIN32_FILE_ATTRIBUTE_DATA file_data;
393 if (GetFileAttributesExA(pdb_file, GetFileExInfoStandard, &file_data)) {
394 /* SymInitialize will try to load symbols on its own, so we first must unload whatever it
395 * did trying to help */
396 SymUnloadModule64(GetCurrentProcess(), (DWORD64)mod);
397
398 DWORD64 module_base = SymLoadModule(GetCurrentProcess(),
399 nullptr,
400 pdb_file,
401 nullptr,
402 (DWORD64)mod,
403 (DWORD)file_data.nFileSizeLow);
404 if (module_base == 0) {
405 fprintf(stderr,
406 "Error loading symbols %s\n\terror:0x%.8x\n\tsize = %d\n\tbase=0x%p\n",
407 pdb_file,
408 GetLastError(),
409 file_data.nFileSizeLow,
410 (LPVOID)mod);
411 }
412 }
413 }
414 }
415 }
416}
417
418void BLI_system_backtrace_with_os_info(FILE *fp, const void *os_info)
419{
420 const EXCEPTION_POINTERS *exception_info = static_cast<const EXCEPTION_POINTERS *>(os_info);
421 SymInitialize(GetCurrentProcess(), nullptr, TRUE);
423 if (exception_info) {
424 bli_windows_system_backtrace_exception_record(fp, exception_info->ExceptionRecord);
425 }
426 if (bli_windows_system_backtrace_stack(fp, exception_info)) {
427 /* When the blender symbols are missing the stack traces will be unreliable
428 * so only run if the previous step completed successfully. */
430 }
432}
433
434void BLI_windows_exception_print_message(const void *os_info)
435{
436 if (!os_info) {
437 return;
438 }
439
440 const EXCEPTION_POINTERS *exception = static_cast<const EXCEPTION_POINTERS *>(os_info);
441 const char *exception_name = bli_windows_get_exception_description(
442 exception->ExceptionRecord->ExceptionCode);
443 LPVOID address = exception->ExceptionRecord->ExceptionAddress;
444 CHAR modulename[MAX_PATH];
445 bli_windows_get_module_name(address, modulename, sizeof(modulename));
446 DWORD threadId = GetCurrentThreadId();
447
448 char message[512];
449 BLI_snprintf(message,
450 512,
451 "Error : %s\n"
452 "Address : 0x%p\n"
453 "Module : %s\n"
454 "Thread : %.8x\n",
455 exception_name,
456 address,
457 modulename,
458 threadId);
459
460 fprintf(stderr, message);
461 fflush(stderr);
462}
463
464/* -------------------------------------------------------------------- */
467
468static std::string get_os_info()
469{
470 OSVERSIONINFOEX osvi;
471 ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
472 osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
473 if (!GetVersionEx((OSVERSIONINFO *)&osvi)) {
474 return "Unknown System";
475 }
476
477 std::string version = std::to_string(osvi.dwMajorVersion) + "-" +
478 std::to_string(osvi.dwMajorVersion) + "." +
479 std::to_string(osvi.dwMinorVersion) + "." +
480 std::to_string(osvi.dwBuildNumber) + "-SP" +
481 std::to_string(osvi.wServicePackMajor);
482
483 SYSTEM_INFO si;
484 GetSystemInfo(&si);
485 std::string architecture;
486 switch (si.wProcessorArchitecture) {
487 case PROCESSOR_ARCHITECTURE_AMD64:
488 architecture = "64 Bits";
489 break;
490 case PROCESSOR_ARCHITECTURE_INTEL:
491 architecture = "32 Bits";
492 break;
493 case PROCESSOR_ARCHITECTURE_ARM:
494 architecture = "ARM Architecture";
495 break;
496 case PROCESSOR_ARCHITECTURE_ARM64:
497 architecture = "ARM64 Architecture";
498 break;
499 case PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64:
500 architecture = "ARM32 on Windows 64-bit";
501 break;
502 case PROCESSOR_ARCHITECTURE_IA32_ON_ARM64:
503 architecture = "IA32 on ARM64";
504 break;
505 default:
506 architecture = "Unknown Architecture";
507 }
508
509 return "Windows-" + version + " " + architecture;
510}
511
516static bool bli_executable_path_get(LPWSTR path, DWORD size)
517{
518 wchar_t executable_path[MAX_PATH];
519 DWORD nSize = GetModuleFileNameW(nullptr, executable_path, MAX_PATH);
520 if (nSize == 0 || nSize == MAX_PATH) {
521 return false;
522 }
523
524 if (size <= nSize) {
525 return false;
526 }
527
528 /* Copy the path to the output buffer. */
529 if (wcscpy_s(path, size, executable_path) != 0) {
530 return false;
531 }
532
533 /* Replace the filename "blender.exe" with "blender-launcher.exe". */
534 if (!PathRemoveFileSpecW(executable_path)) {
535 /* Failed to remove the file spec. Use the original path. */
536 return true;
537 }
538 if (!PathAppendW(executable_path, L"blender-launcher.exe")) {
539 /* Failed to append the new filename. Use the original path. */
540 return true;
541 }
542
543 /* Check if "blender-launcher.exe" exists at this path. */
544 DWORD attributes = GetFileAttributesW(executable_path);
545 if (attributes == INVALID_FILE_ATTRIBUTES || (attributes & FILE_ATTRIBUTE_DIRECTORY)) {
546 /* "blender-launcher.exe" does not exist. Use the original executable path. */
547 return true;
548 }
549
550 if (wcslen(executable_path) + 1 > size) {
551 /* The output buffer is not large enough for the new path. Use the original path. */
552 return true;
553 }
554
555 /* The file exists. Copy the path to the output buffer. */
556 if (wcscpy_s(path, size, executable_path) != 0) {
557 /* Error: It's not supposed to happen. Return false since the buffer has been modified. */
558 return false;
559 }
560
561 return true;
562}
563
564/* Wrapper function for url_encode. */
565static std::wstring url_encode_wstring(const std::string &str)
566{
567 size_t len = str.length();
568
569 /* Maximum encoded length is 3 times the original length +1 for null terminator. */
570 size_t encoded_len_max = len * 3 + 1;
571
572 char *encoded_str = new char[encoded_len_max];
573 url_encode(str.c_str(), encoded_str, encoded_len_max);
574
575 /* Convert the encoded char *to a std::wstring (assuming the encoded string is ASCII). */
576 std::wstring result(encoded_str, encoded_str + strlen(encoded_str));
577
578 delete[] encoded_str;
579
580 return result;
581}
582
583void BLI_windows_exception_show_dialog(const char *filepath_crashlog,
584 const char *filepath_relaunch,
585 const char *gpu_name,
586 const char *build_version)
587{
588 /* Redundant: #InitCommonControls is already called during GHOST System initialization. */
589 // InitCommonControls();
590
591 /* Convert file paths to UTF16 to handle non-ASCII characters. */
592 wchar_t *filepath_crashlog_utf16 = alloc_utf16_from_8(filepath_crashlog, 0);
593 wchar_t *filepath_relaunch_utf16 = filepath_relaunch[0] ?
594 alloc_utf16_from_8(filepath_relaunch, 0) :
595 nullptr;
596
597 std::wstring full_message_16 =
598 L"A problem has caused the program to stop functioning correctly. If you know the steps to "
599 L"reproduce this issue, please submit a bug report.\n"
600 "\n"
601 L"The crash log can be found at:\n" +
602 std::wstring(filepath_crashlog_utf16);
603
604 TASKDIALOGCONFIG config = {0};
605 const TASKDIALOG_BUTTON buttons[] = {{IDRETRY, L"Restart"},
606#if 0
607 /* This lead to a large influx of low quality reports on the tracker,
608 * and has been disabled for that reason, we can re-enable this when
609 * a better workflow has been established. */
610 {IDOK, L"Report a Bug"},
611#endif
612 {IDHELP, L"View Crash Log"},
613 {IDCLOSE, L"Close"}};
614
615 config.cbSize = sizeof(config);
616 config.hwndParent = GetActiveWindow();
617 config.hInstance = 0;
618 config.dwCommonButtons = 0;
619 config.pszMainIcon = TD_ERROR_ICON;
620 config.pszWindowTitle = L"Blender";
621 config.pszMainInstruction = L"Blender has stopped working";
622 config.pszContent = full_message_16.c_str();
623 config.pButtons = buttons;
624 config.cButtons = ARRAY_SIZE(buttons);
625
626 /* Data passed to the callback function for handling button events. */
627 const struct Data {
628 const wchar_t *filepath_crashlog_utf16;
629 const wchar_t *filepath_relaunch_utf16;
630 const char *gpu_name;
631 const char *build_version;
632 } data = {filepath_crashlog_utf16, filepath_relaunch_utf16, gpu_name, build_version};
633 config.lpCallbackData = reinterpret_cast<LONG_PTR>(&data);
634
635 /* Callback for handling button events. */
636 config.pfCallback = [](HWND /*hwnd*/,
637 UINT uNotification,
638 WPARAM wParam,
639 LPARAM /*lParam*/,
640 LONG_PTR dwRefData) -> HRESULT {
641 const Data *data_ptr = reinterpret_cast<const Data *>(dwRefData);
642 if (uNotification != TDN_BUTTON_CLICKED) {
643 return S_OK;
644 }
645 int pnButton = static_cast<int>(wParam);
646 switch (pnButton) {
647 case IDCLOSE:
648 return S_OK;
649 case IDRETRY: {
650 /* Relaunch the application. */
651 wchar_t executable_path[MAX_PATH];
652 if (bli_executable_path_get(executable_path, ARRAYSIZE(executable_path))) {
653 std::wstring parameters;
654 if (data_ptr->filepath_relaunch_utf16) {
655 /* Properly quote the argument to handle spaces and special characters. */
656 parameters = L"\"" + std::wstring(data_ptr->filepath_relaunch_utf16) + L"\"";
657 }
658 else {
659 /* Proceeding without parameters. */
660 parameters = L"";
661 }
662 ShellExecuteW(
663 nullptr, L"open", executable_path, parameters.c_str(), nullptr, SW_SHOWNORMAL);
664 }
665 return S_OK;
666 }
667 case IDHELP:
668 /* Open the crash log. */
669 ShellExecuteW(
670 nullptr, L"open", data_ptr->filepath_crashlog_utf16, nullptr, nullptr, SW_SHOWNORMAL);
671 return S_FALSE;
672 case IDOK: {
673 /* Open the bug report form with pre-filled data. */
674 /* clang-format off */
675 std::wstring link =
676 L"https://redirect.blender.org/"
677 L"?type=bug_report"
678 L"&project=blender"
679 L"&os=" + url_encode_wstring(get_os_info()) +
680 L"&gpu=" + url_encode_wstring(data_ptr->gpu_name) +
681 L"&broken_version=" + url_encode_wstring(data_ptr->build_version);
682 /* clang-format on */
683 ShellExecuteW(nullptr, L"open", link.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
684 return S_FALSE;
685 }
686 default:
687 return S_FALSE;
688 }
689 };
690
691 TaskDialogIndirect(&config, nullptr, nullptr, nullptr);
692 free((void *)filepath_crashlog_utf16);
693 free((void *)filepath_relaunch_utf16);
694}
695
void BLI_kdtree_nd_ free(KDTree *tree)
size_t BLI_snprintf(char *__restrict dst, size_t dst_maxncpy, const char *__restrict format,...) ATTR_NONNULL(1
#define ARRAY_SIZE(arr)
#define FALSE
Read Guarded memory(de)allocation.
BMesh const char void * data
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
#define str(s)
VecBase< float, D > constexpr mod(VecOp< float, D >, VecOp< float, D >) RET
void * MEM_callocN(size_t len, const char *str)
Definition mallocn.cc:118
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
#define L
static struct PyModuleDef module
Definition python.cpp:796
static void bli_windows_get_module_version(const char *file, char *buffer, size_t buffersize)
static bool bli_windows_system_backtrace_stack_thread(FILE *fp, HANDLE hThread)
static std::wstring url_encode_wstring(const std::string &str)
static void bli_windows_get_module_name(LPVOID address, PCHAR buffer, size_t size)
void BLI_windows_exception_print_message(const void *os_info)
static void bli_windows_system_backtrace_modules(FILE *fp)
static void bli_windows_system_backtrace_exception_record(FILE *fp, PEXCEPTION_RECORD record)
static bool bli_private_symbols_loaded()
static std::string get_os_info()
static bool bli_windows_system_backtrace_stack(FILE *fp, const EXCEPTION_POINTERS *exception_info)
void BLI_windows_exception_show_dialog(const char *filepath_crashlog, const char *filepath_relaunch, const char *gpu_name, const char *build_version)
static void bli_windows_system_backtrace_threads(FILE *fp)
static void bli_load_symbols()
void BLI_system_backtrace_with_os_info(FILE *fp, const void *os_info)
static bool BLI_windows_system_backtrace_run_trace(FILE *fp, HANDLE hThread, PCONTEXT context)
static const char * bli_windows_get_exception_description(const DWORD exceptioncode)
static bool bli_executable_path_get(LPWSTR path, DWORD size)
bool url_encode(const char *str, char *dst, size_t dst_size)
Encodes a string into URL format by converting special characters into percent-encoded sequences.
wchar_t * alloc_utf16_from_8(const char *in8, size_t add)
Definition utfconv.cc:294
uint len