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
34 #define PROCQUIT_TIMEOUT 20000
43 static UINT win_count
;
45 static struct window_info
*windows
;
46 static DWORD desktop_pid
;
48 /* store a new window; callback for EnumWindows */
49 static BOOL CALLBACK
enum_proc( HWND hwnd
, LPARAM lp
)
51 if (win_count
>= win_max
)
53 UINT new_count
= win_max
* 2;
54 struct window_info
*new_win
= HeapReAlloc( GetProcessHeap(), 0, windows
,
55 new_count
* sizeof(windows
[0]) );
56 if (!new_win
) return FALSE
;
60 windows
[win_count
].hwnd
= hwnd
;
61 windows
[win_count
].tid
= GetWindowThreadProcessId( hwnd
, &windows
[win_count
].pid
);
66 /* compare two window info structures; callback for qsort */
67 static int cmp_window( const void *ptr1
, const void *ptr2
)
69 const struct window_info
*info1
= ptr1
;
70 const struct window_info
*info2
= ptr2
;
71 int ret
= info1
->pid
- info2
->pid
;
72 if (!ret
) ret
= info1
->tid
- info2
->tid
;
76 /* build the list of all windows (FIXME: handle multiple desktops) */
77 static BOOL
get_all_windows(void)
81 windows
= HeapAlloc( GetProcessHeap(), 0, win_max
* sizeof(windows
[0]) );
82 if (!windows
) return FALSE
;
83 if (!EnumWindows( enum_proc
, 0 )) return FALSE
;
84 /* sort windows by processes */
85 qsort( windows
, win_count
, sizeof(windows
[0]), cmp_window
);
96 static void CALLBACK
end_session_message_callback( HWND hwnd
, UINT msg
, ULONG_PTR data
, LRESULT lresult
)
98 struct callback_data
*cb_data
= (struct callback_data
*)data
;
100 WINE_TRACE( "received response %s hwnd %p lresult %ld\n",
101 msg
== WM_QUERYENDSESSION
? "WM_QUERYENDSESSION" : (msg
== WM_ENDSESSION
? "WM_ENDSESSION" : "Unknown"),
104 /* we only care if a WM_QUERYENDSESSION response is FALSE */
105 cb_data
->result
= cb_data
->result
&& lresult
;
107 /* cheap way of ref-counting callback_data whilst freeing memory at correct
109 if (!(cb_data
->window_count
--) && cb_data
->timed_out
)
110 HeapFree( GetProcessHeap(), 0, cb_data
);
113 struct endtask_dlg_data
115 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
);
144 case MAKEWPARAM(IDCANCEL
, BN_CLICKED
):
145 data
->cancelled
= TRUE
;
153 /* Sends a message to a set of windows, displaying a dialog if the window
154 * doesn't respond to the message within a set amount of time.
155 * If the process has already been terminated, the function returns -1.
156 * If the user or application cancels the process, the function returns 0.
157 * Otherwise the function returns 0. */
158 static LRESULT
send_messages_with_timeout_dialog(
159 struct window_info
*win
, UINT count
, HANDLE process_handle
,
160 UINT msg
, WPARAM wparam
, LPARAM lparam
)
165 struct callback_data
*cb_data
;
166 HWND hwnd_endtask
= NULL
;
167 struct endtask_dlg_data dlg_data
;
170 cb_data
= HeapAlloc( GetProcessHeap(), 0, sizeof(*cb_data
) );
174 cb_data
->result
= TRUE
; /* we only care if a WM_QUERYENDSESSION response is FALSE */
175 cb_data
->timed_out
= FALSE
;
176 cb_data
->window_count
= count
;
179 dlg_data
.cancelled
= FALSE
;
181 for (i
= 0; i
< count
; i
++)
183 if (!SendMessageCallbackW( win
[i
].hwnd
, msg
, wparam
, lparam
,
184 end_session_message_callback
, (ULONG_PTR
)cb_data
))
185 cb_data
->window_count
--;
188 start_time
= GetTickCount();
191 DWORD current_time
= GetTickCount();
193 ret
= MsgWaitForMultipleObjects( 1, &process_handle
, FALSE
,
194 MESSAGE_TIMEOUT
- (current_time
- start_time
),
196 if (ret
== WAIT_OBJECT_0
) /* process exited */
198 HeapFree( GetProcessHeap(), 0, cb_data
);
202 else if (ret
== WAIT_OBJECT_0
+ 1) /* window message */
205 while(PeekMessageW( &msg
, NULL
, 0, 0, PM_REMOVE
))
207 if (!hwnd_endtask
|| !IsDialogMessageW( hwnd_endtask
, &msg
))
209 TranslateMessage( &msg
);
210 DispatchMessageW( &msg
);
213 if (!cb_data
->window_count
)
215 result
= cb_data
->result
;
216 HeapFree( GetProcessHeap(), 0, cb_data
);
221 if (dlg_data
.cancelled
)
223 cb_data
->timed_out
= TRUE
;
228 else if ((ret
== WAIT_TIMEOUT
) && !hwnd_endtask
)
230 hwnd_endtask
= CreateDialogParamW( GetModuleHandle(NULL
),
231 MAKEINTRESOURCEW(IDD_ENDTASK
),
232 NULL
, endtask_dlg_proc
,
241 if (hwnd_endtask
) DestroyWindow( hwnd_endtask
);
245 /* send WM_QUERYENDSESSION and WM_ENDSESSION to all windows of a given process */
246 static DWORD_PTR
send_end_session_messages( struct window_info
*win
, UINT count
, UINT flags
)
248 LRESULT result
, end_session
;
249 HANDLE process_handle
;
252 /* FIXME: Use flags to implement EWX_FORCEIFHUNG! */
253 /* don't kill the desktop process */
254 if (win
[0].pid
== desktop_pid
) return 1;
256 process_handle
= OpenProcess( SYNCHRONIZE
, FALSE
, win
[0].pid
);
260 end_session
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
261 WM_QUERYENDSESSION
, 0, 0 );
262 if (end_session
== -1)
264 CloseHandle( process_handle
);
268 result
= send_messages_with_timeout_dialog( win
, count
, process_handle
,
269 WM_ENDSESSION
, end_session
, 0 );
270 if (end_session
== 0)
272 CloseHandle( process_handle
);
277 CloseHandle( process_handle
);
281 /* wait for app to quit on its own for a while */
282 ret
= WaitForSingleObject( process_handle
, PROCQUIT_TIMEOUT
);
283 CloseHandle( process_handle
);
284 if (ret
== WAIT_TIMEOUT
)
286 /* it didn't quit by itself in time, so terminate it with extreme prejudice */
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
);