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.

1383 lines
38 KiB

  1. // *********************************************************************************
  2. //
  3. // Copyright (c) Microsoft Corporation
  4. //
  5. // Module Name:
  6. //
  7. // TaskKill.cpp
  8. //
  9. // Abstract:
  10. //
  11. // This module implements the termination of processes runnging on either local
  12. // or remote system
  13. //
  14. // Syntax:
  15. // ------
  16. // TaskKill.exe [ -s server [ -u username [ -p password ] ] ] [ -f ] [ -t ]
  17. // [ -fi filter ] [ -im imagename | -pid pid ]
  18. //
  19. // Author:
  20. //
  21. // Sunil G.V.N. Murali ([email protected]) 26-Nov-2000
  22. //
  23. // Revision History:
  24. //
  25. // Sunil G.V.N. Murali ([email protected]) 26-Nov-2000 : Created It.
  26. //
  27. // *********************************************************************************
  28. #include "pch.h"
  29. #include "wmi.h"
  30. #include "TaskKill.h"
  31. //
  32. // local structures
  33. //
  34. typedef struct __tagWindowTitles
  35. {
  36. LPWSTR pwszDesk;
  37. LPWSTR pwszWinsta;
  38. BOOL bFirstLoop;
  39. TARRAY arrWindows;
  40. } TWINDOWTITLES, *PTWINDOWTITLES;
  41. //
  42. // private functions ... prototypes
  43. //
  44. BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam );
  45. BOOL CALLBACK EnumDesktopsFunc( LPWSTR pwsz, LPARAM lParam );
  46. BOOL CALLBACK EnumWindowStationsFunc( LPWSTR pwsz, LPARAM lParam );
  47. BOOL CALLBACK EnumMessageWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam );
  48. BOOL GetPerfDataBlock( HKEY hKey, LPWSTR szObjectIndex, PPERF_DATA_BLOCK* ppdb );
  49. // ***************************************************************************
  50. // Routine Description:
  51. // This the entry point to this utility.
  52. //
  53. // Arguments:
  54. // [ in ] argc : argument(s) count specified at the command prompt
  55. // [ in ] argv : argument(s) specified at the command prompt
  56. //
  57. // Return Value:
  58. // The below are actually not return values but are the exit values
  59. // returned to the OS by this application
  60. // 0 : utility is successfull
  61. // 1 : utility failed
  62. // ***************************************************************************
  63. DWORD __cdecl _tmain( DWORD argc, LPCTSTR argv[] )
  64. {
  65. // local variables
  66. CTaskKill taskkill;
  67. BOOL bResult = FALSE;
  68. DWORD dwExitCode = 0;
  69. // initialize the taskkill utility
  70. if ( taskkill.Initialize() == FALSE )
  71. {
  72. CHString strBuffer;
  73. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  74. ShowMessage( stderr, strBuffer );
  75. EXIT_PROCESS( 1 );
  76. }
  77. // now do parse the command line options
  78. if ( taskkill.ProcessOptions( argc, argv ) == FALSE )
  79. {
  80. CHString strBuffer;
  81. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  82. ShowMessage( stderr, strBuffer );
  83. EXIT_PROCESS( 1 );
  84. }
  85. // check whether usage has to be displayed or not
  86. if ( taskkill.m_bUsage == TRUE )
  87. {
  88. // show the usage of the utility
  89. taskkill.Usage();
  90. // quit from the utility
  91. EXIT_PROCESS( 0 );
  92. }
  93. // now validate the filters and check the result of the filter validation
  94. if ( taskkill.ValidateFilters() == FALSE )
  95. {
  96. // invalid filter
  97. // SPECIAL:
  98. // -------
  99. // for the custom filter "pid" we are setting the tag "INFO:" before
  100. // the message in the filter validation part itself
  101. // so, check and display
  102. FILE* fp = NULL;
  103. CHString strInfo;
  104. CHString strBuffer;
  105. // ...
  106. fp = stdout;
  107. dwExitCode = 255; // info exit code
  108. strBuffer = GetReason();
  109. strInfo = TAG_INFORMATION;
  110. if ( strBuffer.Left( strInfo.GetLength() ) != strInfo )
  111. {
  112. fp = stderr;
  113. dwExitCode = 1;
  114. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  115. }
  116. // display the message
  117. ShowMessage( fp, strBuffer );
  118. EXIT_PROCESS( dwExitCode );
  119. }
  120. // enable the kill privileges
  121. if ( taskkill.EnableDebugPriv() == FALSE )
  122. {
  123. // show the error message and exit
  124. CHString strBuffer;
  125. SaveLastError();
  126. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  127. ShowMessage( stderr, strBuffer );
  128. EXIT_PROCESS( 1 );
  129. }
  130. // connect to the server
  131. bResult = taskkill.Connect();
  132. if ( bResult == FALSE )
  133. {
  134. // show the error message
  135. CHString strBuffer;
  136. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  137. ShowMessage( stderr, strBuffer );
  138. EXIT_PROCESS( 1 );
  139. }
  140. // load the data and check
  141. bResult = taskkill.LoadTasks();
  142. if ( bResult == FALSE )
  143. {
  144. // show the error message
  145. CHString strBuffer;
  146. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  147. ShowMessage( stderr, strBuffer );
  148. EXIT_PROCESS( 1 );
  149. }
  150. // invoke the actual termination and get the exit code
  151. bResult = taskkill.DoTerminate( dwExitCode );
  152. if ( bResult == FALSE )
  153. {
  154. // NOTE: the 'FALSE' return value indicates that some major problem has occured
  155. // while trying to terminate the process. Still the exit code will be determined by
  156. // the function only. Do not change/set the exit code value
  157. CHString strBuffer;
  158. strBuffer.Format( L"%s %s", TAG_ERROR, GetReason() );
  159. ShowMessage( stderr, strBuffer );
  160. }
  161. // exit from the utility
  162. EXIT_PROCESS( dwExitCode );
  163. }
  164. // ***************************************************************************
  165. // Routine Description:
  166. // connects to the remote as well as remote system's WMI
  167. //
  168. // Arguments:
  169. // NONE
  170. //
  171. // Return Value:
  172. // TRUE : if connection is successful
  173. // FALSE : if connection is unsuccessful
  174. //
  175. // ***************************************************************************
  176. BOOL CTaskKill::Connect()
  177. {
  178. // local variables
  179. BOOL bResult = FALSE;
  180. // release the existing auth identity structure
  181. WbemFreeAuthIdentity( &m_pAuthIdentity );
  182. // connect to WMI
  183. bResult = ConnectWmiEx( m_pWbemLocator,
  184. &m_pWbemServices, m_strServer, m_strUserName, m_strPassword,
  185. &m_pAuthIdentity, m_bNeedPassword, WMI_NAMESPACE_CIMV2, &m_bLocalSystem );
  186. // check the result of connection
  187. if ( bResult == FALSE )
  188. return FALSE;
  189. #ifndef _WIN64
  190. // determine the type of the platform if modules info is required
  191. if ( m_bLocalSystem == TRUE && m_bNeedModulesInfo == TRUE )
  192. {
  193. // sub-local variables
  194. DWORD dwPlatform = 0;
  195. // get the platform type
  196. dwPlatform = GetTargetPlatformEx( m_pWbemServices, m_pAuthIdentity );
  197. // if the platform is not 32-bit, error
  198. if ( dwPlatform != PLATFORM_X86 )
  199. {
  200. // let the tool use WMI calls instead of Win32 API
  201. m_bUseRemote = TRUE;
  202. }
  203. }
  204. #endif
  205. try
  206. {
  207. // check the local credentials and if need display warning
  208. if ( GetLastError() == WBEM_E_LOCAL_CREDENTIALS )
  209. {
  210. CHString str;
  211. WMISaveError( WBEM_E_LOCAL_CREDENTIALS );
  212. str.Format( L"\n%s %s", TAG_WARNING, GetReason() );
  213. ShowMessage( stdout, str );
  214. // get the next cursor position
  215. if ( m_hOutput != NULL )
  216. GetConsoleScreenBufferInfo( m_hOutput, &m_csbi );
  217. }
  218. // check the remote system version and its compatiblity
  219. if ( m_bLocalSystem == FALSE )
  220. {
  221. // check the version compatibility
  222. DWORD dwVersion = 0;
  223. dwVersion = GetTargetVersionEx( m_pWbemServices, m_pAuthIdentity );
  224. if ( IsCompatibleOperatingSystem( dwVersion ) == FALSE )
  225. {
  226. SetReason( ERROR_OS_INCOMPATIBLE );
  227. return FALSE;
  228. }
  229. }
  230. // save the server name
  231. m_strUNCServer = L"";
  232. if ( m_strServer.GetLength() != 0 )
  233. {
  234. // check whether the server name is in UNC format or not .. if not prepare it
  235. m_strUNCServer = m_strServer;
  236. if ( IsUNCFormat( m_strServer ) == FALSE )
  237. m_strUNCServer.Format( L"\\\\%s", m_strServer );
  238. }
  239. }
  240. catch( _com_error& e )
  241. {
  242. WMISaveError( e );
  243. return FALSE;
  244. }
  245. // return the result
  246. return TRUE;
  247. }
  248. // ***************************************************************************
  249. // Routine Description:
  250. // initiate the enumeration
  251. //
  252. // Arguments:
  253. // NONE
  254. //
  255. // Return Value:
  256. // TRUE : if successful
  257. // FALSE : if unsuccessful
  258. //
  259. // ***************************************************************************
  260. BOOL CTaskKill::LoadTasks()
  261. {
  262. // local variables
  263. HRESULT hr;
  264. // check the services object
  265. if ( m_pWbemServices == NULL )
  266. {
  267. SetLastError( STG_E_UNKNOWN );
  268. SaveLastError();
  269. return FALSE;
  270. }
  271. try
  272. {
  273. // do the optimization
  274. DoOptimization();
  275. // load the tasks from WMI based on generated query
  276. SAFE_RELEASE( m_pWbemEnumObjects );
  277. hr = m_pWbemServices->ExecQuery( _bstr_t( WMI_QUERY_TYPE ), _bstr_t( m_strQuery ),
  278. WBEM_FLAG_RETURN_IMMEDIATELY | WBEM_FLAG_FORWARD_ONLY, NULL, &m_pWbemEnumObjects );
  279. // check the result of the ExecQuery
  280. if ( FAILED( hr ) )
  281. {
  282. WMISaveError( hr );
  283. return FALSE;
  284. }
  285. // set the interface security and check the result
  286. hr = SetInterfaceSecurity( m_pWbemEnumObjects, m_pAuthIdentity );
  287. if ( FAILED( hr ) )
  288. {
  289. WMISaveError( hr );
  290. return FALSE;
  291. }
  292. //
  293. // get the reference to the Win32_Process class object
  294. // and then reference to the input parameters of the "Terminate" method
  295. IWbemClassObject* pObject = NULL;
  296. SAFE_EXECUTE( m_pWbemServices->GetObject(
  297. _bstr_t( CLASS_PROCESS ), 0, NULL, &pObject, NULL ) );
  298. SAFE_EXECUTE( pObject->GetMethod(
  299. _bstr_t( WIN32_PROCESS_METHOD_TERMINATE ), 0, &m_pWbemTerminateInParams, NULL ) );
  300. // release the WMI object
  301. SAFE_RELEASE( pObject );
  302. //
  303. // retrieve the window title information if needed
  304. // remove the current window titles information
  305. DynArrayRemoveAll( m_arrWindowTitles );
  306. // for the local system, enumerate the window titles of the processes
  307. // there is no provision for collecting the window titles of remote processes
  308. if ( m_bLocalSystem == TRUE )
  309. {
  310. // prepare the tasks list info
  311. TWINDOWTITLES windowtitles;
  312. windowtitles.pwszDesk = NULL;
  313. windowtitles.pwszWinsta = NULL;
  314. windowtitles.bFirstLoop = FALSE;
  315. windowtitles.arrWindows = m_arrWindowTitles;
  316. EnumWindowStations( EnumWindowStationsFunc, ( LPARAM ) &windowtitles );
  317. // free the memory allocated with _tcsdup string function
  318. if ( windowtitles.pwszDesk != NULL )
  319. free( windowtitles.pwszDesk );
  320. if ( windowtitles.pwszWinsta != NULL )
  321. free( windowtitles.pwszWinsta );
  322. }
  323. // load the extended tasks information
  324. LoadTasksEx(); // NOTE: here we are not much bothered abt the return value
  325. // erase the status messages
  326. PrintProgressMsg( m_hOutput, NULL, m_csbi );
  327. }
  328. catch( ... )
  329. {
  330. // save the error
  331. WMISaveError( E_OUTOFMEMORY );
  332. return FALSE;
  333. }
  334. // return success
  335. return TRUE;
  336. }
  337. // ***************************************************************************
  338. // Routine Description:
  339. //
  340. // Arguments:
  341. //
  342. // Return Value:
  343. //
  344. // ***************************************************************************
  345. BOOL CTaskKill::LoadTasksEx()
  346. {
  347. // local variables
  348. BOOL bResult = FALSE;
  349. //
  350. // NOTE: we are relying on NET API for getting the user context / services info
  351. // so if any one of them is needed, establish connection to the remote system using NET API
  352. if ( m_bNeedModulesInfo == FALSE && m_bNeedUserContextInfo == FALSE && m_bNeedServicesInfo == FALSE )
  353. return TRUE;
  354. // init
  355. m_bCloseConnection = FALSE;
  356. // we need to use NET API only in case connecting to the remote system
  357. // with credentials information i.e; m_pAuthIdentity is not NULL
  358. if ( m_bLocalSystem == FALSE && m_pAuthIdentity != NULL )
  359. {
  360. // sub-local variables
  361. DWORD dwConnect = 0;
  362. LPCWSTR pwszUser = NULL;
  363. LPCWSTR pwszPassword = NULL;
  364. // identify the password to connect to the remote system
  365. pwszPassword = m_pAuthIdentity->Password;
  366. if ( m_strUserName.GetLength() != 0 )
  367. pwszUser = m_strUserName;
  368. // establish connection to the remote system using NET API
  369. // this we need to do only for remote system
  370. dwConnect = NO_ERROR;
  371. m_bCloseConnection = TRUE;
  372. dwConnect = ConnectServer( m_strUNCServer, pwszUser, pwszPassword );
  373. if ( dwConnect != NO_ERROR )
  374. {
  375. // connection should not be closed .. this is because we didn't establish the connection
  376. m_bCloseConnection = FALSE;
  377. // this might be 'coz of the conflict in the credentials ... check that
  378. if ( dwConnect != ERROR_SESSION_CREDENTIAL_CONFLICT )
  379. {
  380. // return failure
  381. return FALSE;
  382. }
  383. }
  384. // check the whether we need to close the connection or not
  385. // if user name is NULL (or) password is NULL then don't close the connection
  386. if ( pwszUser == NULL || pwszPassword == NULL )
  387. m_bCloseConnection = FALSE;
  388. }
  389. try
  390. {
  391. // prepare to get the user context info .. if needed
  392. if ( m_bNeedUserContextInfo == TRUE )
  393. {
  394. // sub-local variables
  395. HANDLE hServer = NULL;
  396. // connect to the remote system's winstation
  397. bResult = TRUE;
  398. hServer = SERVERNAME_CURRENT;
  399. if ( m_bLocalSystem == FALSE )
  400. {
  401. // sub-local variables
  402. LPWSTR pwsz = NULL;
  403. // connect to the winsta and check the result
  404. pwsz = m_strUNCServer.GetBuffer( m_strUNCServer.GetLength() );
  405. hServer = WinStationOpenServerW( pwsz );
  406. // proceed furthur only if winstation of the remote system is successfully opened
  407. if ( hServer == NULL )
  408. bResult = FALSE;
  409. }
  410. // check the result of the connection to the remote system
  411. if ( bResult == TRUE )
  412. {
  413. // get all the process details
  414. m_bIsHydra = FALSE;
  415. bResult = WinStationGetAllProcesses( hServer,
  416. GAP_LEVEL_BASIC, &m_ulNumberOfProcesses, (PVOID*) &m_pProcessInfo );
  417. // check the result
  418. if ( bResult == FALSE )
  419. {
  420. // Maybe a Hydra 4 server ?
  421. // Check the return code indicating that the interface is not available.
  422. if ( GetLastError() == RPC_S_PROCNUM_OUT_OF_RANGE )
  423. {
  424. // The new interface is not known
  425. // It must be a Hydra 4 server
  426. // try with the old interface
  427. bResult = WinStationEnumerateProcesses( hServer, (PVOID*) &m_pProcessInfo );
  428. // check the result of enumeration
  429. if ( bResult == TRUE )
  430. m_bIsHydra = TRUE;
  431. }
  432. }
  433. // close the connection to the winsta
  434. if ( hServer != NULL )
  435. WinStationCloseServer( hServer );
  436. }
  437. }
  438. }
  439. catch( ... )
  440. {
  441. SetLastError( E_OUTOFMEMORY );
  442. SaveLastError();
  443. return FALSE;
  444. }
  445. // check whether we need services info or not
  446. if ( m_bNeedServicesInfo == TRUE )
  447. {
  448. // load the services
  449. bResult = LoadServicesInfo();
  450. // check the result
  451. if ( bResult == FALSE )
  452. return FALSE;
  453. }
  454. // check whether we need modules info or not
  455. if ( m_bNeedModulesInfo == TRUE )
  456. {
  457. // load the modules information
  458. bResult = LoadModulesInfo();
  459. // check the result
  460. if ( bResult == FALSE )
  461. return FALSE;
  462. }
  463. // NETWORK OPTIMIZATION -
  464. // while progressing furthur in display of the data, we don't need the connection
  465. // to the remote system with NET API ... but the connection should be live
  466. // if user name has to be retrieved .. so check if user name info is needed or not
  467. // if not, close the connection
  468. if ( m_bNeedUserContextInfo == FALSE && m_bCloseConnection == TRUE )
  469. {
  470. m_bCloseConnection = FALSE; // reset the flag
  471. CloseConnection( m_strUNCServer ); // close connection
  472. }
  473. // return
  474. return TRUE;
  475. }
  476. // ***************************************************************************
  477. // Routine Description:
  478. //
  479. // Arguments:
  480. //
  481. // Return Value:
  482. //
  483. // ***************************************************************************
  484. BOOL CTaskKill::LoadModulesInfo()
  485. {
  486. // local variables
  487. HKEY hKey;
  488. LONG lReturn = 0;
  489. BOOL bResult = FALSE;
  490. BOOL bImagesObject = FALSE;
  491. BOOL bAddressSpaceObject = FALSE;
  492. PPERF_OBJECT_TYPE pot = NULL;
  493. PPERF_COUNTER_DEFINITION pcd = NULL;
  494. // check whether we need the modules infor or not
  495. // NOTE: we need to load the performance data only in case if user is querying for remote system only
  496. if ( m_bNeedModulesInfo == FALSE || m_bLocalSystem == TRUE )
  497. return TRUE;
  498. // display the status message
  499. PrintProgressMsg( m_hOutput, MSG_MODULESINFO, m_csbi );
  500. // open the remote system performance data key
  501. lReturn = RegConnectRegistry( m_strUNCServer, HKEY_PERFORMANCE_DATA, &hKey );
  502. if ( lReturn != ERROR_SUCCESS )
  503. {
  504. SetLastError( lReturn );
  505. SaveLastError();
  506. return FALSE;
  507. }
  508. // get the performance object ( images )
  509. bResult = GetPerfDataBlock( hKey, L"740", &m_pdb );
  510. if ( bResult == FALSE )
  511. {
  512. // close the registry key and return
  513. RegCloseKey( hKey );
  514. return FALSE;
  515. }
  516. // check the validity of the perf block
  517. if ( StringCompare( m_pdb->Signature, L"PERF", FALSE, 4 ) != 0 )
  518. {
  519. // close the registry key and return
  520. RegCloseKey( hKey );
  521. // set the error message
  522. SetLastError( ERROR_ACCESS_DENIED );
  523. SaveLastError();
  524. return FALSE;
  525. }
  526. // close the registry key and return
  527. RegCloseKey( hKey );
  528. //
  529. // check whether we got both 740 and 786 blocks or not
  530. //
  531. bImagesObject = FALSE;
  532. bAddressSpaceObject = FALSE;
  533. pot = (PPERF_OBJECT_TYPE) ( (LPBYTE) m_pdb + m_pdb->HeaderLength );
  534. for( DWORD dw = 0; dw < m_pdb->NumObjectTypes; dw++ )
  535. {
  536. if ( pot->ObjectNameTitleIndex == 740 )
  537. bImagesObject = TRUE;
  538. else if ( pot->ObjectNameTitleIndex == 786 )
  539. bAddressSpaceObject = TRUE;
  540. // move to the next object
  541. if( pot->TotalByteLength != 0 )
  542. pot = ( (PPERF_OBJECT_TYPE) ((PBYTE) pot + pot->TotalByteLength));
  543. }
  544. // check whether we got the needed objects or not
  545. if ( bImagesObject == FALSE || bAddressSpaceObject == FALSE )
  546. {
  547. SetLastError( ERROR_ACCESS_DENIED );
  548. SaveLastError();
  549. return FALSE;
  550. }
  551. // return
  552. return TRUE;
  553. }
  554. // ***************************************************************************
  555. // Routine Description:
  556. //
  557. // Arguments:
  558. //
  559. // Return Value:
  560. //
  561. // ***************************************************************************
  562. BOOL CTaskKill::LoadUserNameFromWinsta( CHString& strDomain, CHString& strUserName )
  563. {
  564. // local variables
  565. PSID pSid = NULL;
  566. BOOL bResult = FALSE;
  567. LPWSTR pwszUser = NULL;
  568. LPWSTR pwszDomain = NULL;
  569. LPCWSTR pwszServer = NULL;
  570. DWORD dwUserLength = 0;
  571. DWORD dwDomainLength = 0;
  572. SID_NAME_USE siduse;
  573. // check whether winsta data exists or not
  574. if ( m_pProcessInfo == NULL )
  575. return FALSE;
  576. try
  577. {
  578. // allocate buffers
  579. dwUserLength = 128;
  580. dwDomainLength = 128;
  581. pwszUser = strUserName.GetBufferSetLength( dwUserLength );
  582. pwszDomain = strDomain.GetBufferSetLength( dwDomainLength );
  583. }
  584. catch( ... )
  585. {
  586. SetLastError( E_OUTOFMEMORY );
  587. SaveLastError();
  588. return FALSE;
  589. }
  590. //
  591. // find for the appropriate the process
  592. pSid = NULL;
  593. if ( m_bIsHydra == FALSE )
  594. {
  595. // sub-local variables
  596. PTS_ALL_PROCESSES_INFO ptsallpi = NULL;
  597. PTS_SYSTEM_PROCESS_INFORMATION pspi = NULL;
  598. // loop ...
  599. ptsallpi = (PTS_ALL_PROCESSES_INFO) m_pProcessInfo;
  600. for( ULONG ul = 0; ul < m_ulNumberOfProcesses; ul++ )
  601. {
  602. pspi = ( PTS_SYSTEM_PROCESS_INFORMATION )( ptsallpi[ ul ].pspiProcessInfo );
  603. if ( pspi->UniqueProcessId == m_dwProcessId )
  604. {
  605. // get the SID and convert it into
  606. pSid = ptsallpi[ ul ].pSid;
  607. break; // break from the loop
  608. }
  609. }
  610. }
  611. else
  612. {
  613. //
  614. // HYDRA ...
  615. //
  616. // sub-local variables
  617. DWORD dwTotalOffset = 0;
  618. PTS_SYSTEM_PROCESS_INFORMATION pspi = NULL;
  619. PCITRIX_PROCESS_INFORMATION pcpi = NULL;
  620. // traverse thru the process info and find the process id
  621. dwTotalOffset = 0;
  622. pspi = ( PTS_SYSTEM_PROCESS_INFORMATION ) m_pProcessInfo;
  623. for( ;; )
  624. {
  625. // check the processid
  626. if ( pspi->UniqueProcessId == m_dwProcessId )
  627. break;
  628. // check whether any more processes exist or not
  629. if( pspi->NextEntryOffset == 0 )
  630. break;
  631. // position to the next process info
  632. dwTotalOffset += pspi->NextEntryOffset;
  633. pspi = (PTS_SYSTEM_PROCESS_INFORMATION) &m_pProcessInfo[ dwTotalOffset ];
  634. }
  635. // get the citrix_information which follows the threads
  636. pcpi = (PCITRIX_PROCESS_INFORMATION)
  637. ( ((PUCHAR) pspi) + sizeof( TS_SYSTEM_PROCESS_INFORMATION ) +
  638. (sizeof( SYSTEM_THREAD_INFORMATION ) * pspi->NumberOfThreads) );
  639. // check the magic number .. if it is not valid ... we haven't got SID
  640. if( pcpi->MagicNumber == CITRIX_PROCESS_INFO_MAGIC )
  641. pSid = pcpi->ProcessSid;
  642. }
  643. // check the sid value
  644. if ( pSid == NULL )
  645. {
  646. // SPECIAL CASE:
  647. // -------------
  648. // PID -> 0 will have a special hard coded user name info
  649. if ( m_dwProcessId == 0 )
  650. {
  651. bResult = TRUE;
  652. lstrcpynW( pwszUser, PID_0_USERNAME, dwUserLength );
  653. lstrcpynW( pwszDomain, PID_0_DOMAIN, dwDomainLength );
  654. }
  655. // release the buffer
  656. strDomain.ReleaseBuffer();
  657. strUserName.ReleaseBuffer();
  658. return bResult;
  659. }
  660. // determine the server
  661. pwszServer = NULL;
  662. if ( m_bLocalSystem == FALSE )
  663. pwszServer = m_strUNCServer;
  664. // map the sid to the user name
  665. bResult = LookupAccountSid( pwszServer, pSid,
  666. pwszUser, &dwUserLength, pwszDomain, &dwDomainLength, &siduse );
  667. // release the buffer
  668. strDomain.ReleaseBuffer();
  669. strUserName.ReleaseBuffer();
  670. // return the result
  671. return bResult;
  672. }
  673. // ***************************************************************************
  674. // Routine Description:
  675. //
  676. // Arguments:
  677. //
  678. // Return Value:
  679. //
  680. // ***************************************************************************
  681. BOOL CTaskKill::LoadServicesInfo()
  682. {
  683. // local variables
  684. DWORD dw = 0; // looping variable
  685. DWORD dwSize = 0; // used in memory allocation
  686. DWORD dwResume = 0; // used in EnumServicesStatusEx
  687. BOOL bResult = FALSE; // captures the result of EnumServicesStatusEx
  688. SC_HANDLE hScm = NULL; // holds the handle to the service
  689. DWORD dwExtraNeeded = 0; // used in EnumServicesStatusEx and memory allocation
  690. LPCWSTR pwszServer = NULL;
  691. LPENUM_SERVICE_STATUS_PROCESS pInfo = NULL; // holds the services info
  692. // Initialize the output parameter(s).
  693. m_dwServicesCount = 0;
  694. m_pServicesInfo = NULL;
  695. // check whether we need to load the services info or not
  696. if ( m_bNeedServicesInfo == FALSE )
  697. return TRUE;
  698. // display the status message
  699. PrintProgressMsg( m_hOutput, MSG_SERVICESINFO, m_csbi );
  700. // determine the server
  701. pwszServer = NULL;
  702. if ( m_bLocalSystem == FALSE )
  703. pwszServer = m_strUNCServer;
  704. // Connect to the service controller and check the result
  705. hScm = OpenSCManager( pwszServer, NULL, SC_MANAGER_CONNECT | SC_MANAGER_ENUMERATE_SERVICE );
  706. if ( hScm == NULL)
  707. {
  708. // set the reason for the failure and return from here itself
  709. SaveLastError();
  710. return FALSE;
  711. }
  712. // enumerate the names of the active win32 services
  713. // for this, first pass through the loop and allocate memory from an initial guess. (4K)
  714. // if that isn't sufficient, we make another pass and allocate
  715. // what is actually needed.
  716. // (we only go through the loop a maximum of two times)
  717. dw = 0; // no. of loops
  718. dwResume = 0; // reset / initialize variables
  719. dwSize = 4 * 1024; // reset / initialize variables
  720. while ( ++dw <= 2 )
  721. {
  722. // set the size
  723. dwSize += dwExtraNeeded;
  724. // allocate memory for storing services information
  725. pInfo = ( LPENUM_SERVICE_STATUS_PROCESS ) __calloc( 1, dwSize );
  726. if ( pInfo == NULL )
  727. {
  728. // failed in allocating needed memory ... error
  729. SetLastError( E_OUTOFMEMORY );
  730. SaveLastError();
  731. return FALSE;
  732. }
  733. // enumerate services, the process identifier and additional flags for the service
  734. dwResume = 0; // lets get all the services again
  735. bResult = EnumServicesStatusEx( hScm, SC_ENUM_PROCESS_INFO, SERVICE_WIN32,
  736. SERVICE_ACTIVE, ( LPBYTE ) pInfo, dwSize, &dwExtraNeeded, &m_dwServicesCount, &dwResume, NULL );
  737. // check the result of the enumeration
  738. if ( bResult )
  739. {
  740. // successfully enumerated all the services information
  741. break; // jump out of the loop
  742. }
  743. // first free the allocated memory
  744. __free( pInfo );
  745. // now lets look at what is the error
  746. if ( GetLastError() == ERROR_MORE_DATA )
  747. {
  748. // some more services are not listed because of less memory
  749. // allocate some more memory and enumerate the remaining services info
  750. continue;
  751. }
  752. else
  753. {
  754. // some strange error occured ... inform the same to the caller
  755. SaveLastError(); // set the reason for the failure
  756. CloseServiceHandle( hScm ); // close the handle to the service
  757. return FALSE; // inform failure
  758. }
  759. }
  760. // check whether there any services or not ... if services count is zero, free the memory
  761. if ( m_dwServicesCount == 0 )
  762. {
  763. // no services exists
  764. __free( pInfo );
  765. }
  766. else
  767. {
  768. // set the local pointer to the out parameter
  769. m_pServicesInfo = pInfo;
  770. }
  771. // inform success
  772. return TRUE;
  773. }
  774. // ***************************************************************************
  775. // Routine Description:
  776. //
  777. // Arguments:
  778. //
  779. // Return Value:
  780. //
  781. // ***************************************************************************
  782. BOOL GetPerfDataBlock( HKEY hKey, LPWSTR pwszObjectIndex, PPERF_DATA_BLOCK* ppdb )
  783. {
  784. // local variables
  785. LONG lReturn = 0;
  786. DWORD dwBytes = 0;
  787. BOOL bResult = FALSE;
  788. // check the input parameters
  789. if ( pwszObjectIndex == NULL || ppdb == NULL )
  790. return FALSE;
  791. // allocate memory for PERF_DATA_BLOCK
  792. dwBytes = 32 * 1024; // initially allocate for 32 K
  793. *ppdb = (PPERF_DATA_BLOCK) HeapAlloc( GetProcessHeap(), 0, dwBytes );
  794. if( *ppdb == NULL )
  795. {
  796. SetLastError( E_OUTOFMEMORY );
  797. SaveLastError();
  798. return FALSE;
  799. }
  800. // get performance data on passed Object
  801. lReturn = RegQueryValueEx( hKey, pwszObjectIndex, NULL, NULL, (LPBYTE) *ppdb, &dwBytes );
  802. while( lReturn == ERROR_MORE_DATA )
  803. {
  804. // increase memory by 8 K
  805. dwBytes += 8192;
  806. // allocated memory is too small reallocate new memory
  807. *ppdb = (PPERF_DATA_BLOCK) HeapReAlloc( GetProcessHeap(), 0, *ppdb, dwBytes );
  808. if( *ppdb == NULL )
  809. {
  810. SetLastError( E_OUTOFMEMORY );
  811. SaveLastError();
  812. return FALSE;
  813. }
  814. // try to get the info again
  815. lReturn = RegQueryValueEx( hKey, pwszObjectIndex, NULL, NULL, (LPBYTE) *ppdb, &dwBytes );
  816. }
  817. // check the reason for coming out of the loop
  818. bResult = TRUE;
  819. if ( lReturn != ERROR_SUCCESS )
  820. {
  821. if ( *ppdb != NULL)
  822. {
  823. HeapFree( GetProcessHeap(), 0, *ppdb );
  824. *ppdb = NULL;
  825. }
  826. // save the error info
  827. bResult = FALSE;
  828. SetLastError( lReturn );
  829. SaveLastError();
  830. }
  831. // return the result
  832. return bResult;
  833. }
  834. // ***************************************************************************
  835. // Routine Description:
  836. //
  837. // Arguments:
  838. //
  839. // Return Value:
  840. //
  841. // ***************************************************************************
  842. VOID CTaskKill::DoOptimization()
  843. {
  844. // local variables
  845. DWORD dwCount = 0;
  846. CHString strCondition;
  847. BOOL bOptimized = FALSE;
  848. LPCWSTR pwsz = NULL;
  849. LPCWSTR pwszTask = NULL;
  850. LPCWSTR pwszClause = NULL;
  851. CHString strInternalQuery;
  852. // do not the optimization if user requested the tree termination
  853. if ( m_bTree == TRUE )
  854. return;
  855. try
  856. {
  857. // traverse thru list of specified tasks to kill and prepare the query
  858. pwszClause = NULL_STRING;
  859. dwCount = DynArrayGetCount( m_arrTasksToKill );
  860. for( DWORD dw = 0; dw < dwCount; dw++ )
  861. {
  862. // get the task
  863. pwszTask = DynArrayItemAsString( m_arrTasksToKill, dw );
  864. if ( pwszTask == NULL )
  865. continue;
  866. // check the for the special input '*' which cannot be optimized
  867. if ( StringCompare( pwszTask, L"*", TRUE, 0 ) == 0 )
  868. return; // query should not be optimized ... return back
  869. // now check whether wild-card is specified in the image name
  870. if ( (pwsz = FindChar( pwszTask, L'*', 0 )) != NULL )
  871. {
  872. // the image name contains wild card in it - this cannot be included in query
  873. if ( lstrlen( pwsz ) == 1 )
  874. continue;
  875. }
  876. // prepare the query based on the type of input
  877. // NOTE: in case of numeric it might be a process id (or) image name
  878. // we are not sure abt what it can be. So prepare query for both
  879. if ( IsNumeric( pwszTask, 10, FALSE ) == TRUE )
  880. {
  881. // prepare condition
  882. strCondition.Format( L" %s %s = %s", pwszClause,
  883. WIN32_PROCESS_PROPERTY_PROCESSID, pwszTask );
  884. }
  885. else
  886. {
  887. // prepare the condition
  888. strCondition.Format( L" %s %s = \"%s\"",
  889. pwszClause, WIN32_PROCESS_PROPERTY_IMAGENAME, pwszTask );
  890. }
  891. // append to the final query
  892. strInternalQuery += strCondition;
  893. // from next time onwards query clause has to be added
  894. bOptimized = TRUE;
  895. pwszClause = WMI_CLAUSE_OR;
  896. }
  897. // do modifications to the query only if tasks were optimized
  898. if ( bOptimized == TRUE )
  899. {
  900. // now this internal query has to be added to the main query
  901. // before adding this check whether query optimization is done in filters or not
  902. // if not, we have to add the where clause also
  903. strCondition.Format( L"%s %s", m_strQuery,
  904. (m_bFiltersOptimized == TRUE ? WMI_CLAUSE_AND : WMI_CLAUSE_WHERE) );
  905. // now add the internal query to the final query
  906. m_bTasksOptimized = TRUE;
  907. m_strQuery.Format( L"%s (%s)", strCondition, strInternalQuery );
  908. }
  909. }
  910. catch( ... )
  911. {
  912. // we can't anything here ... just return
  913. SetLastError( E_OUTOFMEMORY );
  914. SaveLastError();
  915. return;
  916. }
  917. }
  918. // ***************************************************************************
  919. // Routine Description:
  920. // Enumerates the desktops available on a particular window station
  921. // This is a CALLBACK function ... called by EnumWindowStations API function
  922. //
  923. // Arguments:
  924. // [ in ] lpstr : window station name
  925. // [ in ] lParam : user supplied parameter to this function
  926. // in this function, this points to TTASKSLIST structure variable
  927. //
  928. // Return Value:
  929. // TRUE upon success and FALSE on failure
  930. //
  931. // ***************************************************************************
  932. BOOL CALLBACK EnumWindowStationsFunc( LPWSTR pwsz, LPARAM lParam )
  933. {
  934. // local variables
  935. HWINSTA hWinSta = NULL;
  936. HWINSTA hwinstaSave = NULL;
  937. PTWINDOWTITLES pWndTitles = ( PTWINDOWTITLES ) lParam;
  938. // check the input arguments
  939. if ( pwsz == NULL || lParam == NULL )
  940. return FALSE;
  941. // get and save the current window station
  942. hwinstaSave = GetProcessWindowStation();
  943. // open current tasks window station and change the context to the new workstation
  944. hWinSta = OpenWindowStation( pwsz, FALSE, WINSTA_ENUMERATE | WINSTA_ENUMDESKTOPS );
  945. if ( hWinSta == NULL )
  946. {
  947. // failed in getting the process window station
  948. SaveLastError();
  949. return FALSE;
  950. }
  951. else
  952. {
  953. // change the context to the new workstation
  954. if ( hWinSta != hwinstaSave && SetProcessWindowStation( hWinSta ) == FALSE )
  955. {
  956. // failed in changing the context
  957. SaveLastError();
  958. return FALSE;
  959. }
  960. // release the memory allocated for earlier window station
  961. if ( pWndTitles->pwszWinsta != NULL )
  962. {
  963. free( pWndTitles->pwszWinsta );
  964. pWndTitles->pwszWinsta = NULL;
  965. }
  966. // store the window station name
  967. if ( pwsz != NULL )
  968. {
  969. pWndTitles->pwszWinsta = _wcsdup( pwsz );
  970. if ( pWndTitles->pwszWinsta == NULL )
  971. {
  972. SetLastError( E_OUTOFMEMORY );
  973. SaveLastError();
  974. return FALSE;
  975. }
  976. }
  977. }
  978. // enumerate all the desktops for this windowstation
  979. EnumDesktops( hWinSta, EnumDesktopsFunc, lParam );
  980. // restore the context to the previous windowstation
  981. if (hWinSta != hwinstaSave)
  982. {
  983. SetProcessWindowStation( hwinstaSave );
  984. CloseWindowStation( hWinSta );
  985. }
  986. // continue the enumeration
  987. return TRUE;
  988. }
  989. // ***************************************************************************
  990. // Routine Description:
  991. // Enumerates the windows on a particular desktop
  992. // This is a CALLBACK function ... called by EnumDesktops API function
  993. //
  994. // Arguments:
  995. // [ in ] lpstr : desktop name
  996. // [ in ] lParam : user supplied parameter to this function
  997. // in this function, this points to TTASKSLIST structure variable
  998. //
  999. // Return Value:
  1000. // TRUE upon success and FALSE on failure
  1001. //
  1002. // ***************************************************************************
  1003. BOOL CALLBACK EnumDesktopsFunc( LPWSTR pwsz, LPARAM lParam )
  1004. {
  1005. // local variables
  1006. HDESK hDesk = NULL;
  1007. HDESK hdeskSave = NULL;
  1008. PTWINDOWTITLES pWndTitles = ( PTWINDOWTITLES )lParam;
  1009. // check the input arguments
  1010. if ( pwsz == NULL || lParam == NULL )
  1011. return FALSE;
  1012. // get and save the current desktop
  1013. hdeskSave = GetThreadDesktop( GetCurrentThreadId() );
  1014. // open the tasks desktop and change the context to the new desktop
  1015. hDesk = OpenDesktop( pwsz, 0, FALSE, DESKTOP_ENUMERATE );
  1016. if ( hDesk == NULL )
  1017. {
  1018. // failed in getting the process desktop
  1019. SaveLastError();
  1020. return FALSE;
  1021. }
  1022. else
  1023. {
  1024. // change the context to the new desktop
  1025. if ( hDesk != hdeskSave && SetThreadDesktop( hDesk ) == FALSE )
  1026. {
  1027. // failed in changing the context
  1028. SaveLastError();
  1029. // ?? return FALSE; -- needs to uncommented
  1030. }
  1031. // release the memory allocated for earlier window station
  1032. if ( pWndTitles->pwszDesk != NULL )
  1033. {
  1034. free( pWndTitles->pwszDesk );
  1035. pWndTitles->pwszDesk = NULL;
  1036. }
  1037. // store the desktop name
  1038. if ( pwsz != NULL )
  1039. {
  1040. pWndTitles->pwszDesk = _wcsdup( pwsz );
  1041. if ( pWndTitles->pwszDesk == NULL )
  1042. {
  1043. SetLastError( E_OUTOFMEMORY );
  1044. SaveLastError();
  1045. return FALSE;
  1046. }
  1047. }
  1048. }
  1049. // enumerate all windows in the new desktop
  1050. // first try to get only the top level windows and visible windows only
  1051. ( ( PTWINDOWTITLES ) lParam )->bFirstLoop = TRUE;
  1052. EnumWindows( ( WNDENUMPROC ) EnumWindowsProc, lParam );
  1053. EnumMessageWindows( ( WNDENUMPROC ) EnumWindowsProc, lParam );
  1054. // enumerate all windows in the new desktop
  1055. // now try to get window titles of all those processes whose we ignored earlier while
  1056. // looping first time
  1057. ( ( PTWINDOWTITLES ) lParam )->bFirstLoop = FALSE;
  1058. EnumWindows( ( WNDENUMPROC ) EnumWindowsProc, lParam );
  1059. EnumMessageWindows( ( WNDENUMPROC ) EnumWindowsProc, lParam );
  1060. // restore the previous desktop
  1061. if (hDesk != hdeskSave)
  1062. {
  1063. SetThreadDesktop( hdeskSave );
  1064. CloseDesktop( hDesk );
  1065. }
  1066. // continue enumeration
  1067. return TRUE;
  1068. }
  1069. // ***************************************************************************
  1070. // Routine Description:
  1071. // Enumerates the message windows
  1072. //
  1073. // Arguments:
  1074. // [ in ] lpEnumFunc : address of call back function that has to be called for
  1075. // each message window found
  1076. // [ in ] lParam : user supplied parameter to this function
  1077. // in this function, this points to TTASKSLIST structure variable
  1078. //
  1079. // Return Value:
  1080. // TRUE upon success and FALSE on failure
  1081. // ***************************************************************************
  1082. BOOL CALLBACK EnumMessageWindows( WNDENUMPROC lpEnumFunc, LPARAM lParam )
  1083. {
  1084. // local variables
  1085. HWND hWnd = NULL;
  1086. BOOL bResult = FALSE;
  1087. // check the input arguments
  1088. if ( lpEnumFunc == NULL || lParam == NULL )
  1089. return FALSE;
  1090. // enumerate all the message windows
  1091. do
  1092. {
  1093. // find the message window
  1094. hWnd = FindWindowEx( HWND_MESSAGE, hWnd, NULL, NULL );
  1095. // check whether we got the handle to the message window or not
  1096. if ( hWnd != NULL )
  1097. {
  1098. // explicitly call the windows enumerators call back function for this window
  1099. bResult = ( *lpEnumFunc )( hWnd, lParam );
  1100. // check the result of the enumeator call back function
  1101. if ( bResult == FALSE )
  1102. {
  1103. // terminate the enumeration
  1104. break;
  1105. }
  1106. }
  1107. } while ( hWnd != NULL );
  1108. // return the enumeration result
  1109. return bResult;
  1110. }
  1111. // ***************************************************************************
  1112. // Routine Description:
  1113. // call back called by the API for each window
  1114. // retrives the window title and updates the accordingly
  1115. //
  1116. // Arguments:
  1117. // [ in ] hWnd : handle to the window
  1118. // [ in ] lParam : user supplied parameter to this function
  1119. // in this function, this points to TTASKSLIST structure variable
  1120. //
  1121. // Return Value:
  1122. // TRUE upon success and FALSE on failure
  1123. // ***************************************************************************
  1124. BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam )
  1125. {
  1126. // local variables
  1127. LONG lIndex = 0;
  1128. DWORD dwPID = 0;
  1129. BOOL bVisible = FALSE;
  1130. TARRAY arrWindows = NULL;
  1131. PTWINDOWTITLES pWndTitles = NULL;
  1132. __MAX_SIZE_STRING szWindowTitle = NULL_STRING;
  1133. // check the input arguments
  1134. if ( hWnd == NULL || lParam == NULL )
  1135. return FALSE;
  1136. // get the values from the lParam
  1137. pWndTitles = ( PTWINDOWTITLES ) lParam;
  1138. arrWindows = pWndTitles->arrWindows;
  1139. // get the processid for this window
  1140. if ( GetWindowThreadProcessId( hWnd, &dwPID ) == 0 )
  1141. {
  1142. // failed in getting the process id
  1143. return TRUE; // return but, proceed enumerating other window handle
  1144. }
  1145. // get the visibility state of the window
  1146. // if the window is not visible, and if this is the first we are enumerating the
  1147. // window titles, ignore this process
  1148. bVisible = GetWindowLong( hWnd, GWL_STYLE ) & WS_VISIBLE;
  1149. if ( bVisible == FALSE && pWndTitles->bFirstLoop == TRUE )
  1150. return TRUE; // return but, proceed enumerating other window handle
  1151. // check whether the current window ( for which we have the handle )
  1152. // is main window or not. we don't need child windows
  1153. if ( GetWindow(hWnd, GW_OWNER) != NULL )
  1154. {
  1155. // the current window handle is not for a top level window
  1156. return TRUE; // return but, proceed enumerating other window handle
  1157. }
  1158. // check if we are already got the window handle for the curren process or not
  1159. // save it only if we are not having it
  1160. lIndex = DynArrayFindDWORDEx( arrWindows, CTaskKill::twiProcessId, dwPID );
  1161. if ( lIndex == -1 )
  1162. {
  1163. // window for this process is not there ... save it
  1164. lIndex = DynArrayAppendRow( arrWindows, CTaskKill::twiCOUNT );
  1165. }
  1166. else
  1167. {
  1168. // check whether window details already exists or not
  1169. if ( DynArrayItemAsHandle2( arrWindows, lIndex, CTaskKill::twiHandle ) != NULL )
  1170. lIndex = -1; // window details already exists
  1171. }
  1172. // check if window details has to be saved or not ... if needed save them
  1173. if ( lIndex != -1 )
  1174. {
  1175. DynArraySetDWORD2( arrWindows, lIndex, CTaskKill::twiProcessId, dwPID );
  1176. DynArraySetHandle2( arrWindows, lIndex, CTaskKill::twiHandle, hWnd );
  1177. DynArraySetString2( arrWindows, lIndex,
  1178. CTaskKill::twiWinSta, pWndTitles->pwszWinsta, 0 );
  1179. DynArraySetString2( arrWindows, lIndex,
  1180. CTaskKill::twiDesktop, pWndTitles->pwszDesk, 0 );
  1181. // get and save the window title
  1182. if ( GetWindowText( hWnd, szWindowTitle, SIZE_OF_ARRAY( szWindowTitle ) ) != 0 )
  1183. DynArraySetString2( arrWindows, lIndex, CTaskKill::twiTitle, szWindowTitle, 0 );
  1184. }
  1185. // continue the enumeration
  1186. return TRUE;
  1187. }
  1188. // ***************************************************************************
  1189. // Routine Description:
  1190. //
  1191. // Arguments:
  1192. //
  1193. // Return Value:
  1194. //
  1195. // ***************************************************************************
  1196. VOID PrintProgressMsg( HANDLE hOutput, LPCWSTR pwszMsg, const CONSOLE_SCREEN_BUFFER_INFO& csbi )
  1197. {
  1198. // local variables
  1199. COORD coord;
  1200. DWORD dwSize = 0;
  1201. WCHAR wszSpaces[ 80 ] = L"";
  1202. // check the handle. if it is null, it means that output is being redirected. so return
  1203. if ( hOutput == NULL )
  1204. return;
  1205. // set the cursor position
  1206. coord.X = 0;
  1207. coord.Y = csbi.dwCursorPosition.Y;
  1208. // first erase contents on the current line
  1209. ZeroMemory( wszSpaces, 80 );
  1210. SetConsoleCursorPosition( hOutput, coord );
  1211. WriteConsoleW( hOutput, Replicate( wszSpaces, L"", 79 ), 79, &dwSize, NULL );
  1212. // now display the message ( if exists )
  1213. SetConsoleCursorPosition( hOutput, coord );
  1214. if ( pwszMsg != NULL )
  1215. WriteConsoleW( hOutput, pwszMsg, lstrlen( pwszMsg ), &dwSize, NULL );
  1216. }