diff --git a/programs/taskkill/En.rc b/programs/taskkill/En.rc index 67b2dac8294..ccad8855a86 100644 --- a/programs/taskkill/En.rc +++ b/programs/taskkill/En.rc @@ -31,4 +31,6 @@ STRINGTABLE STRING_MISSING_OPTION, "Error: One of options /im or /pid must be specified.\n" STRING_MISSING_PARAM, "Error: Option %s expects a command line parameter.\n" STRING_MUTUAL_EXCLUSIVE, "Error: Options /im and /pid are mutually exclusive.\n" + STRING_CLOSE_PID_SEARCH, "Close message sent to top-level windows of process with PID %u.\n" + STRING_SEARCH_FAILED, "Error: Could not find process \"%s\".\n" } diff --git a/programs/taskkill/taskkill.c b/programs/taskkill/taskkill.c index 0ff638581a3..4b9fb94438c 100644 --- a/programs/taskkill/taskkill.c +++ b/programs/taskkill/taskkill.c @@ -32,6 +32,12 @@ int force_termination; WCHAR **task_list; unsigned int task_count; +struct pid_close_info +{ + DWORD pid; + BOOL found; +}; + static int taskkill_vprintfW(const WCHAR *msg, va_list va_args) { int wlen; @@ -100,6 +106,78 @@ static int taskkill_message(int msg) return taskkill_printfW(formatW, msg_buffer); } +/* Post WM_CLOSE to all top-level windows belonging to the process with specified PID. */ +static BOOL CALLBACK pid_enum_proc(HWND hwnd, LPARAM lParam) +{ + struct pid_close_info *info = (struct pid_close_info *)lParam; + DWORD hwnd_pid; + + GetWindowThreadProcessId(hwnd, &hwnd_pid); + + if (hwnd_pid == info->pid) + { + PostMessageW(hwnd, WM_CLOSE, 0, 0); + info->found = TRUE; + } + + return TRUE; +} + +/* The implemented task enumeration and termination behavior does not + * exactly match native behavior. On Windows: + * + * In the case of terminating by process name, specifying a particular + * process name more times than the number of running instances causes + * all instances to be terminated, but termination failure messages to + * be printed as many times as the difference between the specification + * quantity and the number of running instances. + * + * Successful terminations are all listed first in order, with failing + * terminations being listed at the end. + * + * A PID of zero causes taskkill to warn about the inability to terminate + * system processes. */ +static int send_close_messages(void) +{ + unsigned int i; + int status_code = 0; + + for (i = 0; i < task_count; i++) + { + WCHAR *p = task_list[i]; + BOOL is_numeric = TRUE; + + /* Determine whether the string is not numeric. */ + while (*p) + { + if (!isdigitW(*p++)) + { + is_numeric = FALSE; + break; + } + } + + if (is_numeric) + { + DWORD pid = atoiW(task_list[i]); + struct pid_close_info info = { pid }; + + EnumWindows(pid_enum_proc, (LPARAM)&info); + if (info.found) + taskkill_message_printfW(STRING_CLOSE_PID_SEARCH, pid); + else + { + taskkill_message_printfW(STRING_SEARCH_FAILED, task_list[i]); + status_code = 128; + } + } + else + WINE_FIXME("Termination by name is not implemented\n"); + } + + return status_code; +} + static BOOL add_to_task_list(WCHAR *name) { static unsigned int list_size = 16; @@ -202,14 +280,19 @@ static BOOL process_arguments(int argc, WCHAR *argv[]) int wmain(int argc, WCHAR *argv[]) { + int status_code = 0; + if (!process_arguments(argc, argv)) { HeapFree(GetProcessHeap(), 0, task_list); return 1; } - WINE_FIXME("taskkill.exe functionality is not implemented\n"); + if (force_termination) + WINE_FIXME("Forced termination is not implemented\n"); + else + status_code = send_close_messages(); HeapFree(GetProcessHeap(), 0, task_list); - return 0; + return status_code; } diff --git a/programs/taskkill/taskkill.h b/programs/taskkill/taskkill.h index 281690b2a7e..1cc09af54eb 100644 --- a/programs/taskkill/taskkill.h +++ b/programs/taskkill/taskkill.h @@ -27,3 +27,5 @@ #define STRING_MISSING_OPTION 104 #define STRING_MISSING_PARAM 105 #define STRING_MUTUAL_EXCLUSIVE 106 +#define STRING_CLOSE_PID_SEARCH 107 +#define STRING_SEARCH_FAILED 108