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
;
119 static INT_PTR CALLBACK
endtask_dlg_proc( HWND hwnd
, UINT msg
, WPARAM wparam
, LPARAM lparam
)
121 struct endtask_dlg_data
*data
;
127 SetWindowLongPtrW( hwnd
, DWLP_USER
, lparam
);
128 data
= (struct endtask_dlg_data
*)lparam
;
129 ShowWindow( hwnd
, SW_SHOWNORMAL
);
132 data
= (struct endtask_dlg_data
*)GetWindowLongPtrW( hwnd
, DWLP_USER
);
135 case MAKEWPARAM(IDOK
, BN_CLICKED
):
136 handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, data
->win
[0].pid
);
139 WINE_TRACE( "terminating process %04x\n", data
->win
[0].pid
);
140 TerminateProcess( handle
, 0 );
141 CloseHandle( handle
);
142 data
->terminated
= TRUE
;
145 case MAKEWPARAM(IDCANCEL
, BN_CLICKED
):
146 data
->cancelled
= TRUE
;
154 /* Sends a message to a set of windows, displaying a dialog if the window
155 * doesn't respond to the message within a set amount of time.
156 * If the process has already been terminated, the function returns -1.
157 * If the user or application cancels the process, the function returns 0.
158 * Otherwise the function returns 0. */
159 static LRESULT
send_messages_with_timeout_dialog(
160 struct window_info
*win
, UINT count
, HANDLE process_handle
,
161 UINT msg
, WPARAM wparam
, LPARAM lparam
)
166 struct callback_data
*cb_data
;
167 HWND hwnd_endtask
= NULL
;
168 struct endtask_dlg_data dlg_data
;
171 cb_data
= HeapAlloc( GetProcessHeap(), 0, sizeof(*cb_data
) );
175 cb_data
->result
= TRUE
; /* we only care if a WM_QUERYENDSESSION response is FALSE */
176 cb_data
->timed_out
= FALSE
;
177 cb_data
->window_count
= count
;
180 dlg_data
.terminated
= FALSE
;
181 dlg_data
.cancelled
= FALSE
;
183 for (i
= 0; i
< count
; i
++)
185 if (!SendMessageCallbackW( win
[i
].hwnd
, msg
, wparam
, lparam
,
186 end_session_message_callback
, (ULONG_PTR
)cb_data
))
187 cb_data
->window_count
--;
190 start_time
= GetTickCount();
193 DWORD current_time
= GetTickCount();
195 ret
= MsgWaitForMultipleObjects( 1, &process_handle
, FALSE
,
196 MESSAGE_TIMEOUT
- (current_time
- start_time
),
198 if (ret
== WAIT_OBJECT_0
) /* process exited */
200 HeapFree( GetProcessHeap(), 0, cb_data
);
204 else if (ret
== WAIT_OBJECT_0
+ 1) /* window message */
207 while(PeekMessageW( &msg
, NULL
, 0, 0, PM_REMOVE
))
209 if (!hwnd_endtask
|| !IsDialogMessageW( hwnd_endtask
, &msg
))
211 TranslateMessage( &msg
);
212 DispatchMessageW( &msg
);
215 if (!cb_data
->window_count
)
217 result
= dlg_data
.terminated
|| cb_data
->result
;
218 HeapFree( GetProcessHeap(), 0, cb_data
);
223 if (dlg_data
.cancelled
)
225 cb_data
->timed_out
= TRUE
;
230 else if ((ret
== WAIT_TIMEOUT
) && !hwnd_endtask
)
232 hwnd_endtask
= CreateDialogParamW( GetModuleHandleW(NULL
),
233 MAKEINTRESOURCEW(IDD_ENDTASK
),
234 NULL
, endtask_dlg_proc
,
243 if (hwnd_endtask
) DestroyWindow( hwnd_endtask
);
247 /* send WM_QUERYENDSESSION and WM_ENDSESSION to all windows of a given process */
248 static DWORD_PTR
send_end_session_messages( struct window_info
*win
, UINT count
, UINT flags
)
250 LRESULT result
, end_session
;
251 HANDLE process_handle
;
254 /* FIXME: Use flags to implement EWX_FORCEIFHUNG! */
255 /* don't kill the desktop process */
256 if (win
[0].pid
== desktop_pid
) return 1;
258 process_handle
= OpenProcess( SYNCHRONIZE
, FALSE
, win
[0].pid
);
262 end_session
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
263 WM_QUERYENDSESSION
, 0, 0 );
264 if (end_session
== -1)
266 CloseHandle( process_handle
);
270 result
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
271 WM_ENDSESSION
, end_session
, 0 );
272 if (end_session
== 0)
274 CloseHandle( process_handle
);
279 CloseHandle( process_handle
);
283 /* Check whether the app quit on its own */
284 ret
= WaitForSingleObject( process_handle
, 0 );
285 CloseHandle( process_handle
);
286 if (ret
== WAIT_TIMEOUT
)
288 /* If not, it returned from all WM_ENDSESSION and is finished cleaning
289 * up, so we can safely kill the process. */
290 HANDLE handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, win
[0].pid
);
293 WINE_TRACE( "terminating process %04x\n", win
[0].pid
);
294 TerminateProcess( handle
, 0 );
295 CloseHandle( handle
);
301 /* close all top-level windows and terminate processes cleanly */
302 BOOL
shutdown_close_windows( BOOL force
)
304 UINT send_flags
= force
? SMTO_ABORTIFHUNG
: SMTO_NORMAL
;
305 DWORD_PTR result
= 1;
308 if (!get_all_windows()) return FALSE
;
310 GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid
);
312 for (i
= n
= 0; result
&& i
< win_count
; i
++, n
++)
314 if (n
&& windows
[i
-1].pid
!= windows
[i
].pid
)
316 result
= send_end_session_messages( windows
+ i
- n
, n
, send_flags
);
321 result
= send_end_session_messages( windows
+ win_count
- n
, n
, send_flags
);
323 HeapFree( GetProcessHeap(), 0, windows
);
325 return (result
!= 0);
328 static BOOL CALLBACK
shutdown_one_desktop( LPWSTR name
, LPARAM force
)
332 WINE_TRACE("Shutting down desktop %s\n", wine_dbgstr_w(name
));
334 hdesk
= OpenDesktopW( name
, 0, FALSE
, GENERIC_ALL
);
337 WINE_ERR("Cannot open desktop %s, err=%i\n", wine_dbgstr_w(name
), GetLastError());
341 if (!SetThreadDesktop( hdesk
))
343 CloseDesktop( hdesk
);
344 WINE_ERR("Cannot set thread desktop %s, err=%i\n", wine_dbgstr_w(name
), GetLastError());
348 CloseDesktop( hdesk
);
350 return shutdown_close_windows( force
);
353 BOOL
shutdown_all_desktops( BOOL force
)
358 prev_desktop
= GetThreadDesktop(GetCurrentThreadId());
360 ret
= EnumDesktopsW( NULL
, shutdown_one_desktop
, (LPARAM
)force
);
362 SetThreadDesktop(prev_desktop
);
367 /* forcibly kill all processes without any cleanup */
368 void kill_processes( BOOL kill_desktop
)
372 HANDLE handle
, snapshot
;
373 PROCESSENTRY32W process
;
375 GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid
);
379 if (!(snapshot
= CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS
, 0 ))) break;
382 process
.dwSize
= sizeof(process
);
383 for (res
= Process32FirstW( snapshot
, &process
); res
; res
= Process32NextW( snapshot
, &process
))
385 if (process
.th32ProcessID
== GetCurrentProcessId()) continue;
386 if (process
.th32ProcessID
== desktop_pid
) continue;
387 WINE_TRACE("killing process %04x %s\n",
388 process
.th32ProcessID
, wine_dbgstr_w(process
.szExeFile
) );
389 if (!(handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, process
.th32ProcessID
)))
391 if (TerminateProcess( handle
, 0 )) killed
++;
392 CloseHandle( handle
);
394 CloseHandle( snapshot
);
395 } while (killed
> 0);
397 if (desktop_pid
&& kill_desktop
) /* do this last */
399 if ((handle
= OpenProcess( PROCESS_TERMINATE
, FALSE
, desktop_pid
)))
401 TerminateProcess( handle
, 0 );
402 CloseHandle( handle
);