toolkit/crashreporter/client/crashreporter_win.cpp

Sat, 03 Jan 2015 20:18:00 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Sat, 03 Jan 2015 20:18:00 +0100
branch
TOR_BUG_3246
changeset 7
129ffea94266
permissions
-rw-r--r--

Conditionally enable double key logic according to:
private browsing mode or privacy.thirdparty.isolate preference and
implement in GetCookieStringCommon and FindCookie where it counts...
With some reservations of how to convince FindCookie users to test
condition and pass a nullptr when disabling double key logic.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5
michael@0 6 #ifdef WIN32_LEAN_AND_MEAN
michael@0 7 #undef WIN32_LEAN_AND_MEAN
michael@0 8 #endif
michael@0 9
michael@0 10 #define NOMINMAX
michael@0 11
michael@0 12 #include "crashreporter.h"
michael@0 13
michael@0 14 #include <windows.h>
michael@0 15 #include <commctrl.h>
michael@0 16 #include <richedit.h>
michael@0 17 #include <shellapi.h>
michael@0 18 #include <shlobj.h>
michael@0 19 #include <shlwapi.h>
michael@0 20 #include <math.h>
michael@0 21 #include <set>
michael@0 22 #include <algorithm>
michael@0 23 #include "resource.h"
michael@0 24 #include "client/windows/sender/crash_report_sender.h"
michael@0 25 #include "common/windows/string_utils-inl.h"
michael@0 26 #include "mozilla/NullPtr.h"
michael@0 27
michael@0 28 #define CRASH_REPORTER_VALUE L"Enabled"
michael@0 29 #define SUBMIT_REPORT_VALUE L"SubmitCrashReport"
michael@0 30 #define SUBMIT_REPORT_OLD L"SubmitReport"
michael@0 31 #define INCLUDE_URL_VALUE L"IncludeURL"
michael@0 32 #define EMAIL_ME_VALUE L"EmailMe"
michael@0 33 #define EMAIL_VALUE L"Email"
michael@0 34 #define MAX_EMAIL_LENGTH 1024
michael@0 35
michael@0 36 #define WM_UPLOADCOMPLETE WM_APP
michael@0 37
michael@0 38 // Thanks, Windows.h :(
michael@0 39 #undef min
michael@0 40 #undef max
michael@0 41
michael@0 42 using std::string;
michael@0 43 using std::wstring;
michael@0 44 using std::map;
michael@0 45 using std::vector;
michael@0 46 using std::set;
michael@0 47 using std::ios;
michael@0 48 using std::ifstream;
michael@0 49 using std::ofstream;
michael@0 50
michael@0 51 using namespace CrashReporter;
michael@0 52
michael@0 53 typedef struct {
michael@0 54 HWND hDlg;
michael@0 55 wstring dumpFile;
michael@0 56 map<wstring,wstring> queryParameters;
michael@0 57 wstring sendURL;
michael@0 58
michael@0 59 wstring serverResponse;
michael@0 60 } SendThreadData;
michael@0 61
michael@0 62 /*
michael@0 63 * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx
michael@0 64 * "The DLGTEMPLATEEX structure is not defined in any standard header file.
michael@0 65 * The structure definition is provided here to explain the format of an
michael@0 66 * extended template for a dialog box.
michael@0 67 */
michael@0 68 typedef struct {
michael@0 69 WORD dlgVer;
michael@0 70 WORD signature;
michael@0 71 DWORD helpID;
michael@0 72 DWORD exStyle;
michael@0 73 // There's more to this struct, but it has weird variable-length
michael@0 74 // members, and I only actually need to touch exStyle on an existing
michael@0 75 // instance, so I've omitted the rest.
michael@0 76 } DLGTEMPLATEEX;
michael@0 77
michael@0 78 static HANDLE gThreadHandle;
michael@0 79 static SendThreadData gSendData = { 0, };
michael@0 80 static vector<string> gRestartArgs;
michael@0 81 static map<wstring,wstring> gQueryParameters;
michael@0 82 static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
michael@0 83 static wstring gURLParameter;
michael@0 84 static int gCheckboxPadding = 6;
michael@0 85 static bool gRTLlayout = false;
michael@0 86
michael@0 87 // When vertically resizing the dialog, these items should move down
michael@0 88 static set<UINT> gAttachedBottom;
michael@0 89
michael@0 90 // Default set of items for gAttachedBottom
michael@0 91 static const UINT kDefaultAttachedBottom[] = {
michael@0 92 IDC_SUBMITREPORTCHECK,
michael@0 93 IDC_VIEWREPORTBUTTON,
michael@0 94 IDC_COMMENTTEXT,
michael@0 95 IDC_INCLUDEURLCHECK,
michael@0 96 IDC_EMAILMECHECK,
michael@0 97 IDC_EMAILTEXT,
michael@0 98 IDC_PROGRESSTEXT,
michael@0 99 IDC_THROBBER,
michael@0 100 IDC_CLOSEBUTTON,
michael@0 101 IDC_RESTARTBUTTON,
michael@0 102 };
michael@0 103
michael@0 104 static wstring UTF8ToWide(const string& utf8, bool *success = 0);
michael@0 105 static DWORD WINAPI SendThreadProc(LPVOID param);
michael@0 106
michael@0 107 static wstring Str(const char* key)
michael@0 108 {
michael@0 109 return UTF8ToWide(gStrings[key]);
michael@0 110 }
michael@0 111
michael@0 112 /* === win32 helper functions === */
michael@0 113
michael@0 114 static void DoInitCommonControls()
michael@0 115 {
michael@0 116 INITCOMMONCONTROLSEX ic;
michael@0 117 ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
michael@0 118 ic.dwICC = ICC_PROGRESS_CLASS;
michael@0 119 InitCommonControlsEx(&ic);
michael@0 120 // also get the rich edit control
michael@0 121 LoadLibrary(L"Msftedit.dll");
michael@0 122 }
michael@0 123
michael@0 124 static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
michael@0 125 {
michael@0 126 DWORD type, dataSize;
michael@0 127 dataSize = sizeof(DWORD);
michael@0 128 if (RegQueryValueEx(hRegKey, valueName, nullptr,
michael@0 129 &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS &&
michael@0 130 type == REG_DWORD)
michael@0 131 return true;
michael@0 132
michael@0 133 return false;
michael@0 134 }
michael@0 135
michael@0 136 // Removes a value from HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER, if it exists.
michael@0 137 static void RemoveUnusedValues(const wchar_t* key, LPCTSTR valueName)
michael@0 138 {
michael@0 139 HKEY hRegKey;
michael@0 140
michael@0 141 if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, key, 0, KEY_SET_VALUE, &hRegKey)
michael@0 142 == ERROR_SUCCESS) {
michael@0 143 RegDeleteValue(hRegKey, valueName);
michael@0 144 RegCloseKey(hRegKey);
michael@0 145 }
michael@0 146
michael@0 147 if (RegOpenKeyEx(HKEY_CURRENT_USER, key, 0, KEY_SET_VALUE, &hRegKey)
michael@0 148 == ERROR_SUCCESS) {
michael@0 149 RegDeleteValue(hRegKey, valueName);
michael@0 150 RegCloseKey(hRegKey);
michael@0 151 }
michael@0 152 }
michael@0 153
michael@0 154 static bool CheckBoolKey(const wchar_t* key,
michael@0 155 const wchar_t* valueName,
michael@0 156 bool* enabled)
michael@0 157 {
michael@0 158 /*
michael@0 159 * NOTE! This code needs to stay in sync with the preference checking
michael@0 160 * code in in nsExceptionHandler.cpp.
michael@0 161 */
michael@0 162 *enabled = false;
michael@0 163 bool found = false;
michael@0 164 HKEY hRegKey;
michael@0 165 DWORD val;
michael@0 166 // see if our reg key is set globally
michael@0 167 if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
michael@0 168 if (GetBoolValue(hRegKey, valueName, &val)) {
michael@0 169 *enabled = (val == 1);
michael@0 170 found = true;
michael@0 171 }
michael@0 172 RegCloseKey(hRegKey);
michael@0 173 } else {
michael@0 174 // look for it in user settings
michael@0 175 if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
michael@0 176 if (GetBoolValue(hRegKey, valueName, &val)) {
michael@0 177 *enabled = (val == 1);
michael@0 178 found = true;
michael@0 179 }
michael@0 180 RegCloseKey(hRegKey);
michael@0 181 }
michael@0 182 }
michael@0 183
michael@0 184 return found;
michael@0 185 }
michael@0 186
michael@0 187 static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
michael@0 188 {
michael@0 189 /*
michael@0 190 * NOTE! This code needs to stay in sync with the preference setting
michael@0 191 * code in in nsExceptionHandler.cpp.
michael@0 192 */
michael@0 193 HKEY hRegKey;
michael@0 194
michael@0 195 // remove the old value from the registry if it exists
michael@0 196 RemoveUnusedValues(key, SUBMIT_REPORT_OLD);
michael@0 197
michael@0 198 if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
michael@0 199 DWORD data = (enabled ? 1 : 0);
michael@0 200 RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
michael@0 201 RegCloseKey(hRegKey);
michael@0 202 }
michael@0 203 }
michael@0 204
michael@0 205 static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value)
michael@0 206 {
michael@0 207 DWORD type, dataSize;
michael@0 208 wchar_t buf[2048];
michael@0 209 dataSize = sizeof(buf);
michael@0 210 if (RegQueryValueEx(hRegKey, valueName, nullptr,
michael@0 211 &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS &&
michael@0 212 type == REG_SZ) {
michael@0 213 value = buf;
michael@0 214 return true;
michael@0 215 }
michael@0 216
michael@0 217 return false;
michael@0 218 }
michael@0 219
michael@0 220 static bool GetStringKey(const wchar_t* key,
michael@0 221 const wchar_t* valueName,
michael@0 222 wstring& value)
michael@0 223 {
michael@0 224 value = L"";
michael@0 225 bool found = false;
michael@0 226 HKEY hRegKey;
michael@0 227 // see if our reg key is set globally
michael@0 228 if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
michael@0 229 if (GetStringValue(hRegKey, valueName, value)) {
michael@0 230 found = true;
michael@0 231 }
michael@0 232 RegCloseKey(hRegKey);
michael@0 233 } else {
michael@0 234 // look for it in user settings
michael@0 235 if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
michael@0 236 if (GetStringValue(hRegKey, valueName, value)) {
michael@0 237 found = true;
michael@0 238 }
michael@0 239 RegCloseKey(hRegKey);
michael@0 240 }
michael@0 241 }
michael@0 242
michael@0 243 return found;
michael@0 244 }
michael@0 245
michael@0 246 static void SetStringKey(const wchar_t* key,
michael@0 247 const wchar_t* valueName,
michael@0 248 const wstring& value)
michael@0 249 {
michael@0 250 HKEY hRegKey;
michael@0 251 if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
michael@0 252 RegSetValueEx(hRegKey, valueName, 0, REG_SZ,
michael@0 253 (LPBYTE)value.c_str(),
michael@0 254 (value.length() + 1) * sizeof(wchar_t));
michael@0 255 RegCloseKey(hRegKey);
michael@0 256 }
michael@0 257 }
michael@0 258
michael@0 259 static string FormatLastError()
michael@0 260 {
michael@0 261 DWORD err = GetLastError();
michael@0 262 LPWSTR s;
michael@0 263 string message = "Crash report submission failed: ";
michael@0 264 // odds are it's a WinInet error
michael@0 265 HANDLE hInetModule = GetModuleHandle(L"WinInet.dll");
michael@0 266 if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
michael@0 267 FORMAT_MESSAGE_FROM_SYSTEM |
michael@0 268 FORMAT_MESSAGE_FROM_HMODULE,
michael@0 269 hInetModule,
michael@0 270 err,
michael@0 271 0,
michael@0 272 (LPWSTR)&s,
michael@0 273 0,
michael@0 274 nullptr) != 0) {
michael@0 275 message += WideToUTF8(s, nullptr);
michael@0 276 LocalFree(s);
michael@0 277 // strip off any trailing newlines
michael@0 278 string::size_type n = message.find_last_not_of("\r\n");
michael@0 279 if (n < message.size() - 1) {
michael@0 280 message.erase(n+1);
michael@0 281 }
michael@0 282 }
michael@0 283 else {
michael@0 284 char buf[64];
michael@0 285 sprintf(buf, "Unknown error, error code: 0x%08x", err);
michael@0 286 message += buf;
michael@0 287 }
michael@0 288 return message;
michael@0 289 }
michael@0 290
michael@0 291 #define TS_DRAW 2
michael@0 292 #define BP_CHECKBOX 3
michael@0 293
michael@0 294 typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
michael@0 295 typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
michael@0 296 typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
michael@0 297 int iStateId, RECT* prc, int ts,
michael@0 298 SIZE* psz);
michael@0 299 typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
michael@0 300 int iStateId, const RECT* pRect,
michael@0 301 RECT* pContentRect);
michael@0 302
michael@0 303
michael@0 304 static void GetThemeSizes(HWND hwnd)
michael@0 305 {
michael@0 306 HMODULE themeDLL = LoadLibrary(L"uxtheme.dll");
michael@0 307
michael@0 308 if (!themeDLL)
michael@0 309 return;
michael@0 310
michael@0 311 OpenThemeDataPtr openTheme =
michael@0 312 (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData");
michael@0 313 CloseThemeDataPtr closeTheme =
michael@0 314 (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData");
michael@0 315 GetThemePartSizePtr getThemePartSize =
michael@0 316 (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize");
michael@0 317
michael@0 318 if (!openTheme || !closeTheme || !getThemePartSize) {
michael@0 319 FreeLibrary(themeDLL);
michael@0 320 return;
michael@0 321 }
michael@0 322
michael@0 323 HANDLE buttonTheme = openTheme(hwnd, L"Button");
michael@0 324 if (!buttonTheme) {
michael@0 325 FreeLibrary(themeDLL);
michael@0 326 return;
michael@0 327 }
michael@0 328 HDC hdc = GetDC(hwnd);
michael@0 329 SIZE s;
michael@0 330 getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, nullptr, TS_DRAW, &s);
michael@0 331 gCheckboxPadding = s.cx;
michael@0 332 closeTheme(buttonTheme);
michael@0 333 FreeLibrary(themeDLL);
michael@0 334 }
michael@0 335
michael@0 336 // Gets the position of a window relative to another window's client area
michael@0 337 static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r)
michael@0 338 {
michael@0 339 GetWindowRect(hwnd, r);
michael@0 340 MapWindowPoints(nullptr, hwndParent, (POINT*)r, 2);
michael@0 341 }
michael@0 342
michael@0 343 static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
michael@0 344 {
michael@0 345 HWND hwnd = GetDlgItem(hwndDlg, item);
michael@0 346
michael@0 347 ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE);
michael@0 348 }
michael@0 349
michael@0 350 static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled)
michael@0 351 {
michael@0 352 HWND hwnd = GetDlgItem(hwndDlg, item);
michael@0 353 LONG style = GetWindowLong(hwnd, GWL_STYLE);
michael@0 354 if (!disabled)
michael@0 355 style |= WS_DISABLED;
michael@0 356 else
michael@0 357 style &= ~WS_DISABLED;
michael@0 358
michael@0 359 SetWindowLong(hwnd, GWL_STYLE, style);
michael@0 360 }
michael@0 361
michael@0 362 /* === Crash Reporting Dialog === */
michael@0 363
michael@0 364 static void StretchDialog(HWND hwndDlg, int ydiff)
michael@0 365 {
michael@0 366 RECT r;
michael@0 367 GetWindowRect(hwndDlg, &r);
michael@0 368 r.bottom += ydiff;
michael@0 369 MoveWindow(hwndDlg, r.left, r.top,
michael@0 370 r.right - r.left, r.bottom - r.top, TRUE);
michael@0 371 }
michael@0 372
michael@0 373 static void ReflowDialog(HWND hwndDlg, int ydiff)
michael@0 374 {
michael@0 375 // Move items attached to the bottom down/up by as much as
michael@0 376 // the window resize
michael@0 377 for (set<UINT>::const_iterator item = gAttachedBottom.begin();
michael@0 378 item != gAttachedBottom.end();
michael@0 379 item++) {
michael@0 380 RECT r;
michael@0 381 HWND hwnd = GetDlgItem(hwndDlg, *item);
michael@0 382 GetRelativeRect(hwnd, hwndDlg, &r);
michael@0 383 r.top += ydiff;
michael@0 384 r.bottom += ydiff;
michael@0 385 MoveWindow(hwnd, r.left, r.top,
michael@0 386 r.right - r.left, r.bottom - r.top, TRUE);
michael@0 387 }
michael@0 388 }
michael@0 389
michael@0 390 static DWORD WINAPI SendThreadProc(LPVOID param)
michael@0 391 {
michael@0 392 bool finishedOk;
michael@0 393 SendThreadData* td = (SendThreadData*)param;
michael@0 394
michael@0 395 if (td->sendURL.empty()) {
michael@0 396 finishedOk = false;
michael@0 397 LogMessage("No server URL, not sending report");
michael@0 398 } else {
michael@0 399 google_breakpad::CrashReportSender sender(L"");
michael@0 400 finishedOk = (sender.SendCrashReport(td->sendURL,
michael@0 401 td->queryParameters,
michael@0 402 td->dumpFile,
michael@0 403 &td->serverResponse)
michael@0 404 == google_breakpad::RESULT_SUCCEEDED);
michael@0 405 if (finishedOk) {
michael@0 406 LogMessage("Crash report submitted successfully");
michael@0 407 }
michael@0 408 else {
michael@0 409 // get an error string and print it to the log
michael@0 410 //XXX: would be nice to get the HTTP status code here, filed:
michael@0 411 // http://code.google.com/p/google-breakpad/issues/detail?id=220
michael@0 412 LogMessage(FormatLastError());
michael@0 413 }
michael@0 414 }
michael@0 415
michael@0 416 PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
michael@0 417
michael@0 418 return 0;
michael@0 419 }
michael@0 420
michael@0 421 static void EndCrashReporterDialog(HWND hwndDlg, int code)
michael@0 422 {
michael@0 423 // Save the current values to the registry
michael@0 424 wchar_t email[MAX_EMAIL_LENGTH];
michael@0 425 GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
michael@0 426 sizeof(email) / sizeof(email[0]));
michael@0 427 SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email);
michael@0 428
michael@0 429 SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE,
michael@0 430 IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0);
michael@0 431 SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE,
michael@0 432 IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0);
michael@0 433 SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE,
michael@0 434 IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
michael@0 435
michael@0 436 EndDialog(hwndDlg, code);
michael@0 437 }
michael@0 438
michael@0 439 static void MaybeResizeProgressText(HWND hwndDlg)
michael@0 440 {
michael@0 441 HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT);
michael@0 442 HDC hdc = GetDC(hwndProgress);
michael@0 443 HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0);
michael@0 444 if (hfont)
michael@0 445 SelectObject(hdc, hfont);
michael@0 446 SIZE size;
michael@0 447 RECT rect;
michael@0 448 GetRelativeRect(hwndProgress, hwndDlg, &rect);
michael@0 449
michael@0 450 wchar_t text[1024];
michael@0 451 GetWindowText(hwndProgress, text, 1024);
michael@0 452
michael@0 453 if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size))
michael@0 454 return;
michael@0 455
michael@0 456 if (size.cx < (rect.right - rect.left))
michael@0 457 return;
michael@0 458
michael@0 459 // Figure out how much we need to resize things vertically
michael@0 460 // This is sort of a fudge, but it should be good enough.
michael@0 461 int wantedHeight = size.cy *
michael@0 462 (int)ceil((float)size.cx / (float)(rect.right - rect.left));
michael@0 463 int diff = wantedHeight - (rect.bottom - rect.top);
michael@0 464 if (diff <= 0)
michael@0 465 return;
michael@0 466
michael@0 467 MoveWindow(hwndProgress, rect.left, rect.top,
michael@0 468 rect.right - rect.left,
michael@0 469 wantedHeight,
michael@0 470 TRUE);
michael@0 471
michael@0 472 gAttachedBottom.clear();
michael@0 473 gAttachedBottom.insert(IDC_CLOSEBUTTON);
michael@0 474 gAttachedBottom.insert(IDC_RESTARTBUTTON);
michael@0 475
michael@0 476 StretchDialog(hwndDlg, diff);
michael@0 477
michael@0 478 for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
michael@0 479 gAttachedBottom.insert(kDefaultAttachedBottom[i]);
michael@0 480 }
michael@0 481 }
michael@0 482
michael@0 483 static void MaybeSendReport(HWND hwndDlg)
michael@0 484 {
michael@0 485 if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
michael@0 486 EndCrashReporterDialog(hwndDlg, 0);
michael@0 487 return;
michael@0 488 }
michael@0 489
michael@0 490 // disable all the form controls
michael@0 491 EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false);
michael@0 492 EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false);
michael@0 493 EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false);
michael@0 494 EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false);
michael@0 495 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false);
michael@0 496 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
michael@0 497 EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false);
michael@0 498 EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false);
michael@0 499
michael@0 500 SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str());
michael@0 501 MaybeResizeProgressText(hwndDlg);
michael@0 502 // start throbber
michael@0 503 // play entire AVI, and loop
michael@0 504 Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1);
michael@0 505 SetDlgItemVisible(hwndDlg, IDC_THROBBER, true);
michael@0 506 gThreadHandle = nullptr;
michael@0 507 gSendData.hDlg = hwndDlg;
michael@0 508 gSendData.queryParameters = gQueryParameters;
michael@0 509
michael@0 510 gThreadHandle = CreateThread(nullptr, 0, SendThreadProc, &gSendData, 0,
michael@0 511 nullptr);
michael@0 512 }
michael@0 513
michael@0 514 static void RestartApplication()
michael@0 515 {
michael@0 516 wstring cmdLine;
michael@0 517
michael@0 518 for (unsigned int i = 0; i < gRestartArgs.size(); i++) {
michael@0 519 cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" ";
michael@0 520 }
michael@0 521
michael@0 522 STARTUPINFO si;
michael@0 523 PROCESS_INFORMATION pi;
michael@0 524
michael@0 525 ZeroMemory(&si, sizeof(si));
michael@0 526 si.cb = sizeof(si);
michael@0 527 si.dwFlags = STARTF_USESHOWWINDOW;
michael@0 528 si.wShowWindow = SW_SHOWNORMAL;
michael@0 529 ZeroMemory(&pi, sizeof(pi));
michael@0 530
michael@0 531 if (CreateProcess(nullptr, (LPWSTR)cmdLine.c_str(), nullptr, nullptr, FALSE,
michael@0 532 0, nullptr, nullptr, &si, &pi)) {
michael@0 533 CloseHandle(pi.hProcess);
michael@0 534 CloseHandle(pi.hThread);
michael@0 535 }
michael@0 536 }
michael@0 537
michael@0 538 static void ShowReportInfo(HWND hwndDlg)
michael@0 539 {
michael@0 540 wstring description;
michael@0 541
michael@0 542 for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin();
michael@0 543 i != gQueryParameters.end();
michael@0 544 i++) {
michael@0 545 description += i->first;
michael@0 546 description += L": ";
michael@0 547 description += i->second;
michael@0 548 description += L"\n";
michael@0 549 }
michael@0 550
michael@0 551 description += L"\n";
michael@0 552 description += Str(ST_EXTRAREPORTINFO);
michael@0 553
michael@0 554 SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str());
michael@0 555 }
michael@0 556
michael@0 557 static void UpdateURL(HWND hwndDlg)
michael@0 558 {
michael@0 559 if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) {
michael@0 560 gQueryParameters[L"URL"] = gURLParameter;
michael@0 561 } else {
michael@0 562 gQueryParameters.erase(L"URL");
michael@0 563 }
michael@0 564 }
michael@0 565
michael@0 566 static void UpdateEmail(HWND hwndDlg)
michael@0 567 {
michael@0 568 if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) {
michael@0 569 wchar_t email[MAX_EMAIL_LENGTH];
michael@0 570 GetDlgItemTextW(hwndDlg, IDC_EMAILTEXT, email,
michael@0 571 sizeof(email) / sizeof(email[0]));
michael@0 572 gQueryParameters[L"Email"] = email;
michael@0 573 if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK))
michael@0 574 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true);
michael@0 575 } else {
michael@0 576 gQueryParameters.erase(L"Email");
michael@0 577 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
michael@0 578 }
michael@0 579 }
michael@0 580
michael@0 581 static void UpdateComment(HWND hwndDlg)
michael@0 582 {
michael@0 583 wchar_t comment[MAX_COMMENT_LENGTH + 1];
michael@0 584 GetDlgItemTextW(hwndDlg, IDC_COMMENTTEXT, comment,
michael@0 585 sizeof(comment) / sizeof(comment[0]));
michael@0 586 if (wcslen(comment) > 0)
michael@0 587 gQueryParameters[L"Comments"] = comment;
michael@0 588 else
michael@0 589 gQueryParameters.erase(L"Comments");
michael@0 590 }
michael@0 591
michael@0 592 /*
michael@0 593 * Dialog procedure for the "view report" dialog.
michael@0 594 */
michael@0 595 static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message,
michael@0 596 WPARAM wParam, LPARAM lParam)
michael@0 597 {
michael@0 598 switch (message) {
michael@0 599 case WM_INITDIALOG: {
michael@0 600 SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str());
michael@0 601 SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
michael@0 602 SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
michael@0 603 EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
michael@0 604 ShowReportInfo(hwndDlg);
michael@0 605 SetFocus(GetDlgItem(hwndDlg, IDOK));
michael@0 606 return FALSE;
michael@0 607 }
michael@0 608
michael@0 609 case WM_COMMAND: {
michael@0 610 if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
michael@0 611 EndDialog(hwndDlg, 0);
michael@0 612 return FALSE;
michael@0 613 }
michael@0 614 }
michael@0 615 return FALSE;
michael@0 616 }
michael@0 617
michael@0 618 // Return the number of bytes this string will take encoded
michael@0 619 // in UTF-8
michael@0 620 static inline int BytesInUTF8(wchar_t* str)
michael@0 621 {
michael@0 622 // Just count size of buffer for UTF-8, minus one
michael@0 623 // (we don't need to count the null terminator)
michael@0 624 return WideCharToMultiByte(CP_UTF8, 0, str, -1,
michael@0 625 nullptr, 0, nullptr, nullptr) - 1;
michael@0 626 }
michael@0 627
michael@0 628 // Calculate the length of the text in this edit control (in bytes,
michael@0 629 // in the UTF-8 encoding) after replacing the current selection
michael@0 630 // with |insert|.
michael@0 631 static int NewTextLength(HWND hwndEdit, wchar_t* insert)
michael@0 632 {
michael@0 633 wchar_t current[MAX_COMMENT_LENGTH + 1];
michael@0 634
michael@0 635 GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1);
michael@0 636 DWORD selStart, selEnd;
michael@0 637 SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
michael@0 638
michael@0 639 int selectionLength = 0;
michael@0 640 if (selEnd - selStart > 0) {
michael@0 641 wchar_t selection[MAX_COMMENT_LENGTH + 1];
michael@0 642 google_breakpad::WindowsStringUtils::safe_wcsncpy(selection,
michael@0 643 MAX_COMMENT_LENGTH + 1,
michael@0 644 current + selStart,
michael@0 645 selEnd - selStart);
michael@0 646 selection[selEnd - selStart] = '\0';
michael@0 647 selectionLength = BytesInUTF8(selection);
michael@0 648 }
michael@0 649
michael@0 650 // current string length + replacement text length
michael@0 651 // - replaced selection length
michael@0 652 return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength;
michael@0 653 }
michael@0 654
michael@0 655 // Window procedure for subclassing edit controls
michael@0 656 static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
michael@0 657 LPARAM lParam)
michael@0 658 {
michael@0 659 static WNDPROC super = nullptr;
michael@0 660
michael@0 661 if (super == nullptr)
michael@0 662 super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
michael@0 663
michael@0 664 switch (uMsg) {
michael@0 665 case WM_PAINT: {
michael@0 666 HDC hdc;
michael@0 667 PAINTSTRUCT ps;
michael@0 668 RECT r;
michael@0 669 wchar_t windowText[1024];
michael@0 670
michael@0 671 GetWindowText(hwnd, windowText, 1024);
michael@0 672 // if the control contains text or is focused, draw it normally
michael@0 673 if (GetFocus() == hwnd || windowText[0] != '\0')
michael@0 674 return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
michael@0 675
michael@0 676 GetClientRect(hwnd, &r);
michael@0 677 hdc = BeginPaint(hwnd, &ps);
michael@0 678 FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd)
michael@0 679 ? COLOR_WINDOW : COLOR_BTNFACE));
michael@0 680 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
michael@0 681 SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT));
michael@0 682 SetBkMode(hdc, TRANSPARENT);
michael@0 683 wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT");
michael@0 684 // Get the actual edit control rect
michael@0 685 CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r);
michael@0 686 UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL;
michael@0 687 if (gRTLlayout)
michael@0 688 format |= DT_RIGHT;
michael@0 689 if (txt)
michael@0 690 DrawText(hdc, txt, wcslen(txt), &r, format);
michael@0 691 EndPaint(hwnd, &ps);
michael@0 692 return 0;
michael@0 693 }
michael@0 694
michael@0 695 // We handle WM_CHAR and WM_PASTE to limit the comment box to 500
michael@0 696 // bytes in UTF-8.
michael@0 697 case WM_CHAR: {
michael@0 698 // Leave accelerator keys and non-printing chars (except LF) alone
michael@0 699 if (wParam & (1<<24) || wParam & (1<<29) ||
michael@0 700 (wParam < ' ' && wParam != '\n'))
michael@0 701 break;
michael@0 702
michael@0 703 wchar_t ch[2] = { (wchar_t)wParam, 0 };
michael@0 704 if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH)
michael@0 705 return 0;
michael@0 706
michael@0 707 break;
michael@0 708 }
michael@0 709
michael@0 710 case WM_PASTE: {
michael@0 711 if (IsClipboardFormatAvailable(CF_UNICODETEXT) &&
michael@0 712 OpenClipboard(hwnd)) {
michael@0 713 HGLOBAL hg = GetClipboardData(CF_UNICODETEXT);
michael@0 714 wchar_t* pastedText = (wchar_t*)GlobalLock(hg);
michael@0 715 int newSize = 0;
michael@0 716
michael@0 717 if (pastedText)
michael@0 718 newSize = NewTextLength(hwnd, pastedText);
michael@0 719
michael@0 720 GlobalUnlock(hg);
michael@0 721 CloseClipboard();
michael@0 722
michael@0 723 if (newSize > MAX_COMMENT_LENGTH)
michael@0 724 return 0;
michael@0 725 }
michael@0 726 break;
michael@0 727 }
michael@0 728
michael@0 729 case WM_SETFOCUS:
michael@0 730 case WM_KILLFOCUS: {
michael@0 731 RECT r;
michael@0 732 GetClientRect(hwnd, &r);
michael@0 733 InvalidateRect(hwnd, &r, TRUE);
michael@0 734 break;
michael@0 735 }
michael@0 736
michael@0 737 case WM_DESTROY: {
michael@0 738 // cleanup our property
michael@0 739 HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT");
michael@0 740 if (hData)
michael@0 741 GlobalFree(hData);
michael@0 742 }
michael@0 743 }
michael@0 744
michael@0 745 return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
michael@0 746 }
michael@0 747
michael@0 748 // Resize a control to fit this text
michael@0 749 static int ResizeControl(HWND hwndButton, RECT& rect, wstring text,
michael@0 750 bool shiftLeft, int userDefinedPadding)
michael@0 751 {
michael@0 752 HDC hdc = GetDC(hwndButton);
michael@0 753 HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0);
michael@0 754 if (hfont)
michael@0 755 SelectObject(hdc, hfont);
michael@0 756 SIZE size, oldSize;
michael@0 757 int sizeDiff = 0;
michael@0 758
michael@0 759 wchar_t oldText[1024];
michael@0 760 GetWindowText(hwndButton, oldText, 1024);
michael@0 761
michael@0 762 if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size)
michael@0 763 // default text on the button
michael@0 764 && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) {
michael@0 765 /*
michael@0 766 Expand control widths to accomidate wider text strings. For most
michael@0 767 controls (including buttons) the text padding is defined by the
michael@0 768 dialog's rc file. Some controls (such as checkboxes) have padding
michael@0 769 that extends to the end of the dialog, in which case we ignore the
michael@0 770 rc padding and rely on a user defined value passed in through
michael@0 771 userDefinedPadding.
michael@0 772 */
michael@0 773 int textIncrease = size.cx - oldSize.cx;
michael@0 774 if (textIncrease < 0)
michael@0 775 return 0;
michael@0 776 int existingTextPadding;
michael@0 777 if (userDefinedPadding == 0)
michael@0 778 existingTextPadding = (rect.right - rect.left) - oldSize.cx;
michael@0 779 else
michael@0 780 existingTextPadding = userDefinedPadding;
michael@0 781 sizeDiff = textIncrease + existingTextPadding;
michael@0 782
michael@0 783 if (shiftLeft) {
michael@0 784 // shift left by the amount the button should grow
michael@0 785 rect.left -= sizeDiff;
michael@0 786 }
michael@0 787 else {
michael@0 788 // grow right instead
michael@0 789 rect.right += sizeDiff;
michael@0 790 }
michael@0 791 MoveWindow(hwndButton, rect.left, rect.top,
michael@0 792 rect.right - rect.left,
michael@0 793 rect.bottom - rect.top,
michael@0 794 TRUE);
michael@0 795 }
michael@0 796 return sizeDiff;
michael@0 797 }
michael@0 798
michael@0 799 // The window was resized horizontally, so widen some of our
michael@0 800 // controls to make use of the space
michael@0 801 static void StretchControlsToFit(HWND hwndDlg)
michael@0 802 {
michael@0 803 int controls[] = {
michael@0 804 IDC_DESCRIPTIONTEXT,
michael@0 805 IDC_SUBMITREPORTCHECK,
michael@0 806 IDC_COMMENTTEXT,
michael@0 807 IDC_INCLUDEURLCHECK,
michael@0 808 IDC_EMAILMECHECK,
michael@0 809 IDC_EMAILTEXT,
michael@0 810 IDC_PROGRESSTEXT
michael@0 811 };
michael@0 812
michael@0 813 RECT dlgRect;
michael@0 814 GetClientRect(hwndDlg, &dlgRect);
michael@0 815
michael@0 816 for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) {
michael@0 817 RECT r;
michael@0 818 HWND hwndControl = GetDlgItem(hwndDlg, controls[i]);
michael@0 819 GetRelativeRect(hwndControl, hwndDlg, &r);
michael@0 820 // 6 pixel spacing on the right
michael@0 821 if (r.right + 6 != dlgRect.right) {
michael@0 822 r.right = dlgRect.right - 6;
michael@0 823 MoveWindow(hwndControl, r.left, r.top,
michael@0 824 r.right - r.left,
michael@0 825 r.bottom - r.top,
michael@0 826 TRUE);
michael@0 827 }
michael@0 828 }
michael@0 829 }
michael@0 830
michael@0 831 static void SubmitReportChecked(HWND hwndDlg)
michael@0 832 {
michael@0 833 bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
michael@0 834 EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled);
michael@0 835 EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled);
michael@0 836 EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
michael@0 837 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
michael@0 838 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT),
michael@0 839 enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)
michael@0 840 != 0));
michael@0 841 SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled);
michael@0 842 }
michael@0 843
michael@0 844 static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent,
michael@0 845 DLGPROC dlgProc, LPARAM param)
michael@0 846 {
michael@0 847 INT_PTR rv = 0;
michael@0 848 if (gRTLlayout) {
michael@0 849 // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog
michael@0 850 // template.
michael@0 851 HRSRC hDialogRC = FindResource(nullptr, MAKEINTRESOURCE(idd),
michael@0 852 RT_DIALOG);
michael@0 853 HGLOBAL hDlgTemplate = LoadResource(nullptr, hDialogRC);
michael@0 854 DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate);
michael@0 855 unsigned long sizeDlg = SizeofResource(nullptr, hDialogRC);
michael@0 856 HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg);
michael@0 857 DLGTEMPLATEEX* pMyDlgTemplate =
michael@0 858 (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate);
michael@0 859 memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg);
michael@0 860
michael@0 861 pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL;
michael@0 862
michael@0 863 rv = DialogBoxIndirectParam(nullptr, (LPCDLGTEMPLATE)pMyDlgTemplate,
michael@0 864 hwndParent, dlgProc, param);
michael@0 865 GlobalUnlock(hMyDlgTemplate);
michael@0 866 GlobalFree(hMyDlgTemplate);
michael@0 867 }
michael@0 868 else {
michael@0 869 rv = DialogBoxParam(nullptr, MAKEINTRESOURCE(idd), hwndParent,
michael@0 870 dlgProc, param);
michael@0 871 }
michael@0 872
michael@0 873 return rv;
michael@0 874 }
michael@0 875
michael@0 876
michael@0 877 static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
michael@0 878 WPARAM wParam, LPARAM lParam)
michael@0 879 {
michael@0 880 static int sHeight = 0;
michael@0 881
michael@0 882 bool success;
michael@0 883 bool enabled;
michael@0 884
michael@0 885 switch (message) {
michael@0 886 case WM_INITDIALOG: {
michael@0 887 GetThemeSizes(hwndDlg);
michael@0 888 RECT r;
michael@0 889 GetClientRect(hwndDlg, &r);
michael@0 890 sHeight = r.bottom - r.top;
michael@0 891
michael@0 892 SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
michael@0 893 HICON hIcon = LoadIcon(GetModuleHandle(nullptr),
michael@0 894 MAKEINTRESOURCE(IDI_MAINICON));
michael@0 895 SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
michael@0 896 SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
michael@0 897
michael@0 898 // resize the "View Report" button based on the string length
michael@0 899 RECT rect;
michael@0 900 HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON);
michael@0 901 GetRelativeRect(hwnd, hwndDlg, &rect);
michael@0 902 ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0);
michael@0 903 SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str());
michael@0 904
michael@0 905 hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK);
michael@0 906 GetRelativeRect(hwnd, hwndDlg, &rect);
michael@0 907 long maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false,
michael@0 908 gCheckboxPadding);
michael@0 909 SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
michael@0 910 Str(ST_CHECKSUBMIT).c_str());
michael@0 911
michael@0 912 if (!CheckBoolKey(gCrashReporterKey.c_str(),
michael@0 913 SUBMIT_REPORT_VALUE, &enabled))
michael@0 914 enabled = ShouldEnableSending();
michael@0 915
michael@0 916 CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED
michael@0 917 : BST_UNCHECKED);
michael@0 918 SubmitReportChecked(hwndDlg);
michael@0 919
michael@0 920 HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT);
michael@0 921 WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment,
michael@0 922 GWLP_WNDPROC,
michael@0 923 (LONG_PTR)EditSubclassProc);
michael@0 924
michael@0 925 // Subclass comment edit control to get placeholder text
michael@0 926 SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc);
michael@0 927 wstring commentGrayText = Str(ST_COMMENTGRAYTEXT);
michael@0 928 wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t));
michael@0 929 wcscpy(hMem, commentGrayText.c_str());
michael@0 930 SetProp(hwndComment, L"PROP_GRAYTEXT", hMem);
michael@0 931
michael@0 932 hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK);
michael@0 933 GetRelativeRect(hwnd, hwndDlg, &rect);
michael@0 934 long diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false,
michael@0 935 gCheckboxPadding);
michael@0 936 maxdiff = std::max(diff, maxdiff);
michael@0 937 SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str());
michael@0 938
michael@0 939 // want this on by default
michael@0 940 if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) &&
michael@0 941 !enabled) {
michael@0 942 CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED);
michael@0 943 } else {
michael@0 944 CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED);
michael@0 945 }
michael@0 946
michael@0 947 hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK);
michael@0 948 GetRelativeRect(hwnd, hwndDlg, &rect);
michael@0 949 diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false,
michael@0 950 gCheckboxPadding);
michael@0 951 maxdiff = std::max(diff, maxdiff);
michael@0 952 SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str());
michael@0 953
michael@0 954 if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) &&
michael@0 955 enabled) {
michael@0 956 CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
michael@0 957 } else {
michael@0 958 CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
michael@0 959 }
michael@0 960
michael@0 961 wstring email;
michael@0 962 if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) {
michael@0 963 SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str());
michael@0 964 }
michael@0 965
michael@0 966 // Subclass email edit control to get placeholder text
michael@0 967 HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
michael@0 968 OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail,
michael@0 969 GWLP_WNDPROC,
michael@0 970 (LONG_PTR)EditSubclassProc);
michael@0 971 SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc);
michael@0 972 wstring emailGrayText = Str(ST_EMAILGRAYTEXT);
michael@0 973 hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t));
michael@0 974 wcscpy(hMem, emailGrayText.c_str());
michael@0 975 SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem);
michael@0 976
michael@0 977 SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str());
michael@0 978
michael@0 979 RECT closeRect;
michael@0 980 HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON);
michael@0 981 GetRelativeRect(hwndClose, hwndDlg, &closeRect);
michael@0 982
michael@0 983 RECT restartRect;
michael@0 984 HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON);
michael@0 985 GetRelativeRect(hwndRestart, hwndDlg, &restartRect);
michael@0 986
michael@0 987 // set the close button text and shift the buttons around
michael@0 988 // since the size may need to change
michael@0 989 int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT),
michael@0 990 true, 0);
michael@0 991 restartRect.left -= sizeDiff;
michael@0 992 restartRect.right -= sizeDiff;
michael@0 993 SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str());
michael@0 994
michael@0 995 if (gRestartArgs.size() > 0) {
michael@0 996 // Resize restart button to fit text
michael@0 997 ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0);
michael@0 998 SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str());
michael@0 999 } else {
michael@0 1000 // No restart arguments, so just hide the restart button
michael@0 1001 SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
michael@0 1002 }
michael@0 1003 // See if we need to widen the window
michael@0 1004 // Leave 6 pixels on either side + 6 pixels between the buttons
michael@0 1005 int neededSize = closeRect.right - closeRect.left +
michael@0 1006 restartRect.right - restartRect.left + 6 * 3;
michael@0 1007 GetClientRect(hwndDlg, &r);
michael@0 1008 // We may already have resized one of the checkboxes above
michael@0 1009 maxdiff = std::max(maxdiff, neededSize - (r.right - r.left));
michael@0 1010
michael@0 1011 if (maxdiff > 0) {
michael@0 1012 // widen window
michael@0 1013 GetWindowRect(hwndDlg, &r);
michael@0 1014 r.right += maxdiff;
michael@0 1015 MoveWindow(hwndDlg, r.left, r.top,
michael@0 1016 r.right - r.left, r.bottom - r.top, TRUE);
michael@0 1017 // shift both buttons right
michael@0 1018 if (restartRect.left + maxdiff < 6)
michael@0 1019 maxdiff += 6;
michael@0 1020 closeRect.left += maxdiff;
michael@0 1021 closeRect.right += maxdiff;
michael@0 1022 restartRect.left += maxdiff;
michael@0 1023 restartRect.right += maxdiff;
michael@0 1024 MoveWindow(hwndClose, closeRect.left, closeRect.top,
michael@0 1025 closeRect.right - closeRect.left,
michael@0 1026 closeRect.bottom - closeRect.top,
michael@0 1027 TRUE);
michael@0 1028 StretchControlsToFit(hwndDlg);
michael@0 1029 }
michael@0 1030 // need to move the restart button regardless
michael@0 1031 MoveWindow(hwndRestart, restartRect.left, restartRect.top,
michael@0 1032 restartRect.right - restartRect.left,
michael@0 1033 restartRect.bottom - restartRect.top,
michael@0 1034 TRUE);
michael@0 1035
michael@0 1036 // Resize the description text last, in case the window was resized
michael@0 1037 // before this.
michael@0 1038 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
michael@0 1039 EM_SETEVENTMASK, (WPARAM)nullptr,
michael@0 1040 ENM_REQUESTRESIZE);
michael@0 1041
michael@0 1042 wstring description = Str(ST_CRASHREPORTERHEADER);
michael@0 1043 description += L"\n\n";
michael@0 1044 description += Str(ST_CRASHREPORTERDESCRIPTION);
michael@0 1045 SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str());
michael@0 1046
michael@0 1047
michael@0 1048 // Make the title bold.
michael@0 1049 CHARFORMAT fmt = { 0, };
michael@0 1050 fmt.cbSize = sizeof(fmt);
michael@0 1051 fmt.dwMask = CFM_BOLD;
michael@0 1052 fmt.dwEffects = CFE_BOLD;
michael@0 1053 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL,
michael@0 1054 0, Str(ST_CRASHREPORTERHEADER).length());
michael@0 1055 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT,
michael@0 1056 SCF_SELECTION, (LPARAM)&fmt);
michael@0 1057 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0);
michael@0 1058 // Force redraw.
michael@0 1059 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
michael@0 1060 EM_SETTARGETDEVICE, (WPARAM)nullptr, 0);
michael@0 1061 // Force resize.
michael@0 1062 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
michael@0 1063 EM_REQUESTRESIZE, 0, 0);
michael@0 1064
michael@0 1065 // if no URL was given, hide the URL checkbox
michael@0 1066 if (gQueryParameters.find(L"URL") == gQueryParameters.end()) {
michael@0 1067 RECT urlCheckRect, emailCheckRect;
michael@0 1068 GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect);
michael@0 1069 GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect);
michael@0 1070
michael@0 1071 SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false);
michael@0 1072
michael@0 1073 gAttachedBottom.erase(IDC_VIEWREPORTBUTTON);
michael@0 1074 gAttachedBottom.erase(IDC_SUBMITREPORTCHECK);
michael@0 1075 gAttachedBottom.erase(IDC_COMMENTTEXT);
michael@0 1076
michael@0 1077 StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top);
michael@0 1078
michael@0 1079 gAttachedBottom.insert(IDC_VIEWREPORTBUTTON);
michael@0 1080 gAttachedBottom.insert(IDC_SUBMITREPORTCHECK);
michael@0 1081 gAttachedBottom.insert(IDC_COMMENTTEXT);
michael@0 1082 }
michael@0 1083
michael@0 1084 MaybeResizeProgressText(hwndDlg);
michael@0 1085
michael@0 1086 // Open the AVI resource for the throbber
michael@0 1087 Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER),
michael@0 1088 MAKEINTRESOURCE(IDR_THROBBER));
michael@0 1089
michael@0 1090 UpdateURL(hwndDlg);
michael@0 1091 UpdateEmail(hwndDlg);
michael@0 1092
michael@0 1093 SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK));
michael@0 1094 return FALSE;
michael@0 1095 }
michael@0 1096 case WM_SIZE: {
michael@0 1097 ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
michael@0 1098 sHeight = HIWORD(lParam);
michael@0 1099 InvalidateRect(hwndDlg, nullptr, TRUE);
michael@0 1100 return FALSE;
michael@0 1101 }
michael@0 1102 case WM_NOTIFY: {
michael@0 1103 NMHDR* notification = reinterpret_cast<NMHDR*>(lParam);
michael@0 1104 if (notification->code == EN_REQUESTRESIZE) {
michael@0 1105 // Resizing the rich edit control to fit the description text.
michael@0 1106 REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam);
michael@0 1107 RECT newSize = reqresize->rc;
michael@0 1108 RECT oldSize;
michael@0 1109 GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize);
michael@0 1110
michael@0 1111 // resize the text box as requested
michael@0 1112 MoveWindow(notification->hwndFrom, newSize.left, newSize.top,
michael@0 1113 newSize.right - newSize.left, newSize.bottom - newSize.top,
michael@0 1114 TRUE);
michael@0 1115
michael@0 1116 // Resize the dialog to fit (the WM_SIZE handler will move the controls)
michael@0 1117 StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom);
michael@0 1118 }
michael@0 1119 return FALSE;
michael@0 1120 }
michael@0 1121 case WM_COMMAND: {
michael@0 1122 if (HIWORD(wParam) == BN_CLICKED) {
michael@0 1123 switch(LOWORD(wParam)) {
michael@0 1124 case IDC_VIEWREPORTBUTTON:
michael@0 1125 DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg,
michael@0 1126 (DLGPROC)ViewReportDialogProc, 0);
michael@0 1127 break;
michael@0 1128 case IDC_SUBMITREPORTCHECK:
michael@0 1129 SubmitReportChecked(hwndDlg);
michael@0 1130 break;
michael@0 1131 case IDC_INCLUDEURLCHECK:
michael@0 1132 UpdateURL(hwndDlg);
michael@0 1133 break;
michael@0 1134 case IDC_EMAILMECHECK:
michael@0 1135 UpdateEmail(hwndDlg);
michael@0 1136 break;
michael@0 1137 case IDC_CLOSEBUTTON:
michael@0 1138 MaybeSendReport(hwndDlg);
michael@0 1139 break;
michael@0 1140 case IDC_RESTARTBUTTON:
michael@0 1141 RestartApplication();
michael@0 1142 MaybeSendReport(hwndDlg);
michael@0 1143 break;
michael@0 1144 }
michael@0 1145 } else if (HIWORD(wParam) == EN_CHANGE) {
michael@0 1146 switch(LOWORD(wParam)) {
michael@0 1147 case IDC_EMAILTEXT:
michael@0 1148 UpdateEmail(hwndDlg);
michael@0 1149 break;
michael@0 1150 case IDC_COMMENTTEXT:
michael@0 1151 UpdateComment(hwndDlg);
michael@0 1152 }
michael@0 1153 }
michael@0 1154
michael@0 1155 return FALSE;
michael@0 1156 }
michael@0 1157 case WM_UPLOADCOMPLETE: {
michael@0 1158 WaitForSingleObject(gThreadHandle, INFINITE);
michael@0 1159 success = (wParam == 1);
michael@0 1160 SendCompleted(success, WideToUTF8(gSendData.serverResponse));
michael@0 1161 // hide throbber
michael@0 1162 Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER));
michael@0 1163 SetDlgItemVisible(hwndDlg, IDC_THROBBER, false);
michael@0 1164
michael@0 1165 SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
michael@0 1166 success ?
michael@0 1167 Str(ST_REPORTSUBMITSUCCESS).c_str() :
michael@0 1168 Str(ST_SUBMITFAILED).c_str());
michael@0 1169 MaybeResizeProgressText(hwndDlg);
michael@0 1170 // close dialog after 5 seconds
michael@0 1171 SetTimer(hwndDlg, 0, 5000, nullptr);
michael@0 1172 //
michael@0 1173 return TRUE;
michael@0 1174 }
michael@0 1175
michael@0 1176 case WM_LBUTTONDOWN: {
michael@0 1177 HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
michael@0 1178 POINT p = { LOWORD(lParam), HIWORD(lParam) };
michael@0 1179 // if the email edit control is clicked, enable it,
michael@0 1180 // check the email checkbox, and focus the email edit control
michael@0 1181 if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail &&
michael@0 1182 IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) &&
michael@0 1183 !IsWindowEnabled(hwndEmail) &&
michael@0 1184 IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) {
michael@0 1185 CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
michael@0 1186 UpdateEmail(hwndDlg);
michael@0 1187 SetFocus(hwndEmail);
michael@0 1188 }
michael@0 1189 break;
michael@0 1190 }
michael@0 1191
michael@0 1192 case WM_TIMER: {
michael@0 1193 // The "1" gets used down in UIShowCrashUI to indicate that we at least
michael@0 1194 // tried to send the report.
michael@0 1195 EndCrashReporterDialog(hwndDlg, 1);
michael@0 1196 return FALSE;
michael@0 1197 }
michael@0 1198
michael@0 1199 case WM_CLOSE: {
michael@0 1200 EndCrashReporterDialog(hwndDlg, 0);
michael@0 1201 return FALSE;
michael@0 1202 }
michael@0 1203 }
michael@0 1204 return FALSE;
michael@0 1205 }
michael@0 1206
michael@0 1207 static wstring UTF8ToWide(const string& utf8, bool *success)
michael@0 1208 {
michael@0 1209 wchar_t* buffer = nullptr;
michael@0 1210 int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
michael@0 1211 -1, nullptr, 0);
michael@0 1212 if(buffer_size == 0) {
michael@0 1213 if (success)
michael@0 1214 *success = false;
michael@0 1215 return L"";
michael@0 1216 }
michael@0 1217
michael@0 1218 buffer = new wchar_t[buffer_size];
michael@0 1219 if(buffer == nullptr) {
michael@0 1220 if (success)
michael@0 1221 *success = false;
michael@0 1222 return L"";
michael@0 1223 }
michael@0 1224
michael@0 1225 MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
michael@0 1226 -1, buffer, buffer_size);
michael@0 1227 wstring str = buffer;
michael@0 1228 delete [] buffer;
michael@0 1229
michael@0 1230 if (success)
michael@0 1231 *success = true;
michael@0 1232
michael@0 1233 return str;
michael@0 1234 }
michael@0 1235
michael@0 1236 static string WideToMBCP(const wstring& wide,
michael@0 1237 unsigned int cp,
michael@0 1238 bool* success = nullptr)
michael@0 1239 {
michael@0 1240 char* buffer = nullptr;
michael@0 1241 int buffer_size = WideCharToMultiByte(cp, 0, wide.c_str(),
michael@0 1242 -1, nullptr, 0, nullptr, nullptr);
michael@0 1243 if(buffer_size == 0) {
michael@0 1244 if (success)
michael@0 1245 *success = false;
michael@0 1246 return "";
michael@0 1247 }
michael@0 1248
michael@0 1249 buffer = new char[buffer_size];
michael@0 1250 if(buffer == nullptr) {
michael@0 1251 if (success)
michael@0 1252 *success = false;
michael@0 1253 return "";
michael@0 1254 }
michael@0 1255
michael@0 1256 WideCharToMultiByte(cp, 0, wide.c_str(),
michael@0 1257 -1, buffer, buffer_size, nullptr, nullptr);
michael@0 1258 string mb = buffer;
michael@0 1259 delete [] buffer;
michael@0 1260
michael@0 1261 if (success)
michael@0 1262 *success = true;
michael@0 1263
michael@0 1264 return mb;
michael@0 1265 }
michael@0 1266
michael@0 1267 string WideToUTF8(const wstring& wide, bool* success)
michael@0 1268 {
michael@0 1269 return WideToMBCP(wide, CP_UTF8, success);
michael@0 1270 }
michael@0 1271
michael@0 1272 /* === Crashreporter UI Functions === */
michael@0 1273
michael@0 1274 bool UIInit()
michael@0 1275 {
michael@0 1276 for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
michael@0 1277 gAttachedBottom.insert(kDefaultAttachedBottom[i]);
michael@0 1278 }
michael@0 1279
michael@0 1280 DoInitCommonControls();
michael@0 1281
michael@0 1282 return true;
michael@0 1283 }
michael@0 1284
michael@0 1285 void UIShutdown()
michael@0 1286 {
michael@0 1287 }
michael@0 1288
michael@0 1289 void UIShowDefaultUI()
michael@0 1290 {
michael@0 1291 MessageBox(nullptr, Str(ST_CRASHREPORTERDEFAULT).c_str(),
michael@0 1292 L"Crash Reporter",
michael@0 1293 MB_OK | MB_ICONSTOP);
michael@0 1294 }
michael@0 1295
michael@0 1296 bool UIShowCrashUI(const string& dumpFile,
michael@0 1297 const StringTable& queryParameters,
michael@0 1298 const string& sendURL,
michael@0 1299 const vector<string>& restartArgs)
michael@0 1300 {
michael@0 1301 gSendData.hDlg = nullptr;
michael@0 1302 gSendData.dumpFile = UTF8ToWide(dumpFile);
michael@0 1303 gSendData.sendURL = UTF8ToWide(sendURL);
michael@0 1304
michael@0 1305 for (StringTable::const_iterator i = queryParameters.begin();
michael@0 1306 i != queryParameters.end();
michael@0 1307 i++) {
michael@0 1308 gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
michael@0 1309 }
michael@0 1310
michael@0 1311 if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
michael@0 1312 gCrashReporterKey = L"Software\\";
michael@0 1313 if (!gQueryParameters[L"Vendor"].empty()) {
michael@0 1314 gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
michael@0 1315 }
michael@0 1316 gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter";
michael@0 1317 }
michael@0 1318
michael@0 1319 if (gQueryParameters.find(L"URL") != gQueryParameters.end())
michael@0 1320 gURLParameter = gQueryParameters[L"URL"];
michael@0 1321
michael@0 1322 gRestartArgs = restartArgs;
michael@0 1323
michael@0 1324 if (gStrings.find("isRTL") != gStrings.end() &&
michael@0 1325 gStrings["isRTL"] == "yes")
michael@0 1326 gRTLlayout = true;
michael@0 1327
michael@0 1328 return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, nullptr,
michael@0 1329 (DLGPROC)CrashReporterDialogProc, 0);
michael@0 1330 }
michael@0 1331
michael@0 1332 void UIError_impl(const string& message)
michael@0 1333 {
michael@0 1334 wstring title = Str(ST_CRASHREPORTERTITLE);
michael@0 1335 if (title.empty())
michael@0 1336 title = L"Crash Reporter Error";
michael@0 1337
michael@0 1338 MessageBox(nullptr, UTF8ToWide(message).c_str(), title.c_str(),
michael@0 1339 MB_OK | MB_ICONSTOP);
michael@0 1340 }
michael@0 1341
michael@0 1342 bool UIGetIniPath(string& path)
michael@0 1343 {
michael@0 1344 wchar_t fileName[MAX_PATH];
michael@0 1345 if (GetModuleFileName(nullptr, fileName, MAX_PATH)) {
michael@0 1346 // get crashreporter ini
michael@0 1347 wchar_t* s = wcsrchr(fileName, '.');
michael@0 1348 if (s) {
michael@0 1349 wcscpy(s, L".ini");
michael@0 1350 path = WideToUTF8(fileName);
michael@0 1351 return true;
michael@0 1352 }
michael@0 1353 }
michael@0 1354
michael@0 1355 return false;
michael@0 1356 }
michael@0 1357
michael@0 1358 bool UIGetSettingsPath(const string& vendor,
michael@0 1359 const string& product,
michael@0 1360 string& settings_path)
michael@0 1361 {
michael@0 1362 wchar_t path[MAX_PATH];
michael@0 1363 HRESULT hRes = SHGetFolderPath(nullptr,
michael@0 1364 CSIDL_APPDATA,
michael@0 1365 nullptr,
michael@0 1366 0,
michael@0 1367 path);
michael@0 1368 if (FAILED(hRes)) {
michael@0 1369 // This provides a fallback for getting the path to APPDATA by querying the
michael@0 1370 // registry when the call to SHGetFolderPath is unable to provide this path
michael@0 1371 // (Bug 513958).
michael@0 1372 HKEY key;
michael@0 1373 DWORD type, size, dwRes;
michael@0 1374 dwRes = ::RegOpenKeyExW(HKEY_CURRENT_USER,
michael@0 1375 L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders",
michael@0 1376 0,
michael@0 1377 KEY_READ,
michael@0 1378 &key);
michael@0 1379 if (dwRes != ERROR_SUCCESS)
michael@0 1380 return false;
michael@0 1381
michael@0 1382 dwRes = RegQueryValueExW(key,
michael@0 1383 L"AppData",
michael@0 1384 nullptr,
michael@0 1385 &type,
michael@0 1386 (LPBYTE)&path,
michael@0 1387 &size);
michael@0 1388 ::RegCloseKey(key);
michael@0 1389 // The call to RegQueryValueExW must succeed, the type must be REG_SZ, the
michael@0 1390 // buffer size must not equal 0, and the buffer size be a multiple of 2.
michael@0 1391 if (dwRes != ERROR_SUCCESS || type != REG_SZ || size == 0 || size % 2 != 0)
michael@0 1392 return false;
michael@0 1393 }
michael@0 1394
michael@0 1395 if (!vendor.empty()) {
michael@0 1396 PathAppend(path, UTF8ToWide(vendor).c_str());
michael@0 1397 }
michael@0 1398 PathAppend(path, UTF8ToWide(product).c_str());
michael@0 1399 PathAppend(path, L"Crash Reports");
michael@0 1400 settings_path = WideToUTF8(path);
michael@0 1401 return true;
michael@0 1402 }
michael@0 1403
michael@0 1404 bool UIEnsurePathExists(const string& path)
michael@0 1405 {
michael@0 1406 if (CreateDirectory(UTF8ToWide(path).c_str(), nullptr) == 0) {
michael@0 1407 if (GetLastError() != ERROR_ALREADY_EXISTS)
michael@0 1408 return false;
michael@0 1409 }
michael@0 1410
michael@0 1411 return true;
michael@0 1412 }
michael@0 1413
michael@0 1414 bool UIFileExists(const string& path)
michael@0 1415 {
michael@0 1416 DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str());
michael@0 1417 return (attrs != INVALID_FILE_ATTRIBUTES);
michael@0 1418 }
michael@0 1419
michael@0 1420 bool UIMoveFile(const string& oldfile, const string& newfile)
michael@0 1421 {
michael@0 1422 if (oldfile == newfile)
michael@0 1423 return true;
michael@0 1424
michael@0 1425 return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
michael@0 1426 == TRUE;
michael@0 1427 }
michael@0 1428
michael@0 1429 bool UIDeleteFile(const string& oldfile)
michael@0 1430 {
michael@0 1431 return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE;
michael@0 1432 }
michael@0 1433
michael@0 1434 ifstream* UIOpenRead(const string& filename)
michael@0 1435 {
michael@0 1436 // adapted from breakpad's src/common/windows/http_upload.cc
michael@0 1437
michael@0 1438 // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
michael@0 1439 // wchar_t* filename, so use _wfopen directly in that case. For VC8 and
michael@0 1440 // later, _wfopen has been deprecated in favor of _wfopen_s, which does
michael@0 1441 // not exist in earlier versions, so let the ifstream open the file itself.
michael@0 1442 #if _MSC_VER >= 1400 // MSVC 2005/8
michael@0 1443 ifstream* file = new ifstream();
michael@0 1444 file->open(UTF8ToWide(filename).c_str(), ios::in);
michael@0 1445 #elif defined(_MSC_VER)
michael@0 1446 ifstream* file = new ifstream(_wfopen(UTF8ToWide(filename).c_str(), L"r"));
michael@0 1447 #else // GCC
michael@0 1448 ifstream* file = new ifstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
michael@0 1449 ios::in);
michael@0 1450 #endif // _MSC_VER >= 1400
michael@0 1451
michael@0 1452 return file;
michael@0 1453 }
michael@0 1454
michael@0 1455 ofstream* UIOpenWrite(const string& filename, bool append) // append=false
michael@0 1456 {
michael@0 1457 // adapted from breakpad's src/common/windows/http_upload.cc
michael@0 1458
michael@0 1459 // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
michael@0 1460 // wchar_t* filename, so use _wfopen directly in that case. For VC8 and
michael@0 1461 // later, _wfopen has been deprecated in favor of _wfopen_s, which does
michael@0 1462 // not exist in earlier versions, so let the ifstream open the file itself.
michael@0 1463 #if _MSC_VER >= 1400 // MSVC 2005/8
michael@0 1464 ofstream* file = new ofstream();
michael@0 1465 file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app
michael@0 1466 : ios::out);
michael@0 1467 #elif defined(_MSC_VER)
michael@0 1468 ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(),
michael@0 1469 append ? L"a" : L"w"));
michael@0 1470 #else // GCC
michael@0 1471 ofstream* file = new ofstream(WideToMBCP(UTF8ToWide(filename), CP_ACP).c_str(),
michael@0 1472 append ? ios::out | ios::app : ios::out);
michael@0 1473 #endif // _MSC_VER >= 1400
michael@0 1474
michael@0 1475 return file;
michael@0 1476 }
michael@0 1477
michael@0 1478 struct FileData
michael@0 1479 {
michael@0 1480 FILETIME timestamp;
michael@0 1481 wstring path;
michael@0 1482 };
michael@0 1483
michael@0 1484 static bool CompareFDTime(const FileData& fd1, const FileData& fd2)
michael@0 1485 {
michael@0 1486 return CompareFileTime(&fd1.timestamp, &fd2.timestamp) > 0;
michael@0 1487 }
michael@0 1488
michael@0 1489 void UIPruneSavedDumps(const std::string& directory)
michael@0 1490 {
michael@0 1491 wstring wdirectory = UTF8ToWide(directory);
michael@0 1492
michael@0 1493 WIN32_FIND_DATA fdata;
michael@0 1494 wstring findpath = wdirectory + L"\\*.dmp";
michael@0 1495 HANDLE dirlist = FindFirstFile(findpath.c_str(), &fdata);
michael@0 1496 if (dirlist == INVALID_HANDLE_VALUE)
michael@0 1497 return;
michael@0 1498
michael@0 1499 vector<FileData> dumpfiles;
michael@0 1500
michael@0 1501 for (BOOL ok = true; ok; ok = FindNextFile(dirlist, &fdata)) {
michael@0 1502 FileData fd = {fdata.ftLastWriteTime, wdirectory + L"\\" + fdata.cFileName};
michael@0 1503 dumpfiles.push_back(fd);
michael@0 1504 }
michael@0 1505
michael@0 1506 sort(dumpfiles.begin(), dumpfiles.end(), CompareFDTime);
michael@0 1507
michael@0 1508 while (dumpfiles.size() > kSaveCount) {
michael@0 1509 // get the path of the oldest file
michael@0 1510 wstring path = (--dumpfiles.end())->path;
michael@0 1511 DeleteFile(path.c_str());
michael@0 1512
michael@0 1513 // s/.dmp/.extra/
michael@0 1514 path.replace(path.size() - 4, 4, L".extra");
michael@0 1515 DeleteFile(path.c_str());
michael@0 1516
michael@0 1517 dumpfiles.pop_back();
michael@0 1518 }
michael@0 1519 }

mercurial