Reactos
at master 858 lines 24 kB view raw
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}