Reactos
1/*
2 * Task termination utility
3 *
4 * Copyright 2008 Andrew Riedi
5 * Copyright 2010 Andrew Nguyen
6 * Copyright 2020 He Yang
7 * Copyright 2025 Hermès Bélusca-Maïto
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
18 *
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22 */
23
24#include <stdlib.h>
25#include <windows.h>
26#include <tlhelp32.h>
27#include <wine/debug.h>
28
29#ifdef __REACTOS__
30#define wcsicmp _wcsicmp
31#endif
32
33#include "taskkill.h"
34
35WINE_DEFAULT_DEBUG_CHANNEL(taskkill);
36
37static BOOL force_termination = FALSE;
38static BOOL kill_child_processes = FALSE;
39
40static WCHAR **task_list;
41static unsigned int task_count;
42
43static struct
44{
45 PROCESSENTRY32W p;
46 BOOL matched;
47 BOOL is_numeric;
48}
49*process_list;
50static unsigned int process_count;
51
52struct pid_close_info
53{
54 DWORD pid;
55 BOOL found;
56};
57
58#ifdef __REACTOS__
59unsigned int* pkill_list;
60DWORD pkill_size;
61DWORD self_pid;
62#endif
63
64#ifdef __REACTOS__
65
66static PWCHAR opList[] = {L"f", L"im", L"pid", L"?", L"t"};
67
68#define OP_PARAM_INVALID -1
69
70#define OP_PARAM_FORCE_TERMINATE 0
71#define OP_PARAM_IMAGE 1
72#define OP_PARAM_PID 2
73#define OP_PARAM_HELP 3
74#define OP_PARAM_TERMINATE_CHILD 4
75
76#endif // __REACTOS__
77
78static int taskkill_vprintfW(const WCHAR *msg, va_list va_args)
79{
80 int wlen;
81 DWORD count;
82 WCHAR msg_buffer[8192];
83
84 wlen = FormatMessageW(FORMAT_MESSAGE_FROM_STRING, msg, 0, 0, msg_buffer,
85 ARRAY_SIZE(msg_buffer), &va_args);
86
87 if (!WriteConsoleW(GetStdHandle(STD_OUTPUT_HANDLE), msg_buffer, wlen, &count, NULL))
88 {
89 DWORD len;
90 char *msgA;
91
92 /* On Windows WriteConsoleW() fails if the output is redirected. So fall
93 * back to WriteFile() using OEM code page.
94 */
95 len = WideCharToMultiByte(GetOEMCP(), 0, msg_buffer, wlen,
96 NULL, 0, NULL, NULL);
97 msgA = malloc(len);
98 if (!msgA)
99 return 0;
100
101 WideCharToMultiByte(GetOEMCP(), 0, msg_buffer, wlen, msgA, len, NULL, NULL);
102 WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msgA, len, &count, FALSE);
103 free(msgA);
104 }
105
106 return count;
107}
108
109static int WINAPIV taskkill_printfW(const WCHAR *msg, ...)
110{
111 va_list va_args;
112 int len;
113
114 va_start(va_args, msg);
115 len = taskkill_vprintfW(msg, va_args);
116 va_end(va_args);
117
118 return len;
119}
120
121static int WINAPIV taskkill_message_printfW(int msg, ...)
122{
123 va_list va_args;
124 WCHAR msg_buffer[8192];
125 int len;
126
127 LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
128
129 va_start(va_args, msg);
130 len = taskkill_vprintfW(msg_buffer, va_args);
131 va_end(va_args);
132
133 return len;
134}
135
136static int taskkill_message(int msg)
137{
138 WCHAR msg_buffer[8192];
139
140 LoadStringW(GetModuleHandleW(NULL), msg, msg_buffer, ARRAY_SIZE(msg_buffer));
141
142 return taskkill_printfW(L"%1", msg_buffer);
143}
144
145/* Post WM_CLOSE to all top-level windows belonging to the process with specified PID. */
146static BOOL CALLBACK pid_enum_proc(HWND hwnd, LPARAM lParam)
147{
148 struct pid_close_info *info = (struct pid_close_info *)lParam;
149 DWORD hwnd_pid;
150
151 GetWindowThreadProcessId(hwnd, &hwnd_pid);
152
153 if (hwnd_pid == info->pid)
154 {
155 PostMessageW(hwnd, WM_CLOSE, 0, 0);
156 info->found = TRUE;
157 }
158
159 return TRUE;
160}
161
162static BOOL enumerate_processes(void)
163{
164 unsigned int alloc_count = 128;
165 void *realloc_list;
166 HANDLE snapshot;
167
168 snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
169 if (snapshot == INVALID_HANDLE_VALUE)
170 return FALSE;
171
172 process_list = malloc(alloc_count * sizeof(*process_list));
173 if (!process_list)
174 return FALSE;
175
176 process_list[0].p.dwSize = sizeof(process_list[0].p);
177 if (!Process32FirstW(snapshot, &process_list[0].p))
178 return FALSE;
179
180 do
181 {
182 process_list[process_count].is_numeric = FALSE;
183 process_list[process_count++].matched = FALSE;
184 if (process_count == alloc_count)
185 {
186 alloc_count *= 2;
187 realloc_list = realloc(process_list, alloc_count * sizeof(*process_list));
188 if (!realloc_list)
189 return FALSE;
190 process_list = realloc_list;
191 }
192 process_list[process_count].p.dwSize = sizeof(process_list[process_count].p);
193 } while (Process32NextW(snapshot, &process_list[process_count].p));
194 CloseHandle(snapshot);
195 return TRUE;
196}
197
198static void mark_task_process(const WCHAR *str, int *status_code)
199{
200#ifndef __REACTOS__
201 DWORD self_pid = GetCurrentProcessId();
202#endif
203 const WCHAR *p = str;
204 BOOL is_numeric;
205 unsigned int i;
206 DWORD pid;
207
208 is_numeric = TRUE;
209 while (*p)
210 {
211 if (!iswdigit(*p++))
212 {
213 is_numeric = FALSE;
214 break;
215 }
216 }
217
218 if (is_numeric)
219 {
220 pid = wcstol(str, NULL, 10);
221 for (i = 0; i < process_count; ++i)
222 {
223 if (process_list[i].p.th32ProcessID == pid)
224 break;
225 }
226 if (i == process_count || process_list[i].matched)
227 goto not_found;
228 process_list[i].matched = TRUE;
229 process_list[i].is_numeric = TRUE;
230 if (pid == self_pid)
231 {
232 taskkill_message(STRING_SELF_TERMINATION);
233 *status_code = 1;
234 }
235#ifdef __REACTOS__
236 else
237 {
238 pkill_list[pkill_size++] = i;
239 }
240#endif
241 return;
242 }
243
244 for (i = 0; i < process_count; ++i)
245 {
246 if (!wcsicmp(process_list[i].p.szExeFile, str) && !process_list[i].matched)
247 {
248 process_list[i].matched = TRUE;
249 if (process_list[i].p.th32ProcessID == self_pid)
250 {
251 taskkill_message(STRING_SELF_TERMINATION);
252 *status_code = 1;
253 }
254#ifdef __REACTOS__
255 else
256 {
257 pkill_list[pkill_size++] = i;
258 continue;
259 }
260#endif
261 return;
262 }
263 }
264
265#ifdef __REACTOS__
266 // Cannot find any process matching the PID or name
267 if (pkill_size == 0)
268 {
269#endif
270not_found:
271 taskkill_message_printfW(STRING_SEARCH_FAILED, str);
272 *status_code = 128;
273#ifdef __REACTOS__
274 }
275#endif
276}
277
278static void taskkill_message_print_process(int msg, unsigned int index)
279{
280 WCHAR pid_str[16];
281
282 if (!process_list[index].is_numeric)
283 {
284 taskkill_message_printfW(msg, process_list[index].p.szExeFile);
285 return;
286 }
287 wsprintfW(pid_str, L"%lu", process_list[index].p.th32ProcessID);
288 taskkill_message_printfW(msg, pid_str);
289}
290
291#ifndef __REACTOS__
292/*
293 * Below is the Wine method of terminating child processes.
294 * Its problem is that it doesn't terminate them in either a parent-to-children
295 * or children-to-parent relationship, but instead in the order in which they
296 * appear in the process list. This differs from Windows' (or ReactOS) method.
297 * Wine's termination ordering can cause problems in scenarii where e.g. a
298 * parent process could re-spawn killed children processes, or, where it is
299 * of interest to kill the parent process first and then its children.
300 *
301 * NOTE: The following two functions implicitly assume that the process list
302 * obtained from the system, is such that any child process P[j] of a given
303 * parent process P[i] is enumerated *AFTER* its parent (i.e. i < j).
304 *
305 * Because of these facts, the ReactOS method is employed instead.
306 * Note however that the Wine method (below) has been adapted for
307 * ease of usage and comparison with that of ReactOS.
308 */
309
310static BOOL find_parent(unsigned int process_index, unsigned int *parent_index)
311{
312 DWORD parent_id = process_list[process_index].p.th32ParentProcessID;
313 unsigned int i;
314
315 if (!parent_id)
316 return FALSE;
317
318 for (i = 0; i < process_count; ++i)
319 {
320 if (process_list[i].p.th32ProcessID == parent_id)
321 {
322 *parent_index = i;
323 return TRUE;
324 }
325 }
326 return FALSE;
327}
328
329static void mark_child_processes(void)
330{
331 unsigned int i, parent;
332
333 for (i = 0; i < process_count; ++i)
334 {
335 if (process_list[i].matched)
336 continue;
337#ifdef __REACTOS__
338 // Prevent self-termination if we are in the process tree
339 if (process_list[i].p.th32ProcessID == self_pid)
340 continue;
341#endif
342 parent = i;
343 while (find_parent(parent, &parent))
344 {
345 if (process_list[parent].matched)
346 {
347 WINE_TRACE("Adding child %04lx.\n", process_list[i].p.th32ProcessID);
348 process_list[i].matched = TRUE;
349#ifdef __REACTOS__
350 pkill_list[pkill_size++] = i;
351#endif
352 break;
353 }
354 }
355 }
356}
357
358#endif // !__REACTOS__
359
360/* The implemented task enumeration and termination behavior does not
361 * exactly match native behavior. On Windows:
362 *
363 * In the case of terminating by process name, specifying a particular
364 * process name more times than the number of running instances causes
365 * all instances to be terminated, but termination failure messages to
366 * be printed as many times as the difference between the specification
367 * quantity and the number of running instances.
368 *
369 * Successful terminations are all listed first in order, with failing
370 * terminations being listed at the end.
371 *
372 * A PID of zero causes taskkill to warn about the inability to terminate
373 * system processes. */
374
375#ifdef __REACTOS__
376
377static BOOL get_pid_creation_time(DWORD pid, FILETIME *time)
378{
379 HANDLE process;
380 FILETIME t1, t2, t3;
381 BOOL success;
382
383 process = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid);
384 if (!process)
385 return FALSE;
386
387 success = GetProcessTimes(process, time, &t1, &t2, &t3);
388 CloseHandle(process);
389
390 return success;
391}
392
393static void queue_children(DWORD ppid)
394{
395 FILETIME parent_creation_time;
396 unsigned int i;
397
398 if (!get_pid_creation_time(ppid, &parent_creation_time))
399 return;
400
401 for (i = 0; i < process_count; ++i)
402 {
403 FILETIME child_creation_time;
404 DWORD pid;
405
406 // Ignore processes already marked for termination
407 if (process_list[i].matched)
408 continue;
409
410 // Prevent self-termination if we are in the process tree
411 pid = process_list[i].p.th32ProcessID;
412 if (pid == self_pid)
413 continue;
414
415 if (!get_pid_creation_time(pid, &child_creation_time))
416 continue;
417
418 // Compare creation time to avoid PID reuse cases
419 if (process_list[i].p.th32ParentProcessID == ppid &&
420 CompareFileTime(&parent_creation_time, &child_creation_time) < 0)
421 {
422 // Process marked for termination
423 WINE_TRACE("Adding child %04lx.\n", pid);
424 process_list[i].matched = TRUE;
425 pkill_list[pkill_size++] = i;
426 }
427 }
428}
429
430/*
431 * Based on the root processes to terminate, we perform a level order traversal
432 * (Breadth First Search) of the corresponding process trees, building the list
433 * of all processes and children to terminate,
434 * This allows terminating the processes, starting from parents down to their
435 * children. Note that this is in the reverse order than what Windows' taskkill
436 * does. The reason why we chose to do the reverse, is because there exist
437 * (parent) processes that detect whether their children are terminated, and
438 * if so, attempt to restart their terminated children. We want to avoid this
439 * scenario in order to ensure no extra processes get started, while the user
440 * wanted to terminate them.
441 */
442static void mark_child_processes(void)
443{
444 /*
445 * The temporary FIFO queue for BFS (starting empty), is embedded
446 * inside the result list. The queue resides between the [front, end)
447 * indices (if front == end, the queue is empty), and moves down
448 * through the result list, generating the sought values in order.
449 */
450 unsigned int front = 0; // end = 0; given by pkill_size
451
452 /* The root processes have already been
453 * enqueued in pkill_list[0..pkill_size] */
454
455 /* Now, find and enqueue the children processes */
456 while (pkill_size - front > 0)
457 {
458 /* Begin search at the next level */
459 unsigned int len = pkill_size - front;
460 unsigned int i;
461 for (i = 0; i < len; ++i)
462 {
463 /* Standard BFS would pop the element from the front of
464 * the queue and push it to the end of the result list.
465 * In our case, everything is already correctly positioned
466 * so that we can just simply emulate queue front popping. */
467 DWORD pid = process_list[pkill_list[front++]].p.th32ProcessID;
468
469 /* Enqueue the children processes */
470 queue_children(pid); // Updates pkill_size accordingly.
471 }
472 }
473}
474
475#endif // __REACTOS__
476
477static int send_close_messages(void)
478{
479 const WCHAR *process_name;
480 struct pid_close_info info;
481 unsigned int i;
482 int status_code = 0;
483
484#ifdef __REACTOS__
485 DWORD index;
486 for (index = 0; index < pkill_size; ++index)
487#else
488 for (i = 0; i < process_count; i++)
489#endif
490 {
491#ifdef __REACTOS__
492 i = pkill_list[index];
493#else
494 if (!process_list[i].matched)
495 continue;
496#endif
497
498 info.pid = process_list[i].p.th32ProcessID;
499 process_name = process_list[i].p.szExeFile;
500 info.found = FALSE;
501 WINE_TRACE("Terminating pid %04lx.\n", info.pid);
502 EnumWindows(pid_enum_proc, (LPARAM)&info);
503 if (info.found)
504 {
505 if (kill_child_processes)
506 taskkill_message_printfW(STRING_CLOSE_CHILD, info.pid, process_list[i].p.th32ParentProcessID);
507 else if (process_list[i].is_numeric)
508 taskkill_message_printfW(STRING_CLOSE_PID_SEARCH, info.pid);
509 else
510 taskkill_message_printfW(STRING_CLOSE_PROC_SRCH, process_name, info.pid);
511 continue;
512 }
513 taskkill_message_print_process(STRING_SEARCH_FAILED, i);
514 status_code = 128;
515 }
516
517 return status_code;
518}
519
520static int terminate_processes(void)
521{
522 const WCHAR *process_name;
523 unsigned int i;
524 int status_code = 0;
525 HANDLE process;
526 DWORD pid;
527
528#ifdef __REACTOS__
529 DWORD index;
530 for (index = 0; index < pkill_size; ++index)
531#else
532 for (i = 0; i < process_count; i++)
533#endif
534 {
535#ifdef __REACTOS__
536 i = pkill_list[index];
537#else
538 if (!process_list[i].matched)
539 continue;
540#endif
541
542 pid = process_list[i].p.th32ProcessID;
543 process_name = process_list[i].p.szExeFile;
544 process = OpenProcess(PROCESS_TERMINATE, FALSE, pid);
545 if (!process)
546 {
547 taskkill_message_print_process(STRING_SEARCH_FAILED, i);
548 status_code = 128;
549 continue;
550 }
551 if (!TerminateProcess(process, 1))
552 {
553#ifdef __REACTOS__
554 if (kill_child_processes)
555 taskkill_message_printfW(STRING_TERM_CHILD_FAILED, pid, process_list[i].p.th32ParentProcessID);
556 else
557#endif
558 taskkill_message_print_process(STRING_TERMINATE_FAILED, i);
559 status_code = 1;
560 CloseHandle(process);
561 continue;
562 }
563 if (kill_child_processes)
564 taskkill_message_printfW(STRING_TERM_CHILD, pid, process_list[i].p.th32ParentProcessID);
565 else if (process_list[i].is_numeric)
566 taskkill_message_printfW(STRING_TERM_PID_SEARCH, pid);
567 else
568 taskkill_message_printfW(STRING_TERM_PROC_SEARCH, process_name, pid);
569 CloseHandle(process);
570 }
571 return status_code;
572}
573
574static BOOL add_to_task_list(WCHAR *name)
575{
576 static unsigned int list_size = 16;
577
578 if (!task_list)
579 {
580 task_list = malloc(list_size * sizeof(*task_list));
581 if (!task_list)
582 return FALSE;
583 }
584 else if (task_count == list_size)
585 {
586 void *realloc_list;
587
588 list_size *= 2;
589 realloc_list = realloc(task_list, list_size * sizeof(*task_list));
590 if (!realloc_list)
591 return FALSE;
592
593 task_list = realloc_list;
594 }
595
596 task_list[task_count++] = name;
597 return TRUE;
598}
599
600#ifdef __REACTOS__
601
602static int get_argument_type(WCHAR* argument)
603{
604 int i;
605
606 if (argument[0] != L'/' && argument[0] != L'-')
607 {
608 return OP_PARAM_INVALID;
609 }
610 argument++;
611
612 for (i = 0; i < _countof(opList); i++)
613 {
614 if (!wcsicmp(opList[i], argument))
615 {
616 return i;
617 }
618 }
619 return OP_PARAM_INVALID;
620}
621
622static BOOL process_arguments(int argc, WCHAR* argv[])
623{
624 BOOL has_im = FALSE, has_pid = FALSE, has_help = FALSE;
625
626 if (argc > 1)
627 {
628 int i;
629 for (i = 1; i < argc; i++)
630 {
631 int argument = get_argument_type(argv[i]);
632
633 switch (argument)
634 {
635 case OP_PARAM_FORCE_TERMINATE:
636 {
637 if (force_termination)
638 {
639 // -f already specified
640 taskkill_message_printfW(STRING_PARAM_TOO_MUCH, argv[i], 1);
641 taskkill_message(STRING_USAGE);
642 return FALSE;
643 }
644 force_termination = TRUE;
645 break;
646 }
647 case OP_PARAM_IMAGE:
648 case OP_PARAM_PID:
649 {
650 if (!argv[i + 1])
651 {
652 taskkill_message_printfW(STRING_MISSING_PARAM, argv[i]);
653 taskkill_message(STRING_USAGE);
654 return FALSE;
655 }
656
657 if (argument == OP_PARAM_IMAGE)
658 has_im = TRUE;
659 if (argument == OP_PARAM_PID)
660 has_pid = TRUE;
661
662 if (has_im && has_pid)
663 {
664 taskkill_message(STRING_MUTUAL_EXCLUSIVE);
665 taskkill_message(STRING_USAGE);
666 return FALSE;
667 }
668
669 if (get_argument_type(argv[i + 1]) != OP_PARAM_INVALID)
670 {
671 taskkill_message_printfW(STRING_MISSING_PARAM, argv[i]);
672 taskkill_message(STRING_USAGE);
673 return FALSE;
674 }
675
676 if (!add_to_task_list(argv[++i])) // add next parameters to task_list
677 return FALSE;
678
679 break;
680 }
681 case OP_PARAM_HELP:
682 {
683 if (has_help)
684 {
685 // -? already specified
686 taskkill_message_printfW(STRING_PARAM_TOO_MUCH, argv[i], 1);
687 taskkill_message(STRING_USAGE);
688 return FALSE;
689 }
690 has_help = TRUE;
691 break;
692 }
693 case OP_PARAM_TERMINATE_CHILD:
694 {
695 if (kill_child_processes)
696 {
697 // -t already specified
698 taskkill_message_printfW(STRING_PARAM_TOO_MUCH, argv[i], 1);
699 taskkill_message(STRING_USAGE);
700 return FALSE;
701 }
702 kill_child_processes = TRUE;
703 break;
704 }
705 case OP_PARAM_INVALID:
706 default:
707 {
708 taskkill_message(STRING_INVALID_OPTION);
709 taskkill_message(STRING_USAGE);
710 return FALSE;
711 }
712 }
713 }
714 }
715
716 if (has_help)
717 {
718 if (argc > 2) // any parameters other than -? is specified
719 {
720 taskkill_message(STRING_INVALID_SYNTAX);
721 taskkill_message(STRING_USAGE);
722 return FALSE;
723 }
724 else
725 {
726 taskkill_message(STRING_USAGE);
727 exit(0);
728 }
729 }
730 else if (!has_im && !has_pid) // has_help == FALSE
731 {
732 // both has_im and has_pid are missing (maybe -fi option is missing too, if implemented later)
733 taskkill_message(STRING_MISSING_OPTION);
734 taskkill_message(STRING_USAGE);
735 return FALSE;
736 }
737
738 return TRUE;
739}
740
741#else
742
743/* FIXME Argument processing does not match behavior observed on Windows.
744 * Stringent argument counting and processing is performed, and unrecognized
745 * options are detected as parameters when placed after options that accept one. */
746static BOOL process_arguments(int argc, WCHAR *argv[])
747{
748 if (argc > 1)
749 {
750 int i;
751 WCHAR *argdata;
752 BOOL has_im = FALSE, has_pid = FALSE;
753
754 /* Only the lone help option is recognized. */
755 if (argc == 2)
756 {
757 argdata = argv[1];
758 if ((*argdata == '/' || *argdata == '-') && !lstrcmpW(L"?", argdata + 1))
759 {
760 taskkill_message(STRING_USAGE);
761 exit(0);
762 }
763 }
764
765 for (i = 1; i < argc; i++)
766 {
767 BOOL got_im = FALSE, got_pid = FALSE;
768
769 argdata = argv[i];
770 if (*argdata != '/' && *argdata != '-')
771 goto invalid;
772 argdata++;
773
774 if (!wcsicmp(L"t", argdata))
775 kill_child_processes = TRUE;
776 else if (!wcsicmp(L"f", argdata))
777 force_termination = TRUE;
778 /* Options /IM and /PID appear to behave identically, except for
779 * the fact that they cannot be specified at the same time. */
780 else if ((got_im = !wcsicmp(L"im", argdata)) ||
781 (got_pid = !wcsicmp(L"pid", argdata)))
782 {
783 if (!argv[i + 1])
784 {
785 taskkill_message_printfW(STRING_MISSING_PARAM, argv[i]);
786 taskkill_message(STRING_USAGE);
787 return FALSE;
788 }
789
790 if (got_im) has_im = TRUE;
791 if (got_pid) has_pid = TRUE;
792
793 if (has_im && has_pid)
794 {
795 taskkill_message(STRING_MUTUAL_EXCLUSIVE);
796 taskkill_message(STRING_USAGE);
797 return FALSE;
798 }
799
800 if (!add_to_task_list(argv[i + 1]))
801 return FALSE;
802 i++;
803 }
804 else
805 {
806 invalid:
807 taskkill_message(STRING_INVALID_OPTION);
808 taskkill_message(STRING_USAGE);
809 return FALSE;
810 }
811 }
812 }
813 else
814 {
815 taskkill_message(STRING_MISSING_OPTION);
816 taskkill_message(STRING_USAGE);
817 return FALSE;
818 }
819
820 return TRUE;
821}
822
823#endif // __REACTOS__
824
825int wmain(int argc, WCHAR *argv[])
826{
827 int search_status = 0, terminate_status;
828 unsigned int i;
829
830 if (!process_arguments(argc, argv))
831 return 1;
832
833 if (!enumerate_processes())
834 {
835 taskkill_message(STRING_ENUM_FAILED);
836 return 1;
837 }
838
839#ifdef __REACTOS__
840 pkill_list = malloc(process_count * sizeof(unsigned int*));
841 if (!pkill_list)
842 return 1;
843 memset(pkill_list, 0, process_count * sizeof(unsigned int*));
844 pkill_size = 0;
845
846 self_pid = GetCurrentProcessId();
847#endif
848
849 for (i = 0; i < task_count; ++i)
850 mark_task_process(task_list[i], &search_status);
851 if (kill_child_processes)
852 mark_child_processes();
853 if (force_termination)
854 terminate_status = terminate_processes();
855 else
856 terminate_status = send_close_messages();
857 return search_status ? search_status : terminate_status;
858}