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
= realloc( windows
, new_count
* sizeof(windows
[0]) );
54 if (!new_win
) return FALSE
;
58 windows
[win_count
].hwnd
= hwnd
;
59 windows
[win_count
].tid
= GetWindowThreadProcessId( hwnd
, &windows
[win_count
].pid
);
64 /* compare two window info structures; callback for qsort */
65 static int __cdecl
cmp_window( const void *ptr1
, const void *ptr2
)
67 const struct window_info
*info1
= ptr1
;
68 const struct window_info
*info2
= ptr2
;
69 int ret
= info1
->pid
- info2
->pid
;
70 if (!ret
) ret
= info1
->tid
- info2
->tid
;
74 /* build the list of all windows (FIXME: handle multiple desktops) */
75 static BOOL
get_all_windows(void)
79 windows
= malloc( win_max
* sizeof(windows
[0]) );
80 if (!windows
) return FALSE
;
81 if (!EnumWindows( enum_proc
, 0 )) return FALSE
;
82 /* sort windows by processes */
83 qsort( windows
, win_count
, sizeof(windows
[0]), cmp_window
);
94 static void CALLBACK
end_session_message_callback( HWND hwnd
, UINT msg
, ULONG_PTR data
, LRESULT lresult
)
96 struct callback_data
*cb_data
= (struct callback_data
*)data
;
98 WINE_TRACE( "received response %s hwnd %p lresult %Id\n",
99 msg
== WM_QUERYENDSESSION
? "WM_QUERYENDSESSION" : (msg
== WM_ENDSESSION
? "WM_ENDSESSION" : "Unknown"),
102 /* If the window was destroyed while the message was in its queue, SendMessageCallback()
103 calls us with a default 0 result. Ignore it. */
104 if (!lresult
&& !IsWindow( hwnd
))
106 WINE_TRACE( "window was destroyed; ignoring FALSE lresult\n" );
110 /* we only care if a WM_QUERYENDSESSION response is FALSE */
111 cb_data
->result
= cb_data
->result
&& lresult
;
113 /* cheap way of ref-counting callback_data whilst freeing memory at correct
115 if (!(cb_data
->window_count
--) && cb_data
->timed_out
)
119 struct endtask_dlg_data
121 struct window_info
*win
;
126 static INT_PTR CALLBACK
endtask_dlg_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
128 struct endtask_dlg_data
*data
;
134 SetWindowLongPtrW( hwnd
, DWLP_USER
, lparam
);
135 ShowWindow( hwnd
, SW_SHOWNORMAL
);
138 data
= (struct endtask_dlg_data
*)GetWindowLongPtrW( hwnd
, DWLP_USER
);
141 case MAKEWPARAM(IDOK
, BN_CLICKED
):
142 handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, data
->win
[0].pid
);
145 WINE_TRACE( "terminating process %04lx\n", data
->win
[0].pid
);
146 TerminateProcess( handle
, 0 );
147 CloseHandle( handle
);
148 data
->terminated
= TRUE
;
151 case MAKEWPARAM(IDCANCEL
, BN_CLICKED
):
152 data
->cancelled
= TRUE
;
160 /* Sends a message to a set of windows, displaying a dialog if the window
161 * doesn't respond to the message within a set amount of time.
162 * If the process has already been terminated, the function returns -1.
163 * If the user or application cancels the process, the function returns 0.
164 * Otherwise the function returns 0. */
165 static LRESULT
send_messages_with_timeout_dialog(
166 struct window_info
*win
, UINT count
, HANDLE process_handle
,
167 UINT msg
, WPARAM wparam
, LPARAM lparam
)
172 struct callback_data
*cb_data
;
173 HWND hwnd_endtask
= NULL
;
174 struct endtask_dlg_data dlg_data
;
177 cb_data
= malloc( sizeof(*cb_data
) );
181 cb_data
->result
= TRUE
; /* we only care if a WM_QUERYENDSESSION response is FALSE */
182 cb_data
->timed_out
= FALSE
;
183 cb_data
->window_count
= count
;
186 dlg_data
.terminated
= FALSE
;
187 dlg_data
.cancelled
= FALSE
;
189 for (i
= 0; i
< count
; i
++)
191 if (!SendMessageCallbackW( win
[i
].hwnd
, msg
, wparam
, lparam
,
192 end_session_message_callback
, (ULONG_PTR
)cb_data
))
193 cb_data
->window_count
--;
196 start_time
= GetTickCount();
199 DWORD current_time
= GetTickCount();
201 ret
= MsgWaitForMultipleObjects( 1, &process_handle
, FALSE
,
202 MESSAGE_TIMEOUT
- (current_time
- start_time
),
204 if (ret
== WAIT_OBJECT_0
) /* process exited */
210 else if (ret
== WAIT_OBJECT_0
+ 1) /* window message */
213 while(PeekMessageW( &msg
, NULL
, 0, 0, PM_REMOVE
))
215 if (!hwnd_endtask
|| !IsDialogMessageW( hwnd_endtask
, &msg
))
217 TranslateMessage( &msg
);
218 DispatchMessageW( &msg
);
221 if (!cb_data
->window_count
)
223 result
= dlg_data
.terminated
|| cb_data
->result
;
229 if (dlg_data
.cancelled
)
231 cb_data
->timed_out
= TRUE
;
236 else if ((ret
== WAIT_TIMEOUT
) && !hwnd_endtask
)
238 hwnd_endtask
= CreateDialogParamW( GetModuleHandleW(NULL
),
239 MAKEINTRESOURCEW(IDD_ENDTASK
),
240 NULL
, endtask_dlg_proc
,
249 if (hwnd_endtask
) DestroyWindow( hwnd_endtask
);
253 /* send WM_QUERYENDSESSION and WM_ENDSESSION to all windows of a given process */
254 static DWORD_PTR
send_end_session_messages( struct window_info
*win
, UINT count
, UINT flags
)
256 LRESULT result
, end_session
;
257 HANDLE process_handle
;
260 /* FIXME: Use flags to implement EWX_FORCEIFHUNG! */
261 /* don't kill the desktop process */
262 if (win
[0].pid
== desktop_pid
) return 1;
264 process_handle
= OpenProcess( SYNCHRONIZE
, FALSE
, win
[0].pid
);
268 end_session
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
269 WM_QUERYENDSESSION
, 0, 0 );
270 if (end_session
== -1)
272 CloseHandle( process_handle
);
276 result
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
277 WM_ENDSESSION
, end_session
, 0 );
278 if (end_session
== 0)
280 CloseHandle( process_handle
);
285 CloseHandle( process_handle
);
289 /* Check whether the app quit on its own */
290 ret
= WaitForSingleObject( process_handle
, 0 );
291 CloseHandle( process_handle
);
292 if (ret
== WAIT_TIMEOUT
)
294 /* If not, it returned from all WM_ENDSESSION and is finished cleaning
295 * up, so we can safely kill the process. */
296 HANDLE handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, win
[0].pid
);
299 WINE_TRACE( "terminating process %04lx\n", win
[0].pid
);
300 TerminateProcess( handle
, 0 );
301 CloseHandle( handle
);
307 /* close all top-level windows and terminate processes cleanly */
308 BOOL
shutdown_close_windows( BOOL force
)
310 UINT send_flags
= force
? SMTO_ABORTIFHUNG
: SMTO_NORMAL
;
311 DWORD_PTR result
= 1;
314 if (!get_all_windows()) return FALSE
;
316 GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid
);
318 for (i
= n
= 0; result
&& i
< win_count
; i
++, n
++)
320 if (n
&& windows
[i
-1].pid
!= windows
[i
].pid
)
322 result
= send_end_session_messages( windows
+ i
- n
, n
, send_flags
);
327 result
= send_end_session_messages( windows
+ win_count
- n
, n
, send_flags
);
331 return (result
!= 0);
334 static BOOL CALLBACK
shutdown_one_desktop( LPWSTR name
, LPARAM force
)
338 WINE_TRACE("Shutting down desktop %s\n", wine_dbgstr_w(name
));
340 hdesk
= OpenDesktopW( name
, 0, FALSE
, GENERIC_ALL
);
343 WINE_ERR("Cannot open desktop %s, err=%li\n", wine_dbgstr_w(name
), GetLastError());
347 if (!SetThreadDesktop( hdesk
))
349 CloseDesktop( hdesk
);
350 WINE_ERR("Cannot set thread desktop %s, err=%li\n", wine_dbgstr_w(name
), GetLastError());
354 CloseDesktop( hdesk
);
356 return shutdown_close_windows( force
);
359 BOOL
shutdown_all_desktops( BOOL force
)
364 prev_desktop
= GetThreadDesktop(GetCurrentThreadId());
366 ret
= EnumDesktopsW( NULL
, shutdown_one_desktop
, (LPARAM
)force
);
368 SetThreadDesktop(prev_desktop
);
373 /* forcibly kill all processes without any cleanup */
374 void kill_processes( BOOL kill_desktop
)
378 HANDLE handle
, snapshot
;
379 PROCESSENTRY32W process
;
381 GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid
);
385 if (!(snapshot
= CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS
, 0 ))) break;
388 process
.dwSize
= sizeof(process
);
389 for (res
= Process32FirstW( snapshot
, &process
); res
; res
= Process32NextW( snapshot
, &process
))
391 if (process
.th32ProcessID
== GetCurrentProcessId()) continue;
392 if (process
.th32ProcessID
== desktop_pid
) continue;
393 WINE_TRACE("killing process %04lx %s\n",
394 process
.th32ProcessID
, wine_dbgstr_w(process
.szExeFile
) );
395 if (!(handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, process
.th32ProcessID
)))
397 if (TerminateProcess( handle
, 0 )) killed
++;
398 CloseHandle( handle
);
400 CloseHandle( snapshot
);
401 } while (killed
> 0);
403 if (desktop_pid
&& kill_desktop
) /* do this last */
405 if ((handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, desktop_pid
)))
407 TerminateProcess( handle
, 0 );
408 CloseHandle( handle
);