Source code of Windows XP (NT5)
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.

2073 lines
56 KiB

  1. // *********************************************************************************
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // Module Name:
  6. //
  7. // Terminate.cpp
  8. //
  9. // Abstract:
  10. //
  11. // This module implements the actual termination of process
  12. //
  13. // Author:
  14. //
  15. // Sunil G.V.N. Murali ([email protected]) 26-Nov-2000
  16. //
  17. // Revision History:
  18. //
  19. // Sunil G.V.N. Murali ([email protected]) 26-Nov-2000 : Created It.
  20. //
  21. // *********************************************************************************
  22. #include "pch.h"
  23. #include "wmi.h"
  24. #include "TaskKill.h"
  25. //
  26. // define(s) / constants
  27. //
  28. #define MAX_ENUM_TASKS 5
  29. #define MAX_ENUM_SERVICES 10
  30. #define MAX_ENUM_MODULES 10
  31. #define WAIT_TIME_IN_SECS 1000 // 1 second ( 1000 milliseconds )
  32. #define MAX_TIMEOUT_RETRIES 60 // 60 times
  33. #define MAX_TERMINATE_TIMEOUT 1000 // 1 seconds
  34. // We won't allow the following set of critical system processes to be terminated,
  35. // since the system would bug check immediately, no matter who you are.
  36. #define PROCESS_CSRSS_EXE L"csrss.exe"
  37. #define PROCESS_WINLOGON_EXE L"winlogon.exe"
  38. #define PROCESS_SMSS_EXE L"smss.exe"
  39. #define PROCESS_SERVICES_EXE L"services.exe"
  40. //
  41. // function prototypes
  42. //
  43. #ifndef _WIN64
  44. BOOL EnumLoadedModulesProc( LPSTR lpszModuleName, ULONG ulModuleBase, ULONG ulModuleSize, PVOID pUserData );
  45. #else
  46. BOOL EnumLoadedModulesProc64( LPSTR lpszModuleName, DWORD64 ulModuleBase, ULONG ulModuleSize, PVOID pUserData );
  47. #endif
  48. // ***************************************************************************
  49. // Routine Description:
  50. //
  51. // Arguments:
  52. // NONE
  53. //
  54. // Return Value:
  55. //
  56. // ***************************************************************************
  57. BOOL CTaskKill::DoTerminate( DWORD& dwExitCode )
  58. {
  59. // local variables
  60. HRESULT hr;
  61. CHString str;
  62. LONG lIndex = -1;
  63. DWORD dwCount = 0;
  64. DWORD dwKilled = 0;
  65. DWORD dwFilters = 0;
  66. DWORD dwTimeOuts = 0;
  67. DWORD dwImageNames = 0;
  68. DWORD dwTasksToKill = 0;
  69. DWORD dwMatchedIndex = 0;
  70. BOOL bQueued = FALSE;
  71. BOOL bCanExit = FALSE;
  72. BOOL bAllTasks = FALSE;
  73. BOOL bImageName = FALSE;
  74. ULONG ulReturned = 0;
  75. TARRAY arrTasks = NULL;
  76. TARRAY arrImageNames = NULL;
  77. LPCWSTR pwszTask = NULL;
  78. IWbemClassObject* pObjects[ MAX_ENUM_TASKS ];
  79. // clear the error
  80. SetLastError( NO_ERROR );
  81. try
  82. {
  83. //
  84. // prepare ...
  85. bCanExit = FALSE;
  86. dwImageNames = 0;
  87. dwFilters = DynArrayGetCount( m_arrFiltersEx );
  88. dwTasksToKill = DynArrayGetCount( m_arrTasksToKill );
  89. arrTasks = CreateDynamicArray();
  90. arrImageNames = CreateDynamicArray();
  91. if ( arrImageNames == NULL || arrTasks == NULL )
  92. {
  93. dwExitCode = 1;
  94. SetLastError( E_OUTOFMEMORY );
  95. SaveLastError();
  96. // release the allocations
  97. DESTROY_ARRAY( arrTasks );
  98. DESTROY_ARRAY( arrImageNames );
  99. // inform failure
  100. return FALSE;
  101. }
  102. // check if '*' is specified or not
  103. lIndex = DynArrayFindString( m_arrTasksToKill, L"*", TRUE, 0 );
  104. if ( lIndex != -1 )
  105. {
  106. // wild card specified
  107. dwTasksToKill--; // update the counter
  108. bAllTasks = TRUE; // remember
  109. DynArrayRemove( m_arrTasksToKill, lIndex ); // remove the wildcard entry
  110. }
  111. // init all the objects first
  112. for( DWORD dw = 0; dw < MAX_ENUM_TASKS; dw++ )
  113. pObjects[ dw ] = NULL;
  114. // if -tr is specified, free the already allocated memory for m_arrRecord
  115. if ( m_bTree == TRUE )
  116. {
  117. DESTROY_ARRAY( m_arrRecord );
  118. }
  119. // traverse thru the running processed and terminate the needed
  120. dwCount = 0;
  121. dwKilled = 0;
  122. do
  123. {
  124. // status message
  125. if ( m_bTree == TRUE )
  126. PrintProgressMsg( m_hOutput, MSG_TASKSINFO, m_csbi );
  127. // get the object ... time out should not occur
  128. // NOTE: one-by-one
  129. hr = m_pWbemEnumObjects->Next(
  130. WAIT_TIME_IN_SECS, MAX_ENUM_TASKS, pObjects, &ulReturned );
  131. if ( hr == WBEM_S_FALSE )
  132. {
  133. // we've reached the end of enumeration .. set the flag
  134. bCanExit = TRUE;
  135. }
  136. else if ( hr == WBEM_S_TIMEDOUT )
  137. {
  138. // update the timeouts occured
  139. dwTimeOuts++;
  140. // check if max. retries have reached ... if yes better stop
  141. if ( dwTimeOuts > MAX_TIMEOUT_RETRIES )
  142. {
  143. dwExitCode = 1;
  144. DESTROY_ARRAY( arrTasks );
  145. DESTROY_ARRAY( arrImageNames );
  146. SetLastError( ERROR_TIMEOUT );
  147. SaveLastError();
  148. return FALSE;
  149. }
  150. // still we can do some more tries ...
  151. continue;
  152. }
  153. else if ( FAILED( hr ) )
  154. {
  155. // some error has occured ... oooppps
  156. dwExitCode = 1;
  157. DESTROY_ARRAY( arrTasks );
  158. DESTROY_ARRAY( arrImageNames );
  159. WMISaveError( hr );
  160. return FALSE;
  161. }
  162. // reset the timeout counter
  163. dwTimeOuts = 0;
  164. // loop thru the objects and save the info
  165. for( ULONG ul = 0; ul < ulReturned; ul++ )
  166. {
  167. // get the current cursor position ..
  168. if ( m_hOutput != NULL )
  169. GetConsoleScreenBufferInfo( m_hOutput, &m_csbi );
  170. // if tree option is specified, allocate memory for record every we loop
  171. if ( m_bTree == TRUE )
  172. {
  173. // create a new array
  174. m_arrRecord = CreateDynamicArray();
  175. if ( m_arrRecord == NULL )
  176. {
  177. dwExitCode = 1;
  178. SetLastError( E_OUTOFMEMORY );
  179. SaveLastError();
  180. // release the allocations
  181. DESTROY_ARRAY( arrTasks );
  182. DESTROY_ARRAY( arrImageNames );
  183. // inform failure
  184. return FALSE;
  185. }
  186. }
  187. else
  188. {
  189. // tree option is not specified, so, just remove the contents
  190. DynArrayRemoveAll( m_arrRecord );
  191. }
  192. // add the columns first
  193. DynArrayAddColumns( m_arrRecord, MAX_TASKSINFO );
  194. // retrive and save data
  195. SaveData( pObjects[ ul ] );
  196. // release the object
  197. SAFE_RELEASE( pObjects[ ul ] );
  198. // check if this has to be filtered or not
  199. if ( dwFilters != 0 )
  200. {
  201. BOOL bIgnore = FALSE;
  202. bIgnore = CanFilterRecord( MAX_FILTERS,
  203. m_pfilterConfigs, m_arrRecord, m_arrFiltersEx );
  204. // check if this has to be ignored or not
  205. if ( bIgnore == TRUE )
  206. {
  207. if ( m_bTree == TRUE )
  208. {
  209. // save this record with rank as 0
  210. DynArraySetDWORD( m_arrRecord, TASK_RANK, 0 );
  211. DynArrayAppendEx( arrTasks, m_arrRecord );
  212. }
  213. // continue to the task
  214. continue;
  215. }
  216. }
  217. // crossed from the filter -- update the count
  218. dwCount++;
  219. // find the task that has to be killed
  220. // and check if this task has to be killed or not
  221. lIndex = -1;
  222. pwszTask = NULL;
  223. bImageName = FALSE;
  224. if ( dwTasksToKill != 0 || dwImageNames != 0 )
  225. {
  226. // check if the process is in list
  227. if ( dwTasksToKill != 0 )
  228. lIndex = MatchTaskToKill( dwMatchedIndex );
  229. // if task is not, check if image names exist and if it matches or not
  230. if ( lIndex == -1 && dwImageNames != 0 )
  231. {
  232. // get the image name and search for the same in the image names list
  233. DWORD dwLength = 0;
  234. LPCWSTR pwsz = NULL;
  235. LPCWSTR pwszTemp = NULL;
  236. LPCWSTR pwszImageName = NULL;
  237. pwszImageName = DynArrayItemAsString( m_arrRecord, TASK_IMAGENAME );
  238. if ( pwszImageName == NULL )
  239. {
  240. dwExitCode = 1;
  241. DESTROY_ARRAY( arrTasks );
  242. DESTROY_ARRAY( arrImageNames );
  243. SetLastError( STG_E_UNKNOWN );
  244. SaveLastError();
  245. return FALSE;
  246. }
  247. // ...
  248. for( DWORD dw = 0; dw < dwImageNames; dw++ )
  249. {
  250. // get the image name from the list
  251. pwszTemp = DynArrayItemAsString( arrImageNames, dw );
  252. if ( pwszTemp == NULL )
  253. {
  254. dwExitCode = 1;
  255. DESTROY_ARRAY( arrTasks );
  256. DESTROY_ARRAY( arrImageNames );
  257. SetLastError( STG_E_UNKNOWN );
  258. SaveLastError();
  259. return FALSE;
  260. }
  261. // determine the no. of characters to compare
  262. dwLength = 0;
  263. pwsz = FindChar( pwszTemp, L'*', 0 );
  264. if ( pwsz != NULL )
  265. {
  266. // '*' - wildcard is specified in the image name
  267. // so, determine the no. of characters to compare
  268. // but before that check the length of the string pointer from '*'
  269. // it should be 1 - meaning the '*' can be specified only at the end
  270. // but not in the middle
  271. if ( lstrlen( pwsz ) == 1 )
  272. {
  273. dwLength = lstrlen( pwszTemp ) - lstrlen( pwsz );
  274. }
  275. }
  276. // now do the comparision
  277. if ( StringCompare( pwszImageName, pwszTemp, TRUE, dwLength ) == 0 )
  278. {
  279. // image found - has to be terminated
  280. bImageName = TRUE;
  281. pwszTask = pwszTemp;
  282. }
  283. }
  284. }
  285. else if ( lIndex != -1 && dwMatchedIndex == TASK_IMAGENAME )
  286. {
  287. bImageName = TRUE; // image name
  288. pwszTask = DynArrayItemAsString( m_arrTasksToKill, lIndex );
  289. }
  290. }
  291. // check whether attempt to terminate or not to attempt
  292. if ( bAllTasks == FALSE && lIndex == -1 && bImageName == FALSE )
  293. {
  294. if ( m_bTree == TRUE )
  295. {
  296. // save this record with rank as 0
  297. dwCount--;
  298. DynArraySetDWORD( m_arrRecord, TASK_RANK, 0 );
  299. DynArrayAppendEx( arrTasks, m_arrRecord );
  300. }
  301. // continue to the task
  302. continue;
  303. }
  304. // we need to post-pone the killing of the current identified task till we get the
  305. // entire list of processes
  306. if ( m_bTree == TRUE )
  307. {
  308. // mark this as rank 1 process
  309. DynArraySetDWORD( m_arrRecord, TASK_RANK, 1 );
  310. // now add this record to the tasks array
  311. DynArrayAppendEx( arrTasks, m_arrRecord );
  312. }
  313. else
  314. {
  315. // kill the current task
  316. bQueued = FALSE;
  317. if ( this->Kill( bQueued ) == TRUE )
  318. {
  319. dwKilled++; // updated killed processes counter
  320. // success message will depend on the task info specified by the user
  321. // at the command prompt
  322. if ( bImageName == TRUE )
  323. {
  324. str.Format(MSG_KILL_SUCCESS_EX, m_strImageName, m_dwProcessId);
  325. }
  326. else
  327. {
  328. str.Format( MSG_KILL_SUCCESS, m_dwProcessId );
  329. }
  330. // show the message
  331. ShowMessage( stdout, str );
  332. }
  333. else
  334. {
  335. // failed to kill the process .. save the error message
  336. if ( bImageName == FALSE )
  337. str.Format( ERROR_KILL_FAILED, m_dwProcessId, GetReason() );
  338. else
  339. str.Format( ERROR_KILL_FAILED_EX, m_strImageName, m_dwProcessId, GetReason() );
  340. // show the message
  341. ShowMessage( stderr, str );
  342. }
  343. }
  344. // user might have specified the duplications in the list
  345. // so check for that and remove it
  346. if ( bImageName == TRUE )
  347. {
  348. // sub-local
  349. CHString strProcessId;
  350. LONG lProcessIndex = -1;
  351. strProcessId.Format( L"%ld", m_dwProcessId );
  352. lProcessIndex = DynArrayFindString( m_arrTasksToKill, strProcessId, TRUE, 0 );
  353. if ( lProcessIndex != -1 && lIndex != lProcessIndex )
  354. DynArrayRemove( m_arrTasksToKill, lProcessIndex );
  355. }
  356. else if ( pwszTask != NULL )
  357. {
  358. // sub-local
  359. LONG lProcessIndex = -1;
  360. lProcessIndex = DynArrayFindString( m_arrTasksToKill, pwszTask, TRUE, 0 );
  361. if ( lProcessIndex != -1 && lIndex != lProcessIndex )
  362. {
  363. bImageName = TRUE;
  364. DynArrayRemove( m_arrTasksToKill, lProcessIndex );
  365. }
  366. }
  367. // if this is a image name, all the tasks with this image name
  368. // has to be terminated. so we need to save the image name
  369. // but before doing this, in order to save memory, check if this image name
  370. // already exists in the list .. this will avoid duplication of image names
  371. // in the list and helps in performace
  372. if ( bImageName == TRUE && pwszTask != NULL &&
  373. DynArrayFindString(arrImageNames, pwszTask, TRUE, 0) == -1 )
  374. {
  375. // add to the list
  376. dwImageNames++;
  377. DynArrayAppendString( arrImageNames, pwszTask, 0 );
  378. }
  379. // delete the process info from the arrProcesses ( if needed )
  380. if ( lIndex != -1 )
  381. {
  382. // yes ... current task was killed remove the entry from arrProcess into
  383. // consideration ... so delete it
  384. dwTasksToKill--; // update the counter
  385. DynArrayRemove( m_arrTasksToKill, lIndex );
  386. }
  387. // check whether we need to quit the program or not
  388. if ( m_bTree == FALSE && bAllTasks == FALSE && dwTasksToKill == 0 && dwImageNames == 0 )
  389. {
  390. bCanExit = TRUE;
  391. break;
  392. }
  393. }
  394. } while ( bCanExit == FALSE );
  395. // if the -tr is specified, reset the m_arrRecord variable to NULL
  396. // this will avoid double free-ing the same heap memory
  397. if ( m_bTree == TRUE )
  398. {
  399. m_arrRecord = NULL;
  400. // erase progress message
  401. PrintProgressMsg( m_hOutput, NULL, m_csbi );
  402. }
  403. //
  404. // SPECIAL HANDLING FOR TREE TERMINATION STARTS HERE
  405. //
  406. dwExitCode = 0;
  407. if ( m_bTree == TRUE && dwCount != 0 )
  408. {
  409. //
  410. // prepare the tree
  411. // sub-local variables
  412. LONG lTemp = 0;
  413. DWORD dwTemp = 0;
  414. DWORD dwRank = 0;
  415. DWORD dwIndex = 0;
  416. DWORD dwLastRank = 0;
  417. DWORD dwTasksCount = 0;
  418. DWORD dwProcessId = 0;
  419. DWORD dwParentProcessId = 0;
  420. // show progress message
  421. PrintProgressMsg( m_hOutput, MSG_FORMINGTREE, m_csbi );
  422. // loop thru the list of processes
  423. dwLastRank = 1;
  424. dwTasksCount = DynArrayGetCount( arrTasks );
  425. for( dwIndex = 0; dwIndex < dwTasksCount; dwIndex++ )
  426. {
  427. // get the rank of the current process
  428. // and check whether the current process is marked for termination or not
  429. dwRank = DynArrayItemAsDWORD2( arrTasks, dwIndex, TASK_RANK );
  430. if ( dwRank == 0 )
  431. continue;
  432. // now loop thru the begining of the tasks and
  433. // assign the ranks to the childs of this process
  434. dwProcessId = DynArrayItemAsDWORD2( arrTasks, dwIndex, TASK_PID );
  435. for( DWORD dw = dwIndex + 1; dw < dwTasksCount; dw++ )
  436. {
  437. // get the process id this process
  438. dwTemp = DynArrayItemAsDWORD2( arrTasks, dw, TASK_PID );
  439. if ( dwTemp == dwProcessId )
  440. continue; // skip this process
  441. // get the parent process id of this process
  442. dwParentProcessId = DynArrayItemAsDWORD2( arrTasks, dw, TASK_CREATINGPROCESSID );
  443. if ( dwTemp == dwParentProcessId )
  444. continue; // skip this process also
  445. // check the process relation
  446. if ( dwProcessId == dwParentProcessId )
  447. {
  448. // set the rank to this process
  449. DynArraySetDWORD2( arrTasks, dw, TASK_RANK, dwRank + 1 );
  450. // update the last rank
  451. if ( dwRank + 1 > dwLastRank )
  452. {
  453. dwLastRank = dwRank + 1;
  454. }
  455. // SPECIAL CONDITION:
  456. // -----------------
  457. // we need to check the index of this task in the list of tasks information we have
  458. // if the index of this task information is above its parent process,
  459. // we need to re-initiate the outter loop once again
  460. // this is a sort of optimization which we are doing here instead of looping the
  461. // outter loop unnecessarily
  462. // if ( dw < dwIndex )
  463. // {
  464. // dwIndex = 0;
  465. // }
  466. // ----------------------------------------------------------
  467. // currently we are assuming that the list of processe we get
  468. // will be in sorting order of creation time
  469. // ----------------------------------------------------------
  470. }
  471. }
  472. }
  473. // erase progress message
  474. PrintProgressMsg( m_hOutput, NULL, m_csbi );
  475. //
  476. // now start terminating the tasks based on their ranks
  477. dwKilled = 0;
  478. for( DWORD dwRank = dwLastRank; dwRank > 0; dwRank-- )
  479. {
  480. // loop thru all the processes and terminate
  481. for ( LONG lIndex = 0; lIndex < (LONG) dwTasksCount; lIndex++ )
  482. {
  483. // get the record
  484. m_arrRecord = (TARRAY) DynArrayItem( arrTasks, lIndex );
  485. if ( m_arrRecord == NULL )
  486. continue;
  487. // check the rank
  488. dwTemp = DynArrayItemAsDWORD( m_arrRecord, TASK_RANK );
  489. if ( dwTemp != dwRank )
  490. {
  491. // OPTIMIZATION:
  492. // ------------
  493. // check the rank. if the rank is zero, delete this task from the list
  494. // this improves the performance when we run for the next loop
  495. if ( dwTemp == 0 )
  496. {
  497. DynArrayRemove( arrTasks, lIndex );
  498. lIndex--;
  499. dwTasksCount--;
  500. }
  501. // skip this task
  502. continue;
  503. }
  504. // get the process id and its parent process id
  505. m_dwProcessId = DynArrayItemAsDWORD( m_arrRecord, TASK_PID );
  506. dwParentProcessId = DynArrayItemAsDWORD( m_arrRecord, TASK_CREATINGPROCESSID );
  507. // ensure that there are no child for this process
  508. // NOTE: Termination of some childs might have failed ( this is needed only if -f is not specified )
  509. if ( m_bForce == FALSE )
  510. {
  511. lTemp = DynArrayFindDWORDEx( arrTasks, TASK_CREATINGPROCESSID, m_dwProcessId );
  512. if ( lTemp != -1 )
  513. {
  514. // set the reason
  515. SetReason( ERROR_TASK_HAS_CHILDS );
  516. // format the error message
  517. str.Format( ERROR_TREE_KILL_FAILED, m_dwProcessId, dwParentProcessId, GetReason() );
  518. // show the message
  519. ShowMessage( stderr, str );
  520. // skip this
  521. continue;
  522. }
  523. }
  524. // kill the current task
  525. bQueued = FALSE;
  526. if ( this->Kill( bQueued ) == TRUE )
  527. {
  528. dwKilled++; // updated killed processes counter
  529. // prepare the error message
  530. str.Format( MSG_TREE_KILL_SUCCESS, m_dwProcessId, dwParentProcessId );
  531. // remove the current task entry from the list and update the indexes accordingly
  532. DynArrayRemove( arrTasks, lIndex );
  533. lIndex--;
  534. dwTasksCount--;
  535. // show the message
  536. ShowMessage( stdout, str );
  537. }
  538. else
  539. {
  540. // prepare the error message
  541. str.Format( ERROR_TREE_KILL_FAILED, m_dwProcessId, dwParentProcessId, GetReason() );
  542. // show the message
  543. ShowMessage( stderr, str );
  544. }
  545. }
  546. }
  547. // reset the value of m_arrRecord
  548. m_arrRecord = NULL;
  549. // determine the exit code
  550. if ( dwTasksCount == dwCount )
  551. dwExitCode = 255; // not even one task got terminated
  552. else if ( dwTasksToKill != 0 || dwTasksCount != 0 )
  553. dwExitCode = 128; // tasks were terminated partially
  554. }
  555. //
  556. // SPECIAL HANDLING FOR TREE TERMINATION ENDS HERE
  557. //
  558. }
  559. catch( ... )
  560. {
  561. SetLastError( E_OUTOFMEMORY );
  562. ShowLastError( stderr );
  563. return 255;
  564. }
  565. // free the memory
  566. DESTROY_ARRAY( arrTasks );
  567. DESTROY_ARRAY( arrImageNames );
  568. // final check-up ...
  569. if ( dwCount == 0 && ( dwTasksToKill == 0 || m_bFiltersOptimized || dwFilters != 0 ) )
  570. {
  571. dwExitCode = 0;
  572. ShowMessage( stdout, ERROR_NO_PROCESSES ); // no tasks were found
  573. }
  574. else if ( dwTasksToKill != 0 )
  575. {
  576. // some processes which are requested to kill are not found
  577. LPCWSTR pwszTemp = NULL;
  578. for( DWORD dw = 0; dw < dwTasksToKill; dw++ )
  579. {
  580. // get the task name
  581. pwszTemp = DynArrayItemAsString( m_arrTasksToKill, dw );
  582. if ( pwszTemp == NULL )
  583. continue; // skip
  584. // prepare and display message ...
  585. str.Format( ERROR_PROCESS_NOTFOUND, pwszTemp );
  586. ShowMessage( stderr, str );
  587. }
  588. // exit code
  589. dwExitCode = 128;
  590. }
  591. // return
  592. return TRUE;
  593. }
  594. // ***************************************************************************
  595. // Routine Description:
  596. // Invokes the appropriate kill function based on the mode of termination
  597. //
  598. // Arguments:
  599. // [ in ] bForce : mode of termination (silent/force)
  600. //
  601. // Return Value:
  602. // TRUE upon successfull and FALSE if failed
  603. // ***************************************************************************
  604. inline BOOL CTaskKill::Kill( BOOL& bQueued )
  605. {
  606. // local variables
  607. BOOL bResult = FALSE;
  608. // check whether task can be terminated or not
  609. if ( CanTerminate() == FALSE )
  610. return FALSE;
  611. // check whether local system / remote system
  612. bQueued = FALSE;
  613. if ( m_bLocalSystem == TRUE )
  614. {
  615. //
  616. // process termination on local system
  617. // based on the mode of termination invoke appropriate method
  618. if ( m_bForce == FALSE )
  619. bResult = KillProcessOnLocalSystem( bQueued );
  620. else
  621. bResult = ForciblyKillProcessOnLocalSystem();
  622. }
  623. else
  624. {
  625. //
  626. // process termination on remote system
  627. // silent termination of the process on a remote system is not supported
  628. // it will be always forcible termination
  629. bResult = ForciblyKillProcessOnRemoteSystem();
  630. }
  631. // inform the result
  632. return bResult;
  633. }
  634. // ***************************************************************************
  635. // Routine Description:
  636. // Terminates the process in silence mode ... by posting WM_CLOSE message
  637. // this is for local system only
  638. //
  639. // Arguments:
  640. // NONE
  641. //
  642. // Return Value:
  643. // TRUE upon successfull and FALSE if failed
  644. // ***************************************************************************
  645. BOOL CTaskKill::KillProcessOnLocalSystem( BOOL& bQueued )
  646. {
  647. // local variables
  648. HDESK hDesk = NULL;
  649. HDESK hdeskSave = NULL;
  650. HWINSTA hWinSta = NULL;
  651. HWINSTA hwinstaSave = NULL;
  652. HANDLE hProcess = NULL;
  653. // variables which contains data
  654. HWND hWnd = NULL;
  655. LPCWSTR pwszDesktop = NULL;
  656. LPCWSTR pwszWindowStation = NULL;
  657. // clear the reason
  658. SetReason( NULL_STRING );
  659. // get the window station and desktop information
  660. hWnd = ( HWND ) DynArrayItemAsHandle( m_arrRecord, TASK_HWND );
  661. pwszDesktop = DynArrayItemAsString( m_arrRecord, TASK_DESK );
  662. pwszWindowStation = DynArrayItemAsString( m_arrRecord, TASK_WINSTA );
  663. // check whether window window handle exists for this process or not if not, return
  664. if ( hWnd == NULL )
  665. {
  666. SetLastError( CO_E_NOT_SUPPORTED );
  667. SetReason( ERROR_CANNOT_KILL_SILENTLY );
  668. return FALSE;
  669. }
  670. // get and save the current window station and desktop
  671. hwinstaSave = GetProcessWindowStation();
  672. hdeskSave = GetThreadDesktop( GetCurrentThreadId() );
  673. // open current tasks window station and change the context to the new workstation
  674. if ( pwszWindowStation != NULL )
  675. {
  676. //
  677. // process has window station ... get it
  678. hWinSta = OpenWindowStation( pwszWindowStation,
  679. FALSE, WINSTA_ENUMERATE | WINSTA_ENUMDESKTOPS );
  680. if ( hWinSta == NULL )
  681. {
  682. // failed in getting the process window station
  683. SaveLastError();
  684. return FALSE;
  685. }
  686. else
  687. {
  688. // change the context to the new workstation
  689. if ( hWinSta != hwinstaSave && SetProcessWindowStation( hWinSta ) == FALSE )
  690. {
  691. // failed in changing the context
  692. SaveLastError();
  693. return FALSE;
  694. }
  695. }
  696. }
  697. // open the tasks desktop and change the context to the new desktop
  698. if ( pwszDesktop != NULL )
  699. {
  700. //
  701. // process has desktop ... get it
  702. hDesk = OpenDesktop( pwszDesktop, 0, FALSE, DESKTOP_ENUMERATE );
  703. if ( hDesk == NULL )
  704. {
  705. // failed in getting the process desktop
  706. SaveLastError();
  707. return FALSE;
  708. }
  709. else
  710. {
  711. // change the context to the new desktop
  712. if ( hDesk != hdeskSave && SetThreadDesktop( hDesk ) == FALSE )
  713. {
  714. // failed in changing the context
  715. SaveLastError();
  716. // ?? return FALSE; - has to be uncommented
  717. }
  718. }
  719. }
  720. // atlast ... now kill the process
  721. if ( hWnd != NULL && PostMessage( hWnd, WM_CLOSE, 0, 0 ) == FALSE )
  722. {
  723. // failed in posting the message
  724. SaveLastError();
  725. return FALSE;
  726. }
  727. // restore the previous desktop
  728. if ( hDesk != NULL && hDesk != hdeskSave )
  729. {
  730. SetThreadDesktop( hdeskSave );
  731. CloseDesktop( hDesk );
  732. }
  733. // restore the context to the previous window station
  734. if ( hWinSta != NULL && hWinSta != hwinstaSave )
  735. {
  736. SetProcessWindowStation( hwinstaSave );
  737. CloseWindowStation( hWinSta );
  738. }
  739. // before return from this function check if process is terminated
  740. // or queued for termination
  741. bQueued = FALSE;
  742. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, m_dwProcessId );
  743. if ( hProcess != NULL )
  744. {
  745. //
  746. // process might be in queue for termination
  747. // get the state of the process
  748. DWORD dwExitCode = 0;
  749. if ( GetExitCodeProcess( hProcess, &dwExitCode ) == TRUE && dwExitCode == STILL_ACTIVE )
  750. bQueued = TRUE; // process is in queue
  751. // close the handle to the process
  752. CloseHandle( hProcess );
  753. }
  754. // inform success
  755. return TRUE;
  756. }
  757. // ***************************************************************************
  758. // Routine Description:
  759. // Terminates the process forcibly ... this is for local system only
  760. //
  761. // Arguments:
  762. // NONE
  763. //
  764. // Return Value:
  765. // TRUE upon successfull and FALSE if failed
  766. // ***************************************************************************
  767. BOOL CTaskKill::ForciblyKillProcessOnLocalSystem()
  768. {
  769. // local variables
  770. DWORD dwExitCode = 0;
  771. HANDLE hProcess = NULL;
  772. // get the handle to the process using the process id
  773. hProcess = OpenProcess(
  774. PROCESS_TERMINATE | PROCESS_QUERY_INFORMATION, FALSE, m_dwProcessId );
  775. // check whether we got the handle successfully or not ... if not error
  776. if ( hProcess == NULL )
  777. {
  778. // failed in getting the process handle ... may be process might have finished
  779. // there is one occassion in which, we get the last error as invalid parameter
  780. // 'coz it doesn't convey proper message to the user, we will check for that error
  781. // and change the message appropriately
  782. if ( GetLastError() == ERROR_INVALID_PARAMETER )
  783. SetLastError( CO_E_NOT_SUPPORTED );
  784. // save the error message
  785. SaveLastError();
  786. // return failure
  787. return FALSE;
  788. }
  789. // get the state of the process
  790. if ( GetExitCodeProcess( hProcess, &dwExitCode ) == FALSE )
  791. {
  792. // unknow error has occured ... failed
  793. CloseHandle( hProcess ); // close the process handle
  794. SaveLastError();
  795. return FALSE;
  796. }
  797. // now check whether the process is active or not
  798. if ( dwExitCode != STILL_ACTIVE )
  799. {
  800. // process is not active ... it is already terminated
  801. CloseHandle( hProcess ); // close the process handle
  802. SetLastError( SCHED_E_TASK_NOT_RUNNING );
  803. SaveLastError();
  804. return FALSE;
  805. }
  806. // now forcibly try to terminate the process ( exit code will be 1 )
  807. if ( TerminateProcess( hProcess, 1 ) == FALSE )
  808. {
  809. // failed in terminating the process
  810. CloseHandle( hProcess ); // close the process handle
  811. // there is one occassion in which, we get the last error as invalid parameter
  812. // 'coz it doesn't convey proper message to the user, we will check for that error
  813. // and change the message appropriately
  814. if ( GetLastError() == ERROR_INVALID_PARAMETER )
  815. SetLastError( CO_E_NOT_SUPPORTED );
  816. // save the error message
  817. SaveLastError();
  818. // return failure
  819. return FALSE;
  820. }
  821. // successfully terminated the process with exit code 1
  822. CloseHandle( hProcess ); // close the process handle
  823. return TRUE; // inform success
  824. }
  825. // ***************************************************************************
  826. // Routine Description:
  827. // Terminates the process forcibly ... uses WMI for terminating
  828. // this is for remote system
  829. //
  830. // Arguments:
  831. // NONE
  832. //
  833. // Return Value:
  834. // TRUE upon successfull and FALSE if failed
  835. // ***************************************************************************
  836. BOOL CTaskKill::ForciblyKillProcessOnRemoteSystem()
  837. {
  838. // local variables
  839. HRESULT hr;
  840. _variant_t varTemp;
  841. BOOL bResult = FALSE;
  842. LPCWSTR pwszPath = NULL;
  843. IWbemClassObject* pInParams = NULL;
  844. IWbemClassObject* pOutParams = NULL;
  845. IWbemCallResult* pCallResult = NULL;
  846. // get the object path
  847. pwszPath = DynArrayItemAsString( m_arrRecord, TASK_OBJPATH );
  848. if ( pwszPath == NULL )
  849. {
  850. SetLastError( STG_E_UNKNOWN );
  851. SaveLastError();
  852. return FALSE;
  853. }
  854. try
  855. {
  856. // create an instance for input parameters
  857. SAFE_EXECUTE( m_pWbemTerminateInParams->SpawnInstance( 0, &pInParams ) );
  858. // set the reason ( abnormal termination )
  859. varTemp = 1L;
  860. SAFE_EXECUTE( PropertyPut( pInParams, TERMINATE_INPARAM_REASON, varTemp ) );
  861. // now execute the method ( semi-synchronous call )
  862. SAFE_EXECUTE( m_pWbemServices->ExecMethod(
  863. _bstr_t( pwszPath ), _bstr_t( WIN32_PROCESS_METHOD_TERMINATE ),
  864. WBEM_FLAG_RETURN_IMMEDIATELY, NULL, pInParams, NULL, &pCallResult ) );
  865. // set security info to the interface
  866. SAFE_EXECUTE( SetInterfaceSecurity( pCallResult, m_pAuthIdentity ) );
  867. // keep on retring until we get the control or tries reached max
  868. LONG lStatus = 0;
  869. for ( DWORD dw = 0; dw < MAX_TIMEOUT_RETRIES; dw++ )
  870. {
  871. // get the call status
  872. hr = pCallResult->GetCallStatus( 0, &lStatus );
  873. if ( SUCCEEDED( hr ) )
  874. break;
  875. else if ( hr == WBEM_S_TIMEDOUT )
  876. continue;
  877. else if ( FAILED( hr ) )
  878. _com_issue_error( hr );
  879. }
  880. // check if time out max. retries finished
  881. if ( dw == MAX_TIMEOUT_RETRIES )
  882. _com_issue_error( hr );
  883. // now get the result object
  884. SAFE_EXECUTE( pCallResult->GetResultObject( MAX_TERMINATE_TIMEOUT, &pOutParams ) );
  885. // get the return value of the result object
  886. DWORD dwReturnValue = 0;
  887. if ( PropertyGet( pOutParams, WMI_RETURNVALUE, dwReturnValue ) == FALSE )
  888. _com_issue_error( ERROR_INTERNAL_ERROR );
  889. // now check the return value
  890. // if should be zero .. if not .. failed
  891. if ( dwReturnValue != 0 )
  892. {
  893. //
  894. // format the message and set the reason
  895. // frame the error error message depending on the error
  896. if ( dwReturnValue == 2 )
  897. {
  898. SetLastError( STG_E_ACCESSDENIED );
  899. SaveLastError();
  900. }
  901. else if ( dwReturnValue == 3 )
  902. {
  903. SetLastError( ERROR_DS_INSUFF_ACCESS_RIGHTS );
  904. SaveLastError();
  905. }
  906. else
  907. {
  908. CHString str;
  909. str.Format( ERROR_UNABLE_TO_TERMINATE, dwReturnValue );
  910. SetReason( str );
  911. }
  912. }
  913. else
  914. {
  915. // everything went successfully ... process terminated successfully
  916. bResult = TRUE;
  917. }
  918. }
  919. catch( _com_error& e )
  920. {
  921. // save the error message and mark as failure
  922. WMISaveError( e );
  923. bResult = FALSE;
  924. }
  925. // release the in and out params references
  926. SAFE_RELEASE( pInParams );
  927. SAFE_RELEASE( pOutParams );
  928. SAFE_RELEASE( pCallResult );
  929. // return the result
  930. return bResult;
  931. }
  932. // ***************************************************************************
  933. // Routine Description:
  934. //
  935. // Arguments:
  936. // [ out ] dwMatchTaskToKill :
  937. //
  938. // Return Value:
  939. // ***************************************************************************
  940. LONG CTaskKill::MatchTaskToKill( DWORD& dwMatchedIndex )
  941. {
  942. // local variables
  943. LONG lCount = 0;
  944. DWORD dwLength = 0;
  945. LPCWSTR pwsz = NULL;
  946. LPCWSTR pwszTask = NULL;
  947. // check if this task has to be killed or not
  948. lCount = DynArrayGetCount( m_arrTasksToKill );
  949. for( LONG lIndex = 0; lIndex < lCount; lIndex++ )
  950. {
  951. // get the task specified
  952. pwszTask = DynArrayItemAsString( m_arrTasksToKill, lIndex );
  953. if ( pwszTask == NULL )
  954. return -1;
  955. // check with process id first ( only if task is in numeric format )
  956. dwMatchedIndex = TASK_PID;
  957. if ( IsNumeric(pwszTask, 10, FALSE) && (m_dwProcessId == (DWORD) AsLong(pwszTask, 10)) )
  958. return lIndex; // specified task matched with current process id
  959. // determine the no. of characters to compare
  960. dwLength = 0;
  961. pwsz = FindChar( pwszTask, L'*', 0 );
  962. if ( pwsz != NULL )
  963. {
  964. // '*' - wildcard is specified in the image name
  965. // so, determine the no. of characters to compare
  966. // but before that check the length of the string pointer from '*'
  967. // it should be 1 - meaning the '*' can be specified only at the end
  968. // but not in the middle
  969. if ( lstrlen( pwsz ) == 1 )
  970. {
  971. dwLength = lstrlen( pwszTask ) - lstrlen( pwsz );
  972. }
  973. }
  974. // check with image name
  975. dwMatchedIndex = TASK_IMAGENAME;
  976. if ( StringCompare( m_strImageName, pwszTask, TRUE, dwLength ) == 0 )
  977. return lIndex; // specified task mathed with the image name
  978. }
  979. // return the index
  980. return -1;
  981. }
  982. // ***************************************************************************
  983. // Routine Description:
  984. // Invokes the appropriate kill function based on the mode of termination
  985. //
  986. // Arguments:
  987. // [ in ] bForce : mode of termination (silent/force)
  988. //
  989. // Return Value:
  990. // TRUE upon successfull and FALSE if failed
  991. // ***************************************************************************
  992. BOOL CTaskKill::CanTerminate()
  993. {
  994. // local variables
  995. DWORD dw = 0;
  996. DWORD dwCount = 0;
  997. LPCWSTR pwszTaskToTerminate = NULL;
  998. //
  999. // prepare a list of os critical tasks
  1000. LPCWSTR pwszTasks[] = {
  1001. PROCESS_CSRSS_EXE,
  1002. PROCESS_SMSS_EXE,
  1003. PROCESS_SERVICES_EXE,
  1004. PROCESS_WINLOGON_EXE
  1005. };
  1006. // process id with 0 cannot be terminated
  1007. if ( m_dwProcessId == 0 )
  1008. {
  1009. SetReason( ERROR_CRITICAL_SYSTEM_PROCESS );
  1010. return FALSE; // task should not be terminated
  1011. }
  1012. // the process cannot be terminated itself
  1013. if ( m_dwProcessId == m_dwCurrentPid )
  1014. {
  1015. SetReason( ERROR_CANNOT_KILL_ITSELF );
  1016. return FALSE;
  1017. }
  1018. // get the task name which user is trying to terminate
  1019. pwszTaskToTerminate = DynArrayItemAsString( m_arrRecord, TASK_IMAGENAME );
  1020. if ( pwszTaskToTerminate == NULL )
  1021. {
  1022. SetLastError( STG_E_UNKNOWN );
  1023. SaveLastError();
  1024. return FALSE; // task should not be terminated
  1025. }
  1026. // check if user is trying to terminate the os critical task
  1027. dwCount = SIZE_OF_ARRAY( pwszTasks );
  1028. for( dw = 0; dw < dwCount; dw++ )
  1029. {
  1030. if ( StringCompare( pwszTasks[ dw ], pwszTaskToTerminate, TRUE, 0 ) == 0 )
  1031. {
  1032. SetReason( ERROR_CRITICAL_SYSTEM_PROCESS );
  1033. return FALSE; // task should not be terminated
  1034. }
  1035. }
  1036. // task can be terminated
  1037. return TRUE;
  1038. }
  1039. // ***************************************************************************
  1040. // Routine Description:
  1041. //
  1042. // Arguments:
  1043. //
  1044. // Return Value:
  1045. // Process id as DWORD
  1046. //
  1047. // ***************************************************************************
  1048. VOID CTaskKill::SaveData( IWbemClassObject* pWmiObject )
  1049. {
  1050. // local variables
  1051. CHString str;
  1052. DWORD dwValue = 0;
  1053. // process id
  1054. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_PROCESSID, m_dwProcessId );
  1055. DynArraySetDWORD( m_arrRecord, TASK_PID, m_dwProcessId );
  1056. // image name
  1057. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_IMAGENAME, m_strImageName );
  1058. DynArraySetString( m_arrRecord, TASK_IMAGENAME, m_strImageName, 0 );
  1059. // object path
  1060. PropertyGet( pWmiObject, WIN32_PROCESS_SYSPROPERTY_PATH, str );
  1061. DynArraySetString( m_arrRecord, TASK_OBJPATH, str, 0 );
  1062. // host name
  1063. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_COMPUTER, str );
  1064. DynArraySetString( m_arrRecord, TASK_HOSTNAME, str, 0 );
  1065. // parent process id
  1066. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_PARENTPROCESSID, dwValue, 0 );
  1067. DynArraySetDWORD( m_arrRecord, TASK_CREATINGPROCESSID, dwValue );
  1068. // user context
  1069. SetUserContext( pWmiObject );
  1070. // cpu time
  1071. SetCPUTime( pWmiObject );
  1072. // window title
  1073. SetWindowTitle( pWmiObject );
  1074. // services
  1075. SetServicesInfo( pWmiObject );
  1076. // modules
  1077. SetModulesInfo( pWmiObject );
  1078. // check if the tree termination is requested
  1079. if ( m_bTree == TRUE )
  1080. {
  1081. // session id
  1082. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_SESSION, dwValue, 0 );
  1083. DynArraySetDWORD( m_arrRecord, TASK_SESSION, dwValue );
  1084. // status
  1085. SetStatus( pWmiObject );
  1086. // mem usage
  1087. SetMemUsage( pWmiObject );
  1088. }
  1089. else
  1090. {
  1091. //
  1092. // status, session id, memory usage
  1093. // property retrieval is built into WMI
  1094. //
  1095. }
  1096. }
  1097. // ***************************************************************************
  1098. // Routine Description:
  1099. //
  1100. // Arguments:
  1101. //
  1102. // Return Value:
  1103. //
  1104. // ***************************************************************************
  1105. VOID CTaskKill::SetUserContext( IWbemClassObject* pWmiObject )
  1106. {
  1107. // local variables
  1108. HRESULT hr;
  1109. CHString str;
  1110. CHString strPath;
  1111. CHString strDomain;
  1112. CHString strUserName;
  1113. IWbemClassObject* pOutParams = NULL;
  1114. // check if user name has to be retrieved or not
  1115. if ( m_bNeedUserContextInfo == FALSE )
  1116. return;
  1117. //
  1118. // for getting the user first we will try with API
  1119. // it at all API fails, we will try to get the same information from WMI
  1120. //
  1121. // get the user name
  1122. if ( LoadUserNameFromWinsta( strDomain, strUserName ) == TRUE )
  1123. {
  1124. // format the user name
  1125. str.Format( L"%s\\%s", strDomain, strUserName );
  1126. }
  1127. else
  1128. {
  1129. try
  1130. {
  1131. // user name has to be retrieved - get the path of the current object
  1132. hr = PropertyGet( pWmiObject, WIN32_PROCESS_SYSPROPERTY_PATH, strPath );
  1133. if ( FAILED( hr ) || strPath.GetLength() == 0 )
  1134. return;
  1135. // execute the GetOwner method and get the user name
  1136. // under which the current process is executing
  1137. hr = m_pWbemServices->ExecMethod( _bstr_t( strPath ),
  1138. _bstr_t( WIN32_PROCESS_METHOD_GETOWNER ), 0, NULL, NULL, &pOutParams, NULL );
  1139. if ( FAILED( hr ) )
  1140. return;
  1141. // get the domain and user values from out params object
  1142. // NOTE: do not check the results
  1143. PropertyGet( pOutParams, GETOWNER_RETURNVALUE_DOMAIN, strDomain, L"" );
  1144. PropertyGet( pOutParams, GETOWNER_RETURNVALUE_USER, strUserName, L"" );
  1145. // get the value
  1146. if ( strDomain.GetLength() != 0 )
  1147. str.Format( L"%s\\%s", strDomain, strUserName );
  1148. else if ( strUserName.GetLength() != 0 )
  1149. str = strUserName;
  1150. }
  1151. catch( ... )
  1152. {
  1153. return;
  1154. }
  1155. }
  1156. // save the info
  1157. DynArraySetString( m_arrRecord, TASK_USERNAME, str, 0 );
  1158. }
  1159. // ***************************************************************************
  1160. // Routine Description:
  1161. //
  1162. // Arguments:
  1163. //
  1164. // Return Value:
  1165. //
  1166. // ***************************************************************************
  1167. VOID CTaskKill::SetCPUTime( IWbemClassObject* pWmiObject )
  1168. {
  1169. // local variables
  1170. CHString str;
  1171. ULONGLONG ullCPUTime = 0;
  1172. ULONGLONG ullUserTime = 0;
  1173. ULONGLONG ullKernelTime = 0;
  1174. // get the KernelModeTime value
  1175. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_KERNELMODETIME, ullKernelTime );
  1176. // get the user mode time
  1177. PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_USERMODETIME, ullUserTime );
  1178. // calculate the CPU time
  1179. ullCPUTime = ullUserTime + ullKernelTime;
  1180. // now convert the long time into hours format
  1181. TIME_FIELDS time;
  1182. RtlTimeToElapsedTimeFields ( (LARGE_INTEGER* ) &ullCPUTime, &time );
  1183. // convert the days into hours
  1184. time.Hour = static_cast<CSHORT>( time.Hour + static_cast<SHORT>( time.Day * 24 ) );
  1185. // prepare into time format ( user locale specific time seperator )
  1186. str.Format( L"%d:%02d:%02d", time.Hour, time.Minute, time.Second );
  1187. // save the info
  1188. DynArraySetString( m_arrRecord, TASK_CPUTIME, str, 0 );
  1189. }
  1190. // ***************************************************************************
  1191. // Routine Description:
  1192. //
  1193. // Arguments:
  1194. //
  1195. // Return Value:
  1196. //
  1197. // ***************************************************************************
  1198. VOID CTaskKill::SetWindowTitle( IWbemClassObject* pWmiObject )
  1199. {
  1200. // local variables
  1201. LONG lTemp = 0;
  1202. HWND hWnd = NULL;
  1203. LPCTSTR pszTemp = NULL;
  1204. // get the window details ... window station, desktop, window title
  1205. // NOTE: This will work only for local system
  1206. lTemp = DynArrayFindDWORDEx( m_arrWindowTitles, CTaskKill::twiProcessId, m_dwProcessId );
  1207. if ( lTemp != -1 )
  1208. {
  1209. // save the window title
  1210. pszTemp = DynArrayItemAsString2( m_arrWindowTitles, lTemp, CTaskKill::twiTitle );
  1211. if ( pszTemp != NULL )
  1212. DynArraySetString( m_arrRecord, TASK_WINDOWTITLE, pszTemp, 0 );
  1213. // save the window station
  1214. pszTemp = DynArrayItemAsString2( m_arrWindowTitles, lTemp, CTaskKill::twiWinSta );
  1215. if ( pszTemp != NULL )
  1216. DynArraySetString( m_arrRecord, TASK_WINSTA, pszTemp, 0 );
  1217. // save the desktop information
  1218. pszTemp = DynArrayItemAsString2( m_arrWindowTitles, lTemp, CTaskKill::twiDesktop );
  1219. if ( pszTemp != NULL )
  1220. DynArraySetString( m_arrRecord, TASK_DESK, pszTemp, 0 );
  1221. // save the window handle also
  1222. hWnd = (HWND) DynArrayItemAsHandle2( m_arrWindowTitles, lTemp, CTaskKill::twiHandle );
  1223. if ( hWnd != NULL )
  1224. DynArraySetHandle( m_arrRecord, TASK_HWND, hWnd );
  1225. }
  1226. }
  1227. // ***************************************************************************
  1228. // Routine Description:
  1229. //
  1230. // Arguments:
  1231. //
  1232. // Return Value:
  1233. //
  1234. // ***************************************************************************
  1235. VOID CTaskKill::SetStatus( IWbemClassObject* pWmiObject )
  1236. {
  1237. // local variables
  1238. DWORD dwThreads = 0;
  1239. // get the threads count for the process
  1240. if ( PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_THREADS, dwThreads ) == FALSE )
  1241. return;
  1242. // now determine the status
  1243. if ( dwThreads > 0 )
  1244. DynArraySetString( m_arrRecord, TASK_STATUS, VALUE_RUNNING, 0 );
  1245. else
  1246. DynArraySetString( m_arrRecord, TASK_STATUS, VALUE_NOTRESPONDING, 0 );
  1247. }
  1248. // ***************************************************************************
  1249. // Routine Description:
  1250. //
  1251. // Arguments:
  1252. //
  1253. // Return Value:
  1254. //
  1255. // ***************************************************************************
  1256. VOID CTaskKill::SetMemUsage( IWbemClassObject* pWmiObject )
  1257. {
  1258. // local variables
  1259. CHString str;
  1260. NTSTATUS ntstatus;
  1261. ULONGLONG ullMemUsage = 0;
  1262. LARGE_INTEGER liTemp = { 0, 0 };
  1263. CHAR szTempBuffer[ 33 ] = "\0";
  1264. try
  1265. {
  1266. // NOTE:
  1267. // ----
  1268. // The max. value of
  1269. // (2 ^ 64) - 1 = "18,446,744,073,709,600,000 K" (29 chars).
  1270. //
  1271. // so, the buffer size to store the number is fixed as 32 characters
  1272. // which is more than the 29 characters in actuals
  1273. // set the default value
  1274. DynArraySetString( m_arrRecord, TASK_MEMUSAGE, L"0", 0 );
  1275. // get the KernelModeTime value
  1276. if ( PropertyGet( pWmiObject, WIN32_PROCESS_PROPERTY_MEMUSAGE, ullMemUsage ) == FALSE )
  1277. return;
  1278. // convert the value into K Bytes
  1279. ullMemUsage /= 1024;
  1280. // now again convert the value from ULONGLONG to string and check the result
  1281. liTemp.QuadPart = ullMemUsage;
  1282. ntstatus = RtlLargeIntegerToChar( &liTemp, 10, SIZE_OF_ARRAY( szTempBuffer ), szTempBuffer );
  1283. if ( ! NT_SUCCESS( ntstatus ) )
  1284. return;
  1285. // now copy this info into UNICODE buffer
  1286. str = szTempBuffer;
  1287. // save the id
  1288. DynArraySetString( m_arrRecord, TASK_MEMUSAGE, str, 0 );
  1289. }
  1290. catch( ... )
  1291. {
  1292. SetLastError( E_OUTOFMEMORY );
  1293. SaveLastError();
  1294. }
  1295. }
  1296. // ***************************************************************************
  1297. // Routine Description:
  1298. //
  1299. // Arguments:
  1300. //
  1301. // Return Value:
  1302. //
  1303. // ***************************************************************************
  1304. VOID CTaskKill::SetServicesInfo( IWbemClassObject* pWmiObject )
  1305. {
  1306. // local variables
  1307. HRESULT hr;
  1308. CHString strQuery;
  1309. CHString strService;
  1310. ULONG ulReturned = 0;
  1311. BOOL bResult = FALSE;
  1312. BOOL bCanExit = FALSE;
  1313. TARRAY arrServices = NULL;
  1314. IEnumWbemClassObject* pEnumServices = NULL;
  1315. IWbemClassObject* pObjects[ MAX_ENUM_SERVICES ];
  1316. // check whether we need to gather services info or not .. if not skip
  1317. if ( m_bNeedServicesInfo == FALSE )
  1318. return;
  1319. // create array
  1320. arrServices = CreateDynamicArray();
  1321. if ( arrServices == NULL )
  1322. {
  1323. SetLastError( E_OUTOFMEMORY );
  1324. SaveLastError();
  1325. return;
  1326. }
  1327. //
  1328. // for getting the services info first we will try with the one we got from API
  1329. // it at all API fails, we will try to get the same information from WMI
  1330. //
  1331. // check whether API returned services or not
  1332. if ( m_pServicesInfo != NULL )
  1333. {
  1334. // get the service names related to the current process
  1335. // identify all the services related to the current process ( based on the PID )
  1336. // and save the info
  1337. for ( DWORD dw = 0; dw < m_dwServicesCount; dw++ )
  1338. {
  1339. // compare the PID's
  1340. if ( m_dwProcessId == m_pServicesInfo[ dw ].ServiceStatusProcess.dwProcessId )
  1341. {
  1342. // this service is related with the current process ... store service name
  1343. DynArrayAppendString( arrServices, m_pServicesInfo[ dw ].lpServiceName, 0 );
  1344. }
  1345. }
  1346. }
  1347. else
  1348. {
  1349. try
  1350. {
  1351. // init the objects to NULL's
  1352. for( DWORD dw = 0; dw < MAX_ENUM_SERVICES; dw++ )
  1353. pObjects[ dw ] = NULL;
  1354. // prepare the query
  1355. strQuery.Format( WMI_SERVICE_QUERY, m_dwProcessId );
  1356. // execute the query
  1357. hr = m_pWbemServices->ExecQuery( _bstr_t( WMI_QUERY_TYPE ), _bstr_t( strQuery ),
  1358. WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumServices );
  1359. // check the result
  1360. if ( FAILED( hr ) )
  1361. _com_issue_error( hr );
  1362. // set the security
  1363. hr = SetInterfaceSecurity( pEnumServices, m_pAuthIdentity );
  1364. if ( FAILED( hr ) )
  1365. _com_issue_error( hr );
  1366. // loop thru the service instances
  1367. do
  1368. {
  1369. // get the object ... wait
  1370. // NOTE: one-by-one
  1371. hr = pEnumServices->Next( WBEM_INFINITE, MAX_ENUM_SERVICES, pObjects, &ulReturned );
  1372. if ( hr == WBEM_S_FALSE )
  1373. {
  1374. // we've reached the end of enumeration .. set the flag
  1375. bCanExit = TRUE;
  1376. }
  1377. else if ( hr == WBEM_S_TIMEDOUT || FAILED( hr ) )
  1378. {
  1379. //
  1380. // some error has occured ... oooppps
  1381. // exit from the loop
  1382. break;
  1383. }
  1384. // loop thru the objects and save the info
  1385. for( ULONG ul = 0; ul < ulReturned; ul++ )
  1386. {
  1387. // get the value of the property
  1388. bResult = PropertyGet( pObjects[ ul ], WIN32_SERVICE_PROPERTY_NAME, strService );
  1389. if ( bResult == TRUE )
  1390. DynArrayAppendString( arrServices, strService, 0 );
  1391. // release the interface
  1392. SAFE_RELEASE( pObjects[ ul ] );
  1393. }
  1394. } while ( bCanExit == FALSE );
  1395. }
  1396. catch( _com_error& e )
  1397. {
  1398. // save the error
  1399. WMISaveError( e );
  1400. }
  1401. // release the objects to NULL's
  1402. for( DWORD dw = 0; dw < MAX_ENUM_SERVICES; dw++ )
  1403. {
  1404. // release all the objects
  1405. SAFE_RELEASE( pObjects[ dw ] );
  1406. }
  1407. // now release the enumeration object
  1408. SAFE_RELEASE( pEnumServices );
  1409. }
  1410. // save and return
  1411. DynArraySetEx( m_arrRecord, TASK_SERVICES, arrServices );
  1412. }
  1413. // ***************************************************************************
  1414. // Routine Description:
  1415. //
  1416. // Arguments:
  1417. //
  1418. // Return Value:
  1419. //
  1420. // ***************************************************************************
  1421. BOOL CTaskKill::SetModulesInfo( IWbemClassObject* pWmiObject )
  1422. {
  1423. // local variables
  1424. LONG lPos = 0;
  1425. BOOL bResult = FALSE;
  1426. TARRAY arrModules = NULL;
  1427. // check whether we need to get the modules or not
  1428. if ( m_bNeedModulesInfo == FALSE )
  1429. return TRUE;
  1430. // allocate for memory
  1431. arrModules = CreateDynamicArray();
  1432. if ( arrModules == NULL )
  1433. {
  1434. SetLastError( E_OUTOFMEMORY );
  1435. SaveLastError();
  1436. return FALSE;
  1437. }
  1438. // the way we get the modules information is different for local remote
  1439. // so depending that call appropriate function
  1440. if ( m_bLocalSystem == TRUE && m_bUseRemote == FALSE )
  1441. {
  1442. // enumerate the modules for the current process
  1443. bResult = LoadModulesOnLocal( arrModules );
  1444. }
  1445. else
  1446. {
  1447. // identify the modules information for the current process ... remote system
  1448. bResult = GetModulesOnRemote( arrModules );
  1449. }
  1450. // check the result
  1451. if ( bResult == TRUE )
  1452. {
  1453. // check if the modules list contains the imagename also. If yes remove that entry
  1454. lPos = DynArrayFindString( arrModules, m_strImageName, TRUE, 0 );
  1455. if ( lPos != -1 )
  1456. {
  1457. // remove the entry
  1458. DynArrayRemove( arrModules, lPos );
  1459. }
  1460. }
  1461. // save the modules information to the array
  1462. // NOTE: irrespective of whether enumeration is success or not we will add the array
  1463. DynArraySetEx( m_arrRecord, TASK_MODULES, arrModules );
  1464. // return
  1465. return bResult;
  1466. }
  1467. // ***************************************************************************
  1468. // Routine Description:
  1469. //
  1470. // Arguments:
  1471. //
  1472. // Return Value:
  1473. //
  1474. // ***************************************************************************
  1475. BOOL CTaskKill::LoadModulesOnLocal( TARRAY arrModules )
  1476. {
  1477. // local variables
  1478. LONG lPos = 0;
  1479. BOOL bResult = FALSE;
  1480. HANDLE hProcess = NULL;
  1481. // check the input values
  1482. if ( arrModules == NULL )
  1483. return FALSE;
  1484. // open the process handle
  1485. hProcess = OpenProcess( PROCESS_VM_READ | PROCESS_QUERY_INFORMATION, FALSE, m_dwProcessId );
  1486. if ( hProcess == NULL )
  1487. {
  1488. // failed in getting the process handle
  1489. return FALSE;
  1490. }
  1491. #ifndef _WIN64
  1492. bResult = EnumerateLoadedModules( hProcess, EnumLoadedModulesProc, arrModules );
  1493. #else
  1494. bResult = EnumerateLoadedModules64( hProcess, EnumLoadedModulesProc64, arrModules );
  1495. #endif
  1496. // close the process handle .. we dont need this furthur
  1497. CloseHandle( hProcess );
  1498. hProcess = NULL;
  1499. // return
  1500. return bResult;
  1501. }
  1502. // ***************************************************************************
  1503. // Routine Description:
  1504. //
  1505. // Arguments:
  1506. //
  1507. // Return Value:
  1508. //
  1509. // ***************************************************************************
  1510. BOOL CTaskKill::GetModulesOnRemote( TARRAY arrModules )
  1511. {
  1512. // local variables
  1513. LONG lPos = 0;
  1514. DWORD dwLength = 0;
  1515. DWORD dwOffset = 0;
  1516. DWORD dwInstance = 0;
  1517. PPERF_OBJECT_TYPE pot = NULL;
  1518. PPERF_OBJECT_TYPE potImages = NULL;
  1519. PPERF_INSTANCE_DEFINITION pidImages = NULL;
  1520. PPERF_COUNTER_BLOCK pcbImages = NULL;
  1521. PPERF_OBJECT_TYPE potAddressSpace = NULL;
  1522. PPERF_INSTANCE_DEFINITION pidAddressSpace = NULL;
  1523. PPERF_COUNTER_BLOCK pcbAddressSpace = NULL;
  1524. PPERF_COUNTER_DEFINITION pcd = NULL;
  1525. // check the input values
  1526. if ( arrModules == NULL )
  1527. return FALSE;
  1528. // check whether the performance object exists or not
  1529. // if doesn't exists, get the same using WMI
  1530. if ( m_pdb == NULL )
  1531. {
  1532. // invoke the WMI method
  1533. return GetModulesOnRemoteEx( arrModules );
  1534. }
  1535. // get the perf object types
  1536. pot = (PPERF_OBJECT_TYPE) ( (LPBYTE) m_pdb + m_pdb->HeaderLength );
  1537. for( DWORD dw = 0; dw < m_pdb->NumObjectTypes; dw++ )
  1538. {
  1539. if ( pot->ObjectNameTitleIndex == 740 )
  1540. potImages = pot;
  1541. else if ( pot->ObjectNameTitleIndex == 786 )
  1542. potAddressSpace = pot;
  1543. // move to the next object
  1544. dwOffset = pot->TotalByteLength;
  1545. if( dwOffset != 0 )
  1546. pot = ( (PPERF_OBJECT_TYPE) ((PBYTE) pot + dwOffset));
  1547. }
  1548. // check whether we got both the object types or not
  1549. if ( potImages == NULL || potAddressSpace == NULL )
  1550. return FALSE;
  1551. // find the offset of the process id in the address space object type
  1552. // get the first counter definition of address space object
  1553. pcd = (PPERF_COUNTER_DEFINITION) ( (LPBYTE) potAddressSpace + potAddressSpace->HeaderLength);
  1554. // loop thru the counters and find the offset
  1555. dwOffset = 0;
  1556. for( DWORD dw = 0; dw < potAddressSpace->NumCounters; dw++)
  1557. {
  1558. // 784 is the counter for process id
  1559. if ( pcd->CounterNameTitleIndex == 784 )
  1560. {
  1561. dwOffset = pcd->CounterOffset;
  1562. break;
  1563. }
  1564. // next counter
  1565. pcd = ( (PPERF_COUNTER_DEFINITION) ( (LPBYTE) pcd + pcd->ByteLength) );
  1566. }
  1567. // check whether we got the offset or not
  1568. // if not, we are unsuccessful
  1569. if ( dwOffset == 0 )
  1570. {
  1571. // set the error message
  1572. SetLastError( ERROR_ACCESS_DENIED );
  1573. SaveLastError();
  1574. return FALSE;
  1575. }
  1576. // get the instances
  1577. pidImages = (PPERF_INSTANCE_DEFINITION) ( (LPBYTE) potImages + potImages->DefinitionLength );
  1578. pidAddressSpace = (PPERF_INSTANCE_DEFINITION) ( (LPBYTE) potAddressSpace + potAddressSpace->DefinitionLength );
  1579. // counter blocks
  1580. pcbImages = (PPERF_COUNTER_BLOCK) ( (LPBYTE) pidImages + pidImages->ByteLength );
  1581. pcbAddressSpace = (PPERF_COUNTER_BLOCK) ( (LPBYTE) pidAddressSpace + pidAddressSpace->ByteLength );
  1582. // find the instance number of the process which we are looking for
  1583. for( dwInstance = 0; dwInstance < (DWORD) potAddressSpace->NumInstances; dwInstance++ )
  1584. {
  1585. // sub-local variables
  1586. DWORD dwProcessId = 0;
  1587. // get the process id
  1588. dwProcessId = *((DWORD*) ( (LPBYTE) pcbAddressSpace + dwOffset ));
  1589. // now check if this is the process which we are looking for
  1590. if ( dwProcessId == m_dwProcessId )
  1591. break;
  1592. // continue looping thru other instances
  1593. pidAddressSpace = (PPERF_INSTANCE_DEFINITION) ( (LPBYTE) pcbAddressSpace + pcbAddressSpace->ByteLength );
  1594. pcbAddressSpace = (PPERF_COUNTER_BLOCK) ( (LPBYTE) pidAddressSpace + pidAddressSpace->ByteLength );
  1595. }
  1596. // check whether we got the instance or not
  1597. // if not, there are no modules for this process
  1598. if ( dwInstance == potAddressSpace->NumInstances )
  1599. return TRUE;
  1600. // now based the parent instance, collect all the modules
  1601. for( DWORD dw = 0; (LONG) dw < potImages->NumInstances; dw++)
  1602. {
  1603. // check the parent object instance number
  1604. if ( pidImages->ParentObjectInstance == dwInstance )
  1605. {
  1606. try
  1607. {
  1608. // sub-local variables
  1609. CHString str;
  1610. LPWSTR pwszTemp;
  1611. // get the buffer
  1612. pwszTemp = str.GetBufferSetLength( pidImages->NameLength + 10 ); // +10 to be on safe side
  1613. if ( pwszTemp == NULL )
  1614. {
  1615. SetLastError( E_OUTOFMEMORY );
  1616. SaveLastError();
  1617. return FALSE;
  1618. }
  1619. // get the instance name
  1620. lstrcpyn( pwszTemp, (LPWSTR) ( (LPBYTE) pidImages + pidImages->NameOffset ), pidImages->NameLength + 1 );
  1621. // release buffer
  1622. str.ReleaseBuffer();
  1623. // add the info the userdata ( for us we will get that in the form of an array
  1624. LONG lIndex = DynArrayAppendString( arrModules, str, 0 );
  1625. if ( lIndex == -1 )
  1626. {
  1627. // append is failed .. this could be because of lack of memory .. stop the enumeration
  1628. return FALSE;
  1629. }
  1630. }
  1631. catch( ... )
  1632. {
  1633. SetLastError( E_OUTOFMEMORY );
  1634. SaveLastError();
  1635. return FALSE;
  1636. }
  1637. }
  1638. // continue looping thru other instances
  1639. pidImages = (PPERF_INSTANCE_DEFINITION) ( (LPBYTE) pcbImages + pcbImages->ByteLength );
  1640. pcbImages = (PPERF_COUNTER_BLOCK) ( (LPBYTE) pidImages + pidImages->ByteLength );
  1641. }
  1642. return TRUE;
  1643. }
  1644. // ***************************************************************************
  1645. // Routine Description:
  1646. //
  1647. // Arguments:
  1648. //
  1649. // Return Value:
  1650. //
  1651. // ***************************************************************************
  1652. BOOL CTaskKill::GetModulesOnRemoteEx( TARRAY arrModules )
  1653. {
  1654. // local variables
  1655. HRESULT hr;
  1656. BOOL bStatus = FALSE;
  1657. CHString strQuery;
  1658. CHString strModule;
  1659. CHString strMessage;
  1660. CHString strFileName;
  1661. CHString strExtension;
  1662. ULONG ulReturned = 0;
  1663. BOOL bResult = FALSE;
  1664. BOOL bCanExit = FALSE;
  1665. LPCWSTR pwszPath = NULL;
  1666. IEnumWbemClassObject* pEnumModules = NULL;
  1667. IWbemClassObject* pObjects[ MAX_ENUM_MODULES ];
  1668. // check the input values
  1669. if ( arrModules == NULL )
  1670. return FALSE;
  1671. // get the path of the object from the tasks array
  1672. pwszPath = DynArrayItemAsString( m_arrRecord, TASK_OBJPATH );
  1673. if ( pwszPath == NULL )
  1674. return FALSE;
  1675. //determine the length of the module name ..
  1676. try
  1677. {
  1678. // init the objects to NULL's
  1679. for( DWORD dw = 0; dw < MAX_ENUM_MODULES; dw++ )
  1680. pObjects[ dw ] = NULL;
  1681. // prepare the query
  1682. strQuery.Format( WMI_MODULES_QUERY, pwszPath );
  1683. // preare and display the status message
  1684. bStatus = TRUE;
  1685. strMessage.Format( MSG_MODULESINFO_EX, m_dwProcessId );
  1686. PrintProgressMsg( m_hOutput, strMessage + L" --", m_csbi );
  1687. // execute the query
  1688. hr = m_pWbemServices->ExecQuery( _bstr_t( WMI_QUERY_TYPE ), _bstr_t( strQuery ),
  1689. WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &pEnumModules );
  1690. // check the result
  1691. if ( FAILED( hr ) )
  1692. _com_issue_error( hr );
  1693. // set the security
  1694. hr = SetInterfaceSecurity( pEnumModules, m_pAuthIdentity );
  1695. if ( FAILED( hr ) )
  1696. _com_issue_error( hr );
  1697. // loop thru the instances
  1698. do
  1699. {
  1700. // get the object ... wait
  1701. // NOTE: one-by-one
  1702. hr = pEnumModules->Next( WBEM_INFINITE, MAX_ENUM_MODULES, pObjects, &ulReturned );
  1703. if ( hr == WBEM_S_FALSE )
  1704. {
  1705. // we've reached the end of enumeration .. set the flag
  1706. bCanExit = TRUE;
  1707. }
  1708. else if ( hr == WBEM_S_TIMEDOUT || FAILED( hr ))
  1709. {
  1710. // some error has occured ... oooppps
  1711. WMISaveError( hr );
  1712. SetLastError( STG_E_UNKNOWN );
  1713. break;
  1714. }
  1715. // reset the counter
  1716. bStatus = bStatus ? FALSE : TRUE;
  1717. PrintProgressMsg( m_hOutput, strMessage + (( bStatus ) ? L" --" : L" |"), m_csbi );
  1718. // loop thru the objects and save the info
  1719. for( ULONG ul = 0; ul < ulReturned; ul++ )
  1720. {
  1721. // get the file name
  1722. bResult = PropertyGet( pObjects[ ul ], CIM_DATAFILE_PROPERTY_FILENAME, strFileName );
  1723. if ( bResult == FALSE )
  1724. continue;
  1725. // get the extension
  1726. bResult = PropertyGet( pObjects[ ul ], CIM_DATAFILE_PROPERTY_EXTENSION, strExtension );
  1727. if ( bResult == FALSE )
  1728. continue;
  1729. // format the module name
  1730. strModule.Format( L"%s.%s", strFileName, strExtension );
  1731. // add the info the userdata ( for us we will get that in the form of an array
  1732. LONG lIndex = DynArrayAppendString( arrModules, strModule, 0 );
  1733. if ( lIndex == -1 )
  1734. {
  1735. // append is failed .. this could be because of lack of memory .. stop the enumeration
  1736. PrintProgressMsg( m_hOutput, NULL, m_csbi );
  1737. return FALSE;
  1738. }
  1739. // release the interface
  1740. SAFE_RELEASE( pObjects[ ul ] );
  1741. }
  1742. } while ( bCanExit == FALSE );
  1743. // erase the status message
  1744. PrintProgressMsg( m_hOutput, NULL, m_csbi );
  1745. }
  1746. catch( _com_error& e )
  1747. {
  1748. // save the error
  1749. WMISaveError( e );
  1750. return FALSE;
  1751. }
  1752. catch( ... )
  1753. {
  1754. // out of memory
  1755. WMISaveError( E_OUTOFMEMORY );
  1756. return FALSE;
  1757. }
  1758. // release the objects to NULL's
  1759. for( DWORD dw = 0; dw < MAX_ENUM_MODULES; dw++ )
  1760. {
  1761. // release all the objects
  1762. SAFE_RELEASE( pObjects[ dw ] );
  1763. }
  1764. // now release the enumeration object
  1765. SAFE_RELEASE( pEnumModules );
  1766. // return
  1767. return TRUE;
  1768. }
  1769. // ***************************************************************************
  1770. // Routine Description:
  1771. //
  1772. // Arguments:
  1773. //
  1774. // Return Value:
  1775. //
  1776. // ***************************************************************************
  1777. #ifndef _WIN64
  1778. BOOL EnumLoadedModulesProc( LPSTR lpszModuleName, ULONG ulModuleBase, ULONG ulModuleSize, PVOID pUserData )
  1779. #else
  1780. BOOL EnumLoadedModulesProc64( LPSTR lpszModuleName, DWORD64 ulModuleBase, ULONG ulModuleSize, PVOID pUserData )
  1781. #endif
  1782. {
  1783. // local variables
  1784. CHString str;
  1785. LONG lIndex = 0;
  1786. TARRAY arrModules = NULL;
  1787. // check the input values
  1788. if ( lpszModuleName == NULL || pUserData == NULL )
  1789. return FALSE;
  1790. // get the array pointer into the local variable
  1791. arrModules = (TARRAY) pUserData;
  1792. try
  1793. {
  1794. // copy the module name into the local string variable
  1795. // ( conversion from multibyte to unicode will automatically take place )
  1796. str = lpszModuleName;
  1797. // add the info the userdata ( for us we will get that in the form of an array
  1798. lIndex = DynArrayAppendString( arrModules, str, 0 );
  1799. if ( lIndex == -1 )
  1800. {
  1801. // append is failed .. this could be because of lack of memory .. stop the enumeration
  1802. return FALSE;
  1803. }
  1804. }
  1805. catch( ... )
  1806. {
  1807. // out of memory stop the enumeration
  1808. return FALSE;
  1809. }
  1810. // success .. continue the enumeration
  1811. return TRUE;
  1812. }