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 renderdoccmd(int argc, wchar_t **argv) { CaptureOptions opts; opts.AllowFullscreen = false; opts.AllowVSync = false; opts.DelayForDebugger = 5; opts.HookIntoChildren = true; if(argc >= 2) { // fall through and print usage if(argequal(argv[1], L"--help") || argequal(argv[1], L"-h")) { } // 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); ReplayRenderer_Shutdown(renderer); return 0; } // replay a logfile else if(argequal(argv[1], L"--replay") || argequal(argv[1], L"-r")) { if(argc >= 3) { float progress = 0.0f; ReplayRenderer *renderer = NULL; auto status = RENDERDOC_CreateReplayRenderer(argv[2], &progress, &renderer); if(renderer && status == eReplayCreate_Success) DisplayRendererPreview(renderer); ReplayRenderer_Shutdown(renderer); return 0; } else { fprintf(stderr, "Not enough parameters to --replay"); } } #ifdef WIN32 // if we were given an executable on windows, inject into it // can't do this on other platforms as there's no nice extension // and we don't want to just launch any single parameter in case it's // -h or -help or some other guess/typo else if(wcsstr(argv[1], L".exe") != NULL) { uint32_t ident = RENDERDOC_ExecuteAndInject(argv[1], NULL, NULL, NULL, &opts, false); if(ident == 0) fprintf(stderr, "Failed to create & inject\n"); else fprintf(stderr, "Created & injected as %d\n", ident); return ident; } #endif // capture a program with default capture options else if(argequal(argv[1], L"--capture") || argequal(argv[1], L"-c")) { if(argc >= 3) { uint32_t ident = RENDERDOC_ExecuteAndInject(argv[2], NULL, NULL, NULL, &opts, false); if(ident == 0) fprintf(stderr, "Failed to create & inject to '%ls'\n", argv[2]); else fprintf(stderr, "Created & injected '%ls' as %d\n", argv[2], ident); return ident; } else { fprintf(stderr, "Not enough parameters to --capture"); } } // inject into a running process with default capture options else if(argequal(argv[1], L"--inject") || argequal(argv[1], L"-i")) { if(argc >= 3) { wchar_t *pid = argv[2]; while(*pid == L'"' || iswspace(*pid)) pid++; uint32_t pidNum = (uint32_t)wtoi(pid); uint32_t ident = RENDERDOC_InjectIntoProcess(pidNum, NULL, &opts, false); if(ident == 0) printf("Failed to inject to %u\n", pidNum); else printf("Injected to %u as %u\n", pidNum, ident); return ident; } else { fprintf(stderr, "Not enough parameters to --inject"); } } // spawn remote replay host else if(argequal(argv[1], L"--replayhost") || argequal(argv[1], L"-rh")) { RENDERDOC_SpawnReplayHost(NULL); return 1; } // replay a logfile over the network on a remote host else if(argequal(argv[1], L"--remotereplay") || argequal(argv[1], L"-rr")) { if(argc >= 4) { 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); RemoteRenderer_Shutdown(remote); return 0; } else { fprintf(stderr, "Not enough parameters to --remotereplay"); } } // not documented/useful for manual use on the cmd line, used internally else if(argequal(argv[1], L"--cap32for64")) { if(argc >= 5) { wchar_t *pid = argv[2]; while(*pid == L'"' || iswspace(*pid)) pid++; uint32_t pidNum = (uint32_t)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 { fprintf(stderr, "Not enough parameters to --cap32for64"); } } } fprintf(stderr, "renderdoccmd usage:\n\n"); fprintf(stderr, " <file.rdc> Launch a preview window that replays this logfile and\n"); fprintf(stderr, " displays the backbuffer.\n"); #ifdef WIN32 fprintf(stderr, " <program.exe> Launch a capture of this program with default options.\n"); #endif fprintf(stderr, "\n"); fprintf(stderr, " -h, --help Displays this help message.\n"); fprintf(stderr, " -c, --capture PROGRAM Launches capture of the program with default options.\n"); fprintf(stderr, " -i, --inject PID Injects into the specified PID to capture.\n"); fprintf(stderr, " -r, --replay LOGFILE Launch a preview window that replays this logfile and\n"); fprintf(stderr, " displays the backbuffer.\n"); fprintf(stderr, " -rh, --replayhost Starts a replay host server that can be used to remotely\n"); fprintf(stderr, " replay logfiles from another machine.\n"); fprintf(stderr, " -rr, --remotereplay HOST LOGFILE Launch a replay of the logfile and display a preview\n"); fprintf(stderr, " window. Use the remote host to replay all commands.\n"); return 1; }