2 * Copyright (C) 2006 Alexandre Julliard
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
27 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(wineboot
);
33 #define MESSAGE_TIMEOUT 5000
42 static UINT win_count
;
44 static struct window_info
*windows
;
45 static DWORD desktop_pid
;
47 /* store a new window; callback for EnumWindows */
48 static BOOL CALLBACK
enum_proc( HWND hwnd
, LPARAM lp
)
50 if (win_count
>= win_max
)
52 UINT new_count
= win_max
* 2;
53 struct window_info
*new_win
= HeapReAlloc( GetProcessHeap(), 0, windows
,
54 new_count
* sizeof(windows
[0]) );
55 if (!new_win
) return FALSE
;
59 windows
[win_count
].hwnd
= hwnd
;
60 windows
[win_count
].tid
= GetWindowThreadProcessId( hwnd
, &windows
[win_count
].pid
);
65 /* compare two window info structures; callback for qsort */
66 static int cmp_window( const void *ptr1
, const void *ptr2
)
68 const struct window_info
*info1
= ptr1
;
69 const struct window_info
*info2
= ptr2
;
70 int ret
= info1
->pid
- info2
->pid
;
71 if (!ret
) ret
= info1
->tid
- info2
->tid
;
75 /* build the list of all windows (FIXME: handle multiple desktops) */
76 static BOOL
get_all_windows(void)
80 windows
= HeapAlloc( GetProcessHeap(), 0, win_max
* sizeof(windows
[0]) );
81 if (!windows
) return FALSE
;
82 if (!EnumWindows( enum_proc
, 0 )) return FALSE
;
83 /* sort windows by processes */
84 qsort( windows
, win_count
, sizeof(windows
[0]), cmp_window
);
95 static void CALLBACK
end_session_message_callback( HWND hwnd
, UINT msg
, ULONG_PTR data
, LRESULT lresult
)
97 struct callback_data
*cb_data
= (struct callback_data
*)data
;
99 WINE_TRACE( "received response %s hwnd %p lresult %ld\n",
100 msg
== WM_QUERYENDSESSION
? "WM_QUERYENDSESSION" : (msg
== WM_ENDSESSION
? "WM_ENDSESSION" : "Unknown"),
103 /* we only care if a WM_QUERYENDSESSION response is FALSE */
104 cb_data
->result
= cb_data
->result
&& lresult
;
106 /* cheap way of ref-counting callback_data whilst freeing memory at correct
108 if (!(cb_data
->window_count
--) && cb_data
->timed_out
)
109 HeapFree( GetProcessHeap(), 0, cb_data
);
112 struct endtask_dlg_data
114 struct window_info
*win
;
118 static INT_PTR CALLBACK
endtask_dlg_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
120 struct endtask_dlg_data
*data
;
126 SetWindowLongPtrW( hwnd
, DWLP_USER
, lparam
);
127 data
= (struct endtask_dlg_data
*)lparam
;
128 ShowWindow( hwnd
, SW_SHOWNORMAL
);
131 data
= (struct endtask_dlg_data
*)GetWindowLongPtrW( hwnd
, DWLP_USER
);
134 case MAKEWPARAM(IDOK
, BN_CLICKED
):
135 handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, data
->win
[0].pid
);
138 WINE_TRACE( "terminating process %04x\n", data
->win
[0].pid
);
139 TerminateProcess( handle
, 0 );
140 CloseHandle( handle
);
143 case MAKEWPARAM(IDCANCEL
, BN_CLICKED
):
144 data
->cancelled
= TRUE
;
152 /* Sends a message to a set of windows, displaying a dialog if the window
153 * doesn't respond to the message within a set amount of time.
154 * If the process has already been terminated, the function returns -1.
155 * If the user or application cancels the process, the function returns 0.
156 * Otherwise the function returns 0. */
157 static LRESULT
send_messages_with_timeout_dialog(
158 struct window_info
*win
, UINT count
, HANDLE process_handle
,
159 UINT msg
, WPARAM wparam
, LPARAM lparam
)
164 struct callback_data
*cb_data
;
165 HWND hwnd_endtask
= NULL
;
166 struct endtask_dlg_data dlg_data
;
169 cb_data
= HeapAlloc( GetProcessHeap(), 0, sizeof(*cb_data
) );
173 cb_data
->result
= TRUE
; /* we only care if a WM_QUERYENDSESSION response is FALSE */
174 cb_data
->timed_out
= FALSE
;
175 cb_data
->window_count
= count
;
178 dlg_data
.cancelled
= FALSE
;
180 for (i
= 0; i
< count
; i
++)
182 if (!SendMessageCallbackW( win
[i
].hwnd
, msg
, wparam
, lparam
,
183 end_session_message_callback
, (ULONG_PTR
)cb_data
))
184 cb_data
->window_count
--;
187 start_time
= GetTickCount();
190 DWORD current_time
= GetTickCount();
192 ret
= MsgWaitForMultipleObjects( 1, &process_handle
, FALSE
,
193 MESSAGE_TIMEOUT
- (current_time
- start_time
),
195 if (ret
== WAIT_OBJECT_0
) /* process exited */
197 HeapFree( GetProcessHeap(), 0, cb_data
);
201 else if (ret
== WAIT_OBJECT_0
+ 1) /* window message */
204 while(PeekMessageW( &msg
, NULL
, 0, 0, PM_REMOVE
))
206 if (!hwnd_endtask
|| !IsDialogMessageW( hwnd_endtask
, &msg
))
208 TranslateMessage( &msg
);
209 DispatchMessageW( &msg
);
212 if (!cb_data
->window_count
)
214 result
= cb_data
->result
;
215 HeapFree( GetProcessHeap(), 0, cb_data
);
220 if (dlg_data
.cancelled
)
222 cb_data
->timed_out
= TRUE
;
227 else if ((ret
== WAIT_TIMEOUT
) && !hwnd_endtask
)
229 hwnd_endtask
= CreateDialogParamW( GetModuleHandleW(NULL
),
230 MAKEINTRESOURCEW(IDD_ENDTASK
),
231 NULL
, endtask_dlg_proc
,
240 if (hwnd_endtask
) DestroyWindow( hwnd_endtask
);
244 /* send WM_QUERYENDSESSION and WM_ENDSESSION to all windows of a given process */
245 static DWORD_PTR
send_end_session_messages( struct window_info
*win
, UINT count
, UINT flags
)
247 LRESULT result
, end_session
;
248 HANDLE process_handle
;
251 /* FIXME: Use flags to implement EWX_FORCEIFHUNG! */
252 /* don't kill the desktop process */
253 if (win
[0].pid
== desktop_pid
) return 1;
255 process_handle
= OpenProcess( SYNCHRONIZE
, FALSE
, win
[0].pid
);
259 end_session
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
260 WM_QUERYENDSESSION
, 0, 0 );
261 if (end_session
== -1)
263 CloseHandle( process_handle
);
267 result
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
268 WM_ENDSESSION
, end_session
, 0 );
269 if (end_session
== 0)
271 CloseHandle( process_handle
);
276 CloseHandle( process_handle
);
280 /* Check whether the app quit on its own */
281 ret
= WaitForSingleObject( process_handle
, 0 );
282 CloseHandle( process_handle
);
283 if (ret
== WAIT_TIMEOUT
)
285 /* If not, it returned from all WM_ENDSESSION and is finished cleaning
286 * up, so we can safely kill the process. */
287 HANDLE handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, win
[0].pid
);
290 WINE_TRACE( "terminating process %04x\n", win
[0].pid
);
291 TerminateProcess( handle
, 0 );
292 CloseHandle( handle
);
298 /* close all top-level windows and terminate processes cleanly */
299 BOOL
shutdown_close_windows( BOOL force
)
301 UINT send_flags
= force
? SMTO_ABORTIFHUNG
: SMTO_NORMAL
;
302 DWORD_PTR result
= 1;
305 if (!get_all_windows()) return FALSE
;
307 GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid
);
309 for (i
= n
= 0; result
&& i
< win_count
; i
++, n
++)
311 if (n
&& windows
[i
-1].pid
!= windows
[i
].pid
)
313 result
= send_end_session_messages( windows
+ i
- n
, n
, send_flags
);
318 result
= send_end_session_messages( windows
+ win_count
- n
, n
, send_flags
);
320 HeapFree( GetProcessHeap(), 0, windows
);
322 return (result
!= 0);
325 /* forcibly kill all processes without any cleanup */
326 void kill_processes( BOOL kill_desktop
)
330 HANDLE handle
, snapshot
;
331 PROCESSENTRY32W process
;
333 GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid
);
337 if (!(snapshot
= CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS
, 0 ))) break;
340 process
.dwSize
= sizeof(process
);
341 for (res
= Process32FirstW( snapshot
, &process
); res
; res
= Process32NextW( snapshot
, &process
))
343 if (process
.th32ProcessID
== GetCurrentProcessId()) continue;
344 if (process
.th32ProcessID
== desktop_pid
) continue;
345 WINE_TRACE("killing process %04x %s\n",
346 process
.th32ProcessID
, wine_dbgstr_w(process
.szExeFile
) );
347 if (!(handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, process
.th32ProcessID
)))
349 if (TerminateProcess( handle
, 0 )) killed
++;
350 CloseHandle( handle
);
352 CloseHandle( snapshot
);
353 } while (killed
> 0);
355 if (desktop_pid
&& kill_desktop
) /* do this last */
357 if ((handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, desktop_pid
)))
359 TerminateProcess( handle
, 0 );
360 CloseHandle( handle
);