From: Zhiyi Zhang Subject: [PATCH] taskmgr: Implement end process tree Message-Id: <5fac1810-e384-eccd-273f-a4ca3c49be09@codeweavers.com> Date: Thu, 8 Mar 2018 19:30:20 +0800 This should get https://bugs.winehq.org/show_bug.cgi?id=39640 closed. Partially base on François's idea. Signed-off-by: Zhiyi Zhang --- programs/taskmgr/endproc.c | 137 ++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 130 insertions(+), 7 deletions(-) diff --git a/programs/taskmgr/endproc.c b/programs/taskmgr/endproc.c index 89c2d7bf80..6e711cba97 100644 --- a/programs/taskmgr/endproc.c +++ b/programs/taskmgr/endproc.c @@ -27,6 +27,7 @@ #include #include #include +#include #include "wine/unicode.h" #include "taskmgr.h" @@ -36,6 +37,17 @@ static WCHAR wszWarnMsg[511]; static WCHAR wszWarnTitle[255]; static WCHAR wszUnable2Terminate[255]; +typedef struct { + /* Parent Process ID */ + DWORD ppid; + /* Process ID */ + DWORD pid; +} PID_PAIR, *PPID_PAIR; + +static PPID_PAIR g_pids = NULL; +static ULONG g_pid_count = 0; +static ULONG g_pid_pos = 0; + static void load_message_strings(void) { LoadStringW(hInst, IDS_TERMINATE_MESSAGE, wszWarnMsg, sizeof(wszWarnMsg)/sizeof(WCHAR)); @@ -93,13 +105,90 @@ void ProcessPage_OnEndProcess(void) CloseHandle(hProcess); } +/* copied from winetest/main.c */ +static DWORD wait_process(HANDLE process, DWORD timeout) +{ + DWORD wait, diff = 0, start = GetTickCount(); + MSG msg; + + while(diff < timeout) + { + wait = MsgWaitForMultipleObjects(1, &process, FALSE, timeout - diff, QS_ALLINPUT); + if(wait != WAIT_OBJECT_0 + 1) return wait; + while(PeekMessageA(&msg, 0, 0, 0, PM_REMOVE)) DispatchMessageA(&msg); + diff = GetTickCount() - start; + } + return WAIT_TIMEOUT; +} + +/* Kill process and its subprocesses created before kill_time. + * Return FALSE on failure or TRUE on success. + */ +static BOOL kill_process_tree(DWORD pid, const FILETIME *kill_time) +{ + HANDLE process; + FILETIME creation_time; + FILETIME exit_time; + FILETIME kernel_time; + FILETIME user_time; + DWORD status; + BOOL ret = TRUE; + ULONG i; + + /* Kill children first */ + for(i = 0; i < g_pid_pos; i++) + { + if(g_pids[i].ppid == pid) + { + kill_process_tree(g_pids[i].pid, kill_time); + } + } + + process = OpenProcess(SYNCHRONIZE|PROCESS_TERMINATE|PROCESS_QUERY_INFORMATION, FALSE, pid); + if (!process) + { + /* The process exited before we could kill it */ + return FALSE; + } + + /* Only kill processes created before action and then + * terminate the process with 1 as the exit code just as taskmgr on Windows does. + */ + if(GetProcessTimes(process, &creation_time, &exit_time, &kernel_time, &user_time) + && !(CompareFileTime(&creation_time, kill_time) == 1)) + { + if(!TerminateProcess(process, 1)) + { + ret = FALSE; + } + } + + /* Wait for parent process to deal with termination */ + status = wait_process(process, 5000); + switch (status) + { + case WAIT_FAILED: + case WAIT_TIMEOUT: + ret = FALSE; + default: + break; + } + + CloseHandle(process); + return ret; +} + void ProcessPage_OnEndProcessTree(void) { LVITEMW lvitem; ULONG Index, Count; DWORD dwProcessId; - HANDLE hProcess; WCHAR wstrErrorText[256]; + HANDLE snapshot; + SYSTEMTIME systemTime; + FILETIME killTime; + PROCESSENTRY32 pe; + PPID_PAIR oldPids; load_message_strings(); @@ -125,20 +214,54 @@ void ProcessPage_OnEndProcessTree(void) if (MessageBoxW(hMainWnd, wszWarnMsg, wszWarnTitle, MB_YESNO|MB_ICONWARNING) != IDYES) return; - hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId); + GetSystemTime(&systemTime); + if (!SystemTimeToFileTime(&systemTime, &killTime)) + { + return; + } - if (!hProcess) + /* Preallocate space for pid pairs */ + g_pids = HeapAlloc(GetProcessHeap(), 0, 16 * sizeof(PID_PAIR)); + if(!g_pids) + return; + g_pid_count = 16; + g_pid_pos = 0; + + snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if(snapshot == INVALID_HANDLE_VALUE) + return; + + ZeroMemory(&pe, sizeof(pe)); + pe.dwSize = sizeof(pe); + if(!Process32First(snapshot, &pe)) { - GetLastErrorText(wstrErrorText, sizeof(wstrErrorText)/sizeof(WCHAR)); - MessageBoxW(hMainWnd, wstrErrorText,wszUnable2Terminate, MB_OK|MB_ICONSTOP); return; } - if (!TerminateProcess(hProcess, 0)) + do + { + g_pids[g_pid_pos].ppid = pe.th32ParentProcessID; + g_pids[g_pid_pos++].pid = pe.th32ProcessID; + /* If not enough space */ + if(g_pid_pos >= g_pid_count) + { + g_pid_count += 16; + oldPids = g_pids; + g_pids = HeapReAlloc(GetProcessHeap(), 0, g_pids, g_pid_count * sizeof(PID_PAIR)); + if(!g_pids) + { + CloseHandle(snapshot); + HeapFree(GetProcessHeap(), 0, oldPids); + } + } + } while (Process32Next(snapshot, &pe)); + CloseHandle(snapshot); + + if (!kill_process_tree(dwProcessId, &killTime)) { GetLastErrorText(wstrErrorText, sizeof(wstrErrorText)/sizeof(WCHAR)); MessageBoxW(hMainWnd, wstrErrorText,wszUnable2Terminate, MB_OK|MB_ICONSTOP); } - CloseHandle(hProcess); + HeapFree(GetProcessHeap(), 0, g_pids); }