|  | /* | 
|  | * Copyright (C) 2006 Alexandre Julliard | 
|  | * | 
|  | * This library is free software; you can redistribute it and/or | 
|  | * modify it under the terms of the GNU Lesser General Public | 
|  | * License as published by the Free Software Foundation; either | 
|  | * version 2.1 of the License, or (at your option) any later version. | 
|  | * | 
|  | * This library is distributed in the hope that it will be useful, | 
|  | * but WITHOUT ANY WARRANTY; without even the implied warranty of | 
|  | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU | 
|  | * Lesser General Public License for more details. | 
|  | * | 
|  | * You should have received a copy of the GNU Lesser General Public | 
|  | * License along with this library; if not, write to the Free Software | 
|  | * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "winuser.h" | 
|  | #include "tlhelp32.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(wineboot); | 
|  |  | 
|  | struct window_info | 
|  | { | 
|  | HWND  hwnd; | 
|  | DWORD pid; | 
|  | DWORD tid; | 
|  | }; | 
|  |  | 
|  | static UINT win_count; | 
|  | static UINT win_max; | 
|  | static struct window_info *windows; | 
|  | static DWORD desktop_pid; | 
|  |  | 
|  | /* store a new window; callback for EnumWindows */ | 
|  | static BOOL CALLBACK enum_proc( HWND hwnd, LPARAM lp ) | 
|  | { | 
|  | if (win_count >= win_max) | 
|  | { | 
|  | UINT new_count = win_max * 2; | 
|  | struct window_info *new_win = HeapReAlloc( GetProcessHeap(), 0, windows, | 
|  | new_count * sizeof(windows[0]) ); | 
|  | if (!new_win) return FALSE; | 
|  | windows = new_win; | 
|  | win_max = new_count; | 
|  | } | 
|  | windows[win_count].hwnd = hwnd; | 
|  | windows[win_count].tid = GetWindowThreadProcessId( hwnd, &windows[win_count].pid ); | 
|  | win_count++; | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* compare two window info structures; callback for qsort */ | 
|  | static int cmp_window( const void *ptr1, const void *ptr2 ) | 
|  | { | 
|  | const struct window_info *info1 = ptr1; | 
|  | const struct window_info *info2 = ptr2; | 
|  | int ret = info1->pid - info2->pid; | 
|  | if (!ret) ret = info1->tid - info2->tid; | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* build the list of all windows (FIXME: handle multiple desktops) */ | 
|  | static BOOL get_all_windows(void) | 
|  | { | 
|  | win_count = 0; | 
|  | win_max = 16; | 
|  | windows = HeapAlloc( GetProcessHeap(), 0, win_max * sizeof(windows[0]) ); | 
|  | if (!windows) return FALSE; | 
|  | if (!EnumWindows( enum_proc, 0 )) return FALSE; | 
|  | /* sort windows by processes */ | 
|  | qsort( windows, win_count, sizeof(windows[0]), cmp_window ); | 
|  | return TRUE; | 
|  | } | 
|  |  | 
|  | /* send WM_QUERYENDSESSION and WM_ENDSESSION to all windows of a given process */ | 
|  | /* FIXME: should display a confirmation dialog if process doesn't respond to the messages */ | 
|  | static DWORD_PTR send_end_session_messages( struct window_info *win, UINT count, UINT flags ) | 
|  | { | 
|  | unsigned int i; | 
|  | DWORD_PTR result, ret = 1; | 
|  |  | 
|  | /* don't kill the desktop process */ | 
|  | if (win[0].pid == desktop_pid) return 1; | 
|  |  | 
|  | for (i = 0; ret && i < count; i++) | 
|  | { | 
|  | if (SendMessageTimeoutW( win[i].hwnd, WM_QUERYENDSESSION, 0, 0, flags, 0, &result )) | 
|  | { | 
|  | WINE_TRACE( "sent MW_QUERYENDSESSION hwnd %p pid %04x result %ld\n", | 
|  | win[i].hwnd, win[i].pid, result ); | 
|  | ret = result; | 
|  | } | 
|  | else win[i].hwnd = 0;  /* ignore this window */ | 
|  | } | 
|  |  | 
|  | for (i = 0; i < count; i++) | 
|  | { | 
|  | if (!win[i].hwnd) continue; | 
|  | WINE_TRACE( "sending WM_ENDSESSION hwnd %p pid %04x wp %ld\n", win[i].hwnd, win[i].pid, ret ); | 
|  | SendMessageTimeoutW( win[i].hwnd, WM_ENDSESSION, ret, 0, flags, 0, &result ); | 
|  | } | 
|  |  | 
|  | if (ret) | 
|  | { | 
|  | HANDLE handle = OpenProcess( PROCESS_TERMINATE, FALSE, win[0].pid ); | 
|  | if (handle) | 
|  | { | 
|  | WINE_TRACE( "terminating process %04x\n", win[0].pid ); | 
|  | TerminateProcess( handle, 0 ); | 
|  | CloseHandle( handle ); | 
|  | } | 
|  | } | 
|  | return ret; | 
|  | } | 
|  |  | 
|  | /* close all top-level windows and terminate processes cleanly */ | 
|  | BOOL shutdown_close_windows( BOOL force ) | 
|  | { | 
|  | UINT send_flags = force ? SMTO_ABORTIFHUNG : SMTO_NORMAL; | 
|  | DWORD_PTR result = 1; | 
|  | UINT i, n; | 
|  |  | 
|  | if (!get_all_windows()) return FALSE; | 
|  |  | 
|  | GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid ); | 
|  |  | 
|  | for (i = n = 0; result && i < win_count; i++, n++) | 
|  | { | 
|  | if (n && windows[i-1].pid != windows[i].pid) | 
|  | { | 
|  | result = send_end_session_messages( windows + i - n, n, send_flags ); | 
|  | n = 0; | 
|  | } | 
|  | } | 
|  | if (n && result) | 
|  | result = send_end_session_messages( windows + win_count - n, n, send_flags ); | 
|  |  | 
|  | HeapFree( GetProcessHeap(), 0, windows ); | 
|  |  | 
|  | return (result != 0); | 
|  | } | 
|  |  | 
|  | /* forcibly kill all processes without any cleanup */ | 
|  | void kill_processes( BOOL kill_desktop ) | 
|  | { | 
|  | BOOL res; | 
|  | UINT killed; | 
|  | HANDLE handle, snapshot; | 
|  | PROCESSENTRY32W process; | 
|  |  | 
|  | GetWindowThreadProcessId( GetDesktopWindow(), &desktop_pid ); | 
|  |  | 
|  | do | 
|  | { | 
|  | if (!(snapshot = CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ))) break; | 
|  |  | 
|  | killed = 0; | 
|  | process.dwSize = sizeof(process); | 
|  | for (res = Process32FirstW( snapshot, &process ); res; res = Process32NextW( snapshot, &process )) | 
|  | { | 
|  | if (process.th32ProcessID == GetCurrentProcessId()) continue; | 
|  | if (process.th32ProcessID == desktop_pid) continue; | 
|  | WINE_TRACE("killing process %04x %s\n", | 
|  | process.th32ProcessID, wine_dbgstr_w(process.szExeFile) ); | 
|  | if (!(handle = OpenProcess( PROCESS_TERMINATE, FALSE, process.th32ProcessID ))) | 
|  | continue; | 
|  | if (TerminateProcess( handle, 0 )) killed++; | 
|  | CloseHandle( handle ); | 
|  | } | 
|  | CloseHandle( snapshot ); | 
|  | } while (killed > 0); | 
|  |  | 
|  | if (desktop_pid && kill_desktop)  /* do this last */ | 
|  | { | 
|  | if ((handle = OpenProcess( PROCESS_TERMINATE, FALSE, desktop_pid ))) | 
|  | { | 
|  | TerminateProcess( handle, 0 ); | 
|  | CloseHandle( handle ); | 
|  | } | 
|  | } | 
|  | } |