Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

788 lines
22 KiB

  1. #include "stdafx.h"
  2. #include <windows.h>
  3. #include <winuserp.h>
  4. #include <winperf.h>
  5. #include <stdio.h>
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include "kill.h"
  9. PUCHAR g_CommonLargeBuffer = NULL;
  10. ULONG g_CommonLargeBufferSize = 64*1024;
  11. DWORD
  12. GetTaskListEx(
  13. PTASK_LIST pTask,
  14. DWORD dwNumTasks,
  15. BOOL fThreadInfo,
  16. DWORD dwNumServices,
  17. const _ENUM_SERVICE_STATUS_PROCESSA* pServiceInfo
  18. )
  19. /*++
  20. Routine Description:
  21. Provides an API for getting a list of tasks running at the time of the
  22. API call. This function uses internal NT apis and data structures. This
  23. api is MUCH faster that the non-internal version that uses the registry.
  24. Arguments:
  25. pTask - Array of TASK_LIST structures to fill.
  26. dwNumTasks - Maximum number of tasks that the pTask array can hold.
  27. fThreadInfo - TRUE if thread information is desired.
  28. dwNumServices - Maximum number of entries in pServiceInfo.
  29. pServiceInfo - Array of service status structures to reference
  30. for supporting services in processes.
  31. Return Value:
  32. Number of tasks placed into the pTask array.
  33. --*/
  34. {
  35. #ifndef _CHICAGO_
  36. PSYSTEM_PROCESS_INFORMATION ProcessInfo = NULL;
  37. NTSTATUS status;
  38. ANSI_STRING pname;
  39. PCHAR p = NULL;
  40. ULONG TotalOffset;
  41. ULONG totalTasks = 0;
  42. retry:
  43. if (g_CommonLargeBuffer == NULL)
  44. {
  45. g_CommonLargeBuffer = (PUCHAR) VirtualAlloc(NULL,g_CommonLargeBufferSize,MEM_COMMIT,PAGE_READWRITE);
  46. if (g_CommonLargeBuffer == NULL)
  47. {
  48. return 0;
  49. }
  50. }
  51. status = NtQuerySystemInformation(SystemProcessInformation,g_CommonLargeBuffer,g_CommonLargeBufferSize,NULL);
  52. if (status == STATUS_INFO_LENGTH_MISMATCH)
  53. {
  54. g_CommonLargeBufferSize += 8192;
  55. VirtualFree (g_CommonLargeBuffer, 0, MEM_RELEASE);
  56. g_CommonLargeBuffer = NULL;
  57. goto retry;
  58. }
  59. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) g_CommonLargeBuffer;
  60. TotalOffset = 0;
  61. while (TRUE)
  62. {
  63. pname.Buffer = NULL;
  64. if ( ProcessInfo->ImageName.Buffer )
  65. {
  66. RtlUnicodeStringToAnsiString(&pname,(PUNICODE_STRING)&ProcessInfo->ImageName,TRUE);
  67. if (pname.Buffer)
  68. {
  69. p = strrchr(pname.Buffer,'\\');
  70. if ( p )
  71. {
  72. p++;
  73. }
  74. else
  75. {
  76. p = pname.Buffer;
  77. }
  78. }
  79. else
  80. {
  81. p = "";
  82. }
  83. }
  84. else
  85. {
  86. p = "System Process";
  87. }
  88. strncpy( pTask->ProcessName, p, PROCESS_SIZE );
  89. pTask->ProcessName[ PROCESS_SIZE - 1 ] = '\0';
  90. pTask->flags = 0;
  91. pTask->dwProcessId = (DWORD)(DWORD_PTR)ProcessInfo->UniqueProcessId;
  92. pTask->dwInheritedFromProcessId = (DWORD)(DWORD_PTR)ProcessInfo->InheritedFromUniqueProcessId;
  93. pTask->CreateTime.QuadPart = (ULONGLONG)ProcessInfo->CreateTime.QuadPart;
  94. pTask->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
  95. pTask->VirtualSize = ProcessInfo->VirtualSize;
  96. pTask->PageFaultCount = ProcessInfo->PageFaultCount;
  97. pTask->PeakWorkingSetSize = ProcessInfo->PeakWorkingSetSize;
  98. pTask->WorkingSetSize = ProcessInfo->WorkingSetSize;
  99. pTask->NumberOfThreads = ProcessInfo->NumberOfThreads;
  100. if (fThreadInfo)
  101. {
  102. pTask->pThreadInfo = (PTHREAD_INFO) malloc(pTask->NumberOfThreads * sizeof(THREAD_INFO));
  103. if ( pTask->pThreadInfo ) {
  104. UINT nThread = pTask->NumberOfThreads;
  105. PTHREAD_INFO pThreadInfo = pTask->pThreadInfo;
  106. PSYSTEM_THREAD_INFORMATION pSysThreadInfo =
  107. (PSYSTEM_THREAD_INFORMATION)(ProcessInfo + 1);
  108. while (nThread--) {
  109. pThreadInfo->ThreadState = pSysThreadInfo->ThreadState;
  110. pThreadInfo->UniqueThread = pSysThreadInfo->ClientId.UniqueThread;
  111. pThreadInfo++;
  112. pSysThreadInfo++;
  113. }
  114. }
  115. }
  116. else
  117. {
  118. pTask->pThreadInfo = NULL;
  119. }
  120. // Initialize the ServiceNames if this task hosts any.
  121. //
  122. *pTask->ServiceNames = 0;
  123. if (dwNumServices)
  124. {
  125. // For each service with this process id, append it's service
  126. // name to the buffer. Separate each with a comma.
  127. //
  128. BOOL fFirstTime = TRUE;
  129. DWORD iSvc;
  130. size_t cchRemain = SERVICENAMES_SIZE - 1;
  131. size_t cch;
  132. for (iSvc = 0; iSvc < dwNumServices; iSvc++) {
  133. if (pTask->dwProcessId == pServiceInfo[iSvc].ServiceStatusProcess.dwProcessId) {
  134. cch = strlen(pServiceInfo[iSvc].lpServiceName);
  135. if (fFirstTime) {
  136. fFirstTime = FALSE;
  137. strncpy(
  138. pTask->ServiceNames,
  139. pServiceInfo[iSvc].lpServiceName,
  140. cchRemain);
  141. // strncpy may not terminate the string if
  142. // cchRemain <= cch so we do it regardless.
  143. //
  144. pTask->ServiceNames[SERVICENAMES_SIZE - 1] = '\0';
  145. } else if (cchRemain > 1) { // ensure room for the comma
  146. strncat(
  147. pTask->ServiceNames,
  148. ",",
  149. cchRemain--);
  150. strncat(
  151. pTask->ServiceNames,
  152. pServiceInfo[iSvc].lpServiceName,
  153. cchRemain);
  154. }
  155. // Counts are unsigned so we have to check before
  156. // subtracting.
  157. //
  158. if (cchRemain < cch) {
  159. // No more room for any more.
  160. break;
  161. } else {
  162. cchRemain -= cch;
  163. }
  164. }
  165. }
  166. }
  167. pTask++;
  168. totalTasks++;
  169. if (totalTasks >= dwNumTasks)
  170. {
  171. break;
  172. }
  173. if (ProcessInfo->NextEntryOffset == 0)
  174. {
  175. break;
  176. }
  177. TotalOffset += ProcessInfo->NextEntryOffset;
  178. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)&g_CommonLargeBuffer[TotalOffset];
  179. }
  180. return totalTasks;
  181. #else
  182. return 0;
  183. #endif
  184. }
  185. DWORD
  186. GetTaskList(
  187. PTASK_LIST pTask,
  188. DWORD dwNumTasks
  189. )
  190. {
  191. return GetTaskListEx(pTask, dwNumTasks, FALSE, 0, NULL);
  192. }
  193. void FreeTaskListMem(void)
  194. {
  195. if (g_CommonLargeBuffer)
  196. {
  197. VirtualFree (g_CommonLargeBuffer, 0, MEM_RELEASE);
  198. g_CommonLargeBuffer = NULL;
  199. }
  200. return;
  201. }
  202. BOOL DetectOrphans(PTASK_LIST pTask,DWORD dwNumTasks)
  203. {
  204. DWORD i, j;
  205. BOOL Result = FALSE;
  206. for (i=0; i<dwNumTasks; i++) {
  207. if (pTask[i].dwInheritedFromProcessId != 0) {
  208. for (j=0; j<dwNumTasks; j++) {
  209. if (i != j && pTask[i].dwInheritedFromProcessId == pTask[j].dwProcessId) {
  210. if (pTask[i].CreateTime.QuadPart <= pTask[j].CreateTime.QuadPart) {
  211. pTask[i].dwInheritedFromProcessId = 0;
  212. Result = TRUE;
  213. }
  214. break;
  215. }
  216. }
  217. }
  218. }
  219. return Result;
  220. }
  221. /*++
  222. Routine Description:
  223. Changes the tlist process's privilige so that kill works properly.
  224. Return Value:
  225. TRUE - success
  226. FALSE - failure
  227. --*/
  228. BOOL EnableDebugPriv(VOID)
  229. {
  230. HANDLE hToken;
  231. LUID DebugValue;
  232. TOKEN_PRIVILEGES tkp;
  233. //
  234. // Retrieve a handle of the access token
  235. //
  236. if (!OpenProcessToken(GetCurrentProcess(),
  237. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  238. &hToken)) {
  239. //printf("OpenProcessToken failed with %d\n", GetLastError());
  240. return FALSE;
  241. }
  242. //
  243. // Enable the SE_DEBUG_NAME privilege or disable
  244. // all privileges, depending on the fEnable flag.
  245. //
  246. //if (!LookupPrivilegeValue((LPSTR) NULL,SE_DEBUG_NAME,&DebugValue))
  247. if (!LookupPrivilegeValueA((LPSTR) NULL,"SeDebugPrivilege",&DebugValue))
  248. {
  249. //printf("LookupPrivilegeValue failed with %d\n", GetLastError());
  250. return FALSE;
  251. }
  252. tkp.PrivilegeCount = 1;
  253. tkp.Privileges[0].Luid = DebugValue;
  254. tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  255. if (!AdjustTokenPrivileges(
  256. hToken,
  257. FALSE,
  258. &tkp,
  259. sizeof(TOKEN_PRIVILEGES),
  260. (PTOKEN_PRIVILEGES) NULL,
  261. (PDWORD) NULL)) {
  262. //
  263. // The return value of AdjustTokenPrivileges be texted
  264. //
  265. //printf("AdjustTokenPrivileges failed with %d\n", GetLastError());
  266. return FALSE;
  267. }
  268. return TRUE;
  269. }
  270. BOOL KillProcess(PTASK_LIST tlist,BOOL fForce)
  271. {
  272. HANDLE hProcess1 = NULL;
  273. HANDLE hProcess2 = NULL;
  274. HDESK hdeskSave = NULL;
  275. HDESK hdesk = NULL;
  276. HWINSTA hwinsta = NULL;
  277. HWINSTA hwinstaSave = NULL;
  278. if (fForce || !tlist->hwnd) {
  279. hProcess1 = OpenProcess( PROCESS_ALL_ACCESS, FALSE, (DWORD) (DWORD_PTR) tlist->dwProcessId );
  280. if (hProcess1)
  281. {
  282. hProcess2 = OpenProcess( PROCESS_ALL_ACCESS, FALSE, (DWORD) (DWORD_PTR) tlist->dwProcessId );
  283. if (hProcess2 == NULL)
  284. {
  285. // clean up memory already allocated
  286. CloseHandle( hProcess1 );
  287. return FALSE;
  288. }
  289. if (!TerminateProcess( hProcess2, 1 ))
  290. {
  291. CloseHandle( hProcess1 );
  292. CloseHandle( hProcess2 );
  293. return FALSE;
  294. }
  295. CloseHandle( hProcess1 );
  296. CloseHandle( hProcess2 );
  297. return TRUE;
  298. }
  299. }
  300. //
  301. // save the current windowstation
  302. //
  303. hwinstaSave = GetProcessWindowStation();
  304. //
  305. // save the current desktop
  306. //
  307. hdeskSave = GetThreadDesktop( GetCurrentThreadId() );
  308. //
  309. // open the windowstation
  310. //
  311. hwinsta = OpenWindowStationA( tlist->lpWinsta, FALSE, MAXIMUM_ALLOWED );
  312. if (!hwinsta) {
  313. return FALSE;
  314. }
  315. //
  316. // change the context to the new windowstation
  317. //
  318. SetProcessWindowStation( hwinsta );
  319. //
  320. // open the desktop
  321. //
  322. hdesk = OpenDesktopA( tlist->lpDesk, 0, FALSE, MAXIMUM_ALLOWED );
  323. if (!hdesk) {
  324. return FALSE;
  325. }
  326. //
  327. // change the context to the new desktop
  328. //
  329. SetThreadDesktop( hdesk );
  330. //
  331. // kill the process
  332. //
  333. PostMessage( (HWND) tlist->hwnd, WM_CLOSE, 0, 0 );
  334. //
  335. // restore the previous desktop
  336. //
  337. if (hdesk != hdeskSave) {
  338. SetThreadDesktop( hdeskSave );
  339. CloseDesktop( hdesk );
  340. }
  341. //
  342. // restore the context to the previous windowstation
  343. //
  344. if (hwinsta != hwinstaSave) {
  345. SetProcessWindowStation( hwinstaSave );
  346. CloseWindowStation( hwinsta );
  347. }
  348. return TRUE;
  349. }
  350. VOID GetWindowTitles(PTASK_LIST_ENUM te)
  351. {
  352. //
  353. // enumerate all windows and try to get the window
  354. // titles for each task
  355. //
  356. EnumWindowStations( (WINSTAENUMPROC) EnumWindowStationsFunc, (LPARAM)te );
  357. }
  358. /*++
  359. Routine Description:
  360. Callback function for windowstation enumeration.
  361. Arguments:
  362. lpstr - windowstation name
  363. lParam - ** not used **
  364. Return Value:
  365. TRUE - continues the enumeration
  366. --*/
  367. BOOL CALLBACK EnumWindowStationsFunc(LPSTR lpstr,LPARAM lParam)
  368. {
  369. PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam;
  370. HWINSTA hwinsta;
  371. HWINSTA hwinstaSave;
  372. //
  373. // open the windowstation
  374. //
  375. hwinsta = OpenWindowStationA( lpstr, FALSE, MAXIMUM_ALLOWED );
  376. if (!hwinsta) {
  377. return FALSE;
  378. }
  379. //
  380. // save the current windowstation
  381. //
  382. hwinstaSave = GetProcessWindowStation();
  383. //
  384. // change the context to the new windowstation
  385. //
  386. SetProcessWindowStation( hwinsta );
  387. te->lpWinsta = _strdup( lpstr );
  388. //
  389. // enumerate all the desktops for this windowstation
  390. //
  391. EnumDesktops( hwinsta, (DESKTOPENUMPROC) EnumDesktopsFunc, lParam );
  392. //
  393. // restore the context to the previous windowstation
  394. //
  395. if (hwinsta != hwinstaSave) {
  396. SetProcessWindowStation( hwinstaSave );
  397. CloseWindowStation( hwinsta );
  398. }
  399. //
  400. // continue the enumeration
  401. //
  402. return TRUE;
  403. }
  404. /*++
  405. Routine Description:
  406. Callback function for desktop enumeration.
  407. Arguments:
  408. lpstr - desktop name
  409. lParam - ** not used **
  410. Return Value:
  411. TRUE - continues the enumeration
  412. --*/
  413. BOOL CALLBACK EnumDesktopsFunc(LPSTR lpstr,LPARAM lParam)
  414. {
  415. PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam;
  416. HDESK hdeskSave;
  417. HDESK hdesk;
  418. //
  419. // open the desktop
  420. //
  421. hdesk = OpenDesktopA( lpstr, 0, FALSE, MAXIMUM_ALLOWED );
  422. if (!hdesk) {
  423. return FALSE;
  424. }
  425. //
  426. // save the current desktop
  427. //
  428. hdeskSave = GetThreadDesktop( GetCurrentThreadId() );
  429. //
  430. // change the context to the new desktop
  431. //
  432. SetThreadDesktop( hdesk );
  433. te->lpDesk = _strdup( lpstr );
  434. //
  435. // enumerate all windows in the new desktop
  436. //
  437. ((PTASK_LIST_ENUM)lParam)->bFirstLoop = TRUE;
  438. EnumWindows( (WNDENUMPROC)EnumWindowsProc, lParam );
  439. ((PTASK_LIST_ENUM)lParam)->bFirstLoop = FALSE;
  440. EnumWindows( (WNDENUMPROC)EnumWindowsProc, lParam );
  441. //
  442. // restore the previous desktop
  443. //
  444. if (hdesk != hdeskSave) {
  445. SetThreadDesktop( hdeskSave );
  446. CloseDesktop( hdesk );
  447. }
  448. return TRUE;
  449. }
  450. /*++
  451. Routine Description:
  452. Callback function for window enumeration.
  453. Arguments:
  454. hwnd - window handle
  455. lParam - pte
  456. Return Value:
  457. TRUE - continues the enumeration
  458. --*/
  459. BOOL CALLBACK EnumWindowsProc(HWND hwnd,LPARAM lParam)
  460. {
  461. DWORD pid = 0;
  462. DWORD i;
  463. CHAR buf[TITLE_SIZE];
  464. PTASK_LIST_ENUM te = (PTASK_LIST_ENUM)lParam;
  465. PTASK_LIST tlist = te->tlist;
  466. DWORD numTasks = te->numtasks;
  467. //
  468. // Use try/except block when enumerating windows,
  469. // as a window may be destroyed by another thread
  470. // when being enumerated.
  471. //
  472. //try {
  473. //
  474. // get the processid for this window
  475. //
  476. if (!GetWindowThreadProcessId( hwnd, &pid )) {
  477. return TRUE;
  478. }
  479. if ((GetWindow( hwnd, GW_OWNER )) ||
  480. (!(GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE)) && te->bFirstLoop) {
  481. //
  482. // not a top level window
  483. //
  484. return TRUE;
  485. }
  486. //
  487. // look for the task in the task list for this window
  488. // If this is the second time let invisible windows through if we don't
  489. // have a window already
  490. //
  491. for (i=0; i<numTasks; i++) {
  492. if (((DWORD) (DWORD_PTR)tlist[i].dwProcessId == pid) && (te->bFirstLoop || (tlist[i].hwnd == 0))) {
  493. tlist[i].hwnd = hwnd;
  494. tlist[i].lpWinsta = te->lpWinsta;
  495. tlist[i].lpDesk = te->lpDesk;
  496. //
  497. // we found the task no lets try to get the
  498. // window text
  499. //
  500. if (GetWindowTextA( (HWND) tlist[i].hwnd, buf, sizeof(buf) )) {
  501. //
  502. // go it, so lets save it
  503. //
  504. lstrcpyA( tlist[i].WindowTitle, buf );
  505. }
  506. break;
  507. }
  508. }
  509. //} except(EXCEPTION_EXECUTE_HANDLER) {
  510. //}
  511. //
  512. // continue the enumeration
  513. //
  514. return TRUE;
  515. }
  516. BOOL MatchPattern(PUCHAR String,PUCHAR Pattern)
  517. {
  518. UCHAR c, p, l;
  519. for (; ;) {
  520. switch (p = *Pattern++) {
  521. case 0: // end of pattern
  522. return *String ? FALSE : TRUE; // if end of string TRUE
  523. case '*':
  524. while (*String) { // match zero or more char
  525. if (MatchPattern (String++, Pattern))
  526. return TRUE;
  527. }
  528. return MatchPattern (String, Pattern);
  529. case '?':
  530. if (*String++ == 0) // match any one char
  531. return FALSE; // not end of string
  532. break;
  533. case '[':
  534. if ( (c = *String++) == 0) // match char set
  535. return FALSE; // syntax
  536. c = (UCHAR)toupper(c);
  537. l = 0;
  538. while ( (p = *Pattern++ ) != '\0' ) {
  539. if (p == ']') // if end of char set, then
  540. return FALSE; // no match found
  541. if (p == '-') { // check a range of chars?
  542. p = *Pattern; // get high limit of range
  543. if (p == 0 || p == ']')
  544. return FALSE; // syntax
  545. if (c >= l && c <= p)
  546. break; // if in range, move on
  547. }
  548. l = p;
  549. if (c == p) // if char matches this element
  550. break; // move on
  551. }
  552. while (p && p != ']') // got a match in char set
  553. p = *Pattern++; // skip to end of set
  554. break;
  555. default:
  556. c = *String++;
  557. if (toupper(c) != p) // check for exact char
  558. return FALSE; // not a match
  559. break;
  560. }
  561. }
  562. }
  563. struct _ProcessIDStruct
  564. {
  565. DWORD pid;
  566. CHAR pname[MAX_PATH];
  567. } g_Arguments[ 64 ];
  568. DWORD g_dwNumberOfArguments;
  569. int _cdecl KillProcessNameReturn0(CHAR *ProcessNameToKill)
  570. {
  571. DWORD i, j;
  572. DWORD numTasks;
  573. TASK_LIST_ENUM te;
  574. int rval = 0;
  575. CHAR tname[PROCESS_SIZE];
  576. LPSTR p;
  577. DWORD ThisPid;
  578. BOOL iForceKill = TRUE;
  579. TASK_LIST The_TList[MAX_TASKS];
  580. g_dwNumberOfArguments = 0;
  581. //
  582. // Get the process name into the array
  583. //
  584. g_Arguments[g_dwNumberOfArguments].pid = 0;
  585. // make sure there is no path specified.
  586. char pfilename_only[_MAX_FNAME];
  587. char pextention_only[_MAX_EXT];
  588. _splitpath( ProcessNameToKill, NULL, NULL, pfilename_only, pextention_only);
  589. if (pextention_only) {strcat(pfilename_only,pextention_only);}
  590. if ( strlen(pfilename_only) >= MAX_PATH )
  591. {
  592. return ( ERROR_INVALID_PARAMETER );
  593. }
  594. // make it uppercase
  595. lstrcpyA(g_Arguments[g_dwNumberOfArguments].pname, pfilename_only);
  596. _strupr( g_Arguments[g_dwNumberOfArguments].pname );
  597. g_dwNumberOfArguments += 1;
  598. //
  599. // lets be god
  600. //
  601. EnableDebugPriv();
  602. //
  603. // get the task list for the system
  604. //
  605. numTasks = GetTaskList( The_TList, MAX_TASKS );
  606. //
  607. // enumerate all windows and try to get the window
  608. // titles for each task
  609. //
  610. te.tlist = The_TList;
  611. te.numtasks = numTasks;
  612. GetWindowTitles( &te );
  613. ThisPid = GetCurrentProcessId();
  614. for (i=0; i<numTasks; i++) {
  615. //
  616. // this prevents the user from killing KILL.EXE and
  617. // it's parent cmd window too
  618. //
  619. if (ThisPid == (DWORD) (DWORD_PTR) The_TList[i].dwProcessId) {
  620. continue;
  621. }
  622. if (MatchPattern( (PUCHAR) The_TList[i].WindowTitle, (PUCHAR) "*KILL*" )) {
  623. continue;
  624. }
  625. tname[0] = 0;
  626. lstrcpyA( tname, The_TList[i].ProcessName );
  627. p = strchr( tname, '.' );
  628. if (p) {
  629. p[0] = '\0';
  630. }
  631. for (j=0; j<g_dwNumberOfArguments; j++) {
  632. if (g_Arguments[j].pname) {
  633. if (MatchPattern( (PUCHAR) tname, (PUCHAR) g_Arguments[j].pname )) {
  634. The_TList[i].flags = TRUE;
  635. } else if (MatchPattern( (PUCHAR) The_TList[i].ProcessName, (PUCHAR) g_Arguments[j].pname )) {
  636. The_TList[i].flags = TRUE;
  637. } else if (MatchPattern( (PUCHAR) The_TList[i].WindowTitle, (PUCHAR) g_Arguments[j].pname )) {
  638. The_TList[i].flags = TRUE;
  639. }
  640. } else if (g_Arguments[j].pid) {
  641. if ((DWORD) (DWORD_PTR) The_TList[i].dwProcessId == g_Arguments[j].pid) {
  642. The_TList[i].flags = TRUE;
  643. }
  644. }
  645. }
  646. }
  647. for (i=0; i<numTasks; i++)
  648. {
  649. if (The_TList[i].flags)
  650. {
  651. if (KillProcess( &The_TList[i], iForceKill ))
  652. {
  653. //printf( "process %s (%d) - '%s' killed\n", The_TList[i].ProcessName,The_TList[i].dwProcessId,The_TList[i].hwnd ? The_TList[i].WindowTitle : "");
  654. }
  655. else
  656. {
  657. //printf( "process %s (%d) - '%s' could not be killed\n",The_TList[i].ProcessName,The_TList[i].dwProcessId,The_TList[i].hwnd ? The_TList[i].WindowTitle : "");
  658. rval = 1;
  659. }
  660. }
  661. }
  662. FreeTaskListMem();
  663. return rval;
  664. }