1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/toolkit/crashreporter/client/crashreporter_win.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,1519 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this 1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 + 1.9 +#ifdef WIN32_LEAN_AND_MEAN 1.10 +#undef WIN32_LEAN_AND_MEAN 1.11 +#endif 1.12 + 1.13 +#define NOMINMAX 1.14 + 1.15 +#include "crashreporter.h" 1.16 + 1.17 +#include <windows.h> 1.18 +#include <commctrl.h> 1.19 +#include <richedit.h> 1.20 +#include <shellapi.h> 1.21 +#include <shlobj.h> 1.22 +#include <shlwapi.h> 1.23 +#include <math.h> 1.24 +#include <set> 1.25 +#include <algorithm> 1.26 +#include "resource.h" 1.27 +#include "client/windows/sender/crash_report_sender.h" 1.28 +#include "common/windows/string_utils-inl.h" 1.29 +#include "mozilla/NullPtr.h" 1.30 + 1.31 +#define CRASH_REPORTER_VALUE L"Enabled" 1.32 +#define SUBMIT_REPORT_VALUE L"SubmitCrashReport" 1.33 +#define SUBMIT_REPORT_OLD L"SubmitReport" 1.34 +#define INCLUDE_URL_VALUE L"IncludeURL" 1.35 +#define EMAIL_ME_VALUE L"EmailMe" 1.36 +#define EMAIL_VALUE L"Email" 1.37 +#define MAX_EMAIL_LENGTH 1024 1.38 + 1.39 +#define WM_UPLOADCOMPLETE WM_APP 1.40 + 1.41 +// Thanks, Windows.h :( 1.42 +#undef min 1.43 +#undef max 1.44 + 1.45 +using std::string; 1.46 +using std::wstring; 1.47 +using std::map; 1.48 +using std::vector; 1.49 +using std::set; 1.50 +using std::ios; 1.51 +using std::ifstream; 1.52 +using std::ofstream; 1.53 + 1.54 +using namespace CrashReporter; 1.55 + 1.56 +typedef struct { 1.57 + HWND hDlg; 1.58 + wstring dumpFile; 1.59 + map<wstring,wstring> queryParameters; 1.60 + wstring sendURL; 1.61 + 1.62 + wstring serverResponse; 1.63 +} SendThreadData; 1.64 + 1.65 +/* 1.66 + * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx 1.67 + * "The DLGTEMPLATEEX structure is not defined in any standard header file. 1.68 + * The structure definition is provided here to explain the format of an 1.69 + * extended template for a dialog box. 1.70 +*/ 1.71 +typedef struct { 1.72 + WORD dlgVer; 1.73 + WORD signature; 1.74 + DWORD helpID; 1.75 + DWORD exStyle; 1.76 + // There's more to this struct, but it has weird variable-length 1.77 + // members, and I only actually need to touch exStyle on an existing 1.78 + // instance, so I've omitted the rest. 1.79 +} DLGTEMPLATEEX; 1.80 + 1.81 +static HANDLE gThreadHandle; 1.82 +static SendThreadData gSendData = { 0, }; 1.83 +static vector<string> gRestartArgs; 1.84 +static map<wstring,wstring> gQueryParameters; 1.85 +static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter"); 1.86 +static wstring gURLParameter; 1.87 +static int gCheckboxPadding = 6; 1.88 +static bool gRTLlayout = false; 1.89 + 1.90 +// When vertically resizing the dialog, these items should move down 1.91 +static set<UINT> gAttachedBottom; 1.92 + 1.93 +// Default set of items for gAttachedBottom 1.94 +static const UINT kDefaultAttachedBottom[] = { 1.95 + IDC_SUBMITREPORTCHECK, 1.96 + IDC_VIEWREPORTBUTTON, 1.97 + IDC_COMMENTTEXT, 1.98 + IDC_INCLUDEURLCHECK, 1.99 + IDC_EMAILMECHECK, 1.100 + IDC_EMAILTEXT, 1.101 + IDC_PROGRESSTEXT, 1.102 + IDC_THROBBER, 1.103 + IDC_CLOSEBUTTON, 1.104 + IDC_RESTARTBUTTON, 1.105 +}; 1.106 + 1.107 +static wstring UTF8ToWide(const string& utf8, bool *success = 0); 1.108 +static DWORD WINAPI SendThreadProc(LPVOID param); 1.109 + 1.110 +static wstring Str(const char* key) 1.111 +{ 1.112 + return UTF8ToWide(gStrings[key]); 1.113 +} 1.114 + 1.115 +/* === win32 helper functions === */ 1.116 + 1.117 +static void DoInitCommonControls() 1.118 +{ 1.119 + INITCOMMONCONTROLSEX ic; 1.120 + ic.dwSize = sizeof(INITCOMMONCONTROLSEX); 1.121 + ic.dwICC = ICC_PROGRESS_CLASS; 1.122 + InitCommonControlsEx(&ic); 1.123 + // also get the rich edit control 1.124 + LoadLibrary(L"Msftedit.dll"); 1.125 +} 1.126 + 1.127 +static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value) 1.128 +{ 1.129 + DWORD type, dataSize; 1.130 + dataSize = sizeof(DWORD); 1.131 + if (RegQueryValueEx(hRegKey, valueName, nullptr, 1.132 + &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS && 1.133 + type == REG_DWORD) 1.134 + return true; 1.135 + 1.136 + return false; 1.137 +} 1.138 + 1.139 +// Removes a value from HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER, if it exists. 1.140 +static void RemoveUnusedValues(const wchar_t* key, LPCTSTR valueName) 1.141 +{ 1.142 + HKEY hRegKey; 1.143 + 1.144 + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_SET_VALUE, &hRegKey) 1.145 + == ERROR_SUCCESS) { 1.146 + RegDeleteValue(hRegKey, valueName); 1.147 + RegCloseKey(hRegKey); 1.148 + } 1.149 + 1.150 + if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_SET_VALUE, &hRegKey) 1.151 + == ERROR_SUCCESS) { 1.152 + RegDeleteValue(hRegKey, valueName); 1.153 + RegCloseKey(hRegKey); 1.154 + } 1.155 +} 1.156 + 1.157 +static bool CheckBoolKey(const wchar_t* key, 1.158 + const wchar_t* valueName, 1.159 + bool* enabled) 1.160 +{ 1.161 + /* 1.162 + * NOTE! This code needs to stay in sync with the preference checking 1.163 + * code in in nsExceptionHandler.cpp. 1.164 + */ 1.165 + *enabled = false; 1.166 + bool found = false; 1.167 + HKEY hRegKey; 1.168 + DWORD val; 1.169 + // see if our reg key is set globally 1.170 + if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) { 1.171 + if (GetBoolValue(hRegKey, valueName, &val)) { 1.172 + *enabled = (val == 1); 1.173 + found = true; 1.174 + } 1.175 + RegCloseKey(hRegKey); 1.176 + } else { 1.177 + // look for it in user settings 1.178 + if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { 1.179 + if (GetBoolValue(hRegKey, valueName, &val)) { 1.180 + *enabled = (val == 1); 1.181 + found = true; 1.182 + } 1.183 + RegCloseKey(hRegKey); 1.184 + } 1.185 + } 1.186 + 1.187 + return found; 1.188 +} 1.189 + 1.190 +static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled) 1.191 +{ 1.192 + /* 1.193 + * NOTE! This code needs to stay in sync with the preference setting 1.194 + * code in in nsExceptionHandler.cpp. 1.195 + */ 1.196 + HKEY hRegKey; 1.197 + 1.198 + // remove the old value from the registry if it exists 1.199 + RemoveUnusedValues(key, SUBMIT_REPORT_OLD); 1.200 + 1.201 + if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { 1.202 + DWORD data = (enabled ? 1 : 0); 1.203 + RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data)); 1.204 + RegCloseKey(hRegKey); 1.205 + } 1.206 +} 1.207 + 1.208 +static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value) 1.209 +{ 1.210 + DWORD type, dataSize; 1.211 + wchar_t buf[2048]; 1.212 + dataSize = sizeof(buf); 1.213 + if (RegQueryValueEx(hRegKey, valueName, nullptr, 1.214 + &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS && 1.215 + type == REG_SZ) { 1.216 + value = buf; 1.217 + return true; 1.218 + } 1.219 + 1.220 + return false; 1.221 +} 1.222 + 1.223 +static bool GetStringKey(const wchar_t* key, 1.224 + const wchar_t* valueName, 1.225 + wstring& value) 1.226 +{ 1.227 + value = L""; 1.228 + bool found = false; 1.229 + HKEY hRegKey; 1.230 + // see if our reg key is set globally 1.231 + if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) { 1.232 + if (GetStringValue(hRegKey, valueName, value)) { 1.233 + found = true; 1.234 + } 1.235 + RegCloseKey(hRegKey); 1.236 + } else { 1.237 + // look for it in user settings 1.238 + if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { 1.239 + if (GetStringValue(hRegKey, valueName, value)) { 1.240 + found = true; 1.241 + } 1.242 + RegCloseKey(hRegKey); 1.243 + } 1.244 + } 1.245 + 1.246 + return found; 1.247 +} 1.248 + 1.249 +static void SetStringKey(const wchar_t* key, 1.250 + const wchar_t* valueName, 1.251 + const wstring& value) 1.252 +{ 1.253 + HKEY hRegKey; 1.254 + if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) { 1.255 + RegSetValueEx(hRegKey, valueName, 0, REG_SZ, 1.256 + (LPBYTE)value.c_str(), 1.257 + (value.length() + 1) * sizeof(wchar_t)); 1.258 + RegCloseKey(hRegKey); 1.259 + } 1.260 +} 1.261 + 1.262 +static string FormatLastError() 1.263 +{ 1.264 + DWORD err = GetLastError(); 1.265 + LPWSTR s; 1.266 + string message = "Crash report submission failed: "; 1.267 + // odds are it's a WinInet error 1.268 + HANDLE hInetModule = GetModuleHandle(L"WinInet.dll"); 1.269 + if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | 1.270 + FORMAT_MESSAGE_FROM_SYSTEM | 1.271 + FORMAT_MESSAGE_FROM_HMODULE, 1.272 + hInetModule, 1.273 + err, 1.274 + 0, 1.275 + (LPWSTR)&s, 1.276 + 0, 1.277 + nullptr) != 0) { 1.278 + message += WideToUTF8(s, nullptr); 1.279 + LocalFree(s); 1.280 + // strip off any trailing newlines 1.281 + string::size_type n = message.find_last_not_of("\r\n"); 1.282 + if (n < message.size() - 1) { 1.283 + message.erase(n+1); 1.284 + } 1.285 + } 1.286 + else { 1.287 + char buf[64]; 1.288 + sprintf(buf, "Unknown error, error code: 0x%08x", err); 1.289 + message += buf; 1.290 + } 1.291 + return message; 1.292 +} 1.293 + 1.294 +#define TS_DRAW 2 1.295 +#define BP_CHECKBOX 3 1.296 + 1.297 +typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList); 1.298 +typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme); 1.299 +typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId, 1.300 + int iStateId, RECT* prc, int ts, 1.301 + SIZE* psz); 1.302 +typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId, 1.303 + int iStateId, const RECT* pRect, 1.304 + RECT* pContentRect); 1.305 + 1.306 + 1.307 +static void GetThemeSizes(HWND hwnd) 1.308 +{ 1.309 + HMODULE themeDLL = LoadLibrary(L"uxtheme.dll"); 1.310 + 1.311 + if (!themeDLL) 1.312 + return; 1.313 + 1.314 + OpenThemeDataPtr openTheme = 1.315 + (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData"); 1.316 + CloseThemeDataPtr closeTheme = 1.317 + (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData"); 1.318 + GetThemePartSizePtr getThemePartSize = 1.319 + (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize"); 1.320 + 1.321 + if (!openTheme || !closeTheme || !getThemePartSize) { 1.322 + FreeLibrary(themeDLL); 1.323 + return; 1.324 + } 1.325 + 1.326 + HANDLE buttonTheme = openTheme(hwnd, L"Button"); 1.327 + if (!buttonTheme) { 1.328 + FreeLibrary(themeDLL); 1.329 + return; 1.330 + } 1.331 + HDC hdc = GetDC(hwnd); 1.332 + SIZE s; 1.333 + getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, nullptr, TS_DRAW, &s); 1.334 + gCheckboxPadding = s.cx; 1.335 + closeTheme(buttonTheme); 1.336 + FreeLibrary(themeDLL); 1.337 +} 1.338 + 1.339 +// Gets the position of a window relative to another window's client area 1.340 +static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r) 1.341 +{ 1.342 + GetWindowRect(hwnd, r); 1.343 + MapWindowPoints(nullptr, hwndParent, (POINT*)r, 2); 1.344 +} 1.345 + 1.346 +static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible) 1.347 +{ 1.348 + HWND hwnd = GetDlgItem(hwndDlg, item); 1.349 + 1.350 + ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE); 1.351 +} 1.352 + 1.353 +static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled) 1.354 +{ 1.355 + HWND hwnd = GetDlgItem(hwndDlg, item); 1.356 + LONG style = GetWindowLong(hwnd, GWL_STYLE); 1.357 + if (!disabled) 1.358 + style |= WS_DISABLED; 1.359 + else 1.360 + style &= ~WS_DISABLED; 1.361 + 1.362 + SetWindowLong(hwnd, GWL_STYLE, style); 1.363 +} 1.364 + 1.365 +/* === Crash Reporting Dialog === */ 1.366 + 1.367 +static void StretchDialog(HWND hwndDlg, int ydiff) 1.368 +{ 1.369 + RECT r; 1.370 + GetWindowRect(hwndDlg, &r); 1.371 + r.bottom += ydiff; 1.372 + MoveWindow(hwndDlg, r.left, r.top, 1.373 + r.right - r.left, r.bottom - r.top, TRUE); 1.374 +} 1.375 + 1.376 +static void ReflowDialog(HWND hwndDlg, int ydiff) 1.377 +{ 1.378 + // Move items attached to the bottom down/up by as much as 1.379 + // the window resize 1.380 + for (set<UINT>::const_iterator item = gAttachedBottom.begin(); 1.381 + item != gAttachedBottom.end(); 1.382 + item++) { 1.383 + RECT r; 1.384 + HWND hwnd = GetDlgItem(hwndDlg, *item); 1.385 + GetRelativeRect(hwnd, hwndDlg, &r); 1.386 + r.top += ydiff; 1.387 + r.bottom += ydiff; 1.388 + MoveWindow(hwnd, r.left, r.top, 1.389 + r.right - r.left, r.bottom - r.top, TRUE); 1.390 + } 1.391 +} 1.392 + 1.393 +static DWORD WINAPI SendThreadProc(LPVOID param) 1.394 +{ 1.395 + bool finishedOk; 1.396 + SendThreadData* td = (SendThreadData*)param; 1.397 + 1.398 + if (td->sendURL.empty()) { 1.399 + finishedOk = false; 1.400 + LogMessage("No server URL, not sending report"); 1.401 + } else { 1.402 + google_breakpad::CrashReportSender sender(L""); 1.403 + finishedOk = (sender.SendCrashReport(td->sendURL, 1.404 + td->queryParameters, 1.405 + td->dumpFile, 1.406 + &td->serverResponse) 1.407 + == google_breakpad::RESULT_SUCCEEDED); 1.408 + if (finishedOk) { 1.409 + LogMessage("Crash report submitted successfully"); 1.410 + } 1.411 + else { 1.412 + // get an error string and print it to the log 1.413 + //XXX: would be nice to get the HTTP status code here, filed: 1.414 + // http://code.google.com/p/google-breakpad/issues/detail?id=220 1.415 + LogMessage(FormatLastError()); 1.416 + } 1.417 + } 1.418 + 1.419 + PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0); 1.420 + 1.421 + return 0; 1.422 +} 1.423 + 1.424 +static void EndCrashReporterDialog(HWND hwndDlg, int code) 1.425 +{ 1.426 + // Save the current values to the registry 1.427 + wchar_t email[MAX_EMAIL_LENGTH]; 1.428 + GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email, 1.429 + sizeof(email) / sizeof(email[0])); 1.430 + SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email); 1.431 + 1.432 + SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, 1.433 + IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0); 1.434 + SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, 1.435 + IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0); 1.436 + SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE, 1.437 + IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0); 1.438 + 1.439 + EndDialog(hwndDlg, code); 1.440 +} 1.441 + 1.442 +static void MaybeResizeProgressText(HWND hwndDlg) 1.443 +{ 1.444 + HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT); 1.445 + HDC hdc = GetDC(hwndProgress); 1.446 + HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0); 1.447 + if (hfont) 1.448 + SelectObject(hdc, hfont); 1.449 + SIZE size; 1.450 + RECT rect; 1.451 + GetRelativeRect(hwndProgress, hwndDlg, &rect); 1.452 + 1.453 + wchar_t text[1024]; 1.454 + GetWindowText(hwndProgress, text, 1024); 1.455 + 1.456 + if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size)) 1.457 + return; 1.458 + 1.459 + if (size.cx < (rect.right - rect.left)) 1.460 + return; 1.461 + 1.462 + // Figure out how much we need to resize things vertically 1.463 + // This is sort of a fudge, but it should be good enough. 1.464 + int wantedHeight = size.cy * 1.465 + (int)ceil((float)size.cx / (float)(rect.right - rect.left)); 1.466 + int diff = wantedHeight - (rect.bottom - rect.top); 1.467 + if (diff <= 0) 1.468 + return; 1.469 + 1.470 + MoveWindow(hwndProgress, rect.left, rect.top, 1.471 + rect.right - rect.left, 1.472 + wantedHeight, 1.473 + TRUE); 1.474 + 1.475 + gAttachedBottom.clear(); 1.476 + gAttachedBottom.insert(IDC_CLOSEBUTTON); 1.477 + gAttachedBottom.insert(IDC_RESTARTBUTTON); 1.478 + 1.479 + StretchDialog(hwndDlg, diff); 1.480 + 1.481 + for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) { 1.482 + gAttachedBottom.insert(kDefaultAttachedBottom[i]); 1.483 + } 1.484 +} 1.485 + 1.486 +static void MaybeSendReport(HWND hwndDlg) 1.487 +{ 1.488 + if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) { 1.489 + EndCrashReporterDialog(hwndDlg, 0); 1.490 + return; 1.491 + } 1.492 + 1.493 + // disable all the form controls 1.494 + EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false); 1.495 + EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false); 1.496 + EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false); 1.497 + EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false); 1.498 + EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false); 1.499 + EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false); 1.500 + EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false); 1.501 + EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false); 1.502 + 1.503 + SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str()); 1.504 + MaybeResizeProgressText(hwndDlg); 1.505 + // start throbber 1.506 + // play entire AVI, and loop 1.507 + Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1); 1.508 + SetDlgItemVisible(hwndDlg, IDC_THROBBER, true); 1.509 + gThreadHandle = nullptr; 1.510 + gSendData.hDlg = hwndDlg; 1.511 + gSendData.queryParameters = gQueryParameters; 1.512 + 1.513 + gThreadHandle = CreateThread(nullptr, 0, SendThreadProc, &gSendData, 0, 1.514 + nullptr); 1.515 +} 1.516 + 1.517 +static void RestartApplication() 1.518 +{ 1.519 + wstring cmdLine; 1.520 + 1.521 + for (unsigned int i = 0; i < gRestartArgs.size(); i++) { 1.522 + cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" "; 1.523 + } 1.524 + 1.525 + STARTUPINFO si; 1.526 + PROCESS_INFORMATION pi; 1.527 + 1.528 + ZeroMemory(&si, sizeof(si)); 1.529 + si.cb = sizeof(si); 1.530 + si.dwFlags = STARTF_USESHOWWINDOW; 1.531 + si.wShowWindow = SW_SHOWNORMAL; 1.532 + ZeroMemory(&pi, sizeof(pi)); 1.533 + 1.534 + if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE, 1.535 + 0, nullptr, nullptr, &si, &pi)) { 1.536 + CloseHandle(pi.hProcess); 1.537 + CloseHandle(pi.hThread); 1.538 + } 1.539 +} 1.540 + 1.541 +static void ShowReportInfo(HWND hwndDlg) 1.542 +{ 1.543 + wstring description; 1.544 + 1.545 + for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin(); 1.546 + i != gQueryParameters.end(); 1.547 + i++) { 1.548 + description += i->first; 1.549 + description += L": "; 1.550 + description += i->second; 1.551 + description += L"\n"; 1.552 + } 1.553 + 1.554 + description += L"\n"; 1.555 + description += Str(ST_EXTRAREPORTINFO); 1.556 + 1.557 + SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str()); 1.558 +} 1.559 + 1.560 +static void UpdateURL(HWND hwndDlg) 1.561 +{ 1.562 + if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) { 1.563 + gQueryParameters[L"URL"] = gURLParameter; 1.564 + } else { 1.565 + gQueryParameters.erase(L"URL"); 1.566 + } 1.567 +} 1.568 + 1.569 +static void UpdateEmail(HWND hwndDlg) 1.570 +{ 1.571 + if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) { 1.572 + wchar_t email[MAX_EMAIL_LENGTH]; 1.573 + GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email, 1.574 + sizeof(email) / sizeof(email[0])); 1.575 + gQueryParameters[L"Email"] = email; 1.576 + if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) 1.577 + EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true); 1.578 + } else { 1.579 + gQueryParameters.erase(L"Email"); 1.580 + EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false); 1.581 + } 1.582 +} 1.583 + 1.584 +static void UpdateComment(HWND hwndDlg) 1.585 +{ 1.586 + wchar_t comment[MAX_COMMENT_LENGTH + 1]; 1.587 + GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment, 1.588 + sizeof(comment) / sizeof(comment[0])); 1.589 + if (wcslen(comment) > 0) 1.590 + gQueryParameters[L"Comments"] = comment; 1.591 + else 1.592 + gQueryParameters.erase(L"Comments"); 1.593 +} 1.594 + 1.595 +/* 1.596 + * Dialog procedure for the "view report" dialog. 1.597 + */ 1.598 +static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message, 1.599 + WPARAM wParam, LPARAM lParam) 1.600 +{ 1.601 + switch (message) { 1.602 + case WM_INITDIALOG: { 1.603 + SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str()); 1.604 + SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str()); 1.605 + SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT, 1.606 + EM_SETTARGETDEVICE, (WPARAM)nullptr, 0); 1.607 + ShowReportInfo(hwndDlg); 1.608 + SetFocus(GetDlgItem(hwndDlg, IDOK)); 1.609 + return FALSE; 1.610 + } 1.611 + 1.612 + case WM_COMMAND: { 1.613 + if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK) 1.614 + EndDialog(hwndDlg, 0); 1.615 + return FALSE; 1.616 + } 1.617 + } 1.618 + return FALSE; 1.619 +} 1.620 + 1.621 +// Return the number of bytes this string will take encoded 1.622 +// in UTF-8 1.623 +static inline int BytesInUTF8(wchar_t* str) 1.624 +{ 1.625 + // Just count size of buffer for UTF-8, minus one 1.626 + // (we don't need to count the null terminator) 1.627 + return WideCharToMultiByte(CP_UTF8, 0, str, -1, 1.628 + nullptr, 0, nullptr, nullptr) - 1; 1.629 +} 1.630 + 1.631 +// Calculate the length of the text in this edit control (in bytes, 1.632 +// in the UTF-8 encoding) after replacing the current selection 1.633 +// with |insert|. 1.634 +static int NewTextLength(HWND hwndEdit, wchar_t* insert) 1.635 +{ 1.636 + wchar_t current[MAX_COMMENT_LENGTH + 1]; 1.637 + 1.638 + GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1); 1.639 + DWORD selStart, selEnd; 1.640 + SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd); 1.641 + 1.642 + int selectionLength = 0; 1.643 + if (selEnd - selStart > 0) { 1.644 + wchar_t selection[MAX_COMMENT_LENGTH + 1]; 1.645 + google_breakpad::WindowsStringUtils::safe_wcsncpy(selection, 1.646 + MAX_COMMENT_LENGTH + 1, 1.647 + current + selStart, 1.648 + selEnd - selStart); 1.649 + selection[selEnd - selStart] = '\0'; 1.650 + selectionLength = BytesInUTF8(selection); 1.651 + } 1.652 + 1.653 + // current string length + replacement text length 1.654 + // - replaced selection length 1.655 + return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength; 1.656 +} 1.657 + 1.658 +// Window procedure for subclassing edit controls 1.659 +static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, 1.660 + LPARAM lParam) 1.661 +{ 1.662 + static WNDPROC super = nullptr; 1.663 + 1.664 + if (super == nullptr) 1.665 + super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA); 1.666 + 1.667 + switch (uMsg) { 1.668 + case WM_PAINT: { 1.669 + HDC hdc; 1.670 + PAINTSTRUCT ps; 1.671 + RECT r; 1.672 + wchar_t windowText[1024]; 1.673 + 1.674 + GetWindowText(hwnd, windowText, 1024); 1.675 + // if the control contains text or is focused, draw it normally 1.676 + if (GetFocus() == hwnd || windowText[0] != '\0') 1.677 + return CallWindowProc(super, hwnd, uMsg, wParam, lParam); 1.678 + 1.679 + GetClientRect(hwnd, &r); 1.680 + hdc = BeginPaint(hwnd, &ps); 1.681 + FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd) 1.682 + ? COLOR_WINDOW : COLOR_BTNFACE)); 1.683 + SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT)); 1.684 + SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT)); 1.685 + SetBkMode(hdc, TRANSPARENT); 1.686 + wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT"); 1.687 + // Get the actual edit control rect 1.688 + CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r); 1.689 + UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL; 1.690 + if (gRTLlayout) 1.691 + format |= DT_RIGHT; 1.692 + if (txt) 1.693 + DrawText(hdc, txt, wcslen(txt), &r, format); 1.694 + EndPaint(hwnd, &ps); 1.695 + return 0; 1.696 + } 1.697 + 1.698 + // We handle WM_CHAR and WM_PASTE to limit the comment box to 500 1.699 + // bytes in UTF-8. 1.700 + case WM_CHAR: { 1.701 + // Leave accelerator keys and non-printing chars (except LF) alone 1.702 + if (wParam & (1<<24) || wParam & (1<<29) || 1.703 + (wParam < ' ' && wParam != '\n')) 1.704 + break; 1.705 + 1.706 + wchar_t ch[2] = { (wchar_t)wParam, 0 }; 1.707 + if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH) 1.708 + return 0; 1.709 + 1.710 + break; 1.711 + } 1.712 + 1.713 + case WM_PASTE: { 1.714 + if (IsClipboardFormatAvailable(CF_UNICODETEXT) && 1.715 + OpenClipboard(hwnd)) { 1.716 + HGLOBAL hg = GetClipboardData(CF_UNICODETEXT); 1.717 + wchar_t* pastedText = (wchar_t*)GlobalLock(hg); 1.718 + int newSize = 0; 1.719 + 1.720 + if (pastedText) 1.721 + newSize = NewTextLength(hwnd, pastedText); 1.722 + 1.723 + GlobalUnlock(hg); 1.724 + CloseClipboard(); 1.725 + 1.726 + if (newSize > MAX_COMMENT_LENGTH) 1.727 + return 0; 1.728 + } 1.729 + break; 1.730 + } 1.731 + 1.732 + case WM_SETFOCUS: 1.733 + case WM_KILLFOCUS: { 1.734 + RECT r; 1.735 + GetClientRect(hwnd, &r); 1.736 + InvalidateRect(hwnd, &r, TRUE); 1.737 + break; 1.738 + } 1.739 + 1.740 + case WM_DESTROY: { 1.741 + // cleanup our property 1.742 + HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT"); 1.743 + if (hData) 1.744 + GlobalFree(hData); 1.745 + } 1.746 + } 1.747 + 1.748 + return CallWindowProc(super, hwnd, uMsg, wParam, lParam); 1.749 +} 1.750 + 1.751 +// Resize a control to fit this text 1.752 +static int ResizeControl(HWND hwndButton, RECT& rect, wstring text, 1.753 + bool shiftLeft, int userDefinedPadding) 1.754 +{ 1.755 + HDC hdc = GetDC(hwndButton); 1.756 + HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0); 1.757 + if (hfont) 1.758 + SelectObject(hdc, hfont); 1.759 + SIZE size, oldSize; 1.760 + int sizeDiff = 0; 1.761 + 1.762 + wchar_t oldText[1024]; 1.763 + GetWindowText(hwndButton, oldText, 1024); 1.764 + 1.765 + if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size) 1.766 + // default text on the button 1.767 + && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) { 1.768 + /* 1.769 + Expand control widths to accomidate wider text strings. For most 1.770 + controls (including buttons) the text padding is defined by the 1.771 + dialog's rc file. Some controls (such as checkboxes) have padding 1.772 + that extends to the end of the dialog, in which case we ignore the 1.773 + rc padding and rely on a user defined value passed in through 1.774 + userDefinedPadding. 1.775 + */ 1.776 + int textIncrease = size.cx - oldSize.cx; 1.777 + if (textIncrease < 0) 1.778 + return 0; 1.779 + int existingTextPadding; 1.780 + if (userDefinedPadding == 0) 1.781 + existingTextPadding = (rect.right - rect.left) - oldSize.cx; 1.782 + else 1.783 + existingTextPadding = userDefinedPadding; 1.784 + sizeDiff = textIncrease + existingTextPadding; 1.785 + 1.786 + if (shiftLeft) { 1.787 + // shift left by the amount the button should grow 1.788 + rect.left -= sizeDiff; 1.789 + } 1.790 + else { 1.791 + // grow right instead 1.792 + rect.right += sizeDiff; 1.793 + } 1.794 + MoveWindow(hwndButton, rect.left, rect.top, 1.795 + rect.right - rect.left, 1.796 + rect.bottom - rect.top, 1.797 + TRUE); 1.798 + } 1.799 + return sizeDiff; 1.800 +} 1.801 + 1.802 +// The window was resized horizontally, so widen some of our 1.803 +// controls to make use of the space 1.804 +static void StretchControlsToFit(HWND hwndDlg) 1.805 +{ 1.806 + int controls[] = { 1.807 + IDC_DESCRIPTIONTEXT, 1.808 + IDC_SUBMITREPORTCHECK, 1.809 + IDC_COMMENTTEXT, 1.810 + IDC_INCLUDEURLCHECK, 1.811 + IDC_EMAILMECHECK, 1.812 + IDC_EMAILTEXT, 1.813 + IDC_PROGRESSTEXT 1.814 + }; 1.815 + 1.816 + RECT dlgRect; 1.817 + GetClientRect(hwndDlg, &dlgRect); 1.818 + 1.819 + for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) { 1.820 + RECT r; 1.821 + HWND hwndControl = GetDlgItem(hwndDlg, controls[i]); 1.822 + GetRelativeRect(hwndControl, hwndDlg, &r); 1.823 + // 6 pixel spacing on the right 1.824 + if (r.right + 6 != dlgRect.right) { 1.825 + r.right = dlgRect.right - 6; 1.826 + MoveWindow(hwndControl, r.left, r.top, 1.827 + r.right - r.left, 1.828 + r.bottom - r.top, 1.829 + TRUE); 1.830 + } 1.831 + } 1.832 +} 1.833 + 1.834 +static void SubmitReportChecked(HWND hwndDlg) 1.835 +{ 1.836 + bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0); 1.837 + EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled); 1.838 + EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled); 1.839 + EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled); 1.840 + EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled); 1.841 + EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), 1.842 + enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) 1.843 + != 0)); 1.844 + SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled); 1.845 +} 1.846 + 1.847 +static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent, 1.848 + DLGPROC dlgProc, LPARAM param) 1.849 +{ 1.850 + INT_PTR rv = 0; 1.851 + if (gRTLlayout) { 1.852 + // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog 1.853 + // template. 1.854 + HRSRC hDialogRC = FindResource(nullptr, MAKEINTRESOURCE(idd), 1.855 + RT_DIALOG); 1.856 + HGLOBAL hDlgTemplate = LoadResource(nullptr, hDialogRC); 1.857 + DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate); 1.858 + unsigned long sizeDlg = SizeofResource(nullptr, hDialogRC); 1.859 + HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg); 1.860 + DLGTEMPLATEEX* pMyDlgTemplate = 1.861 + (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate); 1.862 + memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg); 1.863 + 1.864 + pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL; 1.865 + 1.866 + rv = DialogBoxIndirectParam(nullptr, (LPCDLGTEMPLATE)pMyDlgTemplate, 1.867 + hwndParent, dlgProc, param); 1.868 + GlobalUnlock(hMyDlgTemplate); 1.869 + GlobalFree(hMyDlgTemplate); 1.870 + } 1.871 + else { 1.872 + rv = DialogBoxParam(nullptr, MAKEINTRESOURCE(idd), hwndParent, 1.873 + dlgProc, param); 1.874 + } 1.875 + 1.876 + return rv; 1.877 +} 1.878 + 1.879 + 1.880 +static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message, 1.881 + WPARAM wParam, LPARAM lParam) 1.882 +{ 1.883 + static int sHeight = 0; 1.884 + 1.885 + bool success; 1.886 + bool enabled; 1.887 + 1.888 + switch (message) { 1.889 + case WM_INITDIALOG: { 1.890 + GetThemeSizes(hwndDlg); 1.891 + RECT r; 1.892 + GetClientRect(hwndDlg, &r); 1.893 + sHeight = r.bottom - r.top; 1.894 + 1.895 + SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str()); 1.896 + HICON hIcon = LoadIcon(GetModuleHandle(nullptr), 1.897 + MAKEINTRESOURCE(IDI_MAINICON)); 1.898 + SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon); 1.899 + SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon); 1.900 + 1.901 + // resize the "View Report" button based on the string length 1.902 + RECT rect; 1.903 + HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON); 1.904 + GetRelativeRect(hwnd, hwndDlg, &rect); 1.905 + ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0); 1.906 + SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str()); 1.907 + 1.908 + hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK); 1.909 + GetRelativeRect(hwnd, hwndDlg, &rect); 1.910 + long maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false, 1.911 + gCheckboxPadding); 1.912 + SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK, 1.913 + Str(ST_CHECKSUBMIT).c_str()); 1.914 + 1.915 + if (!CheckBoolKey(gCrashReporterKey.c_str(), 1.916 + SUBMIT_REPORT_VALUE, &enabled)) 1.917 + enabled = ShouldEnableSending(); 1.918 + 1.919 + CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED 1.920 + : BST_UNCHECKED); 1.921 + SubmitReportChecked(hwndDlg); 1.922 + 1.923 + HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT); 1.924 + WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment, 1.925 + GWLP_WNDPROC, 1.926 + (LONG_PTR)EditSubclassProc); 1.927 + 1.928 + // Subclass comment edit control to get placeholder text 1.929 + SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc); 1.930 + wstring commentGrayText = Str(ST_COMMENTGRAYTEXT); 1.931 + wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t)); 1.932 + wcscpy(hMem, commentGrayText.c_str()); 1.933 + SetProp(hwndComment, L"PROP_GRAYTEXT", hMem); 1.934 + 1.935 + hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK); 1.936 + GetRelativeRect(hwnd, hwndDlg, &rect); 1.937 + long diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false, 1.938 + gCheckboxPadding); 1.939 + maxdiff = std::max(diff, maxdiff); 1.940 + SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str()); 1.941 + 1.942 + // want this on by default 1.943 + if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) && 1.944 + !enabled) { 1.945 + CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED); 1.946 + } else { 1.947 + CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED); 1.948 + } 1.949 + 1.950 + hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK); 1.951 + GetRelativeRect(hwnd, hwndDlg, &rect); 1.952 + diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false, 1.953 + gCheckboxPadding); 1.954 + maxdiff = std::max(diff, maxdiff); 1.955 + SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str()); 1.956 + 1.957 + if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) && 1.958 + enabled) { 1.959 + CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED); 1.960 + } else { 1.961 + CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED); 1.962 + } 1.963 + 1.964 + wstring email; 1.965 + if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) { 1.966 + SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str()); 1.967 + } 1.968 + 1.969 + // Subclass email edit control to get placeholder text 1.970 + HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT); 1.971 + OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail, 1.972 + GWLP_WNDPROC, 1.973 + (LONG_PTR)EditSubclassProc); 1.974 + SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc); 1.975 + wstring emailGrayText = Str(ST_EMAILGRAYTEXT); 1.976 + hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t)); 1.977 + wcscpy(hMem, emailGrayText.c_str()); 1.978 + SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem); 1.979 + 1.980 + SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str()); 1.981 + 1.982 + RECT closeRect; 1.983 + HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON); 1.984 + GetRelativeRect(hwndClose, hwndDlg, &closeRect); 1.985 + 1.986 + RECT restartRect; 1.987 + HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON); 1.988 + GetRelativeRect(hwndRestart, hwndDlg, &restartRect); 1.989 + 1.990 + // set the close button text and shift the buttons around 1.991 + // since the size may need to change 1.992 + int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT), 1.993 + true, 0); 1.994 + restartRect.left -= sizeDiff; 1.995 + restartRect.right -= sizeDiff; 1.996 + SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str()); 1.997 + 1.998 + if (gRestartArgs.size() > 0) { 1.999 + // Resize restart button to fit text 1.1000 + ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0); 1.1001 + SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str()); 1.1002 + } else { 1.1003 + // No restart arguments, so just hide the restart button 1.1004 + SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false); 1.1005 + } 1.1006 + // See if we need to widen the window 1.1007 + // Leave 6 pixels on either side + 6 pixels between the buttons 1.1008 + int neededSize = closeRect.right - closeRect.left + 1.1009 + restartRect.right - restartRect.left + 6 * 3; 1.1010 + GetClientRect(hwndDlg, &r); 1.1011 + // We may already have resized one of the checkboxes above 1.1012 + maxdiff = std::max(maxdiff, neededSize - (r.right - r.left)); 1.1013 + 1.1014 + if (maxdiff > 0) { 1.1015 + // widen window 1.1016 + GetWindowRect(hwndDlg, &r); 1.1017 + r.right += maxdiff; 1.1018 + MoveWindow(hwndDlg, r.left, r.top, 1.1019 + r.right - r.left, r.bottom - r.top, TRUE); 1.1020 + // shift both buttons right 1.1021 + if (restartRect.left + maxdiff < 6) 1.1022 + maxdiff += 6; 1.1023 + closeRect.left += maxdiff; 1.1024 + closeRect.right += maxdiff; 1.1025 + restartRect.left += maxdiff; 1.1026 + restartRect.right += maxdiff; 1.1027 + MoveWindow(hwndClose, closeRect.left, closeRect.top, 1.1028 + closeRect.right - closeRect.left, 1.1029 + closeRect.bottom - closeRect.top, 1.1030 + TRUE); 1.1031 + StretchControlsToFit(hwndDlg); 1.1032 + } 1.1033 + // need to move the restart button regardless 1.1034 + MoveWindow(hwndRestart, restartRect.left, restartRect.top, 1.1035 + restartRect.right - restartRect.left, 1.1036 + restartRect.bottom - restartRect.top, 1.1037 + TRUE); 1.1038 + 1.1039 + // Resize the description text last, in case the window was resized 1.1040 + // before this. 1.1041 + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, 1.1042 + EM_SETEVENTMASK, (WPARAM)nullptr, 1.1043 + ENM_REQUESTRESIZE); 1.1044 + 1.1045 + wstring description = Str(ST_CRASHREPORTERHEADER); 1.1046 + description += L"\n\n"; 1.1047 + description += Str(ST_CRASHREPORTERDESCRIPTION); 1.1048 + SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str()); 1.1049 + 1.1050 + 1.1051 + // Make the title bold. 1.1052 + CHARFORMAT fmt = { 0, }; 1.1053 + fmt.cbSize = sizeof(fmt); 1.1054 + fmt.dwMask = CFM_BOLD; 1.1055 + fmt.dwEffects = CFE_BOLD; 1.1056 + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 1.1057 + 0, Str(ST_CRASHREPORTERHEADER).length()); 1.1058 + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT, 1.1059 + SCF_SELECTION, (LPARAM)&fmt); 1.1060 + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0); 1.1061 + // Force redraw. 1.1062 + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, 1.1063 + EM_SETTARGETDEVICE, (WPARAM)nullptr, 0); 1.1064 + // Force resize. 1.1065 + SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, 1.1066 + EM_REQUESTRESIZE, 0, 0); 1.1067 + 1.1068 + // if no URL was given, hide the URL checkbox 1.1069 + if (gQueryParameters.find(L"URL") == gQueryParameters.end()) { 1.1070 + RECT urlCheckRect, emailCheckRect; 1.1071 + GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect); 1.1072 + GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect); 1.1073 + 1.1074 + SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false); 1.1075 + 1.1076 + gAttachedBottom.erase(IDC_VIEWREPORTBUTTON); 1.1077 + gAttachedBottom.erase(IDC_SUBMITREPORTCHECK); 1.1078 + gAttachedBottom.erase(IDC_COMMENTTEXT); 1.1079 + 1.1080 + StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top); 1.1081 + 1.1082 + gAttachedBottom.insert(IDC_VIEWREPORTBUTTON); 1.1083 + gAttachedBottom.insert(IDC_SUBMITREPORTCHECK); 1.1084 + gAttachedBottom.insert(IDC_COMMENTTEXT); 1.1085 + } 1.1086 + 1.1087 + MaybeResizeProgressText(hwndDlg); 1.1088 + 1.1089 + // Open the AVI resource for the throbber 1.1090 + Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER), 1.1091 + MAKEINTRESOURCE(IDR_THROBBER)); 1.1092 + 1.1093 + UpdateURL(hwndDlg); 1.1094 + UpdateEmail(hwndDlg); 1.1095 + 1.1096 + SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK)); 1.1097 + return FALSE; 1.1098 + } 1.1099 + case WM_SIZE: { 1.1100 + ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight); 1.1101 + sHeight = HIWORD(lParam); 1.1102 + InvalidateRect(hwndDlg, nullptr, TRUE); 1.1103 + return FALSE; 1.1104 + } 1.1105 + case WM_NOTIFY: { 1.1106 + NMHDR* notification = reinterpret_cast<NMHDR*>(lParam); 1.1107 + if (notification->code == EN_REQUESTRESIZE) { 1.1108 + // Resizing the rich edit control to fit the description text. 1.1109 + REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam); 1.1110 + RECT newSize = reqresize->rc; 1.1111 + RECT oldSize; 1.1112 + GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize); 1.1113 + 1.1114 + // resize the text box as requested 1.1115 + MoveWindow(notification->hwndFrom, newSize.left, newSize.top, 1.1116 + newSize.right - newSize.left, newSize.bottom - newSize.top, 1.1117 + TRUE); 1.1118 + 1.1119 + // Resize the dialog to fit (the WM_SIZE handler will move the controls) 1.1120 + StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom); 1.1121 + } 1.1122 + return FALSE; 1.1123 + } 1.1124 + case WM_COMMAND: { 1.1125 + if (HIWORD(wParam) == BN_CLICKED) { 1.1126 + switch(LOWORD(wParam)) { 1.1127 + case IDC_VIEWREPORTBUTTON: 1.1128 + DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg, 1.1129 + (DLGPROC)ViewReportDialogProc, 0); 1.1130 + break; 1.1131 + case IDC_SUBMITREPORTCHECK: 1.1132 + SubmitReportChecked(hwndDlg); 1.1133 + break; 1.1134 + case IDC_INCLUDEURLCHECK: 1.1135 + UpdateURL(hwndDlg); 1.1136 + break; 1.1137 + case IDC_EMAILMECHECK: 1.1138 + UpdateEmail(hwndDlg); 1.1139 + break; 1.1140 + case IDC_CLOSEBUTTON: 1.1141 + MaybeSendReport(hwndDlg); 1.1142 + break; 1.1143 + case IDC_RESTARTBUTTON: 1.1144 + RestartApplication(); 1.1145 + MaybeSendReport(hwndDlg); 1.1146 + break; 1.1147 + } 1.1148 + } else if (HIWORD(wParam) == EN_CHANGE) { 1.1149 + switch(LOWORD(wParam)) { 1.1150 + case IDC_EMAILTEXT: 1.1151 + UpdateEmail(hwndDlg); 1.1152 + break; 1.1153 + case IDC_COMMENTTEXT: 1.1154 + UpdateComment(hwndDlg); 1.1155 + } 1.1156 + } 1.1157 + 1.1158 + return FALSE; 1.1159 + } 1.1160 + case WM_UPLOADCOMPLETE: { 1.1161 + WaitForSingleObject(gThreadHandle, INFINITE); 1.1162 + success = (wParam == 1); 1.1163 + SendCompleted(success, WideToUTF8(gSendData.serverResponse)); 1.1164 + // hide throbber 1.1165 + Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER)); 1.1166 + SetDlgItemVisible(hwndDlg, IDC_THROBBER, false); 1.1167 + 1.1168 + SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, 1.1169 + success ? 1.1170 + Str(ST_REPORTSUBMITSUCCESS).c_str() : 1.1171 + Str(ST_SUBMITFAILED).c_str()); 1.1172 + MaybeResizeProgressText(hwndDlg); 1.1173 + // close dialog after 5 seconds 1.1174 + SetTimer(hwndDlg, 0, 5000, nullptr); 1.1175 + // 1.1176 + return TRUE; 1.1177 + } 1.1178 + 1.1179 + case WM_LBUTTONDOWN: { 1.1180 + HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT); 1.1181 + POINT p = { LOWORD(lParam), HIWORD(lParam) }; 1.1182 + // if the email edit control is clicked, enable it, 1.1183 + // check the email checkbox, and focus the email edit control 1.1184 + if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail && 1.1185 + IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) && 1.1186 + !IsWindowEnabled(hwndEmail) && 1.1187 + IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) { 1.1188 + CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED); 1.1189 + UpdateEmail(hwndDlg); 1.1190 + SetFocus(hwndEmail); 1.1191 + } 1.1192 + break; 1.1193 + } 1.1194 + 1.1195 + case WM_TIMER: { 1.1196 + // The "1" gets used down in UIShowCrashUI to indicate that we at least 1.1197 + // tried to send the report. 1.1198 + EndCrashReporterDialog(hwndDlg, 1); 1.1199 + return FALSE; 1.1200 + } 1.1201 + 1.1202 + case WM_CLOSE: { 1.1203 + EndCrashReporterDialog(hwndDlg, 0); 1.1204 + return FALSE; 1.1205 + } 1.1206 + } 1.1207 + return FALSE; 1.1208 +} 1.1209 + 1.1210 +static wstring UTF8ToWide(const string& utf8, bool *success) 1.1211 +{ 1.1212 + wchar_t* buffer = nullptr; 1.1213 + int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), 1.1214 + -1, nullptr, 0); 1.1215 + if(buffer_size == 0) { 1.1216 + if (success) 1.1217 + *success = false; 1.1218 + return L""; 1.1219 + } 1.1220 + 1.1221 + buffer = new wchar_t[buffer_size]; 1.1222 + if(buffer == nullptr) { 1.1223 + if (success) 1.1224 + *success = false; 1.1225 + return L""; 1.1226 + } 1.1227 + 1.1228 + MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(), 1.1229 + -1, buffer, buffer_size); 1.1230 + wstring str = buffer; 1.1231 + delete [] buffer; 1.1232 + 1.1233 + if (success) 1.1234 + *success = true; 1.1235 + 1.1236 + return str; 1.1237 +} 1.1238 + 1.1239 +static string WideToMBCP(const wstring& wide, 1.1240 + unsigned int cp, 1.1241 + bool* success = nullptr) 1.1242 +{ 1.1243 + char* buffer = nullptr; 1.1244 + int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(), 1.1245 + -1, nullptr, 0, nullptr, nullptr); 1.1246 + if(buffer_size == 0) { 1.1247 + if (success) 1.1248 + *success = false; 1.1249 + return ""; 1.1250 + } 1.1251 + 1.1252 + buffer = new char[buffer_size]; 1.1253 + if(buffer == nullptr) { 1.1254 + if (success) 1.1255 + *success = false; 1.1256 + return ""; 1.1257 + } 1.1258 + 1.1259 + WideCharToMultiByte(cp, 0, wide.c_str(), 1.1260 + -1, buffer, buffer_size, nullptr, nullptr); 1.1261 + string mb = buffer; 1.1262 + delete [] buffer; 1.1263 + 1.1264 + if (success) 1.1265 + *success = true; 1.1266 + 1.1267 + return mb; 1.1268 +} 1.1269 + 1.1270 +string WideToUTF8(const wstring& wide, bool* success) 1.1271 +{ 1.1272 + return WideToMBCP(wide, CP_UTF8, success); 1.1273 +} 1.1274 + 1.1275 +/* === Crashreporter UI Functions === */ 1.1276 + 1.1277 +bool UIInit() 1.1278 +{ 1.1279 + for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) { 1.1280 + gAttachedBottom.insert(kDefaultAttachedBottom[i]); 1.1281 + } 1.1282 + 1.1283 + DoInitCommonControls(); 1.1284 + 1.1285 + return true; 1.1286 +} 1.1287 + 1.1288 +void UIShutdown() 1.1289 +{ 1.1290 +} 1.1291 + 1.1292 +void UIShowDefaultUI() 1.1293 +{ 1.1294 + MessageBox(nullptr, Str(ST_CRASHREPORTERDEFAULT).c_str(), 1.1295 + L"Crash Reporter", 1.1296 + MB_OK | MB_ICONSTOP); 1.1297 +} 1.1298 + 1.1299 +bool UIShowCrashUI(const string& dumpFile, 1.1300 + const StringTable& queryParameters, 1.1301 + const string& sendURL, 1.1302 + const vector<string>& restartArgs) 1.1303 +{ 1.1304 + gSendData.hDlg = nullptr; 1.1305 + gSendData.dumpFile = UTF8ToWide(dumpFile); 1.1306 + gSendData.sendURL = UTF8ToWide(sendURL); 1.1307 + 1.1308 + for (StringTable::const_iterator i = queryParameters.begin(); 1.1309 + i != queryParameters.end(); 1.1310 + i++) { 1.1311 + gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second); 1.1312 + } 1.1313 + 1.1314 + if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) { 1.1315 + gCrashReporterKey = L"Software\\"; 1.1316 + if (!gQueryParameters[L"Vendor"].empty()) { 1.1317 + gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\"; 1.1318 + } 1.1319 + gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter"; 1.1320 + } 1.1321 + 1.1322 + if (gQueryParameters.find(L"URL") != gQueryParameters.end()) 1.1323 + gURLParameter = gQueryParameters[L"URL"]; 1.1324 + 1.1325 + gRestartArgs = restartArgs; 1.1326 + 1.1327 + if (gStrings.find("isRTL") != gStrings.end() && 1.1328 + gStrings["isRTL"] == "yes") 1.1329 + gRTLlayout = true; 1.1330 + 1.1331 + return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, nullptr, 1.1332 + (DLGPROC)CrashReporterDialogProc, 0); 1.1333 +} 1.1334 + 1.1335 +void UIError_impl(const string& message) 1.1336 +{ 1.1337 + wstring title = Str(ST_CRASHREPORTERTITLE); 1.1338 + if (title.empty()) 1.1339 + title = L"Crash Reporter Error"; 1.1340 + 1.1341 + MessageBox(nullptr, UTF8ToWide(message).c_str(), title.c_str(), 1.1342 + MB_OK | MB_ICONSTOP); 1.1343 +} 1.1344 + 1.1345 +bool UIGetIniPath(string& path) 1.1346 +{ 1.1347 + wchar_t fileName[MAX_PATH]; 1.1348 + if (GetModuleFileName(nullptr, fileName, MAX_PATH)) { 1.1349 + // get crashreporter ini 1.1350 + wchar_t* s = wcsrchr(fileName, '.'); 1.1351 + if (s) { 1.1352 + wcscpy(s, L".ini"); 1.1353 + path = WideToUTF8(fileName); 1.1354 + return true; 1.1355 + } 1.1356 + } 1.1357 + 1.1358 + return false; 1.1359 +} 1.1360 + 1.1361 +bool UIGetSettingsPath(const string& vendor, 1.1362 + const string& product, 1.1363 + string& settings_path) 1.1364 +{ 1.1365 + wchar_t path[MAX_PATH]; 1.1366 + HRESULT hRes = SHGetFolderPath(nullptr, 1.1367 + CSIDL_APPDATA, 1.1368 + nullptr, 1.1369 + 0, 1.1370 + path); 1.1371 + if (FAILED(hRes)) { 1.1372 + // This provides a fallback for getting the path to APPDATA by querying the 1.1373 + // registry when the call to SHGetFolderPath is unable to provide this path 1.1374 + // (Bug 513958). 1.1375 + HKEY key; 1.1376 + DWORD type, size, dwRes; 1.1377 + dwRes = ::RegOpenKeyExW(HKEY_CURRENT_USER, 1.1378 + L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", 1.1379 + 0, 1.1380 + KEY_READ, 1.1381 + &key); 1.1382 + if (dwRes != ERROR_SUCCESS) 1.1383 + return false; 1.1384 + 1.1385 + dwRes = RegQueryValueExW(key, 1.1386 + L"AppData", 1.1387 + nullptr, 1.1388 + &type, 1.1389 + (LPBYTE)&path, 1.1390 + &size); 1.1391 + ::RegCloseKey(key); 1.1392 + // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the 1.1393 + // buffer size must not equal 0, and the buffer size be a multiple of 2. 1.1394 + if (dwRes != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0) 1.1395 + return false; 1.1396 + } 1.1397 + 1.1398 + if (!vendor.empty()) { 1.1399 + PathAppend(path, UTF8ToWide(vendor).c_str()); 1.1400 + } 1.1401 + PathAppend(path, UTF8ToWide(product).c_str()); 1.1402 + PathAppend(path, L"Crash Reports"); 1.1403 + settings_path = WideToUTF8(path); 1.1404 + return true; 1.1405 +} 1.1406 + 1.1407 +bool UIEnsurePathExists(const string& path) 1.1408 +{ 1.1409 + if (CreateDirectory(UTF8ToWide(path).c_str(), nullptr) == 0) { 1.1410 + if (GetLastError() != ERROR_ALREADY_EXISTS) 1.1411 + return false; 1.1412 + } 1.1413 + 1.1414 + return true; 1.1415 +} 1.1416 + 1.1417 +bool UIFileExists(const string& path) 1.1418 +{ 1.1419 + DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str()); 1.1420 + return (attrs != INVALID_FILE_ATTRIBUTES); 1.1421 +} 1.1422 + 1.1423 +bool UIMoveFile(const string& oldfile, const string& newfile) 1.1424 +{ 1.1425 + if (oldfile == newfile) 1.1426 + return true; 1.1427 + 1.1428 + return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str()) 1.1429 + == TRUE; 1.1430 +} 1.1431 + 1.1432 +bool UIDeleteFile(const string& oldfile) 1.1433 +{ 1.1434 + return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE; 1.1435 +} 1.1436 + 1.1437 +ifstream* UIOpenRead(const string& filename) 1.1438 +{ 1.1439 + // adapted from breakpad's src/common/windows/http_upload.cc 1.1440 + 1.1441 + // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a 1.1442 + // wchar_t* filename, so use _wfopen directly in that case. For VC8 and 1.1443 + // later, _wfopen has been deprecated in favor of _wfopen_s, which does 1.1444 + // not exist in earlier versions, so let the ifstream open the file itself. 1.1445 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.1446 + ifstream* file = new ifstream(); 1.1447 + file->open(UTF8ToWide(filename).c_str(), ios::in); 1.1448 +#elif defined(_MSC_VER) 1.1449 + ifstream* file = new ifstream(_wfopen(UTF8ToWide(filename).c_str(), L"r")); 1.1450 +#else // GCC 1.1451 + ifstream* file = new ifstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(), 1.1452 + ios::in); 1.1453 +#endif // _MSC_VER >= 1400 1.1454 + 1.1455 + return file; 1.1456 +} 1.1457 + 1.1458 +ofstream* UIOpenWrite(const string& filename, bool append) // append=false 1.1459 +{ 1.1460 + // adapted from breakpad's src/common/windows/http_upload.cc 1.1461 + 1.1462 + // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a 1.1463 + // wchar_t* filename, so use _wfopen directly in that case. For VC8 and 1.1464 + // later, _wfopen has been deprecated in favor of _wfopen_s, which does 1.1465 + // not exist in earlier versions, so let the ifstream open the file itself. 1.1466 +#if _MSC_VER >= 1400 // MSVC 2005/8 1.1467 + ofstream* file = new ofstream(); 1.1468 + file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app 1.1469 + : ios::out); 1.1470 +#elif defined(_MSC_VER) 1.1471 + ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(), 1.1472 + append ? L"a" : L"w")); 1.1473 +#else // GCC 1.1474 + ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(), 1.1475 + append ? ios::out | ios::app : ios::out); 1.1476 +#endif // _MSC_VER >= 1400 1.1477 + 1.1478 + return file; 1.1479 +} 1.1480 + 1.1481 +struct FileData 1.1482 +{ 1.1483 + FILETIME timestamp; 1.1484 + wstring path; 1.1485 +}; 1.1486 + 1.1487 +static bool CompareFDTime(const FileData& fd1, const FileData& fd2) 1.1488 +{ 1.1489 + return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0; 1.1490 +} 1.1491 + 1.1492 +void UIPruneSavedDumps(const std::string& directory) 1.1493 +{ 1.1494 + wstring wdirectory = UTF8ToWide(directory); 1.1495 + 1.1496 + WIN32_FIND_DATA fdata; 1.1497 + wstring findpath = wdirectory + L"\\*.dmp"; 1.1498 + HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata); 1.1499 + if (dirlist == INVALID_HANDLE_VALUE) 1.1500 + return; 1.1501 + 1.1502 + vector<FileData> dumpfiles; 1.1503 + 1.1504 + for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) { 1.1505 + FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName}; 1.1506 + dumpfiles.push_back(fd); 1.1507 + } 1.1508 + 1.1509 + sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime); 1.1510 + 1.1511 + while (dumpfiles.size() > kSaveCount) { 1.1512 + // get the path of the oldest file 1.1513 + wstring path = (--dumpfiles.end())->path; 1.1514 + DeleteFile(path.c_str()); 1.1515 + 1.1516 + // s/.dmp/.extra/ 1.1517 + path.replace(path.size() - 4, 4, L".extra"); 1.1518 + DeleteFile(path.c_str()); 1.1519 + 1.1520 + dumpfiles.pop_back(); 1.1521 + } 1.1522 +}