int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nShowCmd ) { LPWSTR *argv; int argc; argv = CommandLineToArgvW(GetCommandLine(), &argc); WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = L"renderdoccmd"; wc.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON)); if(!RegisterClassEx(&wc)) { return 1; } CrashGenerationServer *crashServer = NULL; if(argc == 2 && !_wcsicmp(argv[1], L"crashhandle")) { wchar_t tempPath[MAX_PATH] = {0}; GetTempPathW(MAX_PATH-1, tempPath); Sleep(100); wstring dumpFolder = tempPath; dumpFolder += L"RenderDocDumps"; CreateDirectoryW(dumpFolder.c_str(), NULL); crashServer = new CrashGenerationServer(L"\\\\.\\pipe\\RenderDocBreakpadServer", NULL, NULL, NULL, OnClientCrashed, NULL, OnClientExited, NULL, NULL, NULL, true, &dumpFolder); if (!crashServer->Start()) { delete crashServer; crashServer = NULL; return 1; } CrashHandlerInst = hInstance; CrashHandlerWnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdoccmd", L"renderdoccmd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, NULL, NULL, hInstance, NULL); HANDLE hIcon = LoadImage(CrashHandlerInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0); if(hIcon) { SendMessage(CrashHandlerWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); SendMessage(CrashHandlerWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); } ShowWindow(CrashHandlerWnd, SW_HIDE); HANDLE readyEvent = CreateEventA(NULL, TRUE, FALSE, "RENDERDOC_CRASHHANDLE"); if(readyEvent != NULL) { SetEvent(readyEvent); CloseHandle(readyEvent); } MSG msg; ZeroMemory(&msg, sizeof(msg)); while(!exitServer) { // Check to see if any messages are waiting in the queue while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Translate the message and dispatch it to WindowProc() TranslateMessage(&msg); DispatchMessage(&msg); } // If the message is WM_QUIT, exit the while loop if(msg.message == WM_QUIT) break; Sleep(100); } delete crashServer; crashServer = NULL; if(!dump.empty()) { logpath = L""; string report = ""; for(size_t i=0; i < customInfo.size(); i++) { wstring name = customInfo[i].name; wstring val = customInfo[i].value; if(name == L"logpath") { logpath = val; } else if(name == L"ptime") { // breakpad uptime, ignore. } else { report += string(name.begin(), name.end()) + ": " + string(val.begin(), val.end()) + "\n"; } } DialogBox(CrashHandlerInst, MAKEINTRESOURCE(IDD_CRASH_HANDLER), CrashHandlerWnd, (DLGPROC)CrashHandlerProc); report += "\n\nRepro steps/Notes:\n\n" + reproSteps; { FILE *f = NULL; _wfopen_s(&f, logpath.c_str(), L"r"); if(f) { fseek(f, 0, SEEK_END); long filesize = ftell(f); fseek(f, 0, SEEK_SET); if(filesize > 10) { char *error_log = new char[filesize+1]; memset(error_log, 0, filesize+1); fread(error_log, 1, filesize, f); char *managed_callstack = strstr(error_log, "--- Begin C# Exception Data ---"); if(managed_callstack) { report += managed_callstack; report += "\n\n"; } delete[] error_log; } fclose(f); } } if(uploadReport) { mz_zip_archive zip; ZeroMemory(&zip, sizeof(zip)); wstring destzip = dumpFolder + L"\\report.zip"; DeleteFileW(destzip.c_str()); mz_zip_writer_init_wfile(&zip, destzip.c_str(), 0); mz_zip_writer_add_mem(&zip, "report.txt", report.c_str(), report.length(), MZ_BEST_COMPRESSION); if(uploadDump && !dump.empty()) mz_zip_writer_add_wfile(&zip, "minidump.dmp", dump.c_str(), NULL, 0, MZ_BEST_COMPRESSION); if(uploadLog && !logpath.empty()) mz_zip_writer_add_wfile(&zip, "error.log", logpath.c_str(), NULL, 0, MZ_BEST_COMPRESSION); mz_zip_writer_finalize_archive(&zip); mz_zip_writer_end(&zip); int timeout = 10000; wstring body = L""; int code = 0; std::map<wstring, wstring> params; google_breakpad::HTTPUpload::SendRequest(L"http://renderdoc.org/bugsubmit", params, dumpFolder + L"\\report.zip", L"report", &timeout, &body, &code); DeleteFileW(destzip.c_str()); } } if(!dump.empty()) DeleteFileW(dump.c_str()); if(!logpath.empty()) DeleteFileW(logpath.c_str()); return 0; } HMODULE renderdoc = LoadLibrary(_T("renderdoc.dll")); if(!renderdoc) { OutputDebugString(_T("Couldn't load library!")); return 1; } CaptureOptions opts; opts.AllowFullscreen = false; opts.AllowVSync = false; opts.DelayForDebugger = 5; opts.HookIntoChildren = true; if(argc == 2) { // if we were given an exe, inject into it if(wcsstr(argv[1], L".exe") != NULL) { uint32_t ident = RENDERDOC_ExecuteAndInject(argv[1], NULL, NULL, NULL, &opts, false); if(ident == 0) printf("Failed to create & inject\n"); else printf("Created & injected as %d\n", ident); return ident; } // if we were given a logfile, load it and continually replay it. else if(wcsstr(argv[1], L".rdc") != NULL) { float progress = 0.0f; ReplayRenderer *renderer = NULL; auto status = RENDERDOC_CreateReplayRenderer(argv[1], &progress, &renderer); if(renderer && status == eReplayCreate_Success) DisplayRendererPreview(renderer, hInstance); delete renderer; return 0; } else if(wcsstr(argv[1], L"-replayhost") != NULL) { RENDERDOC_SpawnReplayHost(NULL); return 1; } } else if(argc == 3) { if(!_wcsicmp(argv[1], L"-inject")) { wchar_t *pid = argv[2]; while(*pid == L'"' || iswspace(*pid)) pid++; DWORD pidNum = (DWORD)_wtoi(pid); uint32_t ident = RENDERDOC_InjectIntoProcess(pidNum, NULL, &opts, false); if(ident == 0) printf("Failed to inject\n"); else printf("Injected as %d\n", ident); return ident; } else { uint32_t ident = RENDERDOC_ExecuteAndInject(argv[1], NULL, NULL, argv[2], &opts, false); if(ident == 0) printf("Failed to create & inject\n"); else printf("Created & injected as %d\n", ident); return ident; } } else if(argc == 4) { if(argc == 4 && wcsstr(argv[1], L"-replay") != NULL) { RemoteRenderer *remote = NULL; auto status = RENDERDOC_CreateRemoteReplayConnection(argv[2], &remote); if(remote == NULL || status != eReplayCreate_Success) return 1; float progress = 0.0f; ReplayRenderer *renderer = NULL; status = RemoteRenderer_CreateProxyRenderer(remote, 0, argv[3], &progress, &renderer); if(renderer && status == eReplayCreate_Success) DisplayRendererPreview(renderer, hInstance); RemoteRenderer_Shutdown(remote); return 0; } } else if(argc == 5) { if(!_wcsicmp(argv[1], L"-cap32for64")) { wchar_t *pid = argv[2]; while(*pid == L'"' || iswspace(*pid)) pid++; DWORD pidNum = (DWORD)_wtoi(pid); wchar_t *log = argv[3]; CaptureOptions cmdopts; string optstring(&argv[4][0], &argv[4][0] + wcslen(argv[4])); cmdopts.FromString(optstring); return RENDERDOC_InjectIntoProcess(pidNum, log, &cmdopts, false); } else if(!_wcsicmp(argv[1], L"-remotecontrol")) { wchar_t *host = argv[2]; wchar_t *ident = argv[3]; while(*ident == L'"' || iswspace(*ident)) ident++; bool force = argv[4][0] != '0'; DWORD identNum = (DWORD)_wtoi(ident); wchar_t username[256] = {0}; DWORD usersize = 255; GetEnvironmentVariableW(L"renderdoc_username", username, usersize); RemoteAccess *access = RENDERDOC_CreateRemoteAccessConnection(host, identNum, username, force); if(access == NULL) { printf("Failed to connect\n"); } else { printf("Target: %ls, API: %ls, Busy: %ls\n", RemoteAccess_GetTarget(access), RemoteAccess_GetAPI(access), RemoteAccess_GetBusyClient(access)); fflush(stdout); volatile bool run = true; while(run) { RemoteMessage msg; RemoteAccess_ReceiveMessage(access, &msg); if(msg.Type == eRemoteMsg_Disconnected) { printf("Disconnected\n"); RemoteAccess_Shutdown(access); access = NULL; break; } else if(msg.Type == eRemoteMsg_Busy) { printf("Busy: %ls\n", msg.Busy.ClientName.elems); RemoteAccess_Shutdown(access); access = NULL; break; } else if(msg.Type == eRemoteMsg_Noop) { } else if(msg.Type == eRemoteMsg_RegisterAPI) { printf("Updated - Target: %ls, API: %ls\n", RemoteAccess_GetTarget(access), RemoteAccess_GetAPI(access)); } else if(msg.Type == eRemoteMsg_NewCapture) { printf("Got capture - %d @ %llu, %d bytes of thumbnail\n", msg.NewCapture.ID, msg.NewCapture.timestamp, msg.NewCapture.thumbnail.count); } fflush(stdout); } if(access) { RemoteAccess_Shutdown(access); access = NULL; } } fflush(stdout); return 0; } } MessageBoxW(NULL, L"renderdoccmd Usage:\n\n" \ L"renderdoccmd.exe \"full path to exe\" [\"path to capture logfile to save to\"]\n" \ L"renderdoccmd.exe \"full path to logfile to replay\"\n" \ L"renderdoccmd.exe -inject \"Process ID\"\n", L"renderdoccmd", MB_OK); return 1; }
int WINAPI wWinMain(_In_ HINSTANCE hInst, _In_opt_ HINSTANCE hPrevInstance, _In_ LPWSTR lpCmdLine, _In_ int nShowCmd) { LPWSTR *wargv; int argc; wargv = CommandLineToArgvW(GetCommandLine(), &argc); char **argv = new char*[argc]; for(int i=0; i < argc; i++) { size_t len = wcslen(wargv[i]); len *= 4; // worst case, every UTF-8 character takes 4 bytes argv[i] = new char[len + 1]; argv[i][len] = 0; WideCharToMultiByte(CP_UTF8, 0, wargv[i], -1, &argv[i][0], (int)len+1, NULL, NULL); } LocalFree(wargv); hInstance = hInst; WNDCLASSEX wc; wc.cbSize = sizeof(WNDCLASSEX); wc.style = 0; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON)); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszMenuName = NULL; wc.lpszClassName = L"renderdoccmd"; wc.hIconSm = LoadIcon(NULL, MAKEINTRESOURCE(IDI_ICON)); if(!RegisterClassEx(&wc)) { return 1; } // special WIN32 option for launching the crash handler if(argc == 3 && !_stricmp(argv[1], "--update")) { string originalpath = argv[2]; wstring wide_path; { wchar_t *conv = new wchar_t[originalpath.size()+1]; MultiByteToWideChar(CP_UTF8, 0, originalpath.c_str(), -1, conv, int(originalpath.size()+1)); wide_path = conv; } // Wait for UI to exit Sleep(3000); mz_zip_archive zip; ZeroMemory(&zip, sizeof(zip)); mz_bool b = mz_zip_reader_init_file(&zip, "./update.zip", 0); if(b) { mz_uint numfiles = mz_zip_reader_get_num_files(&zip); // first create directories for(mz_uint i=0; i < numfiles; i++) { if(mz_zip_reader_is_file_a_directory(&zip, i)) { mz_zip_archive_file_stat zstat; mz_zip_reader_file_stat(&zip, i, &zstat); const char *fn = zstat.m_filename; // skip first directory because it's RenderDoc_Version_Bitness/ fn = strchr(fn, '/'); if(fn) fn++; if(*fn) { wchar_t conv[MAX_PATH] = {0}; wchar_t *wfn = conv; // I know the zip only contains ASCII chars, just upcast while(*fn) *(wfn++) = wchar_t(*(fn++)); wstring target = wide_path + conv; wfn = &target[0]; // convert slashes because CreateDirectory barfs on // proper slashes. while(*(wfn++)) { if(*wfn == L'/') *wfn = L'\\'; } CreateDirectoryW(target.c_str(), NULL); } } } for(mz_uint i=0; i < numfiles; i++) { if(!mz_zip_reader_is_file_a_directory(&zip, i)) { mz_zip_archive_file_stat zstat; mz_zip_reader_file_stat(&zip, i, &zstat); const char *fn = zstat.m_filename; // skip first directory because it's RenderDoc_Version_Bitness/ fn = strchr(fn, '/'); if(fn) fn++; if(*fn) { wchar_t conv[MAX_PATH] = {0}; wchar_t *wfn = conv; // I know the zip only contains ASCII chars, just upcast while(*fn) *(wfn++) = wchar_t(*(fn++)); wstring target = wide_path + conv; wfn = &target[0]; // convert slashes just to be consistent while(*(wfn++)) { if(*wfn == L'/') *wfn = L'\\'; } mz_zip_reader_extract_to_wfile(&zip, i, target.c_str(), 0); } } } } // run original UI exe and tell it an update succeeded wstring cmdline = L"\""; cmdline += wide_path; cmdline += L"/renderdocui.exe\" --updatedone"; wchar_t *paramsAlloc = new wchar_t[512]; wcscpy_s(paramsAlloc, 511, cmdline.c_str()); PROCESS_INFORMATION pi; STARTUPINFOW si; ZeroMemory(&pi, sizeof(pi)); ZeroMemory(&si, sizeof(si)); CreateProcessW(NULL, paramsAlloc, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi); if(pi.dwProcessId != 0) { CloseHandle(pi.hProcess); CloseHandle(pi.hThread); } return 0; } #if defined(RELEASE) CrashGenerationServer *crashServer = NULL; // special WIN32 option for launching the crash handler if(argc == 2 && !_stricmp(argv[1], "--crashhandle")) { wchar_t tempPath[MAX_PATH] = {0}; GetTempPathW(MAX_PATH-1, tempPath); Sleep(100); wstring dumpFolder = tempPath; dumpFolder += L"RenderDocDumps"; CreateDirectoryW(dumpFolder.c_str(), NULL); crashServer = new CrashGenerationServer(L"\\\\.\\pipe\\RenderDocBreakpadServer", NULL, NULL, NULL, OnClientCrashed, NULL, OnClientExited, NULL, NULL, NULL, true, &dumpFolder); if (!crashServer->Start()) { delete crashServer; crashServer = NULL; return 1; } CrashHandlerInst = hInstance; CrashHandlerWnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdoccmd", L"renderdoccmd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, NULL, NULL, hInstance, NULL); HANDLE hIcon = LoadImage(CrashHandlerInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0); if(hIcon) { SendMessage(CrashHandlerWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); SendMessage(CrashHandlerWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); } ShowWindow(CrashHandlerWnd, SW_HIDE); HANDLE readyEvent = CreateEventA(NULL, TRUE, FALSE, "RENDERDOC_CRASHHANDLE"); if(readyEvent != NULL) { SetEvent(readyEvent); CloseHandle(readyEvent); } MSG msg; ZeroMemory(&msg, sizeof(msg)); while(!exitServer) { // Check to see if any messages are waiting in the queue while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Translate the message and dispatch it to WindowProc() TranslateMessage(&msg); DispatchMessage(&msg); } // If the message is WM_QUIT, exit the while loop if(msg.message == WM_QUIT) break; Sleep(100); } delete crashServer; crashServer = NULL; if(!dump.empty()) { logpath = L""; string report = ""; for(size_t i=0; i < customInfo.size(); i++) { wstring name = customInfo[i].name; wstring val = customInfo[i].value; if(name == L"logpath") { logpath = val; } else if(name == L"ptime") { // breakpad uptime, ignore. } else { report += string(name.begin(), name.end()) + ": " + string(val.begin(), val.end()) + "\n"; } } DialogBox(CrashHandlerInst, MAKEINTRESOURCE(IDD_CRASH_HANDLER), CrashHandlerWnd, (DLGPROC)CrashHandlerProc); report += "\n\nRepro steps/Notes:\n\n" + reproSteps; { FILE *f = NULL; _wfopen_s(&f, logpath.c_str(), L"r"); if(f) { fseek(f, 0, SEEK_END); long filesize = ftell(f); fseek(f, 0, SEEK_SET); if(filesize > 10) { char *error_log = new char[filesize+1]; memset(error_log, 0, filesize+1); fread(error_log, 1, filesize, f); char *managed_callstack = strstr(error_log, "--- Begin C# Exception Data ---"); if(managed_callstack) { report += managed_callstack; report += "\n\n"; } delete[] error_log; } fclose(f); } } if(uploadReport) { mz_zip_archive zip; ZeroMemory(&zip, sizeof(zip)); wstring destzip = dumpFolder + L"\\report.zip"; DeleteFileW(destzip.c_str()); mz_zip_writer_init_wfile(&zip, destzip.c_str(), 0); mz_zip_writer_add_mem(&zip, "report.txt", report.c_str(), report.length(), MZ_BEST_COMPRESSION); if(uploadDump && !dump.empty()) mz_zip_writer_add_wfile(&zip, "minidump.dmp", dump.c_str(), NULL, 0, MZ_BEST_COMPRESSION); if(uploadLog && !logpath.empty()) mz_zip_writer_add_wfile(&zip, "error.log", logpath.c_str(), NULL, 0, MZ_BEST_COMPRESSION); mz_zip_writer_finalize_archive(&zip); mz_zip_writer_end(&zip); int timeout = 10000; wstring body = L""; int code = 0; std::map<wstring, wstring> params; google_breakpad::HTTPUpload::SendRequest(L"http://renderdoc.org/bugsubmit", params, dumpFolder + L"\\report.zip", L"report", &timeout, &body, &code); DeleteFileW(destzip.c_str()); } } if(!dump.empty()) DeleteFileW(dump.c_str()); if(!logpath.empty()) DeleteFileW(logpath.c_str()); return 0; } #endif // this installs a global windows hook pointing at renderdocshim*.dll that filters all running processes and // loads renderdoc.dll in the target one. In any other process it unloads as soon as possible if(argc == 5 && argequal(argv[1], "--globalhook")) { char *pathmatch = argv[2]; char *log = argv[3]; size_t len = strlen(pathmatch); wstring wpathmatch; wpathmatch.resize(len); MultiByteToWideChar(CP_UTF8, 0, pathmatch, -1, &wpathmatch[0], (int)len); wpathmatch.resize(wcslen(wpathmatch.c_str())); CaptureOptions cmdopts; readCapOpts(argv[4], &cmdopts); // make sure the user doesn't accidentally run this with 'a' as a parameter or something. // "a.exe" is over 4 characters so this limit should not be a problem. if(wpathmatch.length() < 4) { fprintf(stderr, "--globalhook path match is too short/general. Danger of matching too many processes!\n"); return 1; } wchar_t rdocpath[1024]; // fetch path to our matching renderdoc.dll HMODULE rdoc = GetModuleHandleA("renderdoc.dll"); if(rdoc == NULL) { fprintf(stderr, "--globalhook couldn't find renderdoc.dll!\n"); return 1; } GetModuleFileNameW(rdoc, rdocpath, _countof(rdocpath)-1); FreeLibrary(rdoc); // Create pipe from control program, to stay open until requested to close HANDLE pipe = CreateFileW(L"\\\\.\\pipe\\" #ifdef WIN64 L"RenderDoc.GlobalHookControl64" #else L"RenderDoc.GlobalHookControl32" #endif , GENERIC_READ|GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if(pipe == INVALID_HANDLE_VALUE) { fprintf(stderr, "--globalhook couldn't open control pipe.\n"); return 1; } HANDLE datahandle = OpenFileMappingA(FILE_MAP_READ, FALSE, GLOBAL_HOOK_DATA_NAME); if(datahandle != NULL) { CloseHandle(pipe); CloseHandle(datahandle); fprintf(stderr, "--globalhook found pre-existing global data, not creating second global hook.\n"); return 1; } datahandle = CreateFileMappingA(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, sizeof(ShimData), GLOBAL_HOOK_DATA_NAME); if(datahandle) { ShimData *shimdata = (ShimData *)MapViewOfFile(datahandle, FILE_MAP_WRITE|FILE_MAP_READ, 0, 0, sizeof(ShimData)); if(shimdata) { memset(shimdata, 0, sizeof(ShimData)); wcsncpy_s(shimdata->pathmatchstring, wpathmatch.c_str(), _TRUNCATE); wcsncpy_s(shimdata->rdocpath, rdocpath, _TRUNCATE); strncpy_s(shimdata->log, log, _TRUNCATE); memcpy (shimdata->opts, &cmdopts, sizeof(CaptureOptions)); static_assert(sizeof(CaptureOptions) <= sizeof(shimdata->opts), "ShimData options is too small"); // wait until a write comes in over the pipe char buf[16]; DWORD read = 0; ReadFile(pipe, buf, 16, &read, NULL); UnmapViewOfFile(shimdata); } else { fprintf(stderr, "--globalhook couldn't map global data store.\n"); } CloseHandle(datahandle); } else { fprintf(stderr, "--globalhook couldn't create global data store.\n"); } CloseHandle(pipe); return 0; } int retval = renderdoccmd(argc, argv); for(int i=0; i < argc; i++) delete[] argv[i]; delete[] argv; return retval; }
virtual int Execute(cmdline::parser &parser, const CaptureOptions &) { CrashGenerationServer *crashServer = NULL; wchar_t tempPath[MAX_PATH] = {0}; GetTempPathW(MAX_PATH - 1, tempPath); Sleep(100); wstring dumpFolder = tempPath; dumpFolder += L"RenderDocDumps"; CreateDirectoryW(dumpFolder.c_str(), NULL); crashServer = new CrashGenerationServer(L"\\\\.\\pipe\\RenderDocBreakpadServer", NULL, NULL, NULL, OnClientCrashed, NULL, OnClientExited, NULL, NULL, NULL, true, &dumpFolder); if(!crashServer->Start()) { delete crashServer; crashServer = NULL; return 1; } CrashHandlerInst = hInstance; CrashHandlerWnd = CreateWindowEx(WS_EX_CLIENTEDGE, L"renderdoccmd", L"renderdoccmd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 10, 10, NULL, NULL, hInstance, NULL); HANDLE hIcon = LoadImage(CrashHandlerInst, MAKEINTRESOURCE(IDI_ICON), IMAGE_ICON, 16, 16, 0); if(hIcon) { SendMessage(CrashHandlerWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); SendMessage(CrashHandlerWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon); } ShowWindow(CrashHandlerWnd, SW_HIDE); HANDLE readyEvent = CreateEventA(NULL, TRUE, FALSE, "RENDERDOC_CRASHHANDLE"); if(readyEvent != NULL) { SetEvent(readyEvent); CloseHandle(readyEvent); } MSG msg; ZeroMemory(&msg, sizeof(msg)); while(!exitServer) { // Check to see if any messages are waiting in the queue while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // Translate the message and dispatch it to WindowProc() TranslateMessage(&msg); DispatchMessage(&msg); } // If the message is WM_QUIT, exit the while loop if(msg.message == WM_QUIT) break; Sleep(100); } delete crashServer; crashServer = NULL; if(!dump.empty()) { logpath = L""; string report = ""; for(size_t i = 0; i < customInfo.size(); i++) { wstring name = customInfo[i].name; wstring val = customInfo[i].value; if(name == L"logpath") { logpath = val; } else if(name == L"ptime") { // breakpad uptime, ignore. } else { report += string(name.begin(), name.end()) + ": " + string(val.begin(), val.end()) + "\n"; } } DialogBox(CrashHandlerInst, MAKEINTRESOURCE(IDD_CRASH_HANDLER), CrashHandlerWnd, (DLGPROC)CrashHandlerProc); report += "\n\nRepro steps/Notes:\n\n" + reproSteps; { FILE *f = NULL; _wfopen_s(&f, logpath.c_str(), L"r"); if(f) { fseek(f, 0, SEEK_END); long filesize = ftell(f); fseek(f, 0, SEEK_SET); if(filesize > 10) { char *error_log = new char[filesize + 1]; memset(error_log, 0, filesize + 1); fread(error_log, 1, filesize, f); char *managed_callstack = strstr(error_log, "--- Begin C# Exception Data ---"); if(managed_callstack) { report += managed_callstack; report += "\n\n"; } delete[] error_log; } fclose(f); } } if(uploadReport) { mz_zip_archive zip; ZeroMemory(&zip, sizeof(zip)); wstring destzip = dumpFolder + L"\\report.zip"; DeleteFileW(destzip.c_str()); mz_zip_writer_init_wfile(&zip, destzip.c_str(), 0); mz_zip_writer_add_mem(&zip, "report.txt", report.c_str(), report.length(), MZ_BEST_COMPRESSION); if(uploadDump && !dump.empty()) mz_zip_writer_add_wfile(&zip, "minidump.dmp", dump.c_str(), NULL, 0, MZ_BEST_COMPRESSION); if(uploadLog && !logpath.empty()) mz_zip_writer_add_wfile(&zip, "error.log", logpath.c_str(), NULL, 0, MZ_BEST_COMPRESSION); mz_zip_writer_finalize_archive(&zip); mz_zip_writer_end(&zip); int timeout = 10000; wstring body = L""; int code = 0; std::map<wstring, wstring> params; google_breakpad::HTTPUpload::SendRequest(L"https://renderdoc.org/bugsubmit", params, dumpFolder + L"\\report.zip", L"report", &timeout, &body, &code); DeleteFileW(destzip.c_str()); } } if(!dump.empty()) DeleteFileW(dump.c_str()); if(!logpath.empty()) DeleteFileW(logpath.c_str()); return 0; }