2 * The dialog that displays after a crash
4 * Copyright 2008 Mikolaj Zalewski
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
29 #include "wine/unicode.h"
33 #define MAX_PROGRAM_NAME_LENGTH 80
35 static char *crash_log
;
37 int msgbox_res_id(HWND hwnd
, UINT textId
, UINT captionId
, UINT uType
)
41 LoadStringW(GetModuleHandleW(NULL
), captionId
, caption
, sizeof(caption
)/sizeof(caption
[0]));
42 LoadStringW(GetModuleHandleW(NULL
), textId
, text
, sizeof(text
)/sizeof(text
[0]));
43 return MessageBoxW(hwnd
, text
, caption
, uType
);
46 static WCHAR
*get_program_name(HANDLE hProcess
)
48 WCHAR image_name
[MAX_PATH
];
52 /* GetProcessImageFileNameW gives no way to query the correct buffer size,
53 * but programs with a path longer than MAX_PATH can't be started by the
54 * shell, so we expect they don't happen often */
55 if (!GetProcessImageFileNameW(hProcess
, image_name
, MAX_PATH
))
57 static WCHAR unidentified
[MAX_PROGRAM_NAME_LENGTH
];
58 LoadStringW(GetModuleHandleW(NULL
), IDS_UNIDENTIFIED
,
59 unidentified
, MAX_PROGRAM_NAME_LENGTH
);
63 programname
= strrchrW(image_name
, '\\');
64 if (programname
!= NULL
)
67 programname
= image_name
;
69 /* TODO: if the image has a VERSIONINFO, we could try to find there a more
70 * user-friendly program name */
72 /* don't display a too long string to the user */
73 if (strlenW(programname
) >= MAX_PROGRAM_NAME_LENGTH
)
75 programname
[MAX_PROGRAM_NAME_LENGTH
- 4] = '.';
76 programname
[MAX_PROGRAM_NAME_LENGTH
- 3] = '.';
77 programname
[MAX_PROGRAM_NAME_LENGTH
- 2] = '.';
78 programname
[MAX_PROGRAM_NAME_LENGTH
- 1] = 0;
81 output
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*(lstrlenW(programname
) + 1));
82 lstrcpyW(output
, programname
);
87 static LPWSTR g_ProgramName
;
88 static HFONT g_hBoldFont
;
89 static HMENU g_hDebugMenu
= NULL
;
91 static void set_bold_font(HWND hDlg
)
93 HFONT hNormalFont
= (HFONT
)SendDlgItemMessageW(hDlg
, IDC_STATIC_TXT1
,
96 GetObjectW(hNormalFont
, sizeof(LOGFONTW
), &font
);
97 font
.lfWeight
= FW_BOLD
;
98 g_hBoldFont
= CreateFontIndirectW(&font
);
99 SendDlgItemMessageW(hDlg
, IDC_STATIC_TXT1
, WM_SETFONT
, (WPARAM
)g_hBoldFont
, TRUE
);
102 static void set_fixed_font( HWND dlg
, UINT id
)
104 HFONT hfont
= (HFONT
)SendDlgItemMessageW( dlg
, id
, WM_GETFONT
, 0, 0);
107 GetObjectW(hfont
, sizeof(LOGFONTW
), &font
);
108 font
.lfPitchAndFamily
= FIXED_PITCH
;
109 font
.lfFaceName
[0] = 0;
110 hfont
= CreateFontIndirectW(&font
);
111 SendDlgItemMessageW( dlg
, id
, WM_SETFONT
, (WPARAM
)hfont
, TRUE
);
114 static void set_message_with_filename(HWND hDlg
)
116 WCHAR originalText
[1000];
117 WCHAR newText
[1000 + MAX_PROGRAM_NAME_LENGTH
];
119 GetDlgItemTextW(hDlg
, IDC_STATIC_TXT1
, originalText
,
120 sizeof(originalText
)/sizeof(originalText
[0]));
121 wsprintfW(newText
, originalText
, g_ProgramName
);
122 SetDlgItemTextW(hDlg
, IDC_STATIC_TXT1
, newText
);
125 static void load_crash_log( HANDLE file
)
127 DWORD len
, pos
= 0, size
= 65536;
129 crash_log
= HeapAlloc( GetProcessHeap(), 0, size
);
130 SetFilePointer( file
, 0, NULL
, FILE_BEGIN
);
131 while (ReadFile( file
, crash_log
+ pos
, size
- pos
- 1, &len
, NULL
) && len
)
134 if (pos
== size
- 1) crash_log
= HeapReAlloc( GetProcessHeap(), 0, crash_log
, size
*= 2 );
139 static void save_crash_log( HWND hwnd
)
144 WCHAR
*p
, path
[MAX_PATH
], buffer
[1024];
145 static const WCHAR default_name
[] = { 'b','a','c','k','t','r','a','c','e','.','t','x','t',0 };
146 static const WCHAR default_ext
[] = { 't','x','t',0 };
147 static const WCHAR txt_files
[] = { '*','.','t','x','t',0 };
148 static const WCHAR all_files
[] = { '*','.','*',0 };
150 memset( &save
, 0, sizeof(save
) );
151 lstrcpyW( path
, default_name
);
153 LoadStringW( GetModuleHandleW(0), IDS_TEXT_FILES
, buffer
, sizeof(buffer
)/sizeof(buffer
[0]) );
154 p
= buffer
+ lstrlenW(buffer
) + 1;
155 lstrcpyW(p
, txt_files
);
156 p
+= lstrlenW(p
) + 1;
157 LoadStringW( GetModuleHandleW(0), IDS_ALL_FILES
, p
, sizeof(buffer
)/sizeof(buffer
[0]) - (p
- buffer
) );
158 p
+= lstrlenW(p
) + 1;
159 lstrcpyW(p
, all_files
);
160 p
+= lstrlenW(p
) + 1;
163 save
.lStructSize
= sizeof(OPENFILENAMEW
);
164 save
.hwndOwner
= hwnd
;
165 save
.hInstance
= GetModuleHandleW(0);
166 save
.lpstrFilter
= buffer
;
167 save
.lpstrFile
= path
;
168 save
.nMaxFile
= MAX_PATH
;
169 save
.Flags
= OFN_EXPLORER
| OFN_PATHMUSTEXIST
| OFN_OVERWRITEPROMPT
|
170 OFN_HIDEREADONLY
| OFN_ENABLESIZING
;
171 save
.lpstrDefExt
= default_ext
;
173 if (!GetSaveFileNameW( &save
)) return;
174 handle
= CreateFileW( save
.lpstrFile
, GENERIC_WRITE
, FILE_SHARE_READ
,
175 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0 );
176 if (handle
!= INVALID_HANDLE_VALUE
)
178 if (!WriteFile( handle
, crash_log
, strlen(crash_log
), &written
, NULL
))
179 err
= GetLastError();
180 else if (written
!= strlen(crash_log
))
181 err
= GetLastError();
184 CloseHandle( handle
);
187 CloseHandle( handle
);
188 DeleteFileW( save
.lpstrFile
);
190 else err
= GetLastError();
192 LoadStringW( GetModuleHandleW(0), IDS_SAVE_ERROR
, buffer
, sizeof(buffer
)/sizeof(WCHAR
) );
193 FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
194 NULL
, err
, 0, (LPWSTR
)&p
, 0, NULL
);
195 MessageBoxW( 0, p
, buffer
, MB_OK
| MB_ICONERROR
);
199 static INT_PTR WINAPI
crash_dlg_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
201 static const WCHAR openW
[] = {'o','p','e','n',0};
207 set_message_with_filename(hwnd
);
210 case WM_CTLCOLORSTATIC
:
212 /* WM_CTLCOLOR* don't use DWLP_MSGRESULT */
213 INT_PTR id
= GetDlgCtrlID((HWND
)lParam
);
214 if (id
== IDC_STATIC_BG
|| id
== IDC_STATIC_TXT1
)
215 return (LONG_PTR
)GetSysColorBrush(COLOR_WINDOW
);
222 if (!(wParam
& MK_SHIFT
))
224 if (g_hDebugMenu
== NULL
)
225 g_hDebugMenu
= LoadMenuW(GetModuleHandleW(NULL
), MAKEINTRESOURCEW(IDM_DEBUG_POPUP
));
226 GetCursorPos(&mousePos
);
227 TrackPopupMenu(GetSubMenu(g_hDebugMenu
, 0), TPM_RIGHTBUTTON
, mousePos
.x
, mousePos
.y
,
232 switch (((NMHDR
*)lParam
)->code
)
236 if (wParam
== IDC_STATIC_TXT2
)
237 ShellExecuteW( NULL
, openW
, ((NMLINK
*)lParam
)->item
.szUrl
, NULL
, NULL
, SW_SHOW
);
243 switch (LOWORD(wParam
))
249 EndDialog(hwnd
, LOWORD(wParam
));
257 static INT_PTR WINAPI
details_dlg_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
259 static const WCHAR openW
[] = {'o','p','e','n',0};
260 static POINT orig_size
, min_size
, edit_size
, text_pos
, save_pos
, close_pos
;
269 set_fixed_font( hwnd
, IDC_CRASH_TXT
);
270 LoadStringW( GetModuleHandleW(0), IDS_LOADING
, buffer
, 256 );
271 SetDlgItemTextW( hwnd
, IDC_CRASH_TXT
, buffer
);
272 EnableWindow( GetDlgItem( hwnd
, IDC_CRASH_TXT
), FALSE
);
273 EnableWindow( GetDlgItem( hwnd
, ID_SAVEAS
), FALSE
);
275 GetClientRect( hwnd
, &rect
);
276 orig_size
.x
= rect
.right
;
277 orig_size
.y
= rect
.bottom
;
279 GetWindowRect( hwnd
, &rect
);
280 min_size
.x
= rect
.right
- rect
.left
;
281 min_size
.y
= rect
.bottom
- rect
.top
;
283 GetWindowRect( GetDlgItem( hwnd
, IDOK
), &rect
);
284 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
285 close_pos
.x
= rect
.left
;
286 close_pos
.y
= rect
.top
;
288 GetWindowRect( GetDlgItem( hwnd
, ID_SAVEAS
), &rect
);
289 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
290 save_pos
.x
= rect
.left
;
291 save_pos
.y
= rect
.top
;
293 GetWindowRect( GetDlgItem( hwnd
, IDC_STATIC_TXT2
), &rect
);
294 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
295 text_pos
.x
= rect
.left
;
296 text_pos
.y
= rect
.top
;
298 GetWindowRect( GetDlgItem( hwnd
, IDC_CRASH_TXT
), &rect
);
299 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
300 edit_size
.x
= rect
.right
- rect
.left
;
301 edit_size
.y
= rect
.bottom
- rect
.top
;
305 case WM_GETMINMAXINFO
:
306 ((MINMAXINFO
*)lparam
)->ptMinTrackSize
= min_size
;
310 if (wparam
== SIZE_RESTORED
|| wparam
== SIZE_MAXIMIZED
)
312 int off_x
= (short)LOWORD( lparam
) - orig_size
.x
;
313 int off_y
= (short)HIWORD( lparam
) - orig_size
.y
;
315 SetWindowPos( GetDlgItem( hwnd
, IDOK
), 0, close_pos
.x
+ off_x
,
316 close_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
317 SetWindowPos( GetDlgItem( hwnd
, ID_SAVEAS
), 0, save_pos
.x
+ off_x
,
318 save_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
319 SetWindowPos( GetDlgItem( hwnd
, IDC_STATIC_TXT2
), 0, text_pos
.x
,
320 text_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
321 SetWindowPos( GetDlgItem( hwnd
, IDC_CRASH_TXT
), 0, 0, 0, edit_size
.x
+ off_x
,
322 edit_size
.y
+ off_y
, SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
327 switch (((NMHDR
*)lparam
)->code
)
331 if (wparam
== IDC_STATIC_TXT2
)
332 ShellExecuteW( NULL
, openW
, ((NMLINK
*)lparam
)->item
.szUrl
, NULL
, NULL
, SW_SHOW
);
338 switch (LOWORD(wparam
))
341 save_crash_log( hwnd
);
345 PostQuitMessage( 0 );
353 int display_crash_dialog(void)
355 static const WCHAR winedeviceW
[] = {'w','i','n','e','d','e','v','i','c','e','.','e','x','e',0};
356 static const INITCOMMONCONTROLSEX init
= { sizeof(init
), ICC_LINK_CLASS
};
358 /* dbg_curr_process->handle is not set */
361 if (!DBG_IVAR(ShowCrashDialog
))
364 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, dbg_curr_pid
);
365 g_ProgramName
= get_program_name(hProcess
);
366 CloseHandle(hProcess
);
367 if (!strcmpW( g_ProgramName
, winedeviceW
)) return TRUE
;
368 InitCommonControlsEx( &init
);
369 return DialogBoxW(GetModuleHandleW(NULL
), MAKEINTRESOURCEW(IDD_CRASH_DLG
), NULL
, crash_dlg_proc
);
372 static DWORD WINAPI
crash_details_thread( void *event
)
377 dialog
= CreateDialogW( GetModuleHandleW(0), MAKEINTRESOURCEW(IDD_DETAILS_DLG
), 0, details_dlg_proc
);
378 if (!dialog
) return 1;
382 if (MsgWaitForMultipleObjects( 1, &event
, FALSE
, INFINITE
, QS_ALLINPUT
) == 0)
384 load_crash_log( dbg_houtput
);
385 SetDlgItemTextA( dialog
, IDC_CRASH_TXT
, crash_log
);
386 EnableWindow( GetDlgItem( dialog
, IDC_CRASH_TXT
), TRUE
);
387 EnableWindow( GetDlgItem( dialog
, ID_SAVEAS
), TRUE
);
390 while (PeekMessageW( &msg
, 0, 0, 0, PM_REMOVE
))
392 if (msg
.message
== WM_QUIT
) return 0;
393 TranslateMessage( &msg
);
394 DispatchMessageW( &msg
);
398 while (GetMessageW( &msg
, 0, 0, 0 ))
400 TranslateMessage( &msg
);
401 DispatchMessageW( &msg
);
406 HANDLE
display_crash_details( HANDLE event
)
408 return CreateThread( NULL
, 0, crash_details_thread
, event
, 0, NULL
);