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
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.
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
44 #include "crashreporter.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
76 using namespace CrashReporter
;
81 map
<wstring
,wstring
> queryParameters
;
84 wstring serverResponse
;
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.
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.
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
,
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
)
160 static bool CheckBoolKey(const wchar_t* key
,
161 const wchar_t* valueName
,
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);
174 RegCloseKey(hRegKey
);
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);
182 RegCloseKey(hRegKey
);
189 static void SetBoolKey(const wchar_t* key
, const wchar_t* value
, bool enabled
)
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
;
203 dataSize
= sizeof(buf
);
204 if (RegQueryValueEx(hRegKey
, valueName
, NULL
, &type
, (LPBYTE
)buf
, &dataSize
) == ERROR_SUCCESS
213 static bool GetStringKey(const wchar_t* key
,
214 const wchar_t* valueName
,
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
)) {
225 RegCloseKey(hRegKey
);
227 // look for it in user settings
228 if (RegOpenKey(HKEY_CURRENT_USER
, key
, &hRegKey
) == ERROR_SUCCESS
) {
229 if (GetStringValue(hRegKey
, valueName
, value
)) {
232 RegCloseKey(hRegKey
);
239 static void SetStringKey(const wchar_t* key
,
240 const wchar_t* valueName
,
241 const wstring
& value
)
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();
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
,
268 message
+= WideToUTF8(s
, NULL
);
270 // strip off any trailing newlines
271 string::size_type n
= message
.find_last_not_of("\r\n");
272 if (n
< message
.size() - 1) {
278 sprintf(buf
, "Unknown error, error code: 0x%08x", err
);
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
,
292 typedef HRESULT (WINAPI
*GetThemeContentRectPtr
)(HANDLE hTheme
, HDC hdc
, int iPartId
,
293 int iStateId
, const RECT
* pRect
,
297 static void GetThemeSizes(HWND hwnd
)
299 HMODULE themeDLL
= LoadLibrary(L
"uxtheme.dll");
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
);
316 HANDLE buttonTheme
= openTheme(hwnd
, L
"Button");
318 FreeLibrary(themeDLL
);
321 HDC hdc
= GetDC(hwnd
);
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
);
348 style
|= WS_DISABLED
;
350 style
&= ~WS_DISABLED
;
352 SetWindowLong(hwnd
, GWL_STYLE
, style
);
355 /* === Crash Reporting Dialog === */
357 static void StretchDialog(HWND hwndDlg
, int ydiff
)
360 GetWindowRect(hwndDlg
, &r
);
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
370 for (set
<UINT
>::const_iterator item
= gAttachedBottom
.begin();
371 item
!= gAttachedBottom
.end();
374 HWND hwnd
= GetDlgItem(hwndDlg
, *item
);
375 GetRelativeRect(hwnd
, hwndDlg
, &r
);
378 MoveWindow(hwnd
, r
.left
, r
.top
,
379 r
.right
- r
.left
, r
.bottom
- r
.top
, TRUE
);
383 static DWORD WINAPI
SendThreadProc(LPVOID param
)
386 SendThreadData
* td
= (SendThreadData
*)param
;
388 if (td
->sendURL
.empty()) {
390 LogMessage("No server URL, not sending report");
392 google_breakpad::CrashReportSender
sender(L
"");
393 finishedOk
= (sender
.SendCrashReport(td
->sendURL
,
397 == google_breakpad::RESULT_SUCCEEDED
);
399 LogMessage("Crash report submitted successfully");
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);
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);
437 SelectObject(hdc
, hfont
);
440 GetRelativeRect(hwndProgress
, hwndDlg
, &rect
);
443 GetWindowText(hwndProgress
, text
, 1024);
445 if (!GetTextExtentPoint32(hdc
, text
, wcslen(text
), &size
))
448 if (size
.cx
< (rect
.right
- rect
.left
))
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
);
459 MoveWindow(hwndProgress
, rect
.left
, rect
.top
,
460 rect
.right
- rect
.left
,
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);
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
);
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()
509 for (unsigned int i
= 0; i
< gRestartArgs
.size(); i
++) {
510 cmdLine
+= L
"\"" + UTF8ToWide(gRestartArgs
[i
]) + L
"\" ";
514 PROCESS_INFORMATION pi
;
516 ZeroMemory(&si
, 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
)
533 for (map
<wstring
,wstring
>::const_iterator i
= gQueryParameters
.begin();
534 i
!= gQueryParameters
.end();
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
;
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);
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
;
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
)
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
));
599 if (HIWORD(wParam
) == BN_CLICKED
&& LOWORD(wParam
) == IDOK
)
600 EndDialog(hwndDlg
, 0);
607 // Return the number of bytes this string will take encoded
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
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,
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
,
647 static WNDPROC super
= NULL
;
650 super
= (WNDPROC
)GetWindowLongPtr(hwnd
, GWLP_USERDATA
);
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
;
678 DrawText(hdc
, txt
, wcslen(txt
), &r
, format
);
683 // We handle WM_CHAR and WM_PASTE to limit the comment box to 500
686 // Leave accelerator keys and non-printing chars (except LF) alone
687 if (wParam
& (1<<24) || wParam
& (1<<29) ||
688 (wParam
< ' ' && wParam
!= '\n'))
691 wchar_t ch
[2] = { (wchar_t)wParam
, 0 };
692 if (NewTextLength(hwnd
, ch
) > MAX_COMMENT_LENGTH
)
699 if (IsClipboardFormatAvailable(CF_UNICODETEXT
) &&
700 OpenClipboard(hwnd
)) {
701 HGLOBAL hg
= GetClipboardData(CF_UNICODETEXT
);
702 wchar_t* pastedText
= (wchar_t*)GlobalLock(hg
);
706 newSize
= NewTextLength(hwnd
, pastedText
);
711 if (newSize
> MAX_COMMENT_LENGTH
)
720 GetClientRect(hwnd
, &r
);
721 InvalidateRect(hwnd
, &r
, TRUE
);
726 // cleanup our property
727 HGLOBAL hData
= RemoveProp(hwnd
, L
"PROP_GRAYTEXT");
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);
743 SelectObject(hdc
, hfont
);
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
761 int textIncrease
= size
.cx
- oldSize
.cx
;
762 if (textIncrease
< 0)
764 int existingTextPadding
;
765 if (userDefinedPadding
== 0)
766 existingTextPadding
= (rect
.right
- rect
.left
) - oldSize
.cx
;
768 existingTextPadding
= userDefinedPadding
;
769 sizeDiff
= textIncrease
+ existingTextPadding
;
772 // shift left by the amount the button should grow
773 rect
.left
-= sizeDiff
;
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
,
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
)
793 IDC_SUBMITREPORTCHECK
,
802 GetClientRect(hwndDlg
, &dlgRect
);
804 for (int i
=0; i
<sizeof(controls
)/sizeof(controls
[0]); i
++) {
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
,
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
)
829 SetDlgItemVisible(hwndDlg
, IDC_PROGRESSTEXT
, enabled
);
832 static INT_PTR
DialogBoxParamMaybeRTL(UINT idd
, HWND hwndParent
,
833 DLGPROC dlgProc
, LPARAM param
)
837 // We need to toggle the WS_EX_LAYOUTRTL style flag on the dialog
839 HRSRC hDialogRC
= FindResource(NULL
, MAKEINTRESOURCE(idd
),
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
);
857 rv
= DialogBoxParam(NULL
, MAKEINTRESOURCE(idd
), hwndParent
,
865 static BOOL CALLBACK
CrashReporterDialogProc(HWND hwndDlg
, UINT message
,
866 WPARAM wParam
, LPARAM lParam
)
868 static int sHeight
= 0;
874 case WM_INITDIALOG
: {
875 GetThemeSizes(hwndDlg
);
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
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,
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
906 SubmitReportChecked(hwndDlg
);
908 HWND hwndComment
= GetDlgItem(hwndDlg
, IDC_COMMENTTEXT
);
909 WNDPROC OldWndProc
= (WNDPROC
)SetWindowLongPtr(hwndComment
,
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,
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
) &&
930 CheckDlgButton(hwndDlg
, IDC_INCLUDEURLCHECK
, BST_UNCHECKED
);
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,
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
) &&
944 CheckDlgButton(hwndDlg
, IDC_EMAILMECHECK
, BST_CHECKED
);
946 CheckDlgButton(hwndDlg
, IDC_EMAILMECHECK
, BST_UNCHECKED
);
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
,
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());
968 HWND hwndClose
= GetDlgItem(hwndDlg
, IDC_CLOSEBUTTON
);
969 GetRelativeRect(hwndClose
, hwndDlg
, &closeRect
);
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
),
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());
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
));
1001 GetWindowRect(hwndDlg
, &r
);
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)
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
,
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
,
1024 // Resize the description text last, in case the window was resized
1026 SendDlgItemMessage(hwndDlg
, IDC_DESCRIPTIONTEXT
,
1027 EM_SETEVENTMASK
, (WPARAM
)NULL
,
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
));
1075 UpdateEmail(hwndDlg
);
1077 SetFocus(GetDlgItem(hwndDlg
, IDC_SUBMITREPORTCHECK
));
1081 ReflowDialog(hwndDlg
, HIWORD(lParam
) - sHeight
);
1082 sHeight
= HIWORD(lParam
);
1083 InvalidateRect(hwndDlg
, NULL
, TRUE
);
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
;
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
,
1100 // Resize the dialog to fit (the WM_SIZE handler will move the controls)
1101 StretchDialog(hwndDlg
, newSize
.bottom
- oldSize
.bottom
);
1106 if (HIWORD(wParam
) == BN_CLICKED
) {
1107 switch(LOWORD(wParam
)) {
1108 case IDC_VIEWREPORTBUTTON
:
1109 DialogBoxParamMaybeRTL(IDD_VIEWREPORTDIALOG
, hwndDlg
,
1110 (DLGPROC
)ViewReportDialogProc
, 0);
1112 case IDC_SUBMITREPORTCHECK
:
1113 SubmitReportChecked(hwndDlg
);
1115 case IDC_INCLUDEURLCHECK
:
1118 case IDC_EMAILMECHECK
:
1119 UpdateEmail(hwndDlg
);
1121 case IDC_CLOSEBUTTON
:
1122 MaybeSendReport(hwndDlg
);
1124 case IDC_RESTARTBUTTON
:
1125 RestartApplication();
1126 MaybeSendReport(hwndDlg
);
1129 } else if (HIWORD(wParam
) == EN_CHANGE
) {
1130 switch(LOWORD(wParam
)) {
1132 UpdateEmail(hwndDlg
);
1134 case IDC_COMMENTTEXT
:
1135 UpdateComment(hwndDlg
);
1141 case WM_UPLOADCOMPLETE
: {
1142 WaitForSingleObject(gThreadHandle
, INFINITE
);
1143 success
= (wParam
== 1);
1144 SendCompleted(success
, WideToUTF8(gSendData
.serverResponse
));
1146 Animate_Stop(GetDlgItem(hwndDlg
, IDC_THROBBER
));
1147 SetDlgItemVisible(hwndDlg
, IDC_THROBBER
, false);
1149 SetDlgItemText(hwndDlg
, IDC_PROGRESSTEXT
,
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
);
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
);
1177 // The "1" gets used down in UIShowCrashUI to indicate that we at least
1178 // tried to send the report.
1179 EndCrashReporterDialog(hwndDlg
, 1);
1184 EndCrashReporterDialog(hwndDlg
, 0);
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(),
1196 if(buffer_size
== 0) {
1202 buffer
= new wchar_t[buffer_size
];
1203 if(buffer
== NULL
) {
1209 MultiByteToWideChar(CP_UTF8
, 0, utf8
.c_str(),
1210 -1, buffer
, buffer_size
);
1211 wstring str
= buffer
;
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) {
1231 buffer
= new char[buffer_size
];
1232 if(buffer
== NULL
) {
1238 WideCharToMultiByte(CP_UTF8
, 0, wide
.c_str(),
1239 -1, buffer
, buffer_size
, NULL
, NULL
);
1240 string utf8
= buffer
;
1249 /* === Crashreporter UI Functions === */
1253 for (int i
= 0; i
< sizeof(kDefaultAttachedBottom
) / sizeof(UINT
); i
++) {
1254 gAttachedBottom
.insert(kDefaultAttachedBottom
[i
]);
1257 DoInitCommonControls();
1266 void UIShowDefaultUI()
1268 MessageBox(NULL
, Str(ST_CRASHREPORTERDEFAULT
).c_str(),
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();
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")
1305 return 1 == DialogBoxParamMaybeRTL(IDD_SENDDIALOG
, NULL
,
1306 (DLGPROC
)CrashReporterDialogProc
, 0);
1309 void UIError_impl(const string
& message
)
1311 wstring title
= Str(ST_CRASHREPORTERTITLE
);
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
, '.');
1327 path
= WideToUTF8(fileName
);
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
,
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
);
1356 bool UIEnsurePathExists(const string
& path
)
1358 if (CreateDirectory(UTF8ToWide(path
).c_str(), NULL
) == 0) {
1359 if (GetLastError() != ERROR_ALREADY_EXISTS
)
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
)
1377 return MoveFile(UTF8ToWide(oldfile
).c_str(), UTF8ToWide(newfile
).c_str())
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
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
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