Bug 452317 - FeedConverter.js: QueryInterface should throw NS_ERROR_NO_INTERFACE...
[wine-gecko.git] / toolkit / crashreporter / client / crashreporter_win.cpp
blob1fe185090ad52f6d692daca83da531a29319c7d2
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is Mozilla Toolkit Crash Reporter
17 * The Initial Developer of the Original Code is
18 * Ted Mielczarek <ted.mielczarek@gmail.com>
19 * Portions created by the Initial Developer are Copyright (C) 2006
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Ted Mielczarek <ted.mielczarek@gmail.com>
24 * Dave Camp <dcamp@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either the GNU General Public License Version 2 or later (the "GPL"), or
28 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #ifdef WIN32_LEAN_AND_MEAN
41 #undef WIN32_LEAN_AND_MEAN
42 #endif
44 #include "crashreporter.h"
46 #include <windows.h>
47 #include <commctrl.h>
48 #include <richedit.h>
49 #include <shellapi.h>
50 #include <shlobj.h>
51 #include <shlwapi.h>
52 #include <math.h>
53 #include <set>
54 #include "resource.h"
55 #include "client/windows/sender/crash_report_sender.h"
56 #include "common/windows/string_utils-inl.h"
58 #define CRASH_REPORTER_VALUE L"Enabled"
59 #define SUBMIT_REPORT_VALUE L"SubmitReport"
60 #define INCLUDE_URL_VALUE L"IncludeURL"
61 #define EMAIL_ME_VALUE L"EmailMe"
62 #define EMAIL_VALUE L"Email"
63 #define MAX_EMAIL_LENGTH 1024
65 #define WM_UPLOADCOMPLETE WM_APP
67 using std::string;
68 using std::wstring;
69 using std::map;
70 using std::vector;
71 using std::set;
72 using std::ios;
73 using std::ifstream;
74 using std::ofstream;
76 using namespace CrashReporter;
78 typedef struct {
79 HWND hDlg;
80 wstring dumpFile;
81 map<wstring,wstring> queryParameters;
82 wstring sendURL;
84 wstring serverResponse;
85 } SendThreadData;
88 * Per http://msdn2.microsoft.com/en-us/library/ms645398(VS.85).aspx
89 * "The DLGTEMPLATEEX structure is not defined in any standard header file.
90 * The structure definition is provided here to explain the format of an
91 * extended template for a dialog box.
93 typedef struct {
94 WORD dlgVer;
95 WORD signature;
96 DWORD helpID;
97 DWORD exStyle;
98 // There's more to this struct, but it has weird variable-length
99 // members, and I only actually need to touch exStyle on an existing
100 // instance, so I've omitted the rest.
101 } DLGTEMPLATEEX;
103 static HANDLE gThreadHandle;
104 static SendThreadData gSendData = { 0, };
105 static vector<string> gRestartArgs;
106 static map<wstring,wstring> gQueryParameters;
107 static wstring gCrashReporterKey(L"Software\\Mozilla\\Crash Reporter");
108 static wstring gURLParameter;
109 static int gCheckboxPadding = 6;
110 static bool gRTLlayout = false;
112 // When vertically resizing the dialog, these items should move down
113 static set<UINT> gAttachedBottom;
115 // Default set of items for gAttachedBottom
116 static const UINT kDefaultAttachedBottom[] = {
117 IDC_SUBMITREPORTCHECK,
118 IDC_VIEWREPORTBUTTON,
119 IDC_COMMENTTEXT,
120 IDC_INCLUDEURLCHECK,
121 IDC_EMAILMECHECK,
122 IDC_EMAILTEXT,
123 IDC_PROGRESSTEXT,
124 IDC_THROBBER,
125 IDC_CLOSEBUTTON,
126 IDC_RESTARTBUTTON,
129 static wstring UTF8ToWide(const string& utf8, bool *success = 0);
130 static DWORD WINAPI SendThreadProc(LPVOID param);
132 static wstring Str(const char* key)
134 return UTF8ToWide(gStrings[key]);
137 /* === win32 helper functions === */
139 static void DoInitCommonControls()
141 INITCOMMONCONTROLSEX ic;
142 ic.dwSize = sizeof(INITCOMMONCONTROLSEX);
143 ic.dwICC = ICC_PROGRESS_CLASS;
144 InitCommonControlsEx(&ic);
145 // also get the rich edit control
146 LoadLibrary(L"riched20.dll");
149 static bool GetBoolValue(HKEY hRegKey, LPCTSTR valueName, DWORD* value)
151 DWORD type, dataSize;
152 dataSize = sizeof(DWORD);
153 if (RegQueryValueEx(hRegKey, valueName, NULL, &type, (LPBYTE)value, &dataSize) == ERROR_SUCCESS
154 && type == REG_DWORD)
155 return true;
157 return false;
160 static bool CheckBoolKey(const wchar_t* key,
161 const wchar_t* valueName,
162 bool* enabled)
164 *enabled = false;
165 bool found = false;
166 HKEY hRegKey;
167 DWORD val;
168 // see if our reg key is set globally
169 if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
170 if (GetBoolValue(hRegKey, valueName, &val)) {
171 *enabled = (val == 1);
172 found = true;
174 RegCloseKey(hRegKey);
175 } else {
176 // look for it in user settings
177 if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
178 if (GetBoolValue(hRegKey, valueName, &val)) {
179 *enabled = (val == 1);
180 found = true;
182 RegCloseKey(hRegKey);
186 return found;
189 static void SetBoolKey(const wchar_t* key, const wchar_t* value, bool enabled)
191 HKEY hRegKey;
192 if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
193 DWORD data = (enabled ? 1 : 0);
194 RegSetValueEx(hRegKey, value, 0, REG_DWORD, (LPBYTE)&data, sizeof(data));
195 RegCloseKey(hRegKey);
199 static bool GetStringValue(HKEY hRegKey, LPCTSTR valueName, wstring& value)
201 DWORD type, dataSize;
202 wchar_t buf[2048];
203 dataSize = sizeof(buf);
204 if (RegQueryValueEx(hRegKey, valueName, NULL, &type, (LPBYTE)buf, &dataSize) == ERROR_SUCCESS
205 && type == REG_SZ) {
206 value = buf;
207 return true;
210 return false;
213 static bool GetStringKey(const wchar_t* key,
214 const wchar_t* valueName,
215 wstring& value)
217 value = L"";
218 bool found = false;
219 HKEY hRegKey;
220 // see if our reg key is set globally
221 if (RegOpenKey(HKEY_LOCAL_MACHINE, key, &hRegKey) == ERROR_SUCCESS) {
222 if (GetStringValue(hRegKey, valueName, value)) {
223 found = true;
225 RegCloseKey(hRegKey);
226 } else {
227 // look for it in user settings
228 if (RegOpenKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
229 if (GetStringValue(hRegKey, valueName, value)) {
230 found = true;
232 RegCloseKey(hRegKey);
236 return found;
239 static void SetStringKey(const wchar_t* key,
240 const wchar_t* valueName,
241 const wstring& value)
243 HKEY hRegKey;
244 if (RegCreateKey(HKEY_CURRENT_USER, key, &hRegKey) == ERROR_SUCCESS) {
245 RegSetValueEx(hRegKey, valueName, 0, REG_SZ,
246 (LPBYTE)value.c_str(),
247 (value.length() + 1) * sizeof(wchar_t));
248 RegCloseKey(hRegKey);
252 static string FormatLastError()
254 DWORD err = GetLastError();
255 LPWSTR s;
256 string message = "Crash report submission failed: ";
257 // odds are it's a WinInet error
258 HANDLE hInetModule = GetModuleHandle(L"WinInet.dll");
259 if(FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
260 FORMAT_MESSAGE_FROM_SYSTEM |
261 FORMAT_MESSAGE_FROM_HMODULE,
262 hInetModule,
263 err,
265 (LPWSTR)&s,
267 NULL) != 0) {
268 message += WideToUTF8(s, NULL);
269 LocalFree(s);
270 // strip off any trailing newlines
271 string::size_type n = message.find_last_not_of("\r\n");
272 if (n < message.size() - 1) {
273 message.erase(n+1);
276 else {
277 char buf[64];
278 sprintf(buf, "Unknown error, error code: 0x%08x", err);
279 message += buf;
281 return message;
284 #define TS_DRAW 2
285 #define BP_CHECKBOX 3
287 typedef HANDLE (WINAPI*OpenThemeDataPtr)(HWND hwnd, LPCWSTR pszClassList);
288 typedef HRESULT (WINAPI*CloseThemeDataPtr)(HANDLE hTheme);
289 typedef HRESULT (WINAPI*GetThemePartSizePtr)(HANDLE hTheme, HDC hdc, int iPartId,
290 int iStateId, RECT* prc, int ts,
291 SIZE* psz);
292 typedef HRESULT (WINAPI*GetThemeContentRectPtr)(HANDLE hTheme, HDC hdc, int iPartId,
293 int iStateId, const RECT* pRect,
294 RECT* pContentRect);
297 static void GetThemeSizes(HWND hwnd)
299 HMODULE themeDLL = LoadLibrary(L"uxtheme.dll");
301 if (!themeDLL)
302 return;
304 OpenThemeDataPtr openTheme =
305 (OpenThemeDataPtr)GetProcAddress(themeDLL, "OpenThemeData");
306 CloseThemeDataPtr closeTheme =
307 (CloseThemeDataPtr)GetProcAddress(themeDLL, "CloseThemeData");
308 GetThemePartSizePtr getThemePartSize =
309 (GetThemePartSizePtr)GetProcAddress(themeDLL, "GetThemePartSize");
311 if (!openTheme || !closeTheme || !getThemePartSize) {
312 FreeLibrary(themeDLL);
313 return;
316 HANDLE buttonTheme = openTheme(hwnd, L"Button");
317 if (!buttonTheme) {
318 FreeLibrary(themeDLL);
319 return;
321 HDC hdc = GetDC(hwnd);
322 SIZE s;
323 getThemePartSize(buttonTheme, hdc, BP_CHECKBOX, 0, NULL, TS_DRAW, &s);
324 gCheckboxPadding = s.cx;
325 closeTheme(buttonTheme);
326 FreeLibrary(themeDLL);
329 // Gets the position of a window relative to another window's client area
330 static void GetRelativeRect(HWND hwnd, HWND hwndParent, RECT* r)
332 GetWindowRect(hwnd, r);
333 MapWindowPoints(NULL, hwndParent, (POINT*)r, 2);
336 static void SetDlgItemVisible(HWND hwndDlg, UINT item, bool visible)
338 HWND hwnd = GetDlgItem(hwndDlg, item);
340 ShowWindow(hwnd, visible ? SW_SHOW : SW_HIDE);
343 static void SetDlgItemDisabled(HWND hwndDlg, UINT item, bool disabled)
345 HWND hwnd = GetDlgItem(hwndDlg, item);
346 LONG style = GetWindowLong(hwnd, GWL_STYLE);
347 if (!disabled)
348 style |= WS_DISABLED;
349 else
350 style &= ~WS_DISABLED;
352 SetWindowLong(hwnd, GWL_STYLE, style);
355 /* === Crash Reporting Dialog === */
357 static void StretchDialog(HWND hwndDlg, int ydiff)
359 RECT r;
360 GetWindowRect(hwndDlg, &r);
361 r.bottom += ydiff;
362 MoveWindow(hwndDlg, r.left, r.top,
363 r.right - r.left, r.bottom - r.top, TRUE);
366 static void ReflowDialog(HWND hwndDlg, int ydiff)
368 // Move items attached to the bottom down/up by as much as
369 // the window resize
370 for (set<UINT>::const_iterator item = gAttachedBottom.begin();
371 item != gAttachedBottom.end();
372 item++) {
373 RECT r;
374 HWND hwnd = GetDlgItem(hwndDlg, *item);
375 GetRelativeRect(hwnd, hwndDlg, &r);
376 r.top += ydiff;
377 r.bottom += ydiff;
378 MoveWindow(hwnd, r.left, r.top,
379 r.right - r.left, r.bottom - r.top, TRUE);
383 static DWORD WINAPI SendThreadProc(LPVOID param)
385 bool finishedOk;
386 SendThreadData* td = (SendThreadData*)param;
388 if (td->sendURL.empty()) {
389 finishedOk = false;
390 LogMessage("No server URL, not sending report");
391 } else {
392 google_breakpad::CrashReportSender sender(L"");
393 finishedOk = (sender.SendCrashReport(td->sendURL,
394 td->queryParameters,
395 td->dumpFile,
396 &td->serverResponse)
397 == google_breakpad::RESULT_SUCCEEDED);
398 if (finishedOk) {
399 LogMessage("Crash report submitted successfully");
401 else {
402 // get an error string and print it to the log
403 //XXX: would be nice to get the HTTP status code here, filed:
404 // http://code.google.com/p/google-breakpad/issues/detail?id=220
405 LogMessage(FormatLastError());
409 PostMessage(td->hDlg, WM_UPLOADCOMPLETE, finishedOk ? 1 : 0, 0);
411 return 0;
414 static void EndCrashReporterDialog(HWND hwndDlg, int code)
416 // Save the current values to the registry
417 wchar_t email[MAX_EMAIL_LENGTH];
418 GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email));
419 SetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email);
421 SetBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE,
422 IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK) != 0);
423 SetBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE,
424 IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK) != 0);
425 SetBoolKey(gCrashReporterKey.c_str(), SUBMIT_REPORT_VALUE,
426 IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
428 EndDialog(hwndDlg, code);
431 static void MaybeResizeProgressText(HWND hwndDlg)
433 HWND hwndProgress = GetDlgItem(hwndDlg, IDC_PROGRESSTEXT);
434 HDC hdc = GetDC(hwndProgress);
435 HFONT hfont = (HFONT)SendMessage(hwndProgress, WM_GETFONT, 0, 0);
436 if (hfont)
437 SelectObject(hdc, hfont);
438 SIZE size;
439 RECT rect;
440 GetRelativeRect(hwndProgress, hwndDlg, &rect);
442 wchar_t text[1024];
443 GetWindowText(hwndProgress, text, 1024);
445 if (!GetTextExtentPoint32(hdc, text, wcslen(text), &size))
446 return;
448 if (size.cx < (rect.right - rect.left))
449 return;
451 // Figure out how much we need to resize things vertically
452 // This is sort of a fudge, but it should be good enough.
453 int wantedHeight = size.cy *
454 (int)ceil((float)size.cx / (float)(rect.right - rect.left));
455 int diff = wantedHeight - (rect.bottom - rect.top);
456 if (diff <= 0)
457 return;
459 MoveWindow(hwndProgress, rect.left, rect.top,
460 rect.right - rect.left,
461 wantedHeight,
462 TRUE);
464 gAttachedBottom.clear();
465 gAttachedBottom.insert(IDC_CLOSEBUTTON);
466 gAttachedBottom.insert(IDC_RESTARTBUTTON);
468 StretchDialog(hwndDlg, diff);
470 for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
471 gAttachedBottom.insert(kDefaultAttachedBottom[i]);
475 static void MaybeSendReport(HWND hwndDlg)
477 if (!IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK)) {
478 EndCrashReporterDialog(hwndDlg, 0);
479 return;
482 // disable all the form controls
483 EnableWindow(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK), false);
484 EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), false);
485 EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), false);
486 EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), false);
487 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), false);
488 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
489 EnableWindow(GetDlgItem(hwndDlg, IDC_CLOSEBUTTON), false);
490 EnableWindow(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON), false);
492 SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTDURINGSUBMIT).c_str());
493 MaybeResizeProgressText(hwndDlg);
494 // start throbber
495 // play entire AVI, and loop
496 Animate_Play(GetDlgItem(hwndDlg, IDC_THROBBER), 0, -1, -1);
497 SetDlgItemVisible(hwndDlg, IDC_THROBBER, true);
498 gThreadHandle = NULL;
499 gSendData.hDlg = hwndDlg;
500 gSendData.queryParameters = gQueryParameters;
502 gThreadHandle = CreateThread(NULL, 0, SendThreadProc, &gSendData, 0, NULL);
505 static void RestartApplication()
507 wstring cmdLine;
509 for (unsigned int i = 0; i < gRestartArgs.size(); i++) {
510 cmdLine += L"\"" + UTF8ToWide(gRestartArgs[i]) + L"\" ";
513 STARTUPINFO si;
514 PROCESS_INFORMATION pi;
516 ZeroMemory(&si, sizeof(si));
517 si.cb = sizeof(si);
518 si.dwFlags = STARTF_USESHOWWINDOW;
519 si.wShowWindow = SW_SHOWNORMAL;
520 ZeroMemory(&pi, sizeof(pi));
522 if (CreateProcess(NULL, (LPWSTR)cmdLine.c_str(), NULL, NULL, FALSE, 0,
523 NULL, NULL, &si, &pi)) {
524 CloseHandle(pi.hProcess);
525 CloseHandle(pi.hThread);
529 static void ShowReportInfo(HWND hwndDlg)
531 wstring description;
533 for (map<wstring,wstring>::const_iterator i = gQueryParameters.begin();
534 i != gQueryParameters.end();
535 i++) {
536 description += i->first;
537 description += L": ";
538 description += i->second;
539 description += L"\n";
542 description += L"\n";
543 description += Str(ST_EXTRAREPORTINFO);
545 SetDlgItemText(hwndDlg, IDC_VIEWREPORTTEXT, description.c_str());
548 static void UpdateURL(HWND hwndDlg)
550 if (IsDlgButtonChecked(hwndDlg, IDC_INCLUDEURLCHECK)) {
551 gQueryParameters[L"URL"] = gURLParameter;
552 } else {
553 gQueryParameters.erase(L"URL");
557 static void UpdateEmail(HWND hwndDlg)
559 if (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)) {
560 wchar_t email[MAX_EMAIL_LENGTH];
561 GetDlgItemText(hwndDlg, IDC_EMAILTEXT, email, sizeof(email));
562 gQueryParameters[L"Email"] = email;
563 if (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK))
564 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), true);
565 } else {
566 gQueryParameters.erase(L"Email");
567 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT), false);
571 static void UpdateComment(HWND hwndDlg)
573 wchar_t comment[MAX_COMMENT_LENGTH + 1];
574 GetDlgItemText(hwndDlg, IDC_COMMENTTEXT, comment, sizeof(comment));
575 if (wcslen(comment) > 0)
576 gQueryParameters[L"Comments"] = comment;
577 else
578 gQueryParameters.erase(L"Comments");
582 * Dialog procedure for the "view report" dialog.
584 static BOOL CALLBACK ViewReportDialogProc(HWND hwndDlg, UINT message,
585 WPARAM wParam, LPARAM lParam)
587 switch (message) {
588 case WM_INITDIALOG: {
589 SetWindowText(hwndDlg, Str(ST_VIEWREPORTTITLE).c_str());
590 SetDlgItemText(hwndDlg, IDOK, Str(ST_OK).c_str());
591 SendDlgItemMessage(hwndDlg, IDC_VIEWREPORTTEXT,
592 EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
593 ShowReportInfo(hwndDlg);
594 SetFocus(GetDlgItem(hwndDlg, IDOK));
595 return FALSE;
598 case WM_COMMAND: {
599 if (HIWORD(wParam) == BN_CLICKED && LOWORD(wParam) == IDOK)
600 EndDialog(hwndDlg, 0);
601 return FALSE;
604 return FALSE;
607 // Return the number of bytes this string will take encoded
608 // in UTF-8
609 static inline int BytesInUTF8(wchar_t* str)
611 // Just count size of buffer for UTF-8, minus one
612 // (we don't need to count the null terminator)
613 return WideCharToMultiByte(CP_UTF8, 0, str, -1, NULL, 0, NULL, NULL) - 1;
616 // Calculate the length of the text in this edit control (in bytes,
617 // in the UTF-8 encoding) after replacing the current selection
618 // with |insert|.
619 static int NewTextLength(HWND hwndEdit, wchar_t* insert)
621 wchar_t current[MAX_COMMENT_LENGTH + 1];
623 GetWindowText(hwndEdit, current, MAX_COMMENT_LENGTH + 1);
624 DWORD selStart, selEnd;
625 SendMessage(hwndEdit, EM_GETSEL, (WPARAM)&selStart, (LPARAM)&selEnd);
627 int selectionLength = 0;
628 if (selEnd - selStart > 0) {
629 wchar_t selection[MAX_COMMENT_LENGTH + 1];
630 google_breakpad::WindowsStringUtils::safe_wcsncpy(selection,
631 MAX_COMMENT_LENGTH + 1,
632 current + selStart,
633 selEnd - selStart);
634 selection[selEnd - selStart] = '\0';
635 selectionLength = BytesInUTF8(selection);
638 // current string length + replacement text length
639 // - replaced selection length
640 return BytesInUTF8(current) + BytesInUTF8(insert) - selectionLength;
643 // Window procedure for subclassing edit controls
644 static LRESULT CALLBACK EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam,
645 LPARAM lParam)
647 static WNDPROC super = NULL;
649 if (super == NULL)
650 super = (WNDPROC)GetWindowLongPtr(hwnd, GWLP_USERDATA);
652 switch (uMsg) {
653 case WM_PAINT: {
654 HDC hdc;
655 PAINTSTRUCT ps;
656 RECT r;
657 wchar_t windowText[1024];
659 GetWindowText(hwnd, windowText, 1024);
660 // if the control contains text or is focused, draw it normally
661 if (GetFocus() == hwnd || windowText[0] != '\0')
662 return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
664 GetClientRect(hwnd, &r);
665 hdc = BeginPaint(hwnd, &ps);
666 FillRect(hdc, &r, GetSysColorBrush(IsWindowEnabled(hwnd)
667 ? COLOR_WINDOW : COLOR_BTNFACE));
668 SetTextColor(hdc, GetSysColor(COLOR_GRAYTEXT));
669 SelectObject(hdc, (HFONT)GetStockObject(DEFAULT_GUI_FONT));
670 SetBkMode(hdc, TRANSPARENT);
671 wchar_t* txt = (wchar_t*)GetProp(hwnd, L"PROP_GRAYTEXT");
672 // Get the actual edit control rect
673 CallWindowProc(super, hwnd, EM_GETRECT, 0, (LPARAM)&r);
674 UINT format = DT_EDITCONTROL | DT_NOPREFIX | DT_WORDBREAK | DT_INTERNAL;
675 if (gRTLlayout)
676 format |= DT_RIGHT;
677 if (txt)
678 DrawText(hdc, txt, wcslen(txt), &r, format);
679 EndPaint(hwnd, &ps);
680 return 0;
683 // We handle WM_CHAR and WM_PASTE to limit the comment box to 500
684 // bytes in UTF-8.
685 case WM_CHAR: {
686 // Leave accelerator keys and non-printing chars (except LF) alone
687 if (wParam & (1<<24) || wParam & (1<<29) ||
688 (wParam < ' ' && wParam != '\n'))
689 break;
691 wchar_t ch[2] = { (wchar_t)wParam, 0 };
692 if (NewTextLength(hwnd, ch) > MAX_COMMENT_LENGTH)
693 return 0;
695 break;
698 case WM_PASTE: {
699 if (IsClipboardFormatAvailable(CF_UNICODETEXT) &&
700 OpenClipboard(hwnd)) {
701 HGLOBAL hg = GetClipboardData(CF_UNICODETEXT);
702 wchar_t* pastedText = (wchar_t*)GlobalLock(hg);
703 int newSize = 0;
705 if (pastedText)
706 newSize = NewTextLength(hwnd, pastedText);
708 GlobalUnlock(hg);
709 CloseClipboard();
711 if (newSize > MAX_COMMENT_LENGTH)
712 return 0;
714 break;
717 case WM_SETFOCUS:
718 case WM_KILLFOCUS: {
719 RECT r;
720 GetClientRect(hwnd, &r);
721 InvalidateRect(hwnd, &r, TRUE);
722 break;
725 case WM_DESTROY: {
726 // cleanup our property
727 HGLOBAL hData = RemoveProp(hwnd, L"PROP_GRAYTEXT");
728 if (hData)
729 GlobalFree(hData);
733 return CallWindowProc(super, hwnd, uMsg, wParam, lParam);
736 // Resize a control to fit this text
737 static int ResizeControl(HWND hwndButton, RECT& rect, wstring text,
738 bool shiftLeft, int userDefinedPadding)
740 HDC hdc = GetDC(hwndButton);
741 HFONT hfont = (HFONT)SendMessage(hwndButton, WM_GETFONT, 0, 0);
742 if (hfont)
743 SelectObject(hdc, hfont);
744 SIZE size, oldSize;
745 int sizeDiff = 0;
747 wchar_t oldText[1024];
748 GetWindowText(hwndButton, oldText, 1024);
750 if (GetTextExtentPoint32(hdc, text.c_str(), text.length(), &size)
751 // default text on the button
752 && GetTextExtentPoint32(hdc, oldText, wcslen(oldText), &oldSize)) {
754 Expand control widths to accomidate wider text strings. For most
755 controls (including buttons) the text padding is defined by the
756 dialog's rc file. Some controls (such as checkboxes) have padding
757 that extends to the end of the dialog, in which case we ignore the
758 rc padding and rely on a user defined value passed in through
759 userDefinedPadding.
761 int textIncrease = size.cx - oldSize.cx;
762 if (textIncrease < 0)
763 return 0;
764 int existingTextPadding;
765 if (userDefinedPadding == 0)
766 existingTextPadding = (rect.right - rect.left) - oldSize.cx;
767 else
768 existingTextPadding = userDefinedPadding;
769 sizeDiff = textIncrease + existingTextPadding;
771 if (shiftLeft) {
772 // shift left by the amount the button should grow
773 rect.left -= sizeDiff;
775 else {
776 // grow right instead
777 rect.right += sizeDiff;
779 MoveWindow(hwndButton, rect.left, rect.top,
780 rect.right - rect.left,
781 rect.bottom - rect.top,
782 TRUE);
784 return sizeDiff;
787 // The window was resized horizontally, so widen some of our
788 // controls to make use of the space
789 static void StretchControlsToFit(HWND hwndDlg)
791 int controls[] = {
792 IDC_DESCRIPTIONTEXT,
793 IDC_SUBMITREPORTCHECK,
794 IDC_COMMENTTEXT,
795 IDC_INCLUDEURLCHECK,
796 IDC_EMAILMECHECK,
797 IDC_EMAILTEXT,
798 IDC_PROGRESSTEXT
801 RECT dlgRect;
802 GetClientRect(hwndDlg, &dlgRect);
804 for (int i=0; i<sizeof(controls)/sizeof(controls[0]); i++) {
805 RECT r;
806 HWND hwndControl = GetDlgItem(hwndDlg, controls[i]);
807 GetRelativeRect(hwndControl, hwndDlg, &r);
808 // 6 pixel spacing on the right
809 if (r.right + 6 != dlgRect.right) {
810 r.right = dlgRect.right - 6;
811 MoveWindow(hwndControl, r.left, r.top,
812 r.right - r.left,
813 r.bottom - r.top,
814 TRUE);
819 static void SubmitReportChecked(HWND hwndDlg)
821 bool enabled = (IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0);
822 EnableWindow(GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON), enabled);
823 EnableWindow(GetDlgItem(hwndDlg, IDC_COMMENTTEXT), enabled);
824 EnableWindow(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), enabled);
825 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), enabled);
826 EnableWindow(GetDlgItem(hwndDlg, IDC_EMAILTEXT),
827 enabled && (IsDlgButtonChecked(hwndDlg, IDC_EMAILMECHECK)
828 != 0));
829 SetDlgItemVisible(hwndDlg, IDC_PROGRESSTEXT, enabled);
832 static INT_PTR DialogBoxParamMaybeRTL(UINT idd, HWND hwndParent,
833 DLGPROC dlgProc, LPARAM param)
835 INT_PTR rv = 0;
836 if (gRTLlayout) {
837 // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog
838 // template.
839 HRSRC hDialogRC = FindResource(NULL, MAKEINTRESOURCE(idd),
840 RT_DIALOG);
841 HGLOBAL hDlgTemplate = LoadResource(NULL, hDialogRC);
842 DLGTEMPLATEEX* pDlgTemplate = (DLGTEMPLATEEX*)LockResource(hDlgTemplate);
843 unsigned long sizeDlg = SizeofResource(NULL, hDialogRC);
844 HGLOBAL hMyDlgTemplate = GlobalAlloc(GPTR, sizeDlg);
845 DLGTEMPLATEEX* pMyDlgTemplate =
846 (DLGTEMPLATEEX*)GlobalLock(hMyDlgTemplate);
847 memcpy(pMyDlgTemplate, pDlgTemplate, sizeDlg);
849 pMyDlgTemplate->exStyle |= WS_EX_LAYOUTRTL;
851 rv = DialogBoxIndirectParam(NULL, (LPCDLGTEMPLATE)pMyDlgTemplate,
852 hwndParent, dlgProc, param);
853 GlobalUnlock(hMyDlgTemplate);
854 GlobalFree(hMyDlgTemplate);
856 else {
857 rv = DialogBoxParam(NULL, MAKEINTRESOURCE(idd), hwndParent,
858 dlgProc, param);
861 return rv;
865 static BOOL CALLBACK CrashReporterDialogProc(HWND hwndDlg, UINT message,
866 WPARAM wParam, LPARAM lParam)
868 static int sHeight = 0;
870 bool success;
871 bool enabled;
873 switch (message) {
874 case WM_INITDIALOG: {
875 GetThemeSizes(hwndDlg);
876 RECT r;
877 GetClientRect(hwndDlg, &r);
878 sHeight = r.bottom - r.top;
880 SetWindowText(hwndDlg, Str(ST_CRASHREPORTERTITLE).c_str());
881 HICON hIcon = LoadIcon(GetModuleHandle(NULL),
882 MAKEINTRESOURCE(IDI_MAINICON));
883 SendMessage(hwndDlg, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
884 SendMessage(hwndDlg, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
886 // resize the "View Report" button based on the string length
887 RECT rect;
888 HWND hwnd = GetDlgItem(hwndDlg, IDC_VIEWREPORTBUTTON);
889 GetRelativeRect(hwnd, hwndDlg, &rect);
890 ResizeControl(hwnd, rect, Str(ST_VIEWREPORT), false, 0);
891 SetDlgItemText(hwndDlg, IDC_VIEWREPORTBUTTON, Str(ST_VIEWREPORT).c_str());
893 hwnd = GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK);
894 GetRelativeRect(hwnd, hwndDlg, &rect);
895 int maxdiff = ResizeControl(hwnd, rect, Str(ST_CHECKSUBMIT), false,
896 gCheckboxPadding);
897 SetDlgItemText(hwndDlg, IDC_SUBMITREPORTCHECK,
898 Str(ST_CHECKSUBMIT).c_str());
900 if (!CheckBoolKey(gCrashReporterKey.c_str(),
901 SUBMIT_REPORT_VALUE, &enabled))
902 enabled = ShouldEnableSending();
904 CheckDlgButton(hwndDlg, IDC_SUBMITREPORTCHECK, enabled ? BST_CHECKED
905 : BST_UNCHECKED);
906 SubmitReportChecked(hwndDlg);
908 HWND hwndComment = GetDlgItem(hwndDlg, IDC_COMMENTTEXT);
909 WNDPROC OldWndProc = (WNDPROC)SetWindowLongPtr(hwndComment,
910 GWLP_WNDPROC,
911 (LONG_PTR)EditSubclassProc);
913 // Subclass comment edit control to get placeholder text
914 SetWindowLongPtr(hwndComment, GWLP_USERDATA, (LONG_PTR)OldWndProc);
915 wstring commentGrayText = Str(ST_COMMENTGRAYTEXT);
916 wchar_t* hMem = (wchar_t*)GlobalAlloc(GPTR, (commentGrayText.length() + 1)*sizeof(wchar_t));
917 wcscpy(hMem, commentGrayText.c_str());
918 SetProp(hwndComment, L"PROP_GRAYTEXT", hMem);
920 hwnd = GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK);
921 GetRelativeRect(hwnd, hwndDlg, &rect);
922 int diff = ResizeControl(hwnd, rect, Str(ST_CHECKURL), false,
923 gCheckboxPadding);
924 maxdiff = max(diff, maxdiff);
925 SetDlgItemText(hwndDlg, IDC_INCLUDEURLCHECK, Str(ST_CHECKURL).c_str());
927 // want this on by default
928 if (CheckBoolKey(gCrashReporterKey.c_str(), INCLUDE_URL_VALUE, &enabled) &&
929 !enabled) {
930 CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_UNCHECKED);
931 } else {
932 CheckDlgButton(hwndDlg, IDC_INCLUDEURLCHECK, BST_CHECKED);
935 hwnd = GetDlgItem(hwndDlg, IDC_EMAILMECHECK);
936 GetRelativeRect(hwnd, hwndDlg, &rect);
937 diff = ResizeControl(hwnd, rect, Str(ST_CHECKEMAIL), false,
938 gCheckboxPadding);
939 maxdiff = max(diff, maxdiff);
940 SetDlgItemText(hwndDlg, IDC_EMAILMECHECK, Str(ST_CHECKEMAIL).c_str());
942 if (CheckBoolKey(gCrashReporterKey.c_str(), EMAIL_ME_VALUE, &enabled) &&
943 enabled) {
944 CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
945 } else {
946 CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_UNCHECKED);
949 wstring email;
950 if (GetStringKey(gCrashReporterKey.c_str(), EMAIL_VALUE, email)) {
951 SetDlgItemText(hwndDlg, IDC_EMAILTEXT, email.c_str());
954 // Subclass email edit control to get placeholder text
955 HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
956 OldWndProc = (WNDPROC)SetWindowLongPtr(hwndEmail,
957 GWLP_WNDPROC,
958 (LONG_PTR)EditSubclassProc);
959 SetWindowLongPtr(hwndEmail, GWLP_USERDATA, (LONG_PTR)OldWndProc);
960 wstring emailGrayText = Str(ST_EMAILGRAYTEXT);
961 hMem = (wchar_t*)GlobalAlloc(GPTR, (emailGrayText.length() + 1)*sizeof(wchar_t));
962 wcscpy(hMem, emailGrayText.c_str());
963 SetProp(hwndEmail, L"PROP_GRAYTEXT", hMem);
965 SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT, Str(ST_REPORTPRESUBMIT).c_str());
967 RECT closeRect;
968 HWND hwndClose = GetDlgItem(hwndDlg, IDC_CLOSEBUTTON);
969 GetRelativeRect(hwndClose, hwndDlg, &closeRect);
971 RECT restartRect;
972 HWND hwndRestart = GetDlgItem(hwndDlg, IDC_RESTARTBUTTON);
973 GetRelativeRect(hwndRestart, hwndDlg, &restartRect);
975 // set the close button text and shift the buttons around
976 // since the size may need to change
977 int sizeDiff = ResizeControl(hwndClose, closeRect, Str(ST_QUIT),
978 true, 0);
979 restartRect.left -= sizeDiff;
980 restartRect.right -= sizeDiff;
981 SetDlgItemText(hwndDlg, IDC_CLOSEBUTTON, Str(ST_QUIT).c_str());
983 if (gRestartArgs.size() > 0) {
984 // Resize restart button to fit text
985 ResizeControl(hwndRestart, restartRect, Str(ST_RESTART), true, 0);
986 SetDlgItemText(hwndDlg, IDC_RESTARTBUTTON, Str(ST_RESTART).c_str());
987 } else {
988 // No restart arguments, so just hide the restart button
989 SetDlgItemVisible(hwndDlg, IDC_RESTARTBUTTON, false);
991 // See if we need to widen the window
992 // Leave 6 pixels on either side + 6 pixels between the buttons
993 int neededSize = closeRect.right - closeRect.left +
994 restartRect.right - restartRect.left + 6 * 3;
995 GetClientRect(hwndDlg, &r);
996 // We may already have resized one of the checkboxes above
997 maxdiff = max(maxdiff, neededSize - (r.right - r.left));
999 if (maxdiff > 0) {
1000 // widen window
1001 GetWindowRect(hwndDlg, &r);
1002 r.right += maxdiff;
1003 MoveWindow(hwndDlg, r.left, r.top,
1004 r.right - r.left, r.bottom - r.top, TRUE);
1005 // shift both buttons right
1006 if (restartRect.left + maxdiff < 6)
1007 maxdiff += 6;
1008 closeRect.left += maxdiff;
1009 closeRect.right += maxdiff;
1010 restartRect.left += maxdiff;
1011 restartRect.right += maxdiff;
1012 MoveWindow(hwndClose, closeRect.left, closeRect.top,
1013 closeRect.right - closeRect.left,
1014 closeRect.bottom - closeRect.top,
1015 TRUE);
1016 StretchControlsToFit(hwndDlg);
1018 // need to move the restart button regardless
1019 MoveWindow(hwndRestart, restartRect.left, restartRect.top,
1020 restartRect.right - restartRect.left,
1021 restartRect.bottom - restartRect.top,
1022 TRUE);
1024 // Resize the description text last, in case the window was resized
1025 // before this.
1026 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
1027 EM_SETEVENTMASK, (WPARAM)NULL,
1028 ENM_REQUESTRESIZE);
1030 wstring description = Str(ST_CRASHREPORTERHEADER);
1031 description += L"\n\n";
1032 description += Str(ST_CRASHREPORTERDESCRIPTION);
1033 SetDlgItemText(hwndDlg, IDC_DESCRIPTIONTEXT, description.c_str());
1035 // Make the title bold.
1036 CHARFORMAT fmt = { 0, };
1037 fmt.cbSize = sizeof(fmt);
1038 fmt.dwMask = CFM_BOLD;
1039 fmt.dwEffects = CFE_BOLD;
1040 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL,
1041 0, Str(ST_CRASHREPORTERHEADER).length());
1042 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETCHARFORMAT,
1043 SCF_SELECTION, (LPARAM)&fmt);
1044 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT, EM_SETSEL, 0, 0);
1046 SendDlgItemMessage(hwndDlg, IDC_DESCRIPTIONTEXT,
1047 EM_SETTARGETDEVICE, (WPARAM)NULL, 0);
1049 // if no URL was given, hide the URL checkbox
1050 if (gQueryParameters.find(L"URL") == gQueryParameters.end()) {
1051 RECT urlCheckRect, emailCheckRect;
1052 GetWindowRect(GetDlgItem(hwndDlg, IDC_INCLUDEURLCHECK), &urlCheckRect);
1053 GetWindowRect(GetDlgItem(hwndDlg, IDC_EMAILMECHECK), &emailCheckRect);
1055 SetDlgItemVisible(hwndDlg, IDC_INCLUDEURLCHECK, false);
1057 gAttachedBottom.erase(IDC_VIEWREPORTBUTTON);
1058 gAttachedBottom.erase(IDC_SUBMITREPORTCHECK);
1059 gAttachedBottom.erase(IDC_COMMENTTEXT);
1061 StretchDialog(hwndDlg, urlCheckRect.top - emailCheckRect.top);
1063 gAttachedBottom.insert(IDC_VIEWREPORTBUTTON);
1064 gAttachedBottom.insert(IDC_SUBMITREPORTCHECK);
1065 gAttachedBottom.insert(IDC_COMMENTTEXT);
1068 MaybeResizeProgressText(hwndDlg);
1070 // Open the AVI resource for the throbber
1071 Animate_Open(GetDlgItem(hwndDlg, IDC_THROBBER),
1072 MAKEINTRESOURCE(IDR_THROBBER));
1074 UpdateURL(hwndDlg);
1075 UpdateEmail(hwndDlg);
1077 SetFocus(GetDlgItem(hwndDlg, IDC_SUBMITREPORTCHECK));
1078 return FALSE;
1080 case WM_SIZE: {
1081 ReflowDialog(hwndDlg, HIWORD(lParam) - sHeight);
1082 sHeight = HIWORD(lParam);
1083 InvalidateRect(hwndDlg, NULL, TRUE);
1084 return FALSE;
1086 case WM_NOTIFY: {
1087 NMHDR* notification = reinterpret_cast<NMHDR*>(lParam);
1088 if (notification->code == EN_REQUESTRESIZE) {
1089 // Resizing the rich edit control to fit the description text.
1090 REQRESIZE* reqresize = reinterpret_cast<REQRESIZE*>(lParam);
1091 RECT newSize = reqresize->rc;
1092 RECT oldSize;
1093 GetRelativeRect(notification->hwndFrom, hwndDlg, &oldSize);
1095 // resize the text box as requested
1096 MoveWindow(notification->hwndFrom, newSize.left, newSize.top,
1097 newSize.right - newSize.left, newSize.bottom - newSize.top,
1098 TRUE);
1100 // Resize the dialog to fit (the WM_SIZE handler will move the controls)
1101 StretchDialog(hwndDlg, newSize.bottom - oldSize.bottom);
1103 return FALSE;
1105 case WM_COMMAND: {
1106 if (HIWORD(wParam) == BN_CLICKED) {
1107 switch(LOWORD(wParam)) {
1108 case IDC_VIEWREPORTBUTTON:
1109 DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG, hwndDlg,
1110 (DLGPROC)ViewReportDialogProc, 0);
1111 break;
1112 case IDC_SUBMITREPORTCHECK:
1113 SubmitReportChecked(hwndDlg);
1114 break;
1115 case IDC_INCLUDEURLCHECK:
1116 UpdateURL(hwndDlg);
1117 break;
1118 case IDC_EMAILMECHECK:
1119 UpdateEmail(hwndDlg);
1120 break;
1121 case IDC_CLOSEBUTTON:
1122 MaybeSendReport(hwndDlg);
1123 break;
1124 case IDC_RESTARTBUTTON:
1125 RestartApplication();
1126 MaybeSendReport(hwndDlg);
1127 break;
1129 } else if (HIWORD(wParam) == EN_CHANGE) {
1130 switch(LOWORD(wParam)) {
1131 case IDC_EMAILTEXT:
1132 UpdateEmail(hwndDlg);
1133 break;
1134 case IDC_COMMENTTEXT:
1135 UpdateComment(hwndDlg);
1139 return FALSE;
1141 case WM_UPLOADCOMPLETE: {
1142 WaitForSingleObject(gThreadHandle, INFINITE);
1143 success = (wParam == 1);
1144 SendCompleted(success, WideToUTF8(gSendData.serverResponse));
1145 // hide throbber
1146 Animate_Stop(GetDlgItem(hwndDlg, IDC_THROBBER));
1147 SetDlgItemVisible(hwndDlg, IDC_THROBBER, false);
1149 SetDlgItemText(hwndDlg, IDC_PROGRESSTEXT,
1150 success ?
1151 Str(ST_REPORTSUBMITSUCCESS).c_str() :
1152 Str(ST_SUBMITFAILED).c_str());
1153 MaybeResizeProgressText(hwndDlg);
1154 // close dialog after 5 seconds
1155 SetTimer(hwndDlg, 0, 5000, NULL);
1157 return TRUE;
1160 case WM_LBUTTONDOWN: {
1161 HWND hwndEmail = GetDlgItem(hwndDlg, IDC_EMAILTEXT);
1162 POINT p = { LOWORD(lParam), HIWORD(lParam) };
1163 // if the email edit control is clicked, enable it,
1164 // check the email checkbox, and focus the email edit control
1165 if (ChildWindowFromPoint(hwndDlg, p) == hwndEmail &&
1166 IsWindowEnabled(GetDlgItem(hwndDlg, IDC_RESTARTBUTTON)) &&
1167 !IsWindowEnabled(hwndEmail) &&
1168 IsDlgButtonChecked(hwndDlg, IDC_SUBMITREPORTCHECK) != 0) {
1169 CheckDlgButton(hwndDlg, IDC_EMAILMECHECK, BST_CHECKED);
1170 UpdateEmail(hwndDlg);
1171 SetFocus(hwndEmail);
1173 break;
1176 case WM_TIMER: {
1177 // The "1" gets used down in UIShowCrashUI to indicate that we at least
1178 // tried to send the report.
1179 EndCrashReporterDialog(hwndDlg, 1);
1180 return FALSE;
1183 case WM_CLOSE: {
1184 EndCrashReporterDialog(hwndDlg, 0);
1185 return FALSE;
1188 return FALSE;
1191 static wstring UTF8ToWide(const string& utf8, bool *success)
1193 wchar_t* buffer = NULL;
1194 int buffer_size = MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
1195 -1, NULL, 0);
1196 if(buffer_size == 0) {
1197 if (success)
1198 *success = false;
1199 return L"";
1202 buffer = new wchar_t[buffer_size];
1203 if(buffer == NULL) {
1204 if (success)
1205 *success = false;
1206 return L"";
1209 MultiByteToWideChar(CP_UTF8, 0, utf8.c_str(),
1210 -1, buffer, buffer_size);
1211 wstring str = buffer;
1212 delete [] buffer;
1214 if (success)
1215 *success = true;
1217 return str;
1220 string WideToUTF8(const wstring& wide, bool* success)
1222 char* buffer = NULL;
1223 int buffer_size = WideCharToMultiByte(CP_UTF8, 0, wide.c_str(),
1224 -1, NULL, 0, NULL, NULL);
1225 if(buffer_size == 0) {
1226 if (success)
1227 *success = false;
1228 return "";
1231 buffer = new char[buffer_size];
1232 if(buffer == NULL) {
1233 if (success)
1234 *success = false;
1235 return "";
1238 WideCharToMultiByte(CP_UTF8, 0, wide.c_str(),
1239 -1, buffer, buffer_size, NULL, NULL);
1240 string utf8 = buffer;
1241 delete [] buffer;
1243 if (success)
1244 *success = true;
1246 return utf8;
1249 /* === Crashreporter UI Functions === */
1251 bool UIInit()
1253 for (int i = 0; i < sizeof(kDefaultAttachedBottom) / sizeof(UINT); i++) {
1254 gAttachedBottom.insert(kDefaultAttachedBottom[i]);
1257 DoInitCommonControls();
1259 return true;
1262 void UIShutdown()
1266 void UIShowDefaultUI()
1268 MessageBox(NULL, Str(ST_CRASHREPORTERDEFAULT).c_str(),
1269 L"Crash Reporter",
1270 MB_OK | MB_ICONSTOP);
1273 bool UIShowCrashUI(const string& dumpFile,
1274 const StringTable& queryParameters,
1275 const string& sendURL,
1276 const vector<string>& restartArgs)
1278 gSendData.hDlg = NULL;
1279 gSendData.dumpFile = UTF8ToWide(dumpFile);
1280 gSendData.sendURL = UTF8ToWide(sendURL);
1282 for (StringTable::const_iterator i = queryParameters.begin();
1283 i != queryParameters.end();
1284 i++) {
1285 gQueryParameters[UTF8ToWide(i->first)] = UTF8ToWide(i->second);
1288 if (gQueryParameters.find(L"Vendor") != gQueryParameters.end()) {
1289 gCrashReporterKey = L"Software\\";
1290 if (!gQueryParameters[L"Vendor"].empty()) {
1291 gCrashReporterKey += gQueryParameters[L"Vendor"] + L"\\";
1293 gCrashReporterKey += gQueryParameters[L"ProductName"] + L"\\Crash Reporter";
1296 if (gQueryParameters.find(L"URL") != gQueryParameters.end())
1297 gURLParameter = gQueryParameters[L"URL"];
1299 gRestartArgs = restartArgs;
1301 if (gStrings.find("isRTL") != gStrings.end() &&
1302 gStrings["isRTL"] == "yes")
1303 gRTLlayout = true;
1305 return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG, NULL,
1306 (DLGPROC)CrashReporterDialogProc, 0);
1309 void UIError_impl(const string& message)
1311 wstring title = Str(ST_CRASHREPORTERTITLE);
1312 if (title.empty())
1313 title = L"Crash Reporter Error";
1315 MessageBox(NULL, UTF8ToWide(message).c_str(), title.c_str(),
1316 MB_OK | MB_ICONSTOP);
1319 bool UIGetIniPath(string& path)
1321 wchar_t fileName[MAX_PATH];
1322 if (GetModuleFileName(NULL, fileName, MAX_PATH)) {
1323 // get crashreporter ini
1324 wchar_t* s = wcsrchr(fileName, '.');
1325 if (s) {
1326 wcscpy(s, L".ini");
1327 path = WideToUTF8(fileName);
1328 return true;
1332 return false;
1335 bool UIGetSettingsPath(const string& vendor,
1336 const string& product,
1337 string& settings_path)
1339 wchar_t path[MAX_PATH];
1340 if(SUCCEEDED(SHGetFolderPath(NULL,
1341 CSIDL_APPDATA,
1342 NULL,
1344 path))) {
1345 if (!vendor.empty()) {
1346 PathAppend(path, UTF8ToWide(vendor).c_str());
1348 PathAppend(path, UTF8ToWide(product).c_str());
1349 PathAppend(path, L"Crash Reports");
1350 settings_path = WideToUTF8(path);
1351 return true;
1353 return false;
1356 bool UIEnsurePathExists(const string& path)
1358 if (CreateDirectory(UTF8ToWide(path).c_str(), NULL) == 0) {
1359 if (GetLastError() != ERROR_ALREADY_EXISTS)
1360 return false;
1363 return true;
1366 bool UIFileExists(const string& path)
1368 DWORD attrs = GetFileAttributes(UTF8ToWide(path).c_str());
1369 return (attrs != INVALID_FILE_ATTRIBUTES);
1372 bool UIMoveFile(const string& oldfile, const string& newfile)
1374 if (oldfile == newfile)
1375 return true;
1377 return MoveFile(UTF8ToWide(oldfile).c_str(), UTF8ToWide(newfile).c_str())
1378 == TRUE;
1381 bool UIDeleteFile(const string& oldfile)
1383 return DeleteFile(UTF8ToWide(oldfile).c_str()) == TRUE;
1386 ifstream* UIOpenRead(const string& filename)
1388 // adapted from breakpad's src/common/windows/http_upload.cc
1390 // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
1391 // wchar_t* filename, so use _wfopen directly in that case. For VC8 and
1392 // later, _wfopen has been deprecated in favor of _wfopen_s, which does
1393 // not exist in earlier versions, so let the ifstream open the file itself.
1394 #if _MSC_VER >= 1400 // MSVC 2005/8
1395 ifstream* file = new ifstream();
1396 file->open(UTF8ToWide(filename).c_str(), ios::in);
1397 #else // _MSC_VER >= 1400
1398 ifstream* file = new ifstream(_wfopen(UTF8ToWide(filename).c_str(), L"r"));
1399 #endif // _MSC_VER >= 1400
1401 return file;
1404 ofstream* UIOpenWrite(const string& filename, bool append) // append=false
1406 // adapted from breakpad's src/common/windows/http_upload.cc
1408 // The "open" method on pre-MSVC8 ifstream implementations doesn't accept a
1409 // wchar_t* filename, so use _wfopen directly in that case. For VC8 and
1410 // later, _wfopen has been deprecated in favor of _wfopen_s, which does
1411 // not exist in earlier versions, so let the ifstream open the file itself.
1412 #if _MSC_VER >= 1400 // MSVC 2005/8
1413 ofstream* file = new ofstream();
1414 file->open(UTF8ToWide(filename).c_str(), append ? ios::out | ios::app
1415 : ios::out);
1416 #else // _MSC_VER >= 1400
1417 ofstream* file = new ofstream(_wfopen(UTF8ToWide(filename).c_str(),
1418 append ? L"a" : L"w"));
1419 #endif // _MSC_VER >= 1400
1421 return file;