include: Define up to date exception flags.
[wine.git] / programs / taskkill / taskkill.c
blob1eff832f0ea675ccdc0c3fc0d3c91bc598db4d3a
1 /*
2 * Task termination utility
4 * Copyright 2008 Andrew Riedi
5 * Copyright 2010 Andrew Nguyen
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 #include <stdlib.h>
23 #include <windows.h>
24 #include <tlhelp32.h>
25 #include <wine/debug.h>
27 #include "taskkill.h"
29 WINE_DEFAULT_DEBUG_CHANNEL(taskkill);
31 static BOOL force_termination = FALSE;
32 static BOOL kill_child_processes;
34 static WCHAR **task_list;
35 static unsigned int task_count;
37 static struct
39 PROCESSENTRY32W p;
40 BOOL matched;
41 BOOL is_numeric;
43 *process_list;
44 static unsigned int process_count;
46 struct pid_close_info
48 DWORD pid;
49 BOOL found;
52 static int taskkill_vprintfW(const WCHAR *msg, va_list va_args)
54 int wlen;
55 DWORD count;
56 WCHAR msg_buffer[8192];
58 wlen = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, msg_buffer,
59 ARRAY_SIZE(msg_buffer), &va_args);
61 if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg_buffer, wlen, &count, NULL))
63 DWORD len;
64 char *msgA;
66 /* On Windows WriteConsoleW() fails if the output is redirected. So fall
67 * back to WriteFile() using OEM code page.
69 len = WideCharToMultiByte(GetOEMCP(), 0, msg_buffer, wlen,
70 NULL, 0, NULL, NULL);
71 msgA = malloc(len);
72 if (!msgA)
73 return 0;
75 WideCharToMultiByte(GetOEMCP(), 0, msg_buffer, wlen, msgA, len, NULL, NULL);
76 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
77 free(msgA);
80 return count;
83 static int WINAPIV taskkill_printfW(const WCHAR *msg, ...)
85 va_list va_args;
86 int len;
88 va_start(va_args, msg);
89 len = taskkill_vprintfW(msg, va_args);
90 va_end(va_args);
92 return len;
95 static int WINAPIV taskkill_message_printfW(int msg, ...)
97 va_list va_args;
98 WCHAR msg_buffer[8192];
99 int len;
101 LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
103 va_start(va_args, msg);
104 len = taskkill_vprintfW(msg_buffer, va_args);
105 va_end(va_args);
107 return len;
110 static int taskkill_message(int msg)
112 WCHAR msg_buffer[8192];
114 LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
116 return taskkill_printfW(L"%1", msg_buffer);
119 /* Post WM_CLOSE to all top-level windows belonging to the process with specified PID. */
120 static BOOL CALLBACK pid_enum_proc(HWND hwnd, LPARAM lParam)
122 struct pid_close_info *info = (struct pid_close_info *)lParam;
123 DWORD hwnd_pid;
125 GetWindowThreadProcessId(hwnd, &hwnd_pid);
127 if (hwnd_pid == info->pid)
129 PostMessageW(hwnd, WM_CLOSE, 0, 0);
130 info->found = TRUE;
133 return TRUE;
136 static BOOL enumerate_processes(void)
138 unsigned int alloc_count = 128;
139 void *realloc_list;
140 HANDLE snapshot;
142 snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
143 if (snapshot == INVALID_HANDLE_VALUE)
144 return FALSE;
146 process_list = malloc(alloc_count * sizeof(*process_list));
147 if (!process_list)
148 return FALSE;
150 process_list[0].p.dwSize = sizeof(process_list[0].p);
151 if (!Process32FirstW(snapshot, &process_list[0].p))
152 return FALSE;
156 process_list[process_count].is_numeric = FALSE;
157 process_list[process_count++].matched = FALSE;
158 if (process_count == alloc_count)
160 alloc_count *= 2;
161 realloc_list = realloc(process_list, alloc_count * sizeof(*process_list));
162 if (!realloc_list)
163 return FALSE;
164 process_list = realloc_list;
166 process_list[process_count].p.dwSize = sizeof(process_list[process_count].p);
167 } while (Process32NextW(snapshot, &process_list[process_count].p));
168 CloseHandle(snapshot);
169 return TRUE;
172 static void mark_task_process(const WCHAR *str, int *status_code)
174 DWORD self_pid = GetCurrentProcessId();
175 const WCHAR *p = str;
176 BOOL is_numeric;
177 unsigned int i;
178 DWORD pid;
180 is_numeric = TRUE;
181 while (*p)
183 if (!iswdigit(*p++))
185 is_numeric = FALSE;
186 break;
190 if (is_numeric)
192 pid = wcstol(str, NULL, 10);
193 for (i = 0; i < process_count; ++i)
195 if (process_list[i].p.th32ProcessID == pid)
196 break;
198 if (i == process_count || process_list[i].matched)
199 goto not_found;
200 process_list[i].matched = TRUE;
201 process_list[i].is_numeric = TRUE;
202 if (pid == self_pid)
204 taskkill_message(STRING_SELF_TERMINATION);
205 *status_code = 1;
207 return;
210 for (i = 0; i < process_count; ++i)
212 if (!wcsicmp(process_list[i].p.szExeFile, str) && !process_list[i].matched)
214 process_list[i].matched = TRUE;
215 if (process_list[i].p.th32ProcessID == self_pid)
217 taskkill_message(STRING_SELF_TERMINATION);
218 *status_code = 1;
220 return;
224 not_found:
225 taskkill_message_printfW(STRING_SEARCH_FAILED, str);
226 *status_code = 128;
229 static void taskkill_message_print_process(int msg, unsigned int index)
231 WCHAR pid_str[16];
233 if (!process_list[index].is_numeric)
235 taskkill_message_printfW(msg, process_list[index].p.szExeFile);
236 return;
238 wsprintfW(pid_str, L"%lu", process_list[index].p.th32ProcessID);
239 taskkill_message_printfW(msg, pid_str);
242 static BOOL find_parent(unsigned int process_index, unsigned int *parent_index)
244 DWORD parent_id = process_list[process_index].p.th32ParentProcessID;
245 unsigned int i;
247 if (!parent_id)
248 return FALSE;
250 for (i = 0; i < process_count; ++i)
252 if (process_list[i].p.th32ProcessID == parent_id)
254 *parent_index = i;
255 return TRUE;
258 return FALSE;
261 static void mark_child_processes(void)
263 unsigned int i, parent;
265 for (i = 0; i < process_count; ++i)
267 if (process_list[i].matched)
268 continue;
269 parent = i;
270 while (find_parent(parent, &parent))
272 if (process_list[parent].matched)
274 WINE_TRACE("Adding child %04lx.\n", process_list[i].p.th32ProcessID);
275 process_list[i].matched = TRUE;
276 break;
282 /* The implemented task enumeration and termination behavior does not
283 * exactly match native behavior. On Windows:
285 * In the case of terminating by process name, specifying a particular
286 * process name more times than the number of running instances causes
287 * all instances to be terminated, but termination failure messages to
288 * be printed as many times as the difference between the specification
289 * quantity and the number of running instances.
291 * Successful terminations are all listed first in order, with failing
292 * terminations being listed at the end.
294 * A PID of zero causes taskkill to warn about the inability to terminate
295 * system processes. */
296 static int send_close_messages(void)
298 const WCHAR *process_name;
299 struct pid_close_info info;
300 unsigned int i;
301 int status_code = 0;
303 for (i = 0; i < process_count; i++)
305 if (!process_list[i].matched)
306 continue;
307 info.pid = process_list[i].p.th32ProcessID;
308 process_name = process_list[i].p.szExeFile;
309 info.found = FALSE;
310 WINE_TRACE("Terminating pid %04lx.\n", info.pid);
311 EnumWindows(pid_enum_proc, (LPARAM)&info);
312 if (info.found)
314 if (kill_child_processes)
315 taskkill_message_printfW(STRING_CLOSE_CHILD, info.pid, process_list[i].p.th32ParentProcessID);
316 else if (process_list[i].is_numeric)
317 taskkill_message_printfW(STRING_CLOSE_PID_SEARCH, info.pid);
318 else
319 taskkill_message_printfW(STRING_CLOSE_PROC_SRCH, process_name, info.pid);
320 continue;
322 taskkill_message_print_process(STRING_SEARCH_FAILED, i);
323 status_code = 128;
326 return status_code;
329 static int terminate_processes(void)
331 const WCHAR *process_name;
332 unsigned int i;
333 int status_code = 0;
334 HANDLE process;
335 DWORD pid;
337 for (i = 0; i < process_count; i++)
339 if (!process_list[i].matched)
340 continue;
342 pid = process_list[i].p.th32ProcessID;
343 process_name = process_list[i].p.szExeFile;
344 process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
345 if (!process)
347 taskkill_message_print_process(STRING_SEARCH_FAILED, i);
348 status_code = 128;
349 continue;
351 if (!TerminateProcess(process, 1))
353 taskkill_message_print_process(STRING_TERMINATE_FAILED, i);
354 status_code = 1;
355 CloseHandle(process);
356 continue;
358 if (kill_child_processes)
359 taskkill_message_printfW(STRING_TERM_CHILD, pid, process_list[i].p.th32ParentProcessID);
360 else if (process_list[i].is_numeric)
361 taskkill_message_printfW(STRING_TERM_PID_SEARCH, pid);
362 else
363 taskkill_message_printfW(STRING_TERM_PROC_SEARCH, process_name, pid);
364 CloseHandle(process);
366 return status_code;
369 static BOOL add_to_task_list(WCHAR *name)
371 static unsigned int list_size = 16;
373 if (!task_list)
375 task_list = malloc(list_size * sizeof(*task_list));
376 if (!task_list)
377 return FALSE;
379 else if (task_count == list_size)
381 void *realloc_list;
383 list_size *= 2;
384 realloc_list = realloc(task_list, list_size * sizeof(*task_list));
385 if (!realloc_list)
386 return FALSE;
388 task_list = realloc_list;
391 task_list[task_count++] = name;
392 return TRUE;
395 /* FIXME Argument processing does not match behavior observed on Windows.
396 * Stringent argument counting and processing is performed, and unrecognized
397 * options are detected as parameters when placed after options that accept one. */
398 static BOOL process_arguments(int argc, WCHAR *argv[])
400 if (argc > 1)
402 int i;
403 WCHAR *argdata;
404 BOOL has_im = FALSE, has_pid = FALSE;
406 /* Only the lone help option is recognized. */
407 if (argc == 2)
409 argdata = argv[1];
410 if ((*argdata == '/' || *argdata == '-') && !lstrcmpW(L"?", argdata + 1))
412 taskkill_message(STRING_USAGE);
413 exit(0);
417 for (i = 1; i < argc; i++)
419 BOOL got_im = FALSE, got_pid = FALSE;
421 argdata = argv[i];
422 if (*argdata != '/' && *argdata != '-')
423 goto invalid;
424 argdata++;
426 if (!wcsicmp(L"t", argdata))
427 kill_child_processes = TRUE;
428 else if (!wcsicmp(L"f", argdata))
429 force_termination = TRUE;
430 /* Options /IM and /PID appear to behave identically, except for
431 * the fact that they cannot be specified at the same time. */
432 else if ((got_im = !wcsicmp(L"im", argdata)) ||
433 (got_pid = !wcsicmp(L"pid", argdata)))
435 if (!argv[i + 1])
437 taskkill_message_printfW(STRING_MISSING_PARAM, argv[i]);
438 taskkill_message(STRING_USAGE);
439 return FALSE;
442 if (got_im) has_im = TRUE;
443 if (got_pid) has_pid = TRUE;
445 if (has_im && has_pid)
447 taskkill_message(STRING_MUTUAL_EXCLUSIVE);
448 taskkill_message(STRING_USAGE);
449 return FALSE;
452 if (!add_to_task_list(argv[i + 1]))
453 return FALSE;
454 i++;
456 else
458 invalid:
459 taskkill_message(STRING_INVALID_OPTION);
460 taskkill_message(STRING_USAGE);
461 return FALSE;
465 else
467 taskkill_message(STRING_MISSING_OPTION);
468 taskkill_message(STRING_USAGE);
469 return FALSE;
472 return TRUE;
475 int __cdecl wmain(int argc, WCHAR *argv[])
477 int search_status = 0, terminate_status;
478 unsigned int i;
480 if (!process_arguments(argc, argv))
481 return 1;
483 if (!enumerate_processes())
485 taskkill_message(STRING_ENUM_FAILED);
486 return 1;
489 for (i = 0; i < task_count; ++i)
490 mark_task_process(task_list[i], &search_status);
491 if (kill_child_processes)
492 mark_child_processes();
493 if (force_termination)
494 terminate_status = terminate_processes();
495 else
496 terminate_status = send_close_messages();
497 return search_status ? search_status : terminate_status;