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
31 #define MAX_PROGRAM_NAME_LENGTH 80
33 static char *crash_log
;
35 int msgbox_res_id(HWND hwnd
, UINT textid
, UINT captionid
, UINT type
)
37 if (DBG_IVAR(ShowCrashDialog
))
41 LoadStringW(GetModuleHandleW(NULL
), captionid
, caption
, ARRAY_SIZE(caption
));
42 LoadStringW(GetModuleHandleW(NULL
), textid
, text
, ARRAY_SIZE(text
));
43 return MessageBoxW(hwnd
, text
, caption
, type
);
49 static BOOL
is_visible(void)
51 USEROBJECTFLAGS flags
;
54 if (!(winstation
= GetProcessWindowStation()))
57 if (!(GetUserObjectInformationA(winstation
, UOI_FLAGS
, &flags
, sizeof(flags
), NULL
)))
60 return flags
.dwFlags
& WSF_VISIBLE
;
63 static WCHAR
*get_program_name(HANDLE hProcess
)
65 WCHAR image_name
[MAX_PATH
];
69 /* GetProcessImageFileNameW gives no way to query the correct buffer size,
70 * but programs with a path longer than MAX_PATH can't be started by the
71 * shell, so we expect they don't happen often */
72 if (!GetProcessImageFileNameW(hProcess
, image_name
, MAX_PATH
))
74 static WCHAR unidentified
[MAX_PROGRAM_NAME_LENGTH
];
75 LoadStringW(GetModuleHandleW(NULL
), IDS_UNIDENTIFIED
,
76 unidentified
, MAX_PROGRAM_NAME_LENGTH
);
80 programname
= wcsrchr(image_name
, '\\');
81 if (programname
!= NULL
)
84 programname
= image_name
;
86 /* TODO: if the image has a VERSIONINFO, we could try to find there a more
87 * user-friendly program name */
89 /* don't display a too long string to the user */
90 if (lstrlenW(programname
) >= MAX_PROGRAM_NAME_LENGTH
)
92 programname
[MAX_PROGRAM_NAME_LENGTH
- 4] = '.';
93 programname
[MAX_PROGRAM_NAME_LENGTH
- 3] = '.';
94 programname
[MAX_PROGRAM_NAME_LENGTH
- 2] = '.';
95 programname
[MAX_PROGRAM_NAME_LENGTH
- 1] = 0;
98 output
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*(lstrlenW(programname
) + 1));
99 lstrcpyW(output
, programname
);
104 static LPWSTR g_ProgramName
;
105 static HFONT g_hBoldFont
;
106 static HMENU g_hDebugMenu
= NULL
;
108 static void set_bold_font(HWND hDlg
)
110 HFONT hNormalFont
= (HFONT
)SendDlgItemMessageW(hDlg
, IDC_STATIC_TXT1
,
113 GetObjectW(hNormalFont
, sizeof(LOGFONTW
), &font
);
114 font
.lfWeight
= FW_BOLD
;
115 g_hBoldFont
= CreateFontIndirectW(&font
);
116 SendDlgItemMessageW(hDlg
, IDC_STATIC_TXT1
, WM_SETFONT
, (WPARAM
)g_hBoldFont
, TRUE
);
119 static void set_fixed_font( HWND dlg
, UINT id
)
121 HFONT hfont
= (HFONT
)SendDlgItemMessageW( dlg
, id
, WM_GETFONT
, 0, 0);
124 GetObjectW(hfont
, sizeof(LOGFONTW
), &font
);
125 font
.lfPitchAndFamily
= FIXED_PITCH
;
126 font
.lfFaceName
[0] = 0;
127 hfont
= CreateFontIndirectW(&font
);
128 SendDlgItemMessageW( dlg
, id
, WM_SETFONT
, (WPARAM
)hfont
, TRUE
);
131 static void set_message_with_filename(HWND hDlg
)
133 WCHAR originalText
[1000];
134 WCHAR newText
[1000 + MAX_PROGRAM_NAME_LENGTH
];
136 GetDlgItemTextW(hDlg
, IDC_STATIC_TXT1
, originalText
, ARRAY_SIZE(originalText
));
137 wsprintfW(newText
, originalText
, g_ProgramName
);
138 SetDlgItemTextW(hDlg
, IDC_STATIC_TXT1
, newText
);
141 static void load_crash_log( HANDLE file
)
143 DWORD len
, pos
= 0, size
= 65536;
145 crash_log
= HeapAlloc( GetProcessHeap(), 0, size
);
146 SetFilePointer( file
, 0, NULL
, FILE_BEGIN
);
147 while (ReadFile( file
, crash_log
+ pos
, size
- pos
- 1, &len
, NULL
) && len
)
150 if (pos
== size
- 1) crash_log
= HeapReAlloc( GetProcessHeap(), 0, crash_log
, size
*= 2 );
155 static void save_crash_log( HWND hwnd
)
160 WCHAR
*p
, path
[MAX_PATH
], buffer
[1024];
162 memset( &save
, 0, sizeof(save
) );
163 lstrcpyW( path
, L
"backtrace.txt" );
165 LoadStringW( GetModuleHandleW(0), IDS_TEXT_FILES
, buffer
, ARRAY_SIZE(buffer
));
166 p
= buffer
+ lstrlenW(buffer
) + 1;
167 lstrcpyW(p
, L
"*.txt");
168 p
+= lstrlenW(p
) + 1;
169 LoadStringW( GetModuleHandleW(0), IDS_ALL_FILES
, p
, ARRAY_SIZE(buffer
) - (p
- buffer
) );
170 p
+= lstrlenW(p
) + 1;
172 p
+= lstrlenW(p
) + 1;
175 save
.lStructSize
= sizeof(OPENFILENAMEW
);
176 save
.hwndOwner
= hwnd
;
177 save
.hInstance
= GetModuleHandleW(0);
178 save
.lpstrFilter
= buffer
;
179 save
.lpstrFile
= path
;
180 save
.nMaxFile
= MAX_PATH
;
181 save
.Flags
= OFN_EXPLORER
| OFN_PATHMUSTEXIST
| OFN_OVERWRITEPROMPT
|
182 OFN_HIDEREADONLY
| OFN_ENABLESIZING
;
183 save
.lpstrDefExt
= L
"txt";
185 if (!GetSaveFileNameW( &save
)) return;
186 handle
= CreateFileW( save
.lpstrFile
, GENERIC_WRITE
, FILE_SHARE_READ
,
187 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0 );
188 if (handle
!= INVALID_HANDLE_VALUE
)
190 if (!WriteFile( handle
, crash_log
, strlen(crash_log
), &written
, NULL
))
191 err
= GetLastError();
192 else if (written
!= strlen(crash_log
))
193 err
= GetLastError();
196 CloseHandle( handle
);
199 CloseHandle( handle
);
200 DeleteFileW( save
.lpstrFile
);
202 else err
= GetLastError();
204 LoadStringW( GetModuleHandleW(0), IDS_SAVE_ERROR
, buffer
, ARRAY_SIZE(buffer
));
205 FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
206 NULL
, err
, 0, (LPWSTR
)&p
, 0, NULL
);
207 MessageBoxW( 0, p
, buffer
, MB_OK
| MB_ICONERROR
);
211 static INT_PTR WINAPI
crash_dlg_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
218 set_message_with_filename(hwnd
);
221 case WM_CTLCOLORSTATIC
:
223 /* WM_CTLCOLOR* don't use DWLP_MSGRESULT */
224 INT_PTR id
= GetDlgCtrlID((HWND
)lParam
);
225 if (id
== IDC_STATIC_BG
|| id
== IDC_STATIC_TXT1
)
226 return (LONG_PTR
)GetSysColorBrush(COLOR_WINDOW
);
233 if (!(wParam
& MK_SHIFT
))
235 if (g_hDebugMenu
== NULL
)
236 g_hDebugMenu
= LoadMenuW(GetModuleHandleW(NULL
), MAKEINTRESOURCEW(IDM_DEBUG_POPUP
));
237 GetCursorPos(&mousePos
);
238 TrackPopupMenu(GetSubMenu(g_hDebugMenu
, 0), TPM_RIGHTBUTTON
, mousePos
.x
, mousePos
.y
,
243 switch (((NMHDR
*)lParam
)->code
)
247 if (wParam
== IDC_STATIC_TXT2
)
248 ShellExecuteW( NULL
, L
"open", ((NMLINK
*)lParam
)->item
.szUrl
, NULL
, NULL
, SW_SHOW
);
254 switch (LOWORD(wParam
))
260 EndDialog(hwnd
, LOWORD(wParam
));
268 static INT_PTR WINAPI
details_dlg_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
270 static POINT orig_size
, min_size
, edit_size
, text_pos
, save_pos
, close_pos
;
279 set_fixed_font( hwnd
, IDC_CRASH_TXT
);
280 LoadStringW( GetModuleHandleW(0), IDS_LOADING
, buffer
, 256 );
281 SetDlgItemTextW( hwnd
, IDC_CRASH_TXT
, buffer
);
282 EnableWindow( GetDlgItem( hwnd
, IDC_CRASH_TXT
), FALSE
);
283 EnableWindow( GetDlgItem( hwnd
, ID_SAVEAS
), FALSE
);
285 GetClientRect( hwnd
, &rect
);
286 orig_size
.x
= rect
.right
;
287 orig_size
.y
= rect
.bottom
;
289 GetWindowRect( hwnd
, &rect
);
290 min_size
.x
= rect
.right
- rect
.left
;
291 min_size
.y
= rect
.bottom
- rect
.top
;
293 GetWindowRect( GetDlgItem( hwnd
, IDOK
), &rect
);
294 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
295 close_pos
.x
= rect
.left
;
296 close_pos
.y
= rect
.top
;
298 GetWindowRect( GetDlgItem( hwnd
, ID_SAVEAS
), &rect
);
299 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
300 save_pos
.x
= rect
.left
;
301 save_pos
.y
= rect
.top
;
303 GetWindowRect( GetDlgItem( hwnd
, IDC_STATIC_TXT2
), &rect
);
304 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
305 text_pos
.x
= rect
.left
;
306 text_pos
.y
= rect
.top
;
308 GetWindowRect( GetDlgItem( hwnd
, IDC_CRASH_TXT
), &rect
);
309 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
310 edit_size
.x
= rect
.right
- rect
.left
;
311 edit_size
.y
= rect
.bottom
- rect
.top
;
315 case WM_GETMINMAXINFO
:
316 ((MINMAXINFO
*)lparam
)->ptMinTrackSize
= min_size
;
320 if (wparam
== SIZE_RESTORED
|| wparam
== SIZE_MAXIMIZED
)
322 int off_x
= (short)LOWORD( lparam
) - orig_size
.x
;
323 int off_y
= (short)HIWORD( lparam
) - orig_size
.y
;
325 SetWindowPos( GetDlgItem( hwnd
, IDOK
), 0, close_pos
.x
+ off_x
,
326 close_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
327 SetWindowPos( GetDlgItem( hwnd
, ID_SAVEAS
), 0, save_pos
.x
+ off_x
,
328 save_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
329 SetWindowPos( GetDlgItem( hwnd
, IDC_STATIC_TXT2
), 0, text_pos
.x
,
330 text_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
331 SetWindowPos( GetDlgItem( hwnd
, IDC_CRASH_TXT
), 0, 0, 0, edit_size
.x
+ off_x
,
332 edit_size
.y
+ off_y
, SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
337 switch (((NMHDR
*)lparam
)->code
)
341 if (wparam
== IDC_STATIC_TXT2
)
342 ShellExecuteW( NULL
, L
"open", ((NMLINK
*)lparam
)->item
.szUrl
, NULL
, NULL
, SW_SHOW
);
348 switch (LOWORD(wparam
))
351 save_crash_log( hwnd
);
355 PostQuitMessage( 0 );
363 int display_crash_dialog(void)
365 static const INITCOMMONCONTROLSEX init
= { sizeof(init
), ICC_LINK_CLASS
};
367 /* dbg_curr_process->handle is not set */
370 if (!DBG_IVAR(ShowCrashDialog
) || !is_visible())
373 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, dbg_curr_pid
);
374 g_ProgramName
= get_program_name(hProcess
);
375 CloseHandle(hProcess
);
376 if (!wcscmp( g_ProgramName
, L
"winedevice.exe" )) return TRUE
;
377 InitCommonControlsEx( &init
);
378 return DialogBoxW(GetModuleHandleW(NULL
), MAKEINTRESOURCEW(IDD_CRASH_DLG
), NULL
, crash_dlg_proc
);
381 static DWORD WINAPI
crash_details_thread( void *event
)
386 dialog
= CreateDialogW( GetModuleHandleW(0), MAKEINTRESOURCEW(IDD_DETAILS_DLG
), 0, details_dlg_proc
);
387 if (!dialog
) return 1;
391 if (MsgWaitForMultipleObjects( 1, &event
, FALSE
, INFINITE
, QS_ALLINPUT
) == 0)
393 load_crash_log( dbg_houtput
);
394 SetDlgItemTextA( dialog
, IDC_CRASH_TXT
, crash_log
);
395 EnableWindow( GetDlgItem( dialog
, IDC_CRASH_TXT
), TRUE
);
396 EnableWindow( GetDlgItem( dialog
, ID_SAVEAS
), TRUE
);
399 while (PeekMessageW( &msg
, 0, 0, 0, PM_REMOVE
))
401 if (msg
.message
== WM_QUIT
) return 0;
402 TranslateMessage( &msg
);
403 DispatchMessageW( &msg
);
407 while (GetMessageW( &msg
, 0, 0, 0 ))
409 TranslateMessage( &msg
);
410 DispatchMessageW( &msg
);
415 HANDLE
display_crash_details( HANDLE event
)
417 return CreateThread( NULL
, 0, crash_details_thread
, event
, 0, NULL
);