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 type
)
39 if (DBG_IVAR(ShowCrashDialog
))
43 LoadStringW(GetModuleHandleW(NULL
), captionid
, caption
, ARRAY_SIZE(caption
));
44 LoadStringW(GetModuleHandleW(NULL
), textid
, text
, ARRAY_SIZE(text
));
45 return MessageBoxW(hwnd
, text
, caption
, type
);
51 static BOOL
is_visible(void)
53 USEROBJECTFLAGS flags
;
56 if (!(winstation
= GetProcessWindowStation()))
59 if (!(GetUserObjectInformationA(winstation
, UOI_FLAGS
, &flags
, sizeof(flags
), NULL
)))
62 return flags
.dwFlags
& WSF_VISIBLE
;
65 static WCHAR
*get_program_name(HANDLE hProcess
)
67 WCHAR image_name
[MAX_PATH
];
71 /* GetProcessImageFileNameW gives no way to query the correct buffer size,
72 * but programs with a path longer than MAX_PATH can't be started by the
73 * shell, so we expect they don't happen often */
74 if (!GetProcessImageFileNameW(hProcess
, image_name
, MAX_PATH
))
76 static WCHAR unidentified
[MAX_PROGRAM_NAME_LENGTH
];
77 LoadStringW(GetModuleHandleW(NULL
), IDS_UNIDENTIFIED
,
78 unidentified
, MAX_PROGRAM_NAME_LENGTH
);
82 programname
= strrchrW(image_name
, '\\');
83 if (programname
!= NULL
)
86 programname
= image_name
;
88 /* TODO: if the image has a VERSIONINFO, we could try to find there a more
89 * user-friendly program name */
91 /* don't display a too long string to the user */
92 if (strlenW(programname
) >= MAX_PROGRAM_NAME_LENGTH
)
94 programname
[MAX_PROGRAM_NAME_LENGTH
- 4] = '.';
95 programname
[MAX_PROGRAM_NAME_LENGTH
- 3] = '.';
96 programname
[MAX_PROGRAM_NAME_LENGTH
- 2] = '.';
97 programname
[MAX_PROGRAM_NAME_LENGTH
- 1] = 0;
100 output
= HeapAlloc(GetProcessHeap(), 0, sizeof(WCHAR
)*(lstrlenW(programname
) + 1));
101 lstrcpyW(output
, programname
);
106 static LPWSTR g_ProgramName
;
107 static HFONT g_hBoldFont
;
108 static HMENU g_hDebugMenu
= NULL
;
110 static void set_bold_font(HWND hDlg
)
112 HFONT hNormalFont
= (HFONT
)SendDlgItemMessageW(hDlg
, IDC_STATIC_TXT1
,
115 GetObjectW(hNormalFont
, sizeof(LOGFONTW
), &font
);
116 font
.lfWeight
= FW_BOLD
;
117 g_hBoldFont
= CreateFontIndirectW(&font
);
118 SendDlgItemMessageW(hDlg
, IDC_STATIC_TXT1
, WM_SETFONT
, (WPARAM
)g_hBoldFont
, TRUE
);
121 static void set_fixed_font( HWND dlg
, UINT id
)
123 HFONT hfont
= (HFONT
)SendDlgItemMessageW( dlg
, id
, WM_GETFONT
, 0, 0);
126 GetObjectW(hfont
, sizeof(LOGFONTW
), &font
);
127 font
.lfPitchAndFamily
= FIXED_PITCH
;
128 font
.lfFaceName
[0] = 0;
129 hfont
= CreateFontIndirectW(&font
);
130 SendDlgItemMessageW( dlg
, id
, WM_SETFONT
, (WPARAM
)hfont
, TRUE
);
133 static void set_message_with_filename(HWND hDlg
)
135 WCHAR originalText
[1000];
136 WCHAR newText
[1000 + MAX_PROGRAM_NAME_LENGTH
];
138 GetDlgItemTextW(hDlg
, IDC_STATIC_TXT1
, originalText
, ARRAY_SIZE(originalText
));
139 wsprintfW(newText
, originalText
, g_ProgramName
);
140 SetDlgItemTextW(hDlg
, IDC_STATIC_TXT1
, newText
);
143 static void load_crash_log( HANDLE file
)
145 DWORD len
, pos
= 0, size
= 65536;
147 crash_log
= HeapAlloc( GetProcessHeap(), 0, size
);
148 SetFilePointer( file
, 0, NULL
, FILE_BEGIN
);
149 while (ReadFile( file
, crash_log
+ pos
, size
- pos
- 1, &len
, NULL
) && len
)
152 if (pos
== size
- 1) crash_log
= HeapReAlloc( GetProcessHeap(), 0, crash_log
, size
*= 2 );
157 static void save_crash_log( HWND hwnd
)
162 WCHAR
*p
, path
[MAX_PATH
], buffer
[1024];
163 static const WCHAR default_name
[] = { 'b','a','c','k','t','r','a','c','e','.','t','x','t',0 };
164 static const WCHAR default_ext
[] = { 't','x','t',0 };
165 static const WCHAR txt_files
[] = { '*','.','t','x','t',0 };
166 static const WCHAR all_files
[] = { '*','.','*',0 };
168 memset( &save
, 0, sizeof(save
) );
169 lstrcpyW( path
, default_name
);
171 LoadStringW( GetModuleHandleW(0), IDS_TEXT_FILES
, buffer
, ARRAY_SIZE(buffer
));
172 p
= buffer
+ lstrlenW(buffer
) + 1;
173 lstrcpyW(p
, txt_files
);
174 p
+= lstrlenW(p
) + 1;
175 LoadStringW( GetModuleHandleW(0), IDS_ALL_FILES
, p
, ARRAY_SIZE(buffer
) - (p
- buffer
) );
176 p
+= lstrlenW(p
) + 1;
177 lstrcpyW(p
, all_files
);
178 p
+= lstrlenW(p
) + 1;
181 save
.lStructSize
= sizeof(OPENFILENAMEW
);
182 save
.hwndOwner
= hwnd
;
183 save
.hInstance
= GetModuleHandleW(0);
184 save
.lpstrFilter
= buffer
;
185 save
.lpstrFile
= path
;
186 save
.nMaxFile
= MAX_PATH
;
187 save
.Flags
= OFN_EXPLORER
| OFN_PATHMUSTEXIST
| OFN_OVERWRITEPROMPT
|
188 OFN_HIDEREADONLY
| OFN_ENABLESIZING
;
189 save
.lpstrDefExt
= default_ext
;
191 if (!GetSaveFileNameW( &save
)) return;
192 handle
= CreateFileW( save
.lpstrFile
, GENERIC_WRITE
, FILE_SHARE_READ
,
193 NULL
, CREATE_ALWAYS
, FILE_ATTRIBUTE_NORMAL
, 0 );
194 if (handle
!= INVALID_HANDLE_VALUE
)
196 if (!WriteFile( handle
, crash_log
, strlen(crash_log
), &written
, NULL
))
197 err
= GetLastError();
198 else if (written
!= strlen(crash_log
))
199 err
= GetLastError();
202 CloseHandle( handle
);
205 CloseHandle( handle
);
206 DeleteFileW( save
.lpstrFile
);
208 else err
= GetLastError();
210 LoadStringW( GetModuleHandleW(0), IDS_SAVE_ERROR
, buffer
, ARRAY_SIZE(buffer
));
211 FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER
| FORMAT_MESSAGE_FROM_SYSTEM
,
212 NULL
, err
, 0, (LPWSTR
)&p
, 0, NULL
);
213 MessageBoxW( 0, p
, buffer
, MB_OK
| MB_ICONERROR
);
217 static INT_PTR WINAPI
crash_dlg_proc(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
)
219 static const WCHAR openW
[] = {'o','p','e','n',0};
225 set_message_with_filename(hwnd
);
228 case WM_CTLCOLORSTATIC
:
230 /* WM_CTLCOLOR* don't use DWLP_MSGRESULT */
231 INT_PTR id
= GetDlgCtrlID((HWND
)lParam
);
232 if (id
== IDC_STATIC_BG
|| id
== IDC_STATIC_TXT1
)
233 return (LONG_PTR
)GetSysColorBrush(COLOR_WINDOW
);
240 if (!(wParam
& MK_SHIFT
))
242 if (g_hDebugMenu
== NULL
)
243 g_hDebugMenu
= LoadMenuW(GetModuleHandleW(NULL
), MAKEINTRESOURCEW(IDM_DEBUG_POPUP
));
244 GetCursorPos(&mousePos
);
245 TrackPopupMenu(GetSubMenu(g_hDebugMenu
, 0), TPM_RIGHTBUTTON
, mousePos
.x
, mousePos
.y
,
250 switch (((NMHDR
*)lParam
)->code
)
254 if (wParam
== IDC_STATIC_TXT2
)
255 ShellExecuteW( NULL
, openW
, ((NMLINK
*)lParam
)->item
.szUrl
, NULL
, NULL
, SW_SHOW
);
261 switch (LOWORD(wParam
))
267 EndDialog(hwnd
, LOWORD(wParam
));
275 static INT_PTR WINAPI
details_dlg_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
277 static const WCHAR openW
[] = {'o','p','e','n',0};
278 static POINT orig_size
, min_size
, edit_size
, text_pos
, save_pos
, close_pos
;
287 set_fixed_font( hwnd
, IDC_CRASH_TXT
);
288 LoadStringW( GetModuleHandleW(0), IDS_LOADING
, buffer
, 256 );
289 SetDlgItemTextW( hwnd
, IDC_CRASH_TXT
, buffer
);
290 EnableWindow( GetDlgItem( hwnd
, IDC_CRASH_TXT
), FALSE
);
291 EnableWindow( GetDlgItem( hwnd
, ID_SAVEAS
), FALSE
);
293 GetClientRect( hwnd
, &rect
);
294 orig_size
.x
= rect
.right
;
295 orig_size
.y
= rect
.bottom
;
297 GetWindowRect( hwnd
, &rect
);
298 min_size
.x
= rect
.right
- rect
.left
;
299 min_size
.y
= rect
.bottom
- rect
.top
;
301 GetWindowRect( GetDlgItem( hwnd
, IDOK
), &rect
);
302 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
303 close_pos
.x
= rect
.left
;
304 close_pos
.y
= rect
.top
;
306 GetWindowRect( GetDlgItem( hwnd
, ID_SAVEAS
), &rect
);
307 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
308 save_pos
.x
= rect
.left
;
309 save_pos
.y
= rect
.top
;
311 GetWindowRect( GetDlgItem( hwnd
, IDC_STATIC_TXT2
), &rect
);
312 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
313 text_pos
.x
= rect
.left
;
314 text_pos
.y
= rect
.top
;
316 GetWindowRect( GetDlgItem( hwnd
, IDC_CRASH_TXT
), &rect
);
317 MapWindowPoints( 0, hwnd
, (POINT
*)&rect
, 2 );
318 edit_size
.x
= rect
.right
- rect
.left
;
319 edit_size
.y
= rect
.bottom
- rect
.top
;
323 case WM_GETMINMAXINFO
:
324 ((MINMAXINFO
*)lparam
)->ptMinTrackSize
= min_size
;
328 if (wparam
== SIZE_RESTORED
|| wparam
== SIZE_MAXIMIZED
)
330 int off_x
= (short)LOWORD( lparam
) - orig_size
.x
;
331 int off_y
= (short)HIWORD( lparam
) - orig_size
.y
;
333 SetWindowPos( GetDlgItem( hwnd
, IDOK
), 0, close_pos
.x
+ off_x
,
334 close_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
335 SetWindowPos( GetDlgItem( hwnd
, ID_SAVEAS
), 0, save_pos
.x
+ off_x
,
336 save_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
337 SetWindowPos( GetDlgItem( hwnd
, IDC_STATIC_TXT2
), 0, text_pos
.x
,
338 text_pos
.y
+ off_y
, 0, 0, SWP_NOSIZE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
339 SetWindowPos( GetDlgItem( hwnd
, IDC_CRASH_TXT
), 0, 0, 0, edit_size
.x
+ off_x
,
340 edit_size
.y
+ off_y
, SWP_NOMOVE
| SWP_NOZORDER
| SWP_NOACTIVATE
);
345 switch (((NMHDR
*)lparam
)->code
)
349 if (wparam
== IDC_STATIC_TXT2
)
350 ShellExecuteW( NULL
, openW
, ((NMLINK
*)lparam
)->item
.szUrl
, NULL
, NULL
, SW_SHOW
);
356 switch (LOWORD(wparam
))
359 save_crash_log( hwnd
);
363 PostQuitMessage( 0 );
371 int display_crash_dialog(void)
373 static const WCHAR winedeviceW
[] = {'w','i','n','e','d','e','v','i','c','e','.','e','x','e',0};
374 static const INITCOMMONCONTROLSEX init
= { sizeof(init
), ICC_LINK_CLASS
};
376 /* dbg_curr_process->handle is not set */
379 if (!DBG_IVAR(ShowCrashDialog
) || !is_visible())
382 hProcess
= OpenProcess(PROCESS_QUERY_INFORMATION
, FALSE
, dbg_curr_pid
);
383 g_ProgramName
= get_program_name(hProcess
);
384 CloseHandle(hProcess
);
385 if (!strcmpW( g_ProgramName
, winedeviceW
)) return TRUE
;
386 InitCommonControlsEx( &init
);
387 return DialogBoxW(GetModuleHandleW(NULL
), MAKEINTRESOURCEW(IDD_CRASH_DLG
), NULL
, crash_dlg_proc
);
390 static DWORD WINAPI
crash_details_thread( void *event
)
395 dialog
= CreateDialogW( GetModuleHandleW(0), MAKEINTRESOURCEW(IDD_DETAILS_DLG
), 0, details_dlg_proc
);
396 if (!dialog
) return 1;
400 if (MsgWaitForMultipleObjects( 1, &event
, FALSE
, INFINITE
, QS_ALLINPUT
) == 0)
402 load_crash_log( dbg_houtput
);
403 SetDlgItemTextA( dialog
, IDC_CRASH_TXT
, crash_log
);
404 EnableWindow( GetDlgItem( dialog
, IDC_CRASH_TXT
), TRUE
);
405 EnableWindow( GetDlgItem( dialog
, ID_SAVEAS
), TRUE
);
408 while (PeekMessageW( &msg
, 0, 0, 0, PM_REMOVE
))
410 if (msg
.message
== WM_QUIT
) return 0;
411 TranslateMessage( &msg
);
412 DispatchMessageW( &msg
);
416 while (GetMessageW( &msg
, 0, 0, 0 ))
418 TranslateMessage( &msg
);
419 DispatchMessageW( &msg
);
424 HANDLE
display_crash_details( HANDLE event
)
426 return CreateThread( NULL
, 0, crash_details_thread
, event
, 0, NULL
);