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.

4666 lines
122 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // TaskMan - NT TaskManager
  4. // Copyright (C) Microsoft
  5. //
  6. // File: procpage.cpp
  7. //
  8. // History: Nov-16-95 DavePl Created
  9. //
  10. //--------------------------------------------------------------------------
  11. #include "precomp.h"
  12. #include <TokenUtil.h> // CPrivilegeEnable
  13. #include <winsta.h> // WinStationGetProcessSid
  14. #include <utildll.h> // CachedGetUserFromSid
  15. //
  16. // Project-scope globals
  17. //
  18. DWORD g_cProcesses = 0;
  19. extern TCHAR g_szTimeSep[];
  20. extern TCHAR g_szGroupThousSep[];
  21. extern ULONG g_ulGroupSep;
  22. //--------------------------------------------------------------------------
  23. // TERMINAL SERVICES
  24. //-- cache this state
  25. BOOL IsUserAdmin( )
  26. {
  27. // Note that local static initialization is not thread safe,
  28. // but this function is only called from the process page dialog
  29. // proc (i.e. single thread).
  30. static BOOL sbIsUserAdmin = SHTestTokenMembership(NULL, DOMAIN_ALIAS_RID_ADMINS);
  31. return sbIsUserAdmin;
  32. }
  33. // get/set current session id.
  34. // we use this session id to filter the processes for current session.
  35. DWORD gdwSessionId = static_cast<DWORD>(-1);
  36. inline DWORD GetCurrentSessionID( )
  37. {
  38. return gdwSessionId;
  39. }
  40. inline VOID SetCurrentSessionID( DWORD dwSessionId )
  41. {
  42. gdwSessionId = dwSessionId;
  43. }
  44. // END OF TERMINAL SERVICES DECLs
  45. //--------------------------------------------------------------------------
  46. //
  47. // File-scope globals
  48. //
  49. SYSTEM_BASIC_INFORMATION g_BasicInfo;
  50. //
  51. // Table of which resource IDs in the column selection dialog
  52. // correspond to which columns
  53. //
  54. const int g_aDlgColIDs[] =
  55. {
  56. IDC_IMAGENAME,
  57. IDC_PID,
  58. IDC_USERNAME,
  59. IDC_SESSIONID,
  60. IDC_CPU,
  61. IDC_CPUTIME,
  62. IDC_MEMUSAGE,
  63. IDC_MEMPEAK,
  64. IDC_MEMUSAGEDIFF,
  65. IDC_PAGEFAULTS,
  66. IDC_PAGEFAULTSDIFF,
  67. IDC_COMMITCHARGE,
  68. IDC_PAGEDPOOL,
  69. IDC_NONPAGEDPOOL,
  70. IDC_BASEPRIORITY,
  71. IDC_HANDLECOUNT,
  72. IDC_THREADCOUNT,
  73. IDC_USEROBJECTS,
  74. IDC_GDIOBJECTS,
  75. IDC_READOPERCOUNT,
  76. IDC_WRITEOPERCOUNT,
  77. IDC_OTHEROPERCOUNT,
  78. IDC_READXFERCOUNT,
  79. IDC_WRITEXFERCOUNT,
  80. IDC_OTHERXFERCOUNT
  81. };
  82. //
  83. // Column ID on which to sort in the listview, and for
  84. // compares in general
  85. //
  86. COLUMNID g_iProcSortColumnID = COL_PID;
  87. INT g_iProcSortDirection = 1; // 1 = asc, -1 = desc
  88. //
  89. // Column Default Info
  90. //
  91. struct
  92. {
  93. INT Format;
  94. INT Width;
  95. } ColumnDefaults[NUM_COLUMN] =
  96. {
  97. { LVCFMT_LEFT, 0x6B }, // COL_IMAGENAME
  98. { LVCFMT_RIGHT, 50 }, // COL_PID
  99. { LVCFMT_LEFT, 0x6B }, // COL_USERNAME
  100. { LVCFMT_RIGHT, 70 }, // COL_SESSIONID
  101. { LVCFMT_RIGHT, 35}, // COL_CPU
  102. { LVCFMT_RIGHT, 70 }, // COL_CPUTIME
  103. { LVCFMT_RIGHT, 70 }, // COL_MEMUSAGE
  104. { LVCFMT_RIGHT, 100 }, // COL_MEMPEAK
  105. { LVCFMT_RIGHT, 70 }, // COL_MEMUSAGEDIFF
  106. { LVCFMT_RIGHT, 70 }, // COL_PAGEFAULTS
  107. { LVCFMT_RIGHT, 70 }, // COL_PAGEFAULTSDIFF
  108. { LVCFMT_RIGHT, 70 }, // COL_COMMITCHARGE
  109. { LVCFMT_RIGHT, 70 }, // COL_PAGEDPOOL
  110. { LVCFMT_RIGHT, 70 }, // COL_NONPAGEDPOOL
  111. { LVCFMT_RIGHT, 60 }, // COL_BASEPRIORITY
  112. { LVCFMT_RIGHT, 60 }, // COL_HANDLECOUNT
  113. { LVCFMT_RIGHT, 60 }, // COL_THREADCOUNT
  114. { LVCFMT_RIGHT, 60 }, // COL_USEROBJECTS
  115. { LVCFMT_RIGHT, 60 }, // COL_GDIOBJECTS
  116. { LVCFMT_RIGHT, 70 }, // COL_READOPERCOUNT
  117. { LVCFMT_RIGHT, 70 }, // COL_WRITEOPERCOUNT
  118. { LVCFMT_RIGHT, 70 }, // COL_OTHEROPERCOUNT
  119. { LVCFMT_RIGHT, 70 }, // COL_READXFERCOUNT
  120. { LVCFMT_RIGHT, 70 }, // COL_WRITEXFERCOUNT
  121. { LVCFMT_RIGHT, 70 } // COL_OTHERXFERCOUNT
  122. };
  123. /*++ class CProcInfo
  124. Class Description:
  125. Represents the last known information about a running process
  126. Arguments:
  127. Return Value:
  128. Revision History:
  129. Nov-16-95 Davepl Created
  130. --*/
  131. class CProcInfo
  132. {
  133. public:
  134. LARGE_INTEGER m_uPassCount;
  135. DWORD m_UniqueProcessId;
  136. LPTSTR m_pszUserName;
  137. ULONG m_SessionId;
  138. BYTE m_CPU;
  139. BYTE m_DisplayCPU;
  140. LARGE_INTEGER m_CPUTime;
  141. LARGE_INTEGER m_DisplayCPUTime;
  142. SIZE_T m_MemUsage;
  143. SSIZE_T m_MemDiff;
  144. ULONG m_PageFaults;
  145. LONG m_PageFaultsDiff;
  146. ULONG_PTR m_CommitCharge;
  147. ULONG_PTR m_PagedPool;
  148. ULONG_PTR m_NonPagedPool;
  149. KPRIORITY m_PriClass;
  150. ULONG m_HandleCount;
  151. ULONG m_ThreadCount;
  152. ULONG m_GDIObjectCount;
  153. ULONG m_USERObjectCount;
  154. LONGLONG m_IoReadOperCount;
  155. LONGLONG m_IoWriteOperCount;
  156. LONGLONG m_IoOtherOperCount;
  157. LONGLONG m_IoReadXferCount;
  158. LONGLONG m_IoWriteXferCount;
  159. LONGLONG m_IoOtherXferCount;
  160. LPTSTR m_pszImageName;
  161. CProcInfo * m_pWowParentProcInfo; // non-NULL for WOW tasks
  162. WORD m_htaskWow; // non-zero for WOW tasks
  163. BOOL m_fWowProcess:1; // TRUE for real WOW process
  164. BOOL m_fWowProcessTested:1; // TRUE once fWowProcess is valid
  165. SIZE_T m_MemPeak;
  166. //
  167. // This is a union of who (which column) is dirty. You can look at
  168. // or set any particular column's bit, or just inspect m_fDirty
  169. // to see if anyone at all is dirty. Used to optimize listview
  170. // painting
  171. //
  172. union
  173. {
  174. DWORD m_fDirty;
  175. struct
  176. {
  177. DWORD m_fDirty_COL_CPU :1;
  178. DWORD m_fDirty_COL_CPUTIME :1;
  179. DWORD m_fDirty_COL_MEMUSAGE :1;
  180. DWORD m_fDirty_COL_MEMUSAGEDIFF :1;
  181. DWORD m_fDirty_COL_PAGEFAULTS :1;
  182. DWORD m_fDirty_COL_PAGEFAULTSDIFF :1;
  183. DWORD m_fDirty_COL_COMMITCHARGE :1;
  184. DWORD m_fDirty_COL_PAGEDPOOL :1;
  185. DWORD m_fDirty_COL_NONPAGEDPOOL :1;
  186. DWORD m_fDirty_COL_BASEPRIORITY :1;
  187. DWORD m_fDirty_COL_HANDLECOUNT :1;
  188. DWORD m_fDirty_COL_IMAGENAME :1;
  189. DWORD m_fDirty_COL_PID :1;
  190. DWORD m_fDirty_COL_SESSIONID :1;
  191. DWORD m_fDirty_COL_USERNAME :1;
  192. DWORD m_fDirty_COL_THREADCOUNT :1;
  193. DWORD m_fDirty_COL_GDIOBJECTS :1;
  194. DWORD m_fDirty_COL_USEROBJECTS :1;
  195. DWORD m_fDirty_COL_MEMPEAK :1;
  196. DWORD m_fDirty_COL_READOPERCOUNT :1;
  197. DWORD m_fDirty_COL_WRITEOPERCOUNT :1;
  198. DWORD m_fDirty_COL_OTHEROPERCOUNT :1;
  199. DWORD m_fDirty_COL_READXFERCOUNT :1;
  200. DWORD m_fDirty_COL_WRITEXFERCOUNT :1;
  201. DWORD m_fDirty_COL_OTHERXFERCOUNT :1;
  202. };
  203. };
  204. HRESULT SetData(LARGE_INTEGER TotalTime,
  205. PSYSTEM_PROCESS_INFORMATION pInfo,
  206. LARGE_INTEGER uPassCount,
  207. CProcPage * pProcPage,
  208. BOOL fUpdateOnly);
  209. HRESULT SetProcessUsername(const FILETIME *CreationTime);
  210. HRESULT SetDataWowTask(LARGE_INTEGER TotalTime,
  211. DWORD dwThreadId,
  212. CHAR * pszFilePath,
  213. LARGE_INTEGER uPassCount,
  214. CProcInfo * pParentProcInfo,
  215. LARGE_INTEGER *pTimeLeft,
  216. WORD htask,
  217. BOOL fUpdateOnly);
  218. CProcInfo()
  219. {
  220. ZeroMemory(this, sizeof(*this));
  221. m_pszUserName = 0;
  222. m_SessionId = 832;
  223. }
  224. ~CProcInfo()
  225. {
  226. if (m_pszImageName)
  227. {
  228. delete [] m_pszImageName;
  229. }
  230. if( m_pszUserName != NULL )
  231. {
  232. delete [] m_pszUserName;
  233. }
  234. }
  235. BOOL OkToShowThisProcess ()
  236. {
  237. // this function determines if the process should be listed in the view.
  238. return GetCurrentSessionID() == m_SessionId;
  239. }
  240. // Invalidate() marks this proc with a bogus pid so that it is removed
  241. // on the next cleanup pass
  242. void Invalidate()
  243. {
  244. m_UniqueProcessId = PtrToUlong(INVALID_HANDLE_VALUE);
  245. }
  246. LONGLONG GetCPUTime() const
  247. {
  248. return m_CPUTime.QuadPart;
  249. }
  250. INT Compare(CProcInfo * pOther);
  251. //
  252. // Is this a WOW task psuedo-process?
  253. //
  254. INT_PTR IsWowTask(void) const
  255. {
  256. return (INT_PTR) m_pWowParentProcInfo;
  257. }
  258. //
  259. // Get the Win32 PID for this task
  260. //
  261. DWORD GetRealPID(void) const
  262. {
  263. return m_pWowParentProcInfo
  264. ? m_pWowParentProcInfo->m_UniqueProcessId
  265. : m_UniqueProcessId;
  266. }
  267. void SetCPU(LARGE_INTEGER CPUTimeDelta,
  268. LARGE_INTEGER TotalTime,
  269. BOOL fDisplayOnly);
  270. };
  271. /*++ ColSelectDlgProc
  272. Function Description:
  273. Dialog Procedure for the column selection dialog
  274. Arguments:
  275. Standard wndproc stuff
  276. Revision History:
  277. Jan-05-96 Davepl Created
  278. --*/
  279. INT_PTR CALLBACK ColSelectDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  280. {
  281. static CProcPage * pPage = NULL;
  282. switch(uMsg)
  283. {
  284. case WM_INITDIALOG:
  285. {
  286. // Looks scary, but we're single threaded
  287. pPage = (CProcPage *) lParam;
  288. // Start with none of the boxes checked
  289. for (int i = 0; i < NUM_COLUMN; i++)
  290. {
  291. CheckDlgButton(hwndDlg, g_aDlgColIDs[i], BST_UNCHECKED);
  292. }
  293. // HIDE the Username and SessionId if its not Terminal Server.
  294. if( !g_fIsTSEnabled )
  295. {
  296. ShowWindow( GetDlgItem( hwndDlg , IDC_USERNAME ) , SW_HIDE );
  297. ShowWindow( GetDlgItem( hwndDlg , IDC_SESSIONID ) , SW_HIDE );
  298. }
  299. // Then turn on the ones for the columns we have active
  300. for (i = 0; i < NUM_COLUMN + 1; i++)
  301. {
  302. if (g_Options.m_ActiveProcCol[i] == -1)
  303. {
  304. break;
  305. }
  306. CheckDlgButton(hwndDlg, g_aDlgColIDs[g_Options.m_ActiveProcCol[i]], BST_CHECKED);
  307. }
  308. return TRUE;
  309. }
  310. case WM_COMMAND:
  311. {
  312. // If user clicked OK, add the columns to the array and reset the listview
  313. if (LOWORD(wParam) == IDOK)
  314. {
  315. // First, make sure the column width array is up to date
  316. pPage->SaveColumnWidths();
  317. INT iCol = 1;
  318. g_Options.m_ActiveProcCol[0] = COL_IMAGENAME;
  319. for (int i = 1; i < NUM_COLUMN && g_aDlgColIDs[i] >= 0; i++)
  320. {
  321. if (BST_CHECKED == IsDlgButtonChecked(hwndDlg, g_aDlgColIDs[i]))
  322. {
  323. // It is checked
  324. if (g_Options.m_ActiveProcCol[iCol] != (COLUMNID) i)
  325. {
  326. // If the column wasn't already there, insert its column
  327. // width into the column width array
  328. ShiftArray(g_Options.m_ColumnWidths, iCol, SHIFT_UP);
  329. ShiftArray(g_Options.m_ActiveProcCol, iCol, SHIFT_UP);
  330. g_Options.m_ColumnWidths[iCol] = ColumnDefaults[ i ].Width;
  331. g_Options.m_ActiveProcCol[iCol] = (COLUMNID) i;
  332. }
  333. iCol++;
  334. }
  335. else
  336. {
  337. // Not checked, column not active. If it used to be active,
  338. // remove its column width from the column width array
  339. if (g_Options.m_ActiveProcCol[iCol] == (COLUMNID) i)
  340. {
  341. ShiftArray(g_Options.m_ColumnWidths, iCol, SHIFT_DOWN);
  342. ShiftArray(g_Options.m_ActiveProcCol, iCol, SHIFT_DOWN);
  343. }
  344. }
  345. }
  346. // Terminate the column list
  347. g_Options.m_ActiveProcCol[iCol] = (COLUMNID) -1;
  348. pPage->SetupColumns();
  349. pPage->TimerEvent();
  350. EndDialog(hwndDlg, IDOK);
  351. }
  352. else if (LOWORD(wParam) == IDCANCEL)
  353. {
  354. EndDialog(hwndDlg, IDCANCEL);
  355. }
  356. }
  357. }
  358. return FALSE;
  359. }
  360. /*++ CProcPage::~CProcPage()
  361. - Destructor
  362. */
  363. CProcPage::~CProcPage()
  364. {
  365. Destroy( );
  366. }
  367. /*++ CProcPage::PickColumns()
  368. Function Description:
  369. Puts up UI that lets the user select what columns to display in the
  370. process page, and then resets the listview with the new column list
  371. Arguments:
  372. none
  373. Return Value:
  374. none
  375. Revision History:
  376. Jan-05-96 Davepl Created
  377. --*/
  378. void CProcPage::PickColumns()
  379. {
  380. DialogBoxParam(g_hInstance,
  381. MAKEINTRESOURCE(IDD_SELECTPROCCOLS),
  382. g_hMainWnd,
  383. ColSelectDlgProc,
  384. (LPARAM) this);
  385. }
  386. /*++ GetPriRanking
  387. Function Description:
  388. Since the priority class defines aren't in order, this helper
  389. exists to make comparisons between pri classes easier. It returns
  390. a larger number for "higher" priority classes
  391. Arguments:
  392. Return Value:
  393. rank of priority (0 to 5)
  394. Revision History:
  395. Nov-27-95 Davepl Created
  396. --*/
  397. DWORD GetPriRanking(DWORD dwClass)
  398. {
  399. switch(dwClass)
  400. {
  401. case REALTIME_PRIORITY_CLASS:
  402. return 5;
  403. case HIGH_PRIORITY_CLASS:
  404. return 4;
  405. case ABOVE_NORMAL_PRIORITY_CLASS:
  406. return 3;
  407. case NORMAL_PRIORITY_CLASS:
  408. return 2;
  409. case BELOW_NORMAL_PRIORITY_CLASS:
  410. return 1;
  411. default:
  412. return 0;
  413. }
  414. }
  415. /*++ QuickConfirm
  416. Function Description:
  417. Gets a confirmation for things like terminating/debugging processes
  418. Arguments:
  419. idtitle - string ID of title for message box
  420. idmsg - string ID of message body
  421. Return Value:
  422. IDNO/IDYES, whatever comes back from MessageBox
  423. Revision History:
  424. Nov-28-95 Davepl Created
  425. --*/
  426. UINT CProcPage::QuickConfirm(UINT idTitle, UINT idBody)
  427. {
  428. /* We have removed the ability to disable confirmations
  429. if (FALSE == g_Options.m_fConfirmations)
  430. {
  431. return IDYES;
  432. }
  433. */
  434. // Get confirmation before we dust the process, or something similar
  435. TCHAR szTitle[MAX_PATH];
  436. TCHAR szBody[MAX_PATH];
  437. if (0 == LoadString(g_hInstance, idTitle, szTitle, ARRAYSIZE(szTitle)) ||
  438. 0 == LoadString(g_hInstance, idBody, szBody, ARRAYSIZE(szBody)))
  439. {
  440. return IDNO;
  441. }
  442. if (IDYES == MessageBox(m_hPage, szBody, szTitle, MB_ICONEXCLAMATION | MB_YESNO))
  443. {
  444. return IDYES;
  445. }
  446. return IDNO;
  447. }
  448. /*++ class CProcPage::SetupColumns
  449. Class Description:
  450. Removes any existing columns from the process listview and
  451. adds all of the columns listed in the g_Options.m_ActiveProcCol array.
  452. Arguments:
  453. Return Value:
  454. HRESULT
  455. Revision History:
  456. Nov-16-95 Davepl Created
  457. --*/
  458. static const _aIDColNames[NUM_COLUMN] =
  459. {
  460. IDS_COL_IMAGENAME,
  461. IDS_COL_PID,
  462. IDS_COL_USERNAME,
  463. IDS_COL_SESSIONID,
  464. IDS_COL_CPU,
  465. IDS_COL_CPUTIME,
  466. IDS_COL_MEMUSAGE,
  467. IDS_COL_MEMPEAK,
  468. IDS_COL_MEMUSAGEDIFF,
  469. IDS_COL_PAGEFAULTS,
  470. IDS_COL_PAGEFAULTSDIFF,
  471. IDS_COL_COMMITCHARGE,
  472. IDS_COL_PAGEDPOOL,
  473. IDS_COL_NONPAGEDPOOL,
  474. IDS_COL_BASEPRIORITY,
  475. IDS_COL_HANDLECOUNT,
  476. IDS_COL_THREADCOUNT,
  477. IDS_COL_USEROBJECTS,
  478. IDS_COL_GDIOBJECTS,
  479. IDS_COL_READOPERCOUNT,
  480. IDS_COL_WRITEOPERCOUNT,
  481. IDS_COL_OTHEROPERCOUNT,
  482. IDS_COL_READXFERCOUNT,
  483. IDS_COL_WRITEXFERCOUNT,
  484. IDS_COL_OTHERXFERCOUNT
  485. };
  486. HRESULT CProcPage::SetupColumns()
  487. {
  488. HWND hwndList = GetDlgItem(m_hPage, IDC_PROCLIST);
  489. if (NULL == hwndList)
  490. {
  491. return E_UNEXPECTED;
  492. }
  493. ListView_DeleteAllItems(hwndList);
  494. // Remove all existing columns
  495. LV_COLUMN lvcolumn;
  496. while(ListView_DeleteColumn(hwndList, 0))
  497. {
  498. NULL;
  499. }
  500. // Add all of the new columns
  501. INT iColumn = 0;
  502. while (g_Options.m_ActiveProcCol[iColumn] >= 0)
  503. {
  504. INT idColumn = g_Options.m_ActiveProcCol[iColumn];
  505. // idc_username or IDC_SESSIONID are available only for terminalserver.
  506. ASSERT((idColumn != COL_USERNAME && idColumn != COL_SESSIONID) || g_fIsTSEnabled);
  507. TCHAR szTitle[MAX_PATH];
  508. LoadString(g_hInstance, _aIDColNames[idColumn], szTitle, ARRAYSIZE(szTitle));
  509. lvcolumn.mask = LVCF_FMT | LVCF_TEXT | LVCF_TEXT | LVCF_WIDTH;
  510. lvcolumn.fmt = ColumnDefaults[ idColumn ].Format;
  511. // If no width preference has been recorded for this column, use the
  512. // default
  513. if (-1 == g_Options.m_ColumnWidths[iColumn])
  514. {
  515. lvcolumn.cx = ColumnDefaults[ idColumn ].Width;
  516. }
  517. else
  518. {
  519. lvcolumn.cx = g_Options.m_ColumnWidths[iColumn];
  520. }
  521. lvcolumn.pszText = szTitle;
  522. lvcolumn.iSubItem = iColumn;
  523. if (-1 == ListView_InsertColumn(hwndList, iColumn, &lvcolumn))
  524. {
  525. return E_FAIL;
  526. }
  527. iColumn++;
  528. }
  529. return S_OK;
  530. }
  531. //
  532. // Take two unsigned 64-bit values and compare them in a manner
  533. // that CProcInfo::Compare likes.
  534. //
  535. int Compare64(unsigned __int64 First, unsigned __int64 Second)
  536. {
  537. if (First < Second)
  538. return -1;
  539. else if (First > Second)
  540. return 1;
  541. else
  542. return 0;
  543. }
  544. /*++ class CProcInfo::Compare
  545. Class Description:
  546. Compares this CProcInfo object to another, and returns its ranking
  547. based on the g_iProcSortColumnID field.
  548. Note that if the objects are equal based on the current sort column,
  549. the PID is used as a secondary sort key to prevent items from
  550. jumping around in the listview
  551. WOW psuedo-processes always sort directly after their parent
  552. ntvdm.exe process. So really the sort order is:
  553. 1. WOW task psuedo-processes under parent in alpha order
  554. 2. User's selected order.
  555. 3. PID
  556. Arguments:
  557. pOther - the CProcInfo object to compare this to
  558. Return Value:
  559. < 0 - This CProcInfo is "less" than the other
  560. 0 - Equal (Can't happen, since PID is used to sort)
  561. > 0 - This CProcInfo is "greater" than the other
  562. Revision History:
  563. Nov-20-95 Davepl Created
  564. --*/
  565. INT CProcInfo::Compare(CProcInfo * pOther)
  566. {
  567. CProcInfo * pMyThis;
  568. CProcInfo * pMyOther;
  569. INT iRet = 0;
  570. //
  571. // Wow psuedo-processes don't have any performance information,
  572. // so use the parent "real" ntvdm.exe CProcInfo for sorting.
  573. //
  574. ASSERT(this != pOther);
  575. pMyThis = this->IsWowTask()
  576. ? this->m_pWowParentProcInfo
  577. : this;
  578. pMyOther = pOther->IsWowTask()
  579. ? pOther->m_pWowParentProcInfo
  580. : pOther;
  581. if (pMyThis == pMyOther) {
  582. //
  583. // This implies one or the other or both this and pOther
  584. // are WOW tasks, and they're in the same WOW VDM. Sort
  585. // the "real" process entry first, followed by its associated
  586. // WOW task entries alphabetical.
  587. //
  588. if (this->IsWowTask()) {
  589. if (pOther->IsWowTask()) {
  590. //
  591. // They are siblings and we sort by
  592. // image name.
  593. //
  594. ASSERT(this->m_pWowParentProcInfo ==
  595. pOther->m_pWowParentProcInfo);
  596. iRet = lstrcmpi(this->m_pszImageName, pOther->m_pszImageName);
  597. } else {
  598. //
  599. // pOther is not a Wow task, it must be ntvdm.exe
  600. // the parent of this. this sorts after pOther.
  601. //
  602. ASSERT(pOther == this->m_pWowParentProcInfo);
  603. iRet = 1;
  604. }
  605. } else {
  606. //
  607. // this is not a Wow task, pOther must be and
  608. // this must be pOther's parent.
  609. //
  610. ASSERT(pOther->IsWowTask());
  611. iRet = -1;
  612. }
  613. }
  614. if (0 == iRet) {
  615. switch (g_iProcSortColumnID)
  616. {
  617. case COL_CPU:
  618. iRet = Compare64(pMyThis->m_CPU, pMyOther->m_CPU);
  619. break;
  620. case COL_CPUTIME:
  621. iRet = Compare64(pMyThis->m_CPUTime.QuadPart, pMyOther->m_CPUTime.QuadPart);
  622. break;
  623. case COL_MEMUSAGE:
  624. iRet = Compare64(pMyThis->m_MemUsage, pMyOther->m_MemUsage);
  625. break;
  626. case COL_MEMUSAGEDIFF:
  627. iRet = Compare64(pMyThis->m_MemDiff, pMyOther->m_MemDiff);
  628. break;
  629. case COL_MEMPEAK:
  630. iRet = Compare64(pMyThis->m_MemPeak, pMyOther->m_MemPeak);
  631. break;
  632. case COL_PAGEFAULTS:
  633. iRet = Compare64(pMyThis->m_PageFaults, pMyOther->m_PageFaults);
  634. break;
  635. case COL_PAGEFAULTSDIFF:
  636. iRet = Compare64(pMyThis->m_PageFaultsDiff, pMyOther->m_PageFaultsDiff);
  637. break;
  638. case COL_COMMITCHARGE:
  639. iRet = Compare64(pMyThis->m_CommitCharge, pMyOther->m_CommitCharge);
  640. break;
  641. case COL_PAGEDPOOL:
  642. iRet = Compare64(pMyThis->m_PagedPool, pMyOther->m_PagedPool);
  643. break;
  644. case COL_NONPAGEDPOOL:
  645. iRet = Compare64(pMyThis->m_NonPagedPool, pMyOther->m_NonPagedPool);
  646. break;
  647. case COL_BASEPRIORITY:
  648. iRet = Compare64(GetPriRanking(pMyThis->m_PriClass), GetPriRanking(pMyOther->m_PriClass));
  649. break;
  650. case COL_HANDLECOUNT:
  651. iRet = Compare64(pMyThis->m_HandleCount, pMyOther->m_HandleCount);
  652. break;
  653. case COL_THREADCOUNT:
  654. iRet = Compare64(pMyThis->m_ThreadCount, pMyOther->m_ThreadCount);
  655. break;
  656. case COL_PID:
  657. iRet = Compare64(pMyThis->m_UniqueProcessId, pMyOther->m_UniqueProcessId);
  658. break;
  659. case COL_SESSIONID:
  660. iRet = Compare64(pMyThis->m_SessionId, pMyOther->m_SessionId);
  661. break;
  662. case COL_USERNAME:
  663. iRet = lstrcmpi( pMyThis->m_pszUserName , pMyOther->m_pszUserName );
  664. break;
  665. case COL_IMAGENAME:
  666. iRet = lstrcmpi(pMyThis->m_pszImageName, pMyOther->m_pszImageName);
  667. break;
  668. case COL_USEROBJECTS:
  669. iRet = Compare64(pMyThis->m_USERObjectCount, pMyOther->m_USERObjectCount);
  670. break;
  671. case COL_GDIOBJECTS:
  672. iRet = Compare64(pMyThis->m_GDIObjectCount, pMyOther->m_GDIObjectCount);
  673. break;
  674. case COL_READOPERCOUNT:
  675. iRet = Compare64(pMyThis->m_IoReadOperCount, pMyOther->m_IoReadOperCount);
  676. break;
  677. case COL_WRITEOPERCOUNT:
  678. iRet = Compare64(pMyThis->m_IoWriteOperCount, pMyOther->m_IoWriteOperCount);
  679. break;
  680. case COL_OTHEROPERCOUNT:
  681. iRet = Compare64(pMyThis->m_IoOtherOperCount, pMyOther->m_IoOtherOperCount);
  682. break;
  683. case COL_READXFERCOUNT:
  684. iRet = Compare64(pMyThis->m_IoReadXferCount, pMyOther->m_IoReadXferCount);
  685. break;
  686. case COL_WRITEXFERCOUNT:
  687. iRet = Compare64(pMyThis->m_IoWriteXferCount, pMyOther->m_IoWriteXferCount);
  688. break;
  689. case COL_OTHERXFERCOUNT:
  690. iRet = Compare64(pMyThis->m_IoOtherXferCount, pMyOther->m_IoOtherXferCount);
  691. break;
  692. default:
  693. ASSERT(FALSE);
  694. iRet = 0;
  695. break;
  696. }
  697. iRet *= g_iProcSortDirection;
  698. }
  699. // If objects look equal, compare on PID as secondary sort column
  700. // so that items don't jump around in the listview
  701. if (0 == iRet)
  702. {
  703. iRet = Compare64(pMyThis->m_UniqueProcessId,
  704. pMyOther->m_UniqueProcessId) *
  705. g_iProcSortDirection;
  706. }
  707. return iRet;
  708. }
  709. /*++ class CProcInfo::SetCPU
  710. Method Description:
  711. Sets the CPU percentage.
  712. Arguments:
  713. CPUTime - Time for this process
  714. TotalTime - Total elapsed time, used as the denominator in calculations
  715. Return Value:
  716. Revision History:
  717. 19-Feb-96 DaveHart Created
  718. --*/
  719. void CProcInfo::SetCPU(LARGE_INTEGER CPUTimeDelta,
  720. LARGE_INTEGER TotalTime,
  721. BOOL fDisplayOnly)
  722. {
  723. // Calc CPU time based on this process's ratio of the total process time used
  724. INT cpu = (BYTE) (((CPUTimeDelta.QuadPart / ((TotalTime.QuadPart / 1000) ?
  725. (TotalTime.QuadPart / 1000) : 1)) + 5)
  726. / 10);
  727. // ASSERT( cpu <= 105 && "CPU much > 100% - Davepl x69731, 425-836-1939 (res)");
  728. if (cpu > 99)
  729. {
  730. cpu = 99;
  731. }
  732. if (m_DisplayCPU != cpu)
  733. {
  734. m_fDirty_COL_CPU = TRUE;
  735. m_DisplayCPU = (BYTE) cpu;
  736. if ( ! fDisplayOnly )
  737. {
  738. m_CPU = (BYTE) cpu;
  739. }
  740. }
  741. }
  742. /*++ CProcPage::GetProcessInfo
  743. Class Description:
  744. Reads the process info table into a virtual alloc'd buffer, resizing
  745. the buffer if needed
  746. Arguments:
  747. Return Value:
  748. Revision History:
  749. Nov-16-95 Davepl Created
  750. --*/
  751. static const int PROCBUF_GROWSIZE = 4096;
  752. HRESULT CProcPage::GetProcessInfo()
  753. {
  754. HRESULT hr = S_OK;
  755. NTSTATUS status;
  756. while(hr == S_OK)
  757. {
  758. if (m_pvBuffer)
  759. {
  760. status = NtQuerySystemInformation(SystemProcessInformation,
  761. m_pvBuffer,
  762. static_cast<ULONG>(m_cbBuffer),
  763. NULL);
  764. //
  765. // If we succeeded, great, get outta here. If not, any error other
  766. // than "buffer too small" is fatal, in which case we bail
  767. //
  768. if (NT_SUCCESS(status))
  769. {
  770. break;
  771. }
  772. if (status != STATUS_INFO_LENGTH_MISMATCH)
  773. {
  774. hr = E_FAIL;
  775. break;
  776. }
  777. }
  778. //
  779. // Buffer wasn't large enough to hold the process info table, so resize it
  780. // to be larger, then retry.
  781. //
  782. if (m_pvBuffer)
  783. {
  784. HeapFree( GetProcessHeap( ), 0, m_pvBuffer );
  785. m_pvBuffer = NULL;
  786. }
  787. m_cbBuffer += PROCBUF_GROWSIZE;
  788. m_pvBuffer = HeapAlloc( GetProcessHeap( ), 0, m_cbBuffer );
  789. if (m_pvBuffer == NULL)
  790. {
  791. hr = E_OUTOFMEMORY;
  792. break;
  793. }
  794. }
  795. return hr;
  796. }
  797. /*++ CProcPage::Int64ToCommaSepString
  798. Class Description:
  799. Convert a 64-bit integer to a string with commas.
  800. (2^64)-1 = "18,446,744,073,709,600,000" (27 chars).
  801. Arguments:
  802. n - 64-bit integer.
  803. pszOut - Destination character buffer.
  804. cchOut - Size of destination buffer in characters.
  805. Return Value:
  806. None.
  807. Revision History:
  808. Jan-11-99 BrianAu Created
  809. --*/
  810. NTSTATUS
  811. MyMoBettaRtlInt64ToUnicodeString (
  812. IN LONGLONG Value,
  813. IN ULONG Base OPTIONAL,
  814. IN OUT PUNICODE_STRING String
  815. )
  816. {
  817. NTSTATUS Status;
  818. char ResultBuffer[32];
  819. ANSI_STRING AnsiString;
  820. LARGE_INTEGER Temp;
  821. if (Value < 0)
  822. {
  823. Temp.QuadPart = -Value;
  824. Status = RtlLargeIntegerToChar(&Temp,
  825. Base,
  826. sizeof(ResultBuffer) - sizeof(CHAR),
  827. &ResultBuffer[sizeof(CHAR)]);
  828. *(ResultBuffer)=L'-';
  829. }
  830. else
  831. {
  832. Temp.QuadPart = Value;
  833. Status = RtlLargeIntegerToChar(&Temp,
  834. Base,
  835. sizeof(ResultBuffer),
  836. ResultBuffer);
  837. }
  838. if (NT_SUCCESS(Status)) {
  839. AnsiString.Buffer = ResultBuffer;
  840. AnsiString.MaximumLength = sizeof(ResultBuffer);
  841. AnsiString.Length = (USHORT)strlen(ResultBuffer);
  842. Status = RtlAnsiStringToUnicodeString(String, &AnsiString, FALSE);
  843. }
  844. return Status;
  845. }
  846. void
  847. CProcPage::Int64ToCommaSepString(
  848. LONGLONG n,
  849. LPTSTR pszOut,
  850. int cchOut
  851. )
  852. {
  853. UNICODE_STRING s;
  854. NUMBERFMTW nfmtW;
  855. LPWSTR pszFmtOutW;
  856. int cchFmtOut;
  857. WCHAR szTextW[32];
  858. //
  859. // Convert the 64-bit int to a text string.
  860. //
  861. s.Length = 0;
  862. s.MaximumLength = sizeof(szTextW) - sizeof(TCHAR);
  863. s.Buffer = szTextW;
  864. MyMoBettaRtlInt64ToUnicodeString(n, 10, &s);
  865. //
  866. // Format the number with commas according to locale conventions.
  867. //
  868. nfmtW.NumDigits = 0;
  869. nfmtW.LeadingZero = 0;
  870. nfmtW.Grouping = UINT(g_ulGroupSep);
  871. nfmtW.lpDecimalSep = nfmtW.lpThousandSep = g_szGroupThousSep;
  872. nfmtW.NegativeOrder = 0;
  873. pszFmtOutW = pszOut;
  874. cchFmtOut = cchOut;
  875. GetNumberFormatW(LOCALE_USER_DEFAULT,
  876. 0,
  877. szTextW,
  878. &nfmtW,
  879. pszFmtOutW,
  880. cchFmtOut);
  881. }
  882. /*++ CProcPage::Int64ToCommaSepKString
  883. Class Description:
  884. Convert a 64-bit integer to a string with commas appended
  885. with the "K" units designator.
  886. (2^64)-1 = "18,446,744,073,709,600,000 K" (29 chars).
  887. Arguments:
  888. n - 64-bit integer.
  889. pszOut - Destination character buffer.
  890. Return Value:
  891. None.
  892. Revision History:
  893. Jan-11-99 BrianAu Created
  894. --*/
  895. void
  896. CProcPage::Int64ToCommaSepKString(
  897. LONGLONG n,
  898. LPTSTR pszOut,
  899. int cchOut
  900. )
  901. {
  902. TCHAR szText[40];
  903. Int64ToCommaSepString(n, szText, ARRAYSIZE(szText));
  904. LPTSTR pszEnd = szText + lstrlen(szText);
  905. *pszEnd++ = TEXT(' ');
  906. lstrcpy(pszEnd, g_szK);
  907. lstrcpyn(pszOut, szText, cchOut);
  908. }
  909. /*++ CProcPage::RestoreColumnOrder
  910. Routine Description:
  911. Sets the column order from the per-user preference data stored
  912. in the global COptions object.
  913. Arguments:
  914. hwndList - Listview window handle.
  915. Return Value:
  916. Revision History:
  917. Jan-11/99 BrianAu Created
  918. --*/
  919. void
  920. CProcPage::RestoreColumnOrder(
  921. HWND hwndList
  922. )
  923. {
  924. INT rgOrder[ARRAYSIZE(g_Options.m_ColumnPositions)];
  925. INT cOrder = 0;
  926. INT iOrder = 0;
  927. for (int i = 0; i < ARRAYSIZE(g_Options.m_ColumnPositions); i++)
  928. {
  929. iOrder = g_Options.m_ColumnPositions[i];
  930. if (-1 == iOrder)
  931. break;
  932. rgOrder[cOrder++] = iOrder;
  933. }
  934. if (0 < cOrder)
  935. {
  936. const HWND hwndHeader = ListView_GetHeader(hwndList);
  937. ASSERT(Header_GetItemCount(hwndHeader) == cOrder);
  938. Header_SetOrderArray(hwndHeader, Header_GetItemCount(hwndHeader), rgOrder);
  939. }
  940. }
  941. /*++ CProcPage::RememberColumnOrder
  942. Routine Description:
  943. Saves the current column order to the global COptions object
  944. which is later saved to the registry for per-user preferences.
  945. Arguments:
  946. hwndList - Listview window handle.
  947. Return Value:
  948. Revision History:
  949. Jan-11/99 BrianAu Created
  950. --*/
  951. void
  952. CProcPage::RememberColumnOrder(
  953. HWND hwndList
  954. )
  955. {
  956. const HWND hwndHeader = ListView_GetHeader(hwndList);
  957. ASSERT(Header_GetItemCount(hwndHeader) <= ARRAYSIZE(g_Options.m_ColumnPositions));
  958. FillMemory(&g_Options.m_ColumnPositions, sizeof(g_Options.m_ColumnPositions), 0xFF);
  959. Header_GetOrderArray(hwndHeader,
  960. Header_GetItemCount(hwndHeader),
  961. g_Options.m_ColumnPositions);
  962. }
  963. /*++ FindProcInArrayByPID
  964. Class Description:
  965. Walks the ptrarray given and looks for the CProcInfo object
  966. that has the PID supplied. If not found, returns NULL
  967. Arguments:
  968. pArray - The CPtrArray where the CProcInfos could live
  969. pid - The pid to search for
  970. Return Value:
  971. CProcInfo * in the array, if found, or NULL if not
  972. Revision History:
  973. Nov-20-95 Davepl Created
  974. --*/
  975. // REVIEW (DavePl) could provide a static search hint here so
  976. // that it doesn't always need to start back at zero, or could
  977. // do a binary search
  978. CProcInfo * FindProcInArrayByPID(CPtrArray * pArray, DWORD pid)
  979. {
  980. for (int i = 0; i < pArray->GetSize(); i++)
  981. {
  982. CProcInfo * pTmp = (CProcInfo *) (pArray->GetAt(i));
  983. if (pTmp->m_UniqueProcessId == pid)
  984. {
  985. // Found it
  986. return pTmp;
  987. }
  988. }
  989. // Not found
  990. return NULL;
  991. }
  992. /*++ InsertIntoSortedArray
  993. Class Description:
  994. Sticks a CProcInfo ptr into the ptrarray supplied at the
  995. appropriate location based on the current sort column (which
  996. is used by the Compare member function)
  997. Arguments:
  998. pArray - The CPtrArray to add to
  999. pProc - The CProcInfo object to add to the array
  1000. Return Value:
  1001. TRUE if successful, FALSE if fails
  1002. Revision History:
  1003. Nov-20-95 Davepl Created
  1004. --*/
  1005. // REVIEW (davepl) Use binary insert here, not linear
  1006. BOOL InsertIntoSortedArray(CPtrArray * pArray, CProcInfo * pProc)
  1007. {
  1008. INT cItems = pArray->GetSize();
  1009. for (INT iIndex = 0; iIndex < cItems; iIndex++)
  1010. {
  1011. CProcInfo * pTmp = (CProcInfo *) pArray->GetAt(iIndex);
  1012. if (pProc->Compare(pTmp) > 0)
  1013. {
  1014. return pArray->InsertAt(iIndex, pProc);
  1015. }
  1016. }
  1017. return pArray->Add(pProc);
  1018. }
  1019. /*++ ResortArray
  1020. Function Description:
  1021. Creates a new ptr array sorted in the current sort order based
  1022. on the old array, and then replaces the old with the new
  1023. Arguments:
  1024. ppArray - The CPtrArray to resort
  1025. Return Value:
  1026. TRUE if successful, FALSE if fails
  1027. Revision History:
  1028. Nov-21-95 Davepl Created
  1029. --*/
  1030. BOOL ResortArray(CPtrArray ** ppArray)
  1031. {
  1032. // Create a new array which will be sorted in the new
  1033. // order and used to replace the existing array
  1034. CPtrArray * pNew = new CPtrArray(GetProcessHeap());
  1035. if (NULL == pNew)
  1036. {
  1037. return FALSE;
  1038. }
  1039. // Insert each of the existing items in the old array into
  1040. // the new array in the correct spot
  1041. INT cItems = (*ppArray)->GetSize();
  1042. for (int i = 0; i < cItems; i++)
  1043. {
  1044. CProcInfo * pItem = (CProcInfo *) (*ppArray)->GetAt(i);
  1045. if (FALSE == InsertIntoSortedArray(pNew, pItem))
  1046. {
  1047. delete pNew;
  1048. return FALSE;
  1049. }
  1050. }
  1051. // Kill off the old array, replace it with the new
  1052. delete (*ppArray);
  1053. (*ppArray) = pNew;
  1054. return TRUE;
  1055. }
  1056. typedef struct
  1057. {
  1058. LARGE_INTEGER uPassCount;
  1059. CProcPage * pProcPage;
  1060. CProcInfo * pParentProcInfo;
  1061. LARGE_INTEGER TotalTime;
  1062. LARGE_INTEGER TimeLeft;
  1063. } WOWTASKCALLBACKPARMS, *PWOWTASKCALLBACKPARMS;
  1064. BOOL WINAPI WowTaskCallback(
  1065. DWORD dwThreadId,
  1066. WORD hMod16,
  1067. WORD hTask16,
  1068. CHAR *pszModName,
  1069. CHAR *pszFileName,
  1070. LPARAM lparam
  1071. )
  1072. {
  1073. PWOWTASKCALLBACKPARMS pParms = (PWOWTASKCALLBACKPARMS)lparam;
  1074. HRESULT hr;
  1075. //
  1076. // See if this task is already in the list.
  1077. //
  1078. CProcInfo * pOldProcInfo;
  1079. pOldProcInfo = FindProcInArrayByPID(
  1080. pParms->pProcPage->m_pProcArray,
  1081. dwThreadId);
  1082. if (NULL == pOldProcInfo)
  1083. {
  1084. //
  1085. // We don't already have this process in our array, so create a new one
  1086. // and add it to the array
  1087. //
  1088. CProcInfo * pNewProcInfo = new CProcInfo;
  1089. if (NULL == pNewProcInfo)
  1090. {
  1091. goto done;
  1092. }
  1093. hr = pNewProcInfo->SetDataWowTask(pParms->TotalTime,
  1094. dwThreadId,
  1095. pszFileName,
  1096. pParms->uPassCount,
  1097. pParms->pParentProcInfo,
  1098. &pParms->TimeLeft,
  1099. hTask16,
  1100. FALSE);
  1101. if (FAILED(hr) ||
  1102. FALSE == pParms->pProcPage->m_pProcArray->Add(pNewProcInfo))
  1103. {
  1104. delete pNewProcInfo;
  1105. goto done;
  1106. }
  1107. }
  1108. else
  1109. {
  1110. //
  1111. // This process already existed in our array, so update its info
  1112. //
  1113. pOldProcInfo->SetDataWowTask(pParms->TotalTime,
  1114. dwThreadId,
  1115. pszFileName,
  1116. pParms->uPassCount,
  1117. pParms->pParentProcInfo,
  1118. &pParms->TimeLeft,
  1119. hTask16,
  1120. TRUE);
  1121. }
  1122. done:
  1123. return FALSE; // continue enumeration
  1124. }
  1125. /*++ class CProcInfo::SetDataWowTask
  1126. Method Description:
  1127. Sets up a single CProcInfo object based on the parameters.
  1128. This is a WOW task pseudo-process entry.
  1129. Arguments:
  1130. dwThreadId
  1131. pszFilePath Fully-qualified path from VDMEnumTaskWOWEx.
  1132. Return Value:
  1133. Revision History:
  1134. 18-Feb-96 DaveHart created
  1135. --*/
  1136. HRESULT CProcInfo::SetDataWowTask(LARGE_INTEGER TotalTime,
  1137. DWORD dwThreadId,
  1138. CHAR * pszFilePath,
  1139. LARGE_INTEGER uPassCount,
  1140. CProcInfo * pParentProcInfo,
  1141. LARGE_INTEGER *pTimeLeft,
  1142. WORD htask,
  1143. BOOL fUpdateOnly)
  1144. {
  1145. CHAR *pchExe;
  1146. //
  1147. // Touch this CProcInfo to indicate the process is still alive
  1148. //
  1149. m_uPassCount.QuadPart = uPassCount.QuadPart;
  1150. //
  1151. // Update the thread's execution times.
  1152. //
  1153. HANDLE hThread;
  1154. NTSTATUS Status;
  1155. OBJECT_ATTRIBUTES obja;
  1156. CLIENT_ID cid;
  1157. InitializeObjectAttributes(
  1158. &obja,
  1159. NULL,
  1160. 0,
  1161. NULL,
  1162. 0 );
  1163. cid.UniqueProcess = 0; // 0 means any process
  1164. cid.UniqueThread = IntToPtr(dwThreadId);
  1165. Status = NtOpenThread(
  1166. &hThread,
  1167. THREAD_QUERY_INFORMATION,
  1168. &obja,
  1169. &cid );
  1170. ULONGLONG ullCreation, ullExit, ullKernel, ullUser;
  1171. if ( NT_SUCCESS(Status) )
  1172. {
  1173. LARGE_INTEGER TimeDelta, Time;
  1174. if (GetThreadTimes(
  1175. hThread,
  1176. (LPFILETIME) &ullCreation,
  1177. (LPFILETIME) &ullExit,
  1178. (LPFILETIME) &ullKernel,
  1179. (LPFILETIME) &ullUser
  1180. ) )
  1181. {
  1182. Time.QuadPart = (LONGLONG)(ullUser + ullKernel);
  1183. TimeDelta.QuadPart = Time.QuadPart - m_CPUTime.QuadPart;
  1184. if (TimeDelta.QuadPart < 0)
  1185. {
  1186. ASSERT(0 && "WOW tasks's cpu total usage went DOWN since last refresh - Bug 247473, Shaunp");
  1187. Invalidate();
  1188. return E_FAIL;
  1189. }
  1190. if (TimeDelta.QuadPart)
  1191. {
  1192. m_fDirty_COL_CPUTIME = TRUE;
  1193. m_CPUTime.QuadPart = Time.QuadPart;
  1194. }
  1195. //
  1196. // Don't allow sum of WOW child task times to
  1197. // exceed ntvdm.exe total. We call GetThreadTimes
  1198. // substantially after we get process times, so
  1199. // this can happen.
  1200. //
  1201. if (TimeDelta.QuadPart > pTimeLeft->QuadPart)
  1202. {
  1203. TimeDelta.QuadPart = pTimeLeft->QuadPart;
  1204. pTimeLeft->QuadPart = 0;
  1205. }
  1206. else
  1207. {
  1208. pTimeLeft->QuadPart -= TimeDelta.QuadPart;
  1209. }
  1210. SetCPU( TimeDelta, TotalTime, FALSE );
  1211. //
  1212. // When WOW tasks are being displayed, the line for ntvdm.exe
  1213. // should show times only for overhead or historic threads,
  1214. // not including any active task threads.
  1215. //
  1216. if (pParentProcInfo->m_DisplayCPUTime.QuadPart > m_CPUTime.QuadPart)
  1217. {
  1218. pParentProcInfo->m_DisplayCPUTime.QuadPart -= m_CPUTime.QuadPart;
  1219. }
  1220. else
  1221. {
  1222. pParentProcInfo->m_DisplayCPUTime.QuadPart = 0;
  1223. }
  1224. m_DisplayCPUTime.QuadPart = m_CPUTime.QuadPart;
  1225. }
  1226. NtClose(hThread);
  1227. }
  1228. if (m_PriClass != pParentProcInfo->m_PriClass) {
  1229. m_fDirty_COL_BASEPRIORITY = TRUE;
  1230. m_PriClass = pParentProcInfo->m_PriClass;
  1231. }
  1232. if( m_SessionId != pParentProcInfo->m_SessionId )
  1233. {
  1234. m_fDirty_COL_SESSIONID = TRUE;
  1235. m_SessionId = pParentProcInfo->m_SessionId;
  1236. }
  1237. if (FALSE == fUpdateOnly)
  1238. {
  1239. UINT uLen;
  1240. //
  1241. // Set the task's image name, thread ID, thread count,
  1242. // htask, and parent CProcInfo which do not change over
  1243. // time.
  1244. //
  1245. m_htaskWow = htask;
  1246. m_fDirty_COL_PID = TRUE;
  1247. m_fDirty_COL_IMAGENAME = TRUE;
  1248. m_fDirty_COL_THREADCOUNT = TRUE;
  1249. m_fDirty_COL_USERNAME = TRUE;
  1250. m_fDirty_COL_SESSIONID = TRUE;
  1251. m_UniqueProcessId = dwThreadId;
  1252. m_ThreadCount = 1;
  1253. //
  1254. // We're only interested in the filename of the EXE
  1255. // with the path stripped.
  1256. //
  1257. pchExe = strrchr(pszFilePath, '\\');
  1258. if (NULL == pchExe) {
  1259. pchExe = pszFilePath;
  1260. }
  1261. else
  1262. {
  1263. // skip backslash
  1264. pchExe++;
  1265. }
  1266. uLen = static_cast<UINT>(strlen(pchExe));
  1267. //
  1268. // Indent the EXE name by two spaces
  1269. // so WOW tasks look subordinate to
  1270. // their ntvdm.exe
  1271. //
  1272. m_pszImageName = new TCHAR[uLen + 3];
  1273. if (NULL == m_pszImageName)
  1274. {
  1275. return E_OUTOFMEMORY;
  1276. }
  1277. m_pszImageName[0] = m_pszImageName[1] = TEXT(' ');
  1278. MultiByteToWideChar(
  1279. CP_ACP,
  1280. 0,
  1281. pchExe,
  1282. uLen,
  1283. &m_pszImageName[2],
  1284. uLen
  1285. );
  1286. m_pszImageName[uLen + 2] = 0;
  1287. //
  1288. // WOW EXE filenames are always uppercase, so lowercase it.
  1289. //
  1290. CharLowerBuff(&m_pszImageName[2], uLen);
  1291. m_pWowParentProcInfo = pParentProcInfo;
  1292. if( g_fIsTSEnabled )
  1293. {
  1294. SetProcessUsername( LPFILETIME( &ullCreation ) );
  1295. }
  1296. }
  1297. return S_OK;
  1298. }
  1299. /*++ class CProcInfo::SetData
  1300. Class Description:
  1301. Sets up a single CProcInfo object based on the data contained in a
  1302. SYSTEM_PROCESS_INFORMATION block.
  1303. If fUpdate is set, the imagename and icon fields are not processed,
  1304. since they do not change throughout the lifetime of the process
  1305. Arguments:
  1306. TotalTime - Total elapsed time, used as the denominator in calculations
  1307. for the process' CPU usage, etc
  1308. pInfo - The SYSTEM_PROCESS_INFORMATION block for this process
  1309. uPassCount- Current passcount, used to timestamp the last update of
  1310. this objectg
  1311. fUpdate - See synopsis
  1312. Return Value:
  1313. Revision History:
  1314. Nov-16-95 Davepl Created
  1315. --*/
  1316. HRESULT CProcInfo::SetData(LARGE_INTEGER TotalTime,
  1317. PSYSTEM_PROCESS_INFORMATION pInfo,
  1318. LARGE_INTEGER uPassCount,
  1319. CProcPage * pProcPage,
  1320. BOOL fUpdateOnly)
  1321. {
  1322. HRESULT hr = S_OK;
  1323. DWORD dwTemp;
  1324. HANDLE hProcess;
  1325. // Touch this CProcInfo to indicate the process is still alive
  1326. m_uPassCount.QuadPart = uPassCount.QuadPart;
  1327. // Calc this process's total time as the sum of its user and kernel time
  1328. LARGE_INTEGER TimeDelta;
  1329. LARGE_INTEGER Time;
  1330. if (pInfo->UserTime.QuadPart + pInfo->KernelTime.QuadPart < m_CPUTime.QuadPart)
  1331. {
  1332. // ASSERT(0 && "Proc's cpu total usage went DOWN since last refresh. - Davepl x69731, 425-836-1939 (res)");
  1333. Invalidate();
  1334. return hr = E_FAIL;
  1335. }
  1336. Time.QuadPart = pInfo->UserTime.QuadPart +
  1337. pInfo->KernelTime.QuadPart;
  1338. TimeDelta.QuadPart = Time.QuadPart - m_CPUTime.QuadPart;
  1339. if (TimeDelta.QuadPart)
  1340. {
  1341. m_CPUTime.QuadPart = m_DisplayCPUTime.QuadPart = Time.QuadPart;
  1342. m_fDirty_COL_CPUTIME = TRUE;
  1343. }
  1344. SetCPU( TimeDelta, TotalTime, FALSE );
  1345. //
  1346. // For each of the fields, we check to see if anything has changed, and if
  1347. // so, we mark that particular column as having changed, and update the value.
  1348. // This allows me to opimize which fields of the listview to repaint, since
  1349. // repainting an entire listview column causes flicker and looks bad in
  1350. // general
  1351. //
  1352. // Miscellaneous fields
  1353. if (m_UniqueProcessId != PtrToUlong(pInfo->UniqueProcessId))
  1354. {
  1355. m_fDirty_COL_PID = TRUE;
  1356. m_UniqueProcessId = PtrToUlong(pInfo->UniqueProcessId);
  1357. }
  1358. if( m_SessionId != pInfo->SessionId )
  1359. {
  1360. m_fDirty_COL_SESSIONID = TRUE;
  1361. m_SessionId = pInfo->SessionId;
  1362. }
  1363. if (m_MemDiff != ((SSIZE_T)pInfo->WorkingSetSize / 1024) - (SSIZE_T)m_MemUsage )
  1364. {
  1365. m_fDirty_COL_MEMUSAGEDIFF = TRUE;
  1366. m_MemDiff = ((SSIZE_T)pInfo->WorkingSetSize / 1024) - (SSIZE_T)m_MemUsage;
  1367. }
  1368. if (m_MemPeak != (pInfo->PeakWorkingSetSize / 1024))
  1369. {
  1370. m_fDirty_COL_MEMPEAK = TRUE;
  1371. m_MemPeak = (pInfo->PeakWorkingSetSize / 1024);
  1372. }
  1373. if (m_MemUsage != pInfo->WorkingSetSize / 1024)
  1374. {
  1375. m_fDirty_COL_MEMUSAGE = TRUE;
  1376. m_MemUsage = (pInfo->WorkingSetSize / 1024);
  1377. }
  1378. if (m_PageFaultsDiff != ((LONG)(pInfo->PageFaultCount) - (LONG)m_PageFaults))
  1379. {
  1380. m_fDirty_COL_PAGEFAULTSDIFF = TRUE;
  1381. m_PageFaultsDiff = ((LONG)(pInfo->PageFaultCount) - (LONG)m_PageFaults);
  1382. }
  1383. if (m_PageFaults != (pInfo->PageFaultCount))
  1384. {
  1385. m_fDirty_COL_PAGEFAULTS = TRUE;
  1386. m_PageFaults = (pInfo->PageFaultCount);
  1387. }
  1388. if (m_CommitCharge != pInfo->PrivatePageCount / 1024)
  1389. {
  1390. m_fDirty_COL_COMMITCHARGE = TRUE;
  1391. m_CommitCharge = pInfo->PrivatePageCount / 1024;
  1392. }
  1393. if (m_PagedPool != pInfo->QuotaPagedPoolUsage / 1024)
  1394. {
  1395. m_fDirty_COL_PAGEDPOOL = TRUE;
  1396. m_PagedPool = pInfo->QuotaPagedPoolUsage / 1024;
  1397. }
  1398. if (m_NonPagedPool != pInfo->QuotaNonPagedPoolUsage / 1024)
  1399. {
  1400. m_fDirty_COL_NONPAGEDPOOL = TRUE;
  1401. m_NonPagedPool = pInfo->QuotaNonPagedPoolUsage / 1024;
  1402. }
  1403. if (m_PriClass != pInfo->BasePriority)
  1404. {
  1405. m_fDirty_COL_BASEPRIORITY = TRUE;
  1406. m_PriClass = pInfo->BasePriority;
  1407. }
  1408. if (m_HandleCount != pInfo->HandleCount)
  1409. {
  1410. m_fDirty_COL_HANDLECOUNT = TRUE;
  1411. m_HandleCount = pInfo->HandleCount;
  1412. }
  1413. if (m_ThreadCount != pInfo->NumberOfThreads)
  1414. {
  1415. m_fDirty_COL_HANDLECOUNT = TRUE;
  1416. m_ThreadCount = pInfo->NumberOfThreads;
  1417. }
  1418. if (m_IoReadOperCount != pInfo->ReadOperationCount.QuadPart)
  1419. {
  1420. m_fDirty_COL_READOPERCOUNT = TRUE;
  1421. m_IoReadOperCount = pInfo->ReadOperationCount.QuadPart;
  1422. }
  1423. if (m_IoWriteOperCount != pInfo->WriteOperationCount.QuadPart)
  1424. {
  1425. m_fDirty_COL_WRITEOPERCOUNT = TRUE;
  1426. m_IoWriteOperCount = pInfo->WriteOperationCount.QuadPart;
  1427. }
  1428. if (m_IoOtherOperCount != pInfo->OtherOperationCount.QuadPart)
  1429. {
  1430. m_fDirty_COL_OTHEROPERCOUNT = TRUE;
  1431. m_IoOtherOperCount = pInfo->OtherOperationCount.QuadPart;
  1432. }
  1433. if (m_IoReadXferCount != pInfo->ReadTransferCount.QuadPart)
  1434. {
  1435. m_fDirty_COL_READXFERCOUNT = TRUE;
  1436. m_IoReadXferCount = pInfo->ReadTransferCount.QuadPart;
  1437. }
  1438. if (m_IoWriteXferCount != pInfo->WriteTransferCount.QuadPart)
  1439. {
  1440. m_fDirty_COL_WRITEXFERCOUNT = TRUE;
  1441. m_IoWriteXferCount = pInfo->WriteTransferCount.QuadPart;
  1442. }
  1443. if (m_IoOtherXferCount != pInfo->OtherTransferCount.QuadPart)
  1444. {
  1445. m_fDirty_COL_OTHERXFERCOUNT = TRUE;
  1446. m_IoOtherXferCount = pInfo->OtherTransferCount.QuadPart;
  1447. }
  1448. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION , FALSE, m_UniqueProcessId);
  1449. if (hProcess && (m_USERObjectCount != (dwTemp = GetGuiResources(hProcess, GR_USEROBJECTS))))
  1450. {
  1451. m_fDirty_COL_USEROBJECTS = TRUE;
  1452. m_USERObjectCount = dwTemp;
  1453. }
  1454. if (hProcess && (m_GDIObjectCount != (dwTemp = GetGuiResources(hProcess, GR_GDIOBJECTS))))
  1455. {
  1456. m_fDirty_COL_GDIOBJECTS = TRUE;
  1457. m_GDIObjectCount = dwTemp;
  1458. }
  1459. if (hProcess)
  1460. CloseHandle(hProcess);
  1461. if (FALSE == fUpdateOnly)
  1462. {
  1463. //
  1464. // Set the process' image name. If its NULL it could be the "Idle Process" or simply
  1465. // a process whose image name is unknown. In both cases we load a string resource
  1466. // with an appropriate replacement name.
  1467. //
  1468. m_fDirty_COL_IMAGENAME = TRUE;
  1469. if (pInfo->ImageName.Buffer == NULL)
  1470. {
  1471. // No image name, so replace it with "Unknown"
  1472. TCHAR szTmp[MAX_PATH];
  1473. szTmp[0] = TEXT('\0');
  1474. UINT uLen = LoadString(g_hInstance, IDS_SYSPROC, szTmp, MAX_PATH);
  1475. m_pszImageName = new TCHAR[uLen + 1];
  1476. if (NULL == m_pszImageName)
  1477. {
  1478. return hr = E_OUTOFMEMORY;
  1479. }
  1480. lstrcpy(m_pszImageName, szTmp);
  1481. }
  1482. else
  1483. {
  1484. //
  1485. // We have a valid image name, so allocate enough space and then
  1486. // make a copy of it
  1487. //
  1488. m_pszImageName = new TCHAR[(pInfo->ImageName.Length / sizeof(WCHAR))+ 1];
  1489. if (NULL == m_pszImageName)
  1490. {
  1491. return hr = E_OUTOFMEMORY;
  1492. }
  1493. lstrcpyn(m_pszImageName, pInfo->ImageName.Buffer, (pInfo->ImageName.Length / sizeof(WCHAR)) + 1);
  1494. m_pszImageName[(pInfo->ImageName.Length / sizeof(WCHAR))] = TEXT('\0');
  1495. }
  1496. if( g_fIsTSEnabled )
  1497. {
  1498. SetProcessUsername(LPFILETIME(&(pInfo->CreateTime)));
  1499. }
  1500. }
  1501. //
  1502. // Check if this process is a WOW process. There is some latency
  1503. // between the time a WOW process is created and the time
  1504. // the shared memory used by VDMEnumTaskWOWEx reflects the new
  1505. // process and tasks. However, once a process becomes a WOW
  1506. // process, it is always a WOW process until it dies.
  1507. //
  1508. if (g_Options.m_fShow16Bit)
  1509. {
  1510. if ( m_fWowProcess ||
  1511. ! m_fWowProcessTested)
  1512. {
  1513. #if !defined (_WIN64)
  1514. if ( ( m_pszImageName != NULL ) && ( ! _wcsicmp(m_pszImageName, TEXT("ntvdm.exe")) ) )
  1515. {
  1516. WOWTASKCALLBACKPARMS WowTaskCallbackParms;
  1517. WowTaskCallbackParms.uPassCount = uPassCount;
  1518. WowTaskCallbackParms.pProcPage = pProcPage;
  1519. WowTaskCallbackParms.pParentProcInfo = this;
  1520. WowTaskCallbackParms.TotalTime.QuadPart = TotalTime.QuadPart;
  1521. WowTaskCallbackParms.TimeLeft.QuadPart = TimeDelta.QuadPart;
  1522. if (VDMEnumTaskWOWEx(m_UniqueProcessId,
  1523. WowTaskCallback,
  1524. (LPARAM) &WowTaskCallbackParms))
  1525. {
  1526. if ( ! m_fWowProcess )
  1527. {
  1528. m_fWowProcessTested =
  1529. m_fWowProcess = TRUE;
  1530. }
  1531. SetCPU( WowTaskCallbackParms.TimeLeft, TotalTime, TRUE );
  1532. }
  1533. else
  1534. {
  1535. //
  1536. // We avoid calling VDMEnumTaskWOWEx if the process has an
  1537. // execution time of more than 10 seconds and has not so
  1538. // far been seen as a WOW process.
  1539. //
  1540. if (GetCPUTime() > (10 * 10 * 1000 * 1000))
  1541. {
  1542. m_fWowProcessTested = TRUE;
  1543. }
  1544. }
  1545. }
  1546. else
  1547. {
  1548. m_fWowProcessTested = TRUE;
  1549. }
  1550. #else
  1551. m_fWowProcessTested = TRUE;
  1552. #endif
  1553. }
  1554. }
  1555. return S_OK;
  1556. }
  1557. //----------------------------------------------------------------
  1558. //
  1559. // No creation info
  1560. //
  1561. // Reviewed by alhen 9 - 3 - 98
  1562. //
  1563. HRESULT CProcInfo::SetProcessUsername(const FILETIME *pCreateTime)
  1564. {
  1565. DWORD dwError = NO_ERROR;
  1566. __try
  1567. {
  1568. // in case of wow tasks assign username same as its parent process's
  1569. if( IsWowTask( ) )
  1570. {
  1571. if( m_pWowParentProcInfo->m_pszUserName != NULL )
  1572. {
  1573. m_pszUserName = ( LPTSTR )new TCHAR[ lstrlen( m_pWowParentProcInfo->m_pszUserName ) + 1 ];
  1574. if( m_pszUserName != NULL )
  1575. {
  1576. lstrcpy( m_pszUserName , m_pWowParentProcInfo->m_pszUserName );
  1577. return S_OK;
  1578. }
  1579. else
  1580. {
  1581. return E_OUTOFMEMORY;
  1582. }
  1583. }
  1584. else
  1585. {
  1586. return E_FAIL;
  1587. }
  1588. }
  1589. if( m_UniqueProcessId == 0 ) // this is a system idle process.
  1590. {
  1591. const TCHAR *szIdleProcessOwner = TEXT( "SYSTEM" );
  1592. m_pszUserName = ( LPTSTR )new TCHAR[ 7 ];
  1593. if( m_pszUserName != NULL )
  1594. {
  1595. lstrcpy(m_pszUserName, szIdleProcessOwner);
  1596. }
  1597. }
  1598. else
  1599. {
  1600. PSID pUserSid = NULL;
  1601. DWORD dwSize = 0;
  1602. if( !WinStationGetProcessSid( NULL , GetRealPID( ) , *pCreateTime, ( PBYTE )pUserSid , &dwSize ) )
  1603. {
  1604. pUserSid = ( PSID ) new BYTE[ dwSize ];
  1605. if( pUserSid != NULL )
  1606. {
  1607. if( WinStationGetProcessSid( NULL , GetRealPID( ) , *pCreateTime, ( PBYTE )pUserSid , &dwSize ) )
  1608. {
  1609. if( IsValidSid( pUserSid ) )
  1610. {
  1611. TCHAR szTmpName[MAX_PATH];
  1612. DWORD dwTmpNameSize = MAX_PATH;
  1613. CachedGetUserFromSid( pUserSid , szTmpName , &dwTmpNameSize );
  1614. m_pszUserName = ( LPTSTR )new TCHAR[ sizeof( szTmpName ) + 1 ];
  1615. if( m_pszUserName != NULL )
  1616. {
  1617. lstrcpy(m_pszUserName, szTmpName);
  1618. }
  1619. }
  1620. }
  1621. delete [] pUserSid;
  1622. }
  1623. else
  1624. {
  1625. dwError = GetLastError();
  1626. }
  1627. } // this would mean that a sid of size zero was returned
  1628. }
  1629. }
  1630. __except (EXCEPTION_EXECUTE_HANDLER)
  1631. {
  1632. // dprintf(TEXT("exception occured: %d",), GetExceptionCode());
  1633. dwError = GetExceptionCode();
  1634. }
  1635. return HRESULT_FROM_WIN32(dwError);
  1636. }
  1637. /*++ CProcPage::UpdateProcListview
  1638. Class Description:
  1639. Walks the listview and checks to see if each line in the
  1640. listview matches the corresponding entry in our process
  1641. array. Those which differe by PID are replaced, and those
  1642. that need updating are updated.
  1643. Items are also added and removed to/from the tail of the
  1644. listview as required.
  1645. Arguments:
  1646. Return Value:
  1647. HRESULT
  1648. Revision History:
  1649. Nov-20-95 Davepl Created
  1650. --*/
  1651. HRESULT CProcPage::UpdateProcListview ()
  1652. {
  1653. HWND hListView = GetDlgItem(m_hPage, IDC_PROCLIST);
  1654. // Stop repaints while we party on the listview
  1655. SendMessage(hListView, WM_SETREDRAW, FALSE, 0);
  1656. INT cListViewItems = ListView_GetItemCount(hListView);
  1657. INT cProcArrayItems = m_pProcArray->GetSize();
  1658. //
  1659. // Walk the existing lines in the listview and replace/update
  1660. // them as needed
  1661. //
  1662. CProcInfo * pSelected = GetSelectedProcess();
  1663. for (int iCurrent = 0, iCurrListViewItem = 0;
  1664. iCurrListViewItem < cListViewItems && iCurrent < cProcArrayItems;
  1665. iCurrent++) // for each process
  1666. {
  1667. CProcInfo * pProc = (CProcInfo *) m_pProcArray->GetAt(iCurrent);
  1668. //get only processes we need to show
  1669. if(g_fIsTSEnabled && !g_Options.m_bShowAllProcess && !pProc->OkToShowThisProcess() ) {
  1670. continue;
  1671. }
  1672. LV_ITEM lvitem = { 0 };
  1673. lvitem.mask = LVIF_PARAM | LVIF_TEXT | LVIF_STATE;
  1674. lvitem.iItem = iCurrListViewItem;
  1675. if (FALSE == ListView_GetItem(hListView, &lvitem))
  1676. {
  1677. SendMessage(hListView, WM_SETREDRAW, TRUE, 0);
  1678. return E_FAIL;
  1679. }
  1680. CProcInfo * pTmp = (CProcInfo *) lvitem.lParam;
  1681. if (pTmp != pProc)
  1682. {
  1683. // If the objects aren't the same, we need to replace this line
  1684. lvitem.pszText = pProc->m_pszImageName;
  1685. lvitem.lParam = (LPARAM) pProc;
  1686. if (pProc == pSelected)
  1687. {
  1688. lvitem.state |= LVIS_SELECTED | LVIS_FOCUSED;
  1689. }
  1690. else
  1691. {
  1692. lvitem.state &= ~(LVIS_SELECTED | LVIS_FOCUSED);
  1693. }
  1694. lvitem.stateMask |= LVIS_SELECTED | LVIS_FOCUSED;
  1695. ListView_SetItem(hListView, &lvitem);
  1696. ListView_RedrawItems(hListView, iCurrListViewItem, iCurrListViewItem);
  1697. }
  1698. else if (pProc->m_fDirty)
  1699. {
  1700. // Same PID, but item needs updating
  1701. ListView_RedrawItems(hListView, iCurrListViewItem, iCurrListViewItem);
  1702. pProc->m_fDirty = 0;
  1703. }
  1704. iCurrListViewItem++;
  1705. }
  1706. //
  1707. // We've either run out of listview items or run out of proc array
  1708. // entries, so remove/add to the listview as appropriate
  1709. //
  1710. while (iCurrListViewItem < cListViewItems)
  1711. {
  1712. // Extra items in the listview (processes gone away), so remove them
  1713. ListView_DeleteItem(hListView, iCurrListViewItem);
  1714. cListViewItems--;
  1715. }
  1716. while (iCurrent < cProcArrayItems)
  1717. {
  1718. // Need to add new items to the listview (new processes appeared)
  1719. CProcInfo * pProc = (CProcInfo *)m_pProcArray->GetAt(iCurrent++);
  1720. //get only processes we need to show
  1721. if(g_fIsTSEnabled && !g_Options.m_bShowAllProcess && !pProc->OkToShowThisProcess() ) {
  1722. continue;
  1723. }
  1724. LV_ITEM lvitem = { 0 };
  1725. lvitem.mask = LVIF_PARAM | LVIF_TEXT;
  1726. lvitem.iItem = iCurrListViewItem;
  1727. lvitem.pszText = pProc->m_pszImageName;
  1728. lvitem.lParam = (LPARAM) pProc;
  1729. // The first item added (actually, every 0 to 1 count transition) gets
  1730. // selected and focused
  1731. if (iCurrListViewItem == 0)
  1732. {
  1733. lvitem.state = LVIS_SELECTED | LVIS_FOCUSED;
  1734. lvitem.stateMask = lvitem.state;
  1735. lvitem.mask |= LVIF_STATE;
  1736. }
  1737. ListView_InsertItem(hListView, &lvitem);
  1738. iCurrListViewItem++;
  1739. }
  1740. ASSERT(iCurrListViewItem == ListView_GetItemCount(hListView));
  1741. ASSERT(iCurrent == cProcArrayItems);
  1742. // Let the listview paint again
  1743. SendMessage(hListView, WM_SETREDRAW, TRUE, 0);
  1744. return S_OK;
  1745. }
  1746. /*++ class CProcPage::UpdateProcInfoArray
  1747. Class Description:
  1748. Retrieves the list of process info blocks from the system,
  1749. and runs through our array of CProcInfo items. Items which
  1750. already exist are updated, and those that do not are added.
  1751. At the end, any process which has not been touched by this
  1752. itteration of the function are considered to have completed
  1753. and are removed from the array.
  1754. Arguments:
  1755. Return Value:
  1756. Revision History:
  1757. Nov-16-95 Davepl Created
  1758. --*/
  1759. // See comments near the usage of this table below for info on why it exists
  1760. static struct
  1761. {
  1762. size_t cbOffset;
  1763. UINT idString;
  1764. }
  1765. g_OffsetMap[] =
  1766. {
  1767. { FIELD_OFFSET(CSysInfo, m_cHandles), IDC_TOTAL_HANDLES },
  1768. { FIELD_OFFSET(CSysInfo, m_cThreads), IDC_TOTAL_THREADS },
  1769. { FIELD_OFFSET(CSysInfo, m_cProcesses), IDC_TOTAL_PROCESSES },
  1770. { FIELD_OFFSET(CSysInfo, m_dwPhysicalMemory), IDC_TOTAL_PHYSICAL },
  1771. { FIELD_OFFSET(CSysInfo, m_dwPhysAvail), IDC_AVAIL_PHYSICAL },
  1772. { FIELD_OFFSET(CSysInfo, m_dwFileCache), IDC_FILE_CACHE },
  1773. { FIELD_OFFSET(CSysInfo, m_dwCommitTotal), IDC_COMMIT_TOTAL },
  1774. { FIELD_OFFSET(CSysInfo, m_dwCommitLimit), IDC_COMMIT_LIMIT },
  1775. { FIELD_OFFSET(CSysInfo, m_dwCommitPeak), IDC_COMMIT_PEAK },
  1776. { FIELD_OFFSET(CSysInfo, m_dwKernelPaged), IDC_KERNEL_PAGED },
  1777. { FIELD_OFFSET(CSysInfo, m_dwKernelNP), IDC_KERNEL_NONPAGED },
  1778. { FIELD_OFFSET(CSysInfo, m_dwKernelTotal), IDC_KERNEL_TOTAL },
  1779. };
  1780. HRESULT CProcPage::UpdateProcInfoArray()
  1781. {
  1782. HRESULT hr;
  1783. INT i;
  1784. INT iField;
  1785. ULONG cbOffset = 0;
  1786. CSysInfo SysInfoTemp;
  1787. NTSTATUS Status;
  1788. SYSTEM_BASIC_INFORMATION BasicInfo;
  1789. PSYSTEM_PROCESS_INFORMATION pCurrent;
  1790. SYSTEM_PERFORMANCE_INFORMATION PerfInfo;
  1791. SYSTEM_FILECACHE_INFORMATION FileCache;
  1792. LARGE_INTEGER TotalTime = {0,0};
  1793. LARGE_INTEGER LastTotalTime = {0,0};
  1794. //
  1795. // Pass-count for this function. It ain't thread-safe, of course, but I
  1796. // can't imagine a scenario where we'll have mode than one thread running
  1797. // through this (the app is currently single threaded anyway). If we
  1798. // overflow LARGE_INTEGER updates, I'll already be long gone, so don't bug me.
  1799. //
  1800. static LARGE_INTEGER uPassCount = {0,0};
  1801. //
  1802. // Get some non-process specific info, like memory status
  1803. //
  1804. Status = NtQuerySystemInformation(
  1805. SystemBasicInformation,
  1806. &BasicInfo,
  1807. sizeof(BasicInfo),
  1808. NULL
  1809. );
  1810. if (!NT_SUCCESS(Status))
  1811. {
  1812. return E_FAIL;
  1813. }
  1814. SysInfoTemp.m_dwPhysicalMemory = BasicInfo.NumberOfPhysicalPages *
  1815. (BasicInfo.PageSize / 1024);
  1816. Status = NtQuerySystemInformation(
  1817. SystemPerformanceInformation,
  1818. &PerfInfo,
  1819. sizeof(PerfInfo),
  1820. NULL
  1821. );
  1822. if (!NT_SUCCESS(Status))
  1823. {
  1824. return E_FAIL;
  1825. }
  1826. SysInfoTemp.m_dwPhysAvail = PerfInfo.AvailablePages * (g_BasicInfo.PageSize / 1024);
  1827. SysInfoTemp.m_dwCommitTotal = PerfInfo.CommittedPages * (g_BasicInfo.PageSize / 1024);
  1828. SysInfoTemp.m_dwCommitLimit = PerfInfo.CommitLimit * (g_BasicInfo.PageSize / 1024);
  1829. SysInfoTemp.m_dwCommitPeak = PerfInfo.PeakCommitment * (g_BasicInfo.PageSize / 1024);
  1830. SysInfoTemp.m_dwKernelPaged = PerfInfo.PagedPoolPages * (g_BasicInfo.PageSize / 1024);
  1831. SysInfoTemp.m_dwKernelNP = PerfInfo.NonPagedPoolPages * (g_BasicInfo.PageSize / 1024);
  1832. SysInfoTemp.m_dwKernelTotal = SysInfoTemp.m_dwKernelNP + SysInfoTemp.m_dwKernelPaged;
  1833. g_MEMMax = SysInfoTemp.m_dwCommitLimit;
  1834. Status = NtQuerySystemInformation(
  1835. SystemFileCacheInformation,
  1836. &FileCache,
  1837. sizeof(FileCache),
  1838. NULL
  1839. );
  1840. if (!NT_SUCCESS(Status))
  1841. {
  1842. return E_FAIL;
  1843. }
  1844. //
  1845. // The DWORD cast below must be fixed as this value can be greater than
  1846. // 32 bits.
  1847. //
  1848. SysInfoTemp.m_dwFileCache = (DWORD)(FileCache.CurrentSizeIncludingTransitionInPages * (g_BasicInfo.PageSize / 1024));
  1849. //
  1850. // Read the process info structures into the flat buffer
  1851. //
  1852. hr = GetProcessInfo();
  1853. if (FAILED(hr))
  1854. {
  1855. goto done;
  1856. }
  1857. //
  1858. // First walk all of the process info blocks and sum their times, so that we can
  1859. // calculate a CPU usage ratio (%) for each individual process
  1860. //
  1861. cbOffset = 0;
  1862. do
  1863. {
  1864. CProcInfo * pOldProcInfo;
  1865. pCurrent = (PSYSTEM_PROCESS_INFORMATION)&((LPBYTE)m_pvBuffer)[cbOffset];
  1866. ASSERT( FALSE == IsBadReadPtr((LPVOID)pCurrent, sizeof(PSYSTEM_PROCESS_INFORMATION)));
  1867. if (pCurrent->UniqueProcessId == NULL && pCurrent->NumberOfThreads == 0)
  1868. {
  1869. // Zombie process, just skip it
  1870. goto next;
  1871. }
  1872. pOldProcInfo = FindProcInArrayByPID(m_pProcArray, PtrToUlong(pCurrent->UniqueProcessId));
  1873. if (pOldProcInfo)
  1874. {
  1875. if (pOldProcInfo->GetCPUTime() > pCurrent->KernelTime.QuadPart + pCurrent->UserTime.QuadPart)
  1876. {
  1877. // If CPU has gone DOWN, its because the PID has been reused, so invalidate this
  1878. // CProcInfo such that it is removed and the new one added
  1879. pOldProcInfo->Invalidate();
  1880. // dprintf(TEXT("Invalidating %08x\n"), pOldProcInfo);
  1881. goto next;
  1882. }
  1883. else if (pCurrent->UniqueProcessId == 0 &&
  1884. pCurrent->KernelTime.QuadPart == 0 &&
  1885. pCurrent->UserTime.QuadPart == 0)
  1886. {
  1887. dprintf(TEXT("System idle process has 0 times\n"));
  1888. pOldProcInfo->Invalidate();
  1889. goto next;
  1890. }
  1891. else
  1892. {
  1893. LastTotalTime.QuadPart += pOldProcInfo->GetCPUTime();
  1894. }
  1895. }
  1896. TotalTime.QuadPart += pCurrent->KernelTime.QuadPart + pCurrent->UserTime.QuadPart;
  1897. SysInfoTemp.m_cHandles += pCurrent->HandleCount;
  1898. SysInfoTemp.m_cThreads += pCurrent->NumberOfThreads;
  1899. SysInfoTemp.m_cProcesses++;
  1900. next:
  1901. cbOffset += pCurrent->NextEntryOffset;
  1902. // if current session id is not set yet, set it now
  1903. //
  1904. // REVIEWER: Previous dev didnot document this, but taskmgr session id
  1905. // is cached so that when the user deselects "show all the processes", only
  1906. // processes with session id's equal to taskmgr session id are listed
  1907. // --alhen
  1908. if( ( GetCurrentSessionID() == -1 ) && ( PtrToUlong(pCurrent->UniqueProcessId) == GetCurrentProcessId( ) ) )
  1909. {
  1910. SetCurrentSessionID( ( DWORD )pCurrent->SessionId );
  1911. }
  1912. } while (pCurrent->NextEntryOffset);
  1913. LARGE_INTEGER TimeDelta;
  1914. TimeDelta.QuadPart = TotalTime.QuadPart - LastTotalTime.QuadPart;
  1915. ASSERT(TimeDelta.QuadPart >= 0);
  1916. // Update the global count (visible to the status bar)
  1917. g_cProcesses = SysInfoTemp.m_cProcesses;
  1918. //
  1919. // We have a number of text fields in the dialog that are based on counts we accumulate
  1920. // here. Rather than painting all of the time, we only change the ones whose values have
  1921. // really changed. We have a table up above of the offsets into the CSysInfo object
  1922. // where these values live (the same offset in the real g_SysInfo object and the temp
  1923. // working copy, of course), and what control ID they correspond to. We then loop through
  1924. // and compare each real one to the temp working copy, updating as needed. Hard to
  1925. // read, but smaller than a dozen if() statements.
  1926. //
  1927. extern CPage * g_pPages[];
  1928. if (g_pPages[PERF_PAGE])
  1929. {
  1930. for (iField = 0; iField < ARRAYSIZE(g_OffsetMap); iField++)
  1931. {
  1932. DWORD * pdwRealCopy = (DWORD *)(((LPBYTE)&m_SysInfo) + g_OffsetMap[iField].cbOffset);
  1933. DWORD * pdwTempCopy = (DWORD *)(((LPBYTE)&SysInfoTemp) + g_OffsetMap[iField].cbOffset);
  1934. *pdwRealCopy = *pdwTempCopy;
  1935. TCHAR szText[32];
  1936. wsprintf(szText, TEXT("%d"), *pdwRealCopy);
  1937. HWND hPage = g_pPages[PERF_PAGE]->GetPageWindow();
  1938. // Updates can come through before page is created, so verify
  1939. // that it exists before we party on its children
  1940. if (hPage)
  1941. {
  1942. SetWindowText(GetDlgItem(hPage, g_OffsetMap[iField].idString), szText);
  1943. }
  1944. }
  1945. }
  1946. //
  1947. // Now walk the process info blocks again and refresh the CProcInfo array for each
  1948. // individual process
  1949. //
  1950. cbOffset = 0;
  1951. do
  1952. {
  1953. //
  1954. // Grab a PROCESS_INFORMATION struct from the buffer
  1955. //
  1956. pCurrent = (PSYSTEM_PROCESS_INFORMATION)&((LPBYTE)m_pvBuffer)[cbOffset];
  1957. ASSERT( FALSE == IsBadReadPtr((LPVOID)pCurrent, sizeof(PSYSTEM_PROCESS_INFORMATION)));
  1958. if (pCurrent->UniqueProcessId == NULL && pCurrent->NumberOfThreads == 0)
  1959. {
  1960. // Zombie process, just skip it
  1961. goto nextprocinfo;
  1962. }
  1963. //
  1964. // This is really ugly, but... NtQuerySystemInfo has too much latency, and if you
  1965. // change a process' priority, you don't see it reflected right away. And, if you
  1966. // don't have autoupdate on, you never do. So, we use GetPriorityClass() to get
  1967. // the value instead. This means BasePriority is now the pri class, not the pri value.
  1968. //
  1969. if (pCurrent->UniqueProcessId)
  1970. {
  1971. HANDLE hProcess;
  1972. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, PtrToUlong(pCurrent->UniqueProcessId) );
  1973. DWORD dwPriClass;
  1974. dwPriClass = 0;
  1975. if (hProcess)
  1976. {
  1977. dwPriClass = GetPriorityClass(hProcess);
  1978. if (dwPriClass)
  1979. {
  1980. pCurrent->BasePriority = dwPriClass;
  1981. }
  1982. CloseHandle( hProcess );
  1983. }
  1984. if (NULL == hProcess || dwPriClass == 0)
  1985. {
  1986. // We're not allowed to open this process, so convert what NtQuerySystemInfo
  1987. // gave us into a priority class... its the next best thing
  1988. if (pCurrent->BasePriority <= 4)
  1989. {
  1990. pCurrent->BasePriority = IDLE_PRIORITY_CLASS;
  1991. }
  1992. else if (pCurrent->BasePriority <= 6)
  1993. {
  1994. pCurrent->BasePriority = BELOW_NORMAL_PRIORITY_CLASS;
  1995. }
  1996. else if (pCurrent->BasePriority <= 8)
  1997. {
  1998. pCurrent->BasePriority = NORMAL_PRIORITY_CLASS;
  1999. }
  2000. else if (pCurrent->BasePriority <= 10)
  2001. {
  2002. pCurrent->BasePriority = ABOVE_NORMAL_PRIORITY_CLASS;
  2003. }
  2004. else if (pCurrent->BasePriority <= 13)
  2005. {
  2006. pCurrent->BasePriority = HIGH_PRIORITY_CLASS;
  2007. }
  2008. else
  2009. {
  2010. pCurrent->BasePriority = REALTIME_PRIORITY_CLASS;
  2011. }
  2012. }
  2013. }
  2014. //
  2015. // Try to find an existing CProcInfo instance which corresponds to this process
  2016. //
  2017. CProcInfo * pProcInfo;
  2018. pProcInfo = FindProcInArrayByPID(m_pProcArray, PtrToUlong(pCurrent->UniqueProcessId));
  2019. if (NULL == pProcInfo)
  2020. {
  2021. //
  2022. // We don't already have this process in our array, so create a new one
  2023. // and add it to the array
  2024. //
  2025. pProcInfo = new CProcInfo;
  2026. if (NULL == pProcInfo)
  2027. {
  2028. hr = E_OUTOFMEMORY;
  2029. goto done;
  2030. }
  2031. hr = pProcInfo->SetData(TimeDelta,
  2032. pCurrent,
  2033. uPassCount,
  2034. this,
  2035. FALSE);
  2036. if (FAILED(hr) || FALSE == m_pProcArray->Add(pProcInfo))
  2037. {
  2038. delete pProcInfo;
  2039. goto done;
  2040. }
  2041. }
  2042. else
  2043. {
  2044. //
  2045. // This process already existed in our array, so update its info
  2046. //
  2047. hr = pProcInfo->SetData(TimeDelta,
  2048. pCurrent,
  2049. uPassCount,
  2050. this,
  2051. TRUE);
  2052. if (FAILED(hr))
  2053. {
  2054. goto done;
  2055. }
  2056. }
  2057. nextprocinfo:
  2058. cbOffset += pCurrent->NextEntryOffset;
  2059. } while (pCurrent->NextEntryOffset);
  2060. //
  2061. // Run through the CProcInfo array and remove anyone that hasn't been touched
  2062. // by this pass through this function (which indicates the process is no
  2063. // longer alive)
  2064. //
  2065. i = 0;
  2066. while (i < m_pProcArray->GetSize())
  2067. {
  2068. CProcInfo * pProcInfo = (CProcInfo *)(m_pProcArray->GetAt(i));
  2069. ASSERT(pProcInfo);
  2070. //
  2071. // If passcount doesn't match, delete the CProcInfo instance and remove
  2072. // its pointer from the array. Note that we _don't_ increment the index
  2073. // if we remove an element, since the next element would now live at
  2074. // the current index after the deletion
  2075. //
  2076. if (pProcInfo->m_uPassCount.QuadPart != uPassCount.QuadPart)
  2077. {
  2078. delete pProcInfo;
  2079. m_pProcArray->RemoveAt(i, 1);
  2080. }
  2081. else
  2082. {
  2083. i++;
  2084. }
  2085. }
  2086. done:
  2087. ResortArray(&m_pProcArray);
  2088. uPassCount.QuadPart++;
  2089. return hr;
  2090. }
  2091. /*++ CPerfPage::SizeProcPage
  2092. Routine Description:
  2093. Sizes its children based on the size of the
  2094. tab control on which it appears.
  2095. Arguments:
  2096. Return Value:
  2097. Revision History:
  2098. Nov-16-95 Davepl Created
  2099. --*/
  2100. void CProcPage::SizeProcPage()
  2101. {
  2102. // Get the coords of the outer dialog
  2103. RECT rcParent;
  2104. GetClientRect(m_hPage, &rcParent);
  2105. HDWP hdwp = BeginDeferWindowPos(10);
  2106. if (!hdwp)
  2107. return;
  2108. // Calc the deltas in the x and y positions that we need to
  2109. // move each of the child controls
  2110. RECT rcTerminate;
  2111. HWND hwndTerminate = GetDlgItem(m_hPage, IDC_TERMINATE);
  2112. GetWindowRect(hwndTerminate, &rcTerminate);
  2113. MapWindowPoints(HWND_DESKTOP, m_hPage, (LPPOINT) &rcTerminate, 2);
  2114. INT dx = ((rcParent.right - g_DefSpacing * 2) - rcTerminate.right);
  2115. INT dy = ((rcParent.bottom - g_DefSpacing * 2) - rcTerminate.bottom);
  2116. // Move the EndProcess button
  2117. DeferWindowPos(hdwp, hwndTerminate, NULL,
  2118. rcTerminate.left + dx,
  2119. rcTerminate.top + dy,
  2120. 0, 0,
  2121. SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  2122. // _HYDRA_
  2123. HWND hwndShowall = GetDlgItem(m_hPage, IDC_SHOWALL);
  2124. if( IsWindow( hwndShowall ) )
  2125. {
  2126. if( g_fIsTSEnabled )
  2127. {
  2128. RECT rcShowall;
  2129. GetWindowRect(hwndShowall, &rcShowall);
  2130. MapWindowPoints(HWND_DESKTOP, m_hPage, (LPPOINT) &rcShowall, 2);
  2131. DeferWindowPos(hdwp, hwndShowall, NULL, rcShowall.left, rcShowall.top + dy, 0, 0, SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE);
  2132. }
  2133. else
  2134. {
  2135. // this window must be hidden.
  2136. ShowWindow(hwndShowall, SW_HIDE);
  2137. }
  2138. }
  2139. // _HYDRA_
  2140. // Size the listbox
  2141. HWND hwndListbox = GetDlgItem(m_hPage, IDC_PROCLIST);
  2142. RECT rcListbox;
  2143. GetWindowRect(hwndListbox, &rcListbox);
  2144. MapWindowPoints(HWND_DESKTOP, m_hPage, (LPPOINT) &rcListbox, 2);
  2145. DeferWindowPos(hdwp, hwndListbox, NULL,
  2146. 0, 0,
  2147. rcTerminate.right - rcListbox.left + dx,
  2148. rcTerminate.top - rcListbox.top + dy - g_DefSpacing,
  2149. SWP_NOMOVE | SWP_NOZORDER | SWP_NOACTIVATE);
  2150. EndDeferWindowPos(hdwp);
  2151. }
  2152. /*++ CProcPage::HandleTaskManNotify
  2153. Routine Description:
  2154. Processes WM_NOTIFY messages received by the procpage dialog
  2155. Arguments:
  2156. hWnd - Control that generated the WM_NOTIFY
  2157. pnmhdr - Ptr to the NMHDR notification stucture
  2158. Return Value:
  2159. BOOL "did we handle it" code
  2160. Revision History:
  2161. Nov-20-95 Davepl Created
  2162. --*/
  2163. INT CProcPage::HandleProcPageNotify(HWND hWnd, LPNMHDR pnmhdr)
  2164. {
  2165. switch(pnmhdr->code)
  2166. {
  2167. case LVN_COLUMNCLICK:
  2168. {
  2169. // User clicked a header control, so set the sort column. If its the
  2170. // same as the current sort column, just invert the sort direction in
  2171. // the column. Then resort the task array
  2172. const NM_LISTVIEW * pnmv = (const NM_LISTVIEW *) pnmhdr;
  2173. if (g_iProcSortColumnID == g_Options.m_ActiveProcCol[pnmv->iSubItem])
  2174. {
  2175. g_iProcSortDirection *= -1;
  2176. }
  2177. else
  2178. {
  2179. g_iProcSortColumnID = g_Options.m_ActiveProcCol[pnmv->iSubItem];
  2180. g_iProcSortDirection = -1;
  2181. }
  2182. ResortArray(&m_pProcArray);
  2183. TimerEvent();
  2184. break;
  2185. }
  2186. case LVN_ITEMCHANGED:
  2187. {
  2188. const NM_LISTVIEW * pnmv = (const NM_LISTVIEW *) pnmhdr;
  2189. if (pnmv->uChanged & LVIF_STATE)
  2190. {
  2191. UINT cSelected = ListView_GetSelectedCount(GetDlgItem(m_hPage, IDC_PROCLIST));
  2192. EnableWindow(GetDlgItem(m_hPage, IDC_TERMINATE), cSelected ? TRUE : FALSE);
  2193. }
  2194. break;
  2195. }
  2196. case LVN_GETDISPINFO:
  2197. {
  2198. LV_ITEM * plvitem = &(((LV_DISPINFO *) pnmhdr)->item);
  2199. // Listview needs a text string
  2200. if (plvitem->mask & LVIF_TEXT)
  2201. {
  2202. COLUMNID columnid = (COLUMNID) g_Options.m_ActiveProcCol[plvitem->iSubItem];
  2203. const CProcInfo * pProcInfo = (const CProcInfo *) plvitem->lParam;
  2204. //
  2205. // Most columns are blank for WOW tasks.
  2206. //
  2207. if (pProcInfo->IsWowTask() &&
  2208. columnid != COL_IMAGENAME &&
  2209. columnid != COL_BASEPRIORITY &&
  2210. columnid != COL_THREADCOUNT &&
  2211. columnid != COL_CPUTIME &&
  2212. columnid != COL_USERNAME &&
  2213. columnid != COL_SESSIONID &&
  2214. columnid != COL_CPU) {
  2215. plvitem->pszText[0] = 0;
  2216. goto done;
  2217. }
  2218. switch(columnid)
  2219. {
  2220. case COL_PID:
  2221. wsprintf(plvitem->pszText, TEXT("%d"), (ULONG) (pProcInfo->m_UniqueProcessId));
  2222. break;
  2223. case COL_USERNAME:
  2224. if( pProcInfo->m_pszUserName )
  2225. {
  2226. lstrcpyn(plvitem->pszText, pProcInfo->m_pszUserName, plvitem->cchTextMax);
  2227. }
  2228. break;
  2229. case COL_SESSIONID:
  2230. wsprintf( plvitem->pszText, TEXT( "%d" ) , pProcInfo->m_SessionId );
  2231. break;
  2232. //#endif
  2233. case COL_CPU:
  2234. wsprintf(plvitem->pszText, TEXT("%02d %"), pProcInfo->m_DisplayCPU);
  2235. break;
  2236. case COL_IMAGENAME:
  2237. lstrcpyn(plvitem->pszText, pProcInfo->m_pszImageName, plvitem->cchTextMax);
  2238. //plvitem->mask |= LVIF_DI_SETITEM;
  2239. break;
  2240. case COL_CPUTIME:
  2241. {
  2242. TIME_FIELDS TimeOut;
  2243. RtlTimeToElapsedTimeFields ( (LARGE_INTEGER *)&(pProcInfo->m_DisplayCPUTime), &TimeOut);
  2244. TimeOut.Hour = static_cast<CSHORT>(TimeOut.Hour + static_cast<SHORT>(TimeOut.Day * 24));
  2245. wsprintf(plvitem->pszText,
  2246. TEXT("%2d%s%02d%s%02d"),
  2247. TimeOut.Hour,
  2248. g_szTimeSep,
  2249. TimeOut.Minute,
  2250. g_szTimeSep,
  2251. TimeOut.Second);
  2252. break;
  2253. }
  2254. case COL_MEMUSAGE:
  2255. Int64ToCommaSepKString(LONGLONG(pProcInfo->m_MemUsage), plvitem->pszText, plvitem->cchTextMax);
  2256. break;
  2257. case COL_MEMUSAGEDIFF:
  2258. Int64ToCommaSepKString(LONGLONG(pProcInfo->m_MemDiff), plvitem->pszText, plvitem->cchTextMax);
  2259. break;
  2260. case COL_MEMPEAK:
  2261. Int64ToCommaSepKString(LONGLONG(pProcInfo->m_MemPeak), plvitem->pszText, plvitem->cchTextMax);
  2262. break;
  2263. case COL_PAGEFAULTS:
  2264. Int64ToCommaSepString(LONGLONG(pProcInfo->m_PageFaults), plvitem->pszText, plvitem->cchTextMax);
  2265. break;
  2266. case COL_PAGEFAULTSDIFF:
  2267. Int64ToCommaSepString(LONGLONG(pProcInfo->m_PageFaultsDiff), plvitem->pszText, plvitem->cchTextMax);
  2268. break;
  2269. case COL_COMMITCHARGE:
  2270. Int64ToCommaSepKString(LONGLONG(pProcInfo->m_CommitCharge), plvitem->pszText, plvitem->cchTextMax);
  2271. break;
  2272. case COL_PAGEDPOOL:
  2273. Int64ToCommaSepKString(LONGLONG(pProcInfo->m_PagedPool), plvitem->pszText, plvitem->cchTextMax);
  2274. break;
  2275. case COL_NONPAGEDPOOL:
  2276. Int64ToCommaSepKString(LONGLONG(pProcInfo->m_NonPagedPool), plvitem->pszText, plvitem->cchTextMax);
  2277. break;
  2278. case COL_BASEPRIORITY:
  2279. {
  2280. LPCTSTR pszClass = NULL;
  2281. switch(pProcInfo->m_PriClass)
  2282. {
  2283. case REALTIME_PRIORITY_CLASS:
  2284. pszClass = g_szRealtime;
  2285. break;
  2286. case HIGH_PRIORITY_CLASS:
  2287. pszClass = g_szHigh;
  2288. break;
  2289. case ABOVE_NORMAL_PRIORITY_CLASS:
  2290. pszClass = g_szAboveNormal;
  2291. break;
  2292. case NORMAL_PRIORITY_CLASS:
  2293. pszClass = g_szNormal;
  2294. break;
  2295. case BELOW_NORMAL_PRIORITY_CLASS:
  2296. pszClass = g_szBelowNormal;
  2297. break;
  2298. case IDLE_PRIORITY_CLASS:
  2299. pszClass = g_szLow;
  2300. break;
  2301. default:
  2302. pszClass = g_szUnknown;
  2303. break;
  2304. }
  2305. lstrcpyn(plvitem->pszText, pszClass, plvitem->cchTextMax);
  2306. break;
  2307. }
  2308. case COL_HANDLECOUNT:
  2309. Int64ToCommaSepString(LONGLONG(pProcInfo->m_HandleCount), plvitem->pszText, plvitem->cchTextMax);
  2310. break;
  2311. case COL_THREADCOUNT:
  2312. Int64ToCommaSepString(LONGLONG(pProcInfo->m_ThreadCount), plvitem->pszText, plvitem->cchTextMax);
  2313. break;
  2314. case COL_USEROBJECTS:
  2315. Int64ToCommaSepString(LONGLONG(pProcInfo->m_USERObjectCount), plvitem->pszText, plvitem->cchTextMax);
  2316. break;
  2317. case COL_GDIOBJECTS:
  2318. Int64ToCommaSepString(LONGLONG(pProcInfo->m_GDIObjectCount), plvitem->pszText, plvitem->cchTextMax);
  2319. break;
  2320. case COL_READOPERCOUNT:
  2321. Int64ToCommaSepString(pProcInfo->m_IoReadOperCount, plvitem->pszText, plvitem->cchTextMax);
  2322. break;
  2323. case COL_WRITEOPERCOUNT:
  2324. Int64ToCommaSepString(pProcInfo->m_IoWriteOperCount, plvitem->pszText, plvitem->cchTextMax);
  2325. break;
  2326. case COL_OTHEROPERCOUNT:
  2327. Int64ToCommaSepString(pProcInfo->m_IoOtherOperCount, plvitem->pszText, plvitem->cchTextMax);
  2328. break;
  2329. case COL_READXFERCOUNT:
  2330. Int64ToCommaSepString(pProcInfo->m_IoReadXferCount, plvitem->pszText, plvitem->cchTextMax);
  2331. break;
  2332. case COL_WRITEXFERCOUNT:
  2333. Int64ToCommaSepString(pProcInfo->m_IoWriteXferCount, plvitem->pszText, plvitem->cchTextMax);
  2334. break;
  2335. case COL_OTHERXFERCOUNT:
  2336. Int64ToCommaSepString(pProcInfo->m_IoOtherXferCount, plvitem->pszText, plvitem->cchTextMax);
  2337. break;
  2338. default:
  2339. Assert( 0 && "Unknown listview subitem" );
  2340. break;
  2341. } // end switch(columnid)
  2342. } // end LVIF_TEXT case
  2343. } // end LVN_GETDISPINFO case
  2344. } // end switch(pnmhdr->code)
  2345. done:
  2346. return 1;
  2347. }
  2348. /*++ CProcPage::TimerEvent
  2349. Routine Description:
  2350. Called by main app when the update time fires
  2351. Arguments:
  2352. Return Value:
  2353. Revision History:
  2354. Nov-20-95 Davepl Created
  2355. --*/
  2356. void CProcPage::TimerEvent()
  2357. {
  2358. // REVIEW (DavePl)
  2359. // We might want to optimize the amount of calculation we do when
  2360. // we are iconic, but we still need to track the deltas (mem usage,
  2361. // faults, etc) so might as well just calc it all. Listview won't
  2362. // repaint anyway until its visible, which is the real work
  2363. if (FALSE == m_fPaused)
  2364. {
  2365. // We only process updates when the display is not paused, ie:
  2366. // not during trackpopupmenu loop
  2367. UpdateProcInfoArray();
  2368. UpdateProcListview();
  2369. }
  2370. }
  2371. /*++ CProcPage::HandleProcListContextMenu
  2372. Routine Description:
  2373. Handles right-clicks (context menu) in the proc list
  2374. Arguments:
  2375. xPos, yPos - coords of where the click occurred
  2376. Return Value:
  2377. Revision History:
  2378. Nov-22-95 Davepl Created
  2379. --*/
  2380. void CProcPage::HandleProcListContextMenu(INT xPos, INT yPos)
  2381. {
  2382. HWND hTaskList = GetDlgItem(m_hPage, IDC_PROCLIST);
  2383. INT iItem = ListView_GetNextItem(hTaskList, -1, LVNI_SELECTED);
  2384. if (-1 != iItem)
  2385. {
  2386. if (0xFFFF == LOWORD(xPos) && 0xFFFF == LOWORD(yPos))
  2387. {
  2388. RECT rcItem;
  2389. ListView_GetItemRect(hTaskList, iItem, &rcItem, LVIR_ICON);
  2390. MapWindowRect(hTaskList, NULL, &rcItem);
  2391. xPos = rcItem.right;
  2392. yPos = rcItem.bottom;
  2393. }
  2394. HMENU hPopup = LoadPopupMenu(g_hInstance, IDR_PROC_CONTEXT);
  2395. if (hPopup)
  2396. {
  2397. if (hPopup && SHRestricted(REST_NORUN))
  2398. {
  2399. DeleteMenu(hPopup, IDM_RUN, MF_BYCOMMAND);
  2400. }
  2401. CProcInfo * pProc = GetSelectedProcess();
  2402. if (NULL == pProc)
  2403. {
  2404. return;
  2405. }
  2406. //
  2407. // If no debugger is installed or it's a 16-bit app
  2408. // ghost the debug menu item
  2409. //
  2410. if (NULL == m_pszDebugger || pProc->IsWowTask())
  2411. {
  2412. EnableMenuItem(hPopup, IDM_PROC_DEBUG, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2413. }
  2414. //
  2415. // If it's a 16-bit task grey the priority choices
  2416. //
  2417. if (pProc->IsWowTask())
  2418. {
  2419. EnableMenuItem(hPopup, IDM_PROC_REALTIME, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2420. EnableMenuItem(hPopup, IDM_PROC_ABOVENORMAL,MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2421. EnableMenuItem(hPopup, IDM_PROC_NORMAL, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2422. EnableMenuItem(hPopup, IDM_PROC_BELOWNORMAL,MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2423. EnableMenuItem(hPopup, IDM_PROC_HIGH, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2424. EnableMenuItem(hPopup, IDM_PROC_LOW, MF_GRAYED | MF_DISABLED | MF_BYCOMMAND);
  2425. }
  2426. //
  2427. // If not an MP machine, remove the affinity option
  2428. //
  2429. if (1 == g_cProcessors || pProc->IsWowTask())
  2430. {
  2431. DeleteMenu(hPopup, IDM_AFFINITY, MF_BYCOMMAND);
  2432. }
  2433. DWORD dwPri = pProc->m_PriClass;
  2434. INT idCheck = 0;
  2435. // These constants are listed in the SDK
  2436. if (dwPri == IDLE_PRIORITY_CLASS)
  2437. {
  2438. idCheck = IDM_PROC_LOW;
  2439. }
  2440. else if (dwPri == BELOW_NORMAL_PRIORITY_CLASS)
  2441. {
  2442. idCheck = IDM_PROC_BELOWNORMAL;
  2443. }
  2444. else if (dwPri == NORMAL_PRIORITY_CLASS)
  2445. {
  2446. idCheck = IDM_PROC_NORMAL;
  2447. }
  2448. else if (dwPri == ABOVE_NORMAL_PRIORITY_CLASS)
  2449. {
  2450. idCheck = IDM_PROC_ABOVENORMAL;
  2451. }
  2452. else if (dwPri == HIGH_PRIORITY_CLASS)
  2453. {
  2454. idCheck = IDM_PROC_HIGH;
  2455. }
  2456. else
  2457. {
  2458. Assert(dwPri == REALTIME_PRIORITY_CLASS);
  2459. idCheck = IDM_PROC_REALTIME;
  2460. }
  2461. // Check the appropriate radio menu for this process' priority class
  2462. CheckMenuRadioItem(hPopup, IDM_PROC_REALTIME, IDM_PROC_LOW, idCheck, MF_BYCOMMAND);
  2463. m_fPaused = TRUE;
  2464. g_fInPopup = TRUE;
  2465. TrackPopupMenuEx(hPopup, 0, xPos, yPos, m_hPage, NULL);
  2466. g_fInPopup = FALSE;
  2467. m_fPaused = FALSE;
  2468. //
  2469. // If one of the context menu actions (ie: Kill) requires that the display
  2470. // get updated, do it now
  2471. //
  2472. DestroyMenu(hPopup);
  2473. }
  2474. }
  2475. }
  2476. /*++ AffinityDlgProc
  2477. Routine Description:
  2478. Dialog procedure for the affinity mask dialog. Basically just tracks 32 check
  2479. boxes that represent the processors
  2480. Arguments:
  2481. standard dlgproc fare 0 - initial lParam is pointer to affinity mask
  2482. Revision History:
  2483. Jan-17-96 Davepl Created
  2484. --*/
  2485. INT_PTR CALLBACK AffinityDlgProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2486. {
  2487. static DWORD * pdwAffinity = NULL; // One of the joys of single threadedness
  2488. switch (uMsg)
  2489. {
  2490. case WM_INITDIALOG:
  2491. {
  2492. pdwAffinity = (DWORD *) lParam;
  2493. for (int i = 0; i < MAX_PROCESSOR; i++)
  2494. {
  2495. EnableWindow(GetDlgItem(hwndDlg, IDC_CPU0 + i), i < g_cProcessors);
  2496. CheckDlgButton(hwndDlg, IDC_CPU0 + i, i < g_cProcessors && ((*pdwAffinity & (1 << i)) != 0));
  2497. }
  2498. return TRUE;
  2499. }
  2500. case WM_COMMAND:
  2501. {
  2502. switch (LOWORD(wParam))
  2503. {
  2504. case IDCANCEL:
  2505. EndDialog(hwndDlg, IDCANCEL);
  2506. break;
  2507. case IDOK:
  2508. {
  2509. *pdwAffinity = 0;
  2510. for (int i = 0; i < g_cProcessors; i++)
  2511. {
  2512. if (IsDlgButtonChecked(hwndDlg, IDC_CPU0 + i))
  2513. {
  2514. *pdwAffinity |= 1 << i;
  2515. }
  2516. }
  2517. if (*pdwAffinity == 0)
  2518. {
  2519. // Can't set affinity to "none"
  2520. TCHAR szTitle[MAX_PATH];
  2521. TCHAR szBody[MAX_PATH];
  2522. if (0 == LoadString(g_hInstance, IDS_INVALIDOPTION, szTitle, ARRAYSIZE(szTitle)) ||
  2523. 0 == LoadString(g_hInstance, IDS_NOAFFINITYMASK, szBody, ARRAYSIZE(szBody)))
  2524. {
  2525. break;
  2526. }
  2527. MessageBox(hwndDlg, szBody, szTitle, MB_ICONERROR);
  2528. break;
  2529. }
  2530. EndDialog(hwndDlg, IDOK);
  2531. }
  2532. }
  2533. }
  2534. }
  2535. return FALSE;
  2536. }
  2537. /*++ SetAffinity
  2538. Routine Description:
  2539. Puts up a dialog that lets the user adjust the processor affinity
  2540. for a process
  2541. Arguments:
  2542. pid - process Id of process to modify
  2543. Return Value:
  2544. boolean success
  2545. Revision History:
  2546. Jan-17-96 Davepl Created
  2547. --*/
  2548. BOOL CProcPage::SetAffinity(DWORD pid)
  2549. {
  2550. BOOL fSuccess = FALSE;
  2551. // REVIEW (Davepl) may only need get/set info access here
  2552. HANDLE hProcess = OpenProcess( PROCESS_SET_INFORMATION | PROCESS_QUERY_INFORMATION, FALSE, pid );
  2553. if (hProcess)
  2554. {
  2555. DWORD_PTR dwAffinity;
  2556. DWORD_PTR dwUnusedSysAfin;
  2557. if (GetProcessAffinityMask(hProcess, &dwAffinity, &dwUnusedSysAfin))
  2558. {
  2559. if (IDOK == DialogBoxParam(g_hInstance,
  2560. MAKEINTRESOURCE(IDD_AFFINITY),
  2561. m_hPage,
  2562. AffinityDlgProc,
  2563. (LPARAM) &dwAffinity))
  2564. {
  2565. if (SetProcessAffinityMask(hProcess, dwAffinity))
  2566. fSuccess = TRUE;
  2567. }
  2568. else
  2569. fSuccess = TRUE; // Cancel, so no failure
  2570. }
  2571. CloseHandle(hProcess);
  2572. }
  2573. if (!fSuccess)
  2574. {
  2575. DWORD dwError = GetLastError();
  2576. DisplayFailureMsg(m_hPage, IDS_CANTSETAFFINITY, dwError);
  2577. }
  2578. return fSuccess;
  2579. }
  2580. BOOL CProcPage::IsSystemProcess(DWORD pid, CProcInfo * pProcInfo)
  2581. {
  2582. // We don't allow the following set of critical system processes to be terminated,
  2583. // since the system would bugcheck immediately, no matter who you are.
  2584. static const LPCTSTR apszCantKill[] =
  2585. {
  2586. TEXT("csrss.exe"), TEXT("winlogon.exe"), TEXT("smss.exe"), TEXT("services.exe"), TEXT("lsass.exe")
  2587. };
  2588. // if they pass in a pProcInfo we'll use it, otherwise find it ourselves
  2589. if (!pProcInfo)
  2590. pProcInfo = FindProcInArrayByPID(m_pProcArray, pid);
  2591. if (!pProcInfo)
  2592. return FALSE;
  2593. for (int i = 0; i < ARRAYSIZE(apszCantKill); ++i)
  2594. {
  2595. if (0 == lstrcmpi(pProcInfo->m_pszImageName, apszCantKill[i]))
  2596. {
  2597. TCHAR szTitle[MAX_PATH];
  2598. TCHAR szBody[MAX_PATH];
  2599. if (0 != LoadString(g_hInstance, IDS_CANTKILL, szTitle, ARRAYSIZE(szTitle)) &&
  2600. 0 != LoadString(g_hInstance, IDS_KILLSYS, szBody, ARRAYSIZE(szBody)))
  2601. {
  2602. MessageBox(m_hPage, szBody, szTitle, MB_ICONEXCLAMATION | MB_OK);
  2603. }
  2604. return TRUE;
  2605. }
  2606. }
  2607. return FALSE;
  2608. }
  2609. /*++ KillProcess
  2610. Routine Description:
  2611. Kills a process
  2612. Arguments:
  2613. pid - process Id of process to kill
  2614. Return Value:
  2615. Revision History:
  2616. Nov-22-95 Davepl Created
  2617. --*/
  2618. BOOL CProcPage::KillProcess(DWORD pid, BOOL bBatchKill)
  2619. {
  2620. DWORD dwError = ERROR_SUCCESS;
  2621. //
  2622. // Special-case killing WOW tasks
  2623. //
  2624. CProcInfo * pProcInfo;
  2625. pProcInfo = FindProcInArrayByPID(m_pProcArray, pid);
  2626. if (NULL == pProcInfo)
  2627. return FALSE;
  2628. if (IsSystemProcess(pid, pProcInfo))
  2629. return FALSE;
  2630. // Grab info from pProcInfo (because once we call QuickConfirm(), the
  2631. // pProcInfo pointer may be invalid)
  2632. INT_PTR fWowTask = pProcInfo->IsWowTask();
  2633. #if defined (_WIN64)
  2634. #else
  2635. DWORD dwRealPID = pProcInfo->GetRealPID();
  2636. WORD hTaskWow = pProcInfo->m_htaskWow;
  2637. #endif
  2638. // OK so far, now confirm that the user really wants to do this.
  2639. if (!bBatchKill && (IDYES != QuickConfirm(IDS_WARNING, IDS_KILL)))
  2640. {
  2641. return FALSE;
  2642. }
  2643. // We can't use this pointer after QuickConfirm() is called.
  2644. // NULL it out to prevent subtle bugs.
  2645. pProcInfo = NULL;
  2646. if (fWowTask) {
  2647. #if defined (_WIN64)
  2648. return FALSE;
  2649. #else
  2650. return VDMTerminateTaskWOW(dwRealPID, hTaskWow);
  2651. #endif
  2652. }
  2653. //
  2654. // If possible, enable the Debug privilege. This allows us to kill
  2655. // processes not owned by the current user, including processes
  2656. // running in other TS sessions.
  2657. //
  2658. // Alternatively, we could first open the process for WRITE_DAC,
  2659. // grant ourselves PROCESS_TERMINATE access, and then reopen the
  2660. // process to kill it.
  2661. //
  2662. CPrivilegeEnable privilege(SE_DEBUG_NAME);
  2663. HANDLE hProcess = OpenProcess( PROCESS_TERMINATE, FALSE, pid );
  2664. if (hProcess)
  2665. {
  2666. if (FALSE == TerminateProcess( hProcess, 1 ))
  2667. {
  2668. dwError = GetLastError();
  2669. dprintf(TEXT("Can't terminate process: %08x\n"), dwError);
  2670. }
  2671. else
  2672. {
  2673. TimerEvent();
  2674. }
  2675. CloseHandle( hProcess );
  2676. }
  2677. else
  2678. {
  2679. dwError = GetLastError();
  2680. }
  2681. if (ERROR_SUCCESS != dwError)
  2682. {
  2683. DisplayFailureMsg(m_hPage, IDS_CANTKILL, dwError);
  2684. return FALSE;
  2685. }
  2686. else
  2687. {
  2688. return TRUE;
  2689. }
  2690. }
  2691. /*++ AttachDebugger
  2692. Routine Description:
  2693. Attaches the debugger listed in the AeDebug reg key to the specified
  2694. running process
  2695. Arguments:
  2696. pid - process Id of process to debug
  2697. Return Value:
  2698. Revision History:
  2699. Nov-27-95 Davepl Created
  2700. --*/
  2701. BOOL CProcPage::AttachDebugger(DWORD pid)
  2702. {
  2703. DWORD dwError = ERROR_SUCCESS;
  2704. if (IDYES != QuickConfirm(IDS_WARNING, IDS_DEBUG))
  2705. {
  2706. return FALSE;
  2707. }
  2708. TCHAR szCmdline[MAX_PATH * 2];
  2709. wsprintf(szCmdline, TEXT("%s -p %ld"), m_pszDebugger, pid);
  2710. STARTUPINFO sinfo =
  2711. {
  2712. sizeof(STARTUPINFO),
  2713. };
  2714. PROCESS_INFORMATION pinfo;
  2715. if (FALSE == CreateProcess(NULL, //m_pszDebugger,
  2716. szCmdline,
  2717. NULL,
  2718. NULL,
  2719. FALSE,
  2720. CREATE_NEW_CONSOLE,
  2721. NULL,
  2722. NULL,
  2723. &sinfo,
  2724. &pinfo))
  2725. {
  2726. dwError = GetLastError();
  2727. }
  2728. else
  2729. {
  2730. CloseHandle(pinfo.hThread);
  2731. CloseHandle(pinfo.hProcess);
  2732. }
  2733. if (ERROR_SUCCESS != dwError)
  2734. {
  2735. DisplayFailureMsg(m_hPage, IDS_CANTDEBUG, dwError);
  2736. return FALSE;
  2737. }
  2738. else
  2739. {
  2740. return TRUE;
  2741. }
  2742. }
  2743. /*++ SetPriority
  2744. Routine Description:
  2745. Sets a process' priority class
  2746. Arguments:
  2747. pid - process Id of process to change
  2748. pri - ID_CMD_XXXXXX menu choice of priority
  2749. Return Value:
  2750. Revision History:
  2751. Nov-27-95 Davepl Created
  2752. --*/
  2753. BOOL CProcPage::SetPriority(CProcInfo * pProc, DWORD idCmd)
  2754. {
  2755. DWORD dwError = ERROR_SUCCESS;
  2756. DWORD oldPri;
  2757. DWORD pri;
  2758. // Determine which priority class we need to use based
  2759. // on the menu selection
  2760. switch (idCmd)
  2761. {
  2762. case IDM_PROC_LOW:
  2763. pri = IDLE_PRIORITY_CLASS;
  2764. break;
  2765. case IDM_PROC_BELOWNORMAL:
  2766. pri = BELOW_NORMAL_PRIORITY_CLASS;
  2767. break;
  2768. case IDM_PROC_ABOVENORMAL:
  2769. pri = ABOVE_NORMAL_PRIORITY_CLASS;
  2770. break;
  2771. case IDM_PROC_HIGH:
  2772. pri = HIGH_PRIORITY_CLASS;
  2773. break;
  2774. case IDM_PROC_REALTIME:
  2775. pri = REALTIME_PRIORITY_CLASS;
  2776. break;
  2777. default:
  2778. Assert(idCmd == IDM_PROC_NORMAL);
  2779. pri = NORMAL_PRIORITY_CLASS;
  2780. }
  2781. oldPri = (DWORD) pProc->m_PriClass;
  2782. if ( oldPri == pri )
  2783. {
  2784. return FALSE; // nothing to do.
  2785. }
  2786. // Get confirmation before we change the priority
  2787. if (IDYES != QuickConfirm(IDS_WARNING, IDS_PRICHANGE))
  2788. {
  2789. return FALSE;
  2790. }
  2791. HANDLE hProcess = OpenProcess( PROCESS_SET_INFORMATION, FALSE, pProc->m_UniqueProcessId);
  2792. if (hProcess)
  2793. {
  2794. if (FALSE == SetPriorityClass( hProcess, pri ))
  2795. {
  2796. dwError = GetLastError();
  2797. dprintf(TEXT("Cant open process for pri change: %08x\n"), dwError);
  2798. }
  2799. else
  2800. {
  2801. TimerEvent();
  2802. }
  2803. CloseHandle( hProcess );
  2804. }
  2805. else
  2806. {
  2807. dwError = GetLastError();
  2808. }
  2809. if (ERROR_SUCCESS != dwError)
  2810. {
  2811. DisplayFailureMsg(m_hPage, IDS_CANTCHANGEPRI, dwError);
  2812. return FALSE;
  2813. }
  2814. else
  2815. {
  2816. return TRUE;
  2817. }
  2818. }
  2819. /*++ CProcPage::GetSelectedProcess
  2820. Routine Description:
  2821. Returns the CProcInfo * of the currently selected process
  2822. Arguments:
  2823. Return Value:
  2824. CProcInfo * on success, NULL on error or nothing selected
  2825. Revision History:
  2826. Nov-22-95 Davepl Created
  2827. --*/
  2828. CProcInfo * CProcPage::GetSelectedProcess()
  2829. {
  2830. HWND hTaskList = GetDlgItem(m_hPage, IDC_PROCLIST);
  2831. INT iItem = ListView_GetNextItem(hTaskList, -1, LVNI_SELECTED);
  2832. CProcInfo * pProc;
  2833. if (-1 != iItem)
  2834. {
  2835. LV_ITEM lvitem = { LVIF_PARAM };
  2836. lvitem.iItem = iItem;
  2837. if (ListView_GetItem(hTaskList, &lvitem))
  2838. {
  2839. pProc = (CProcInfo *) (lvitem.lParam);
  2840. }
  2841. else
  2842. {
  2843. pProc = NULL;
  2844. }
  2845. }
  2846. else
  2847. {
  2848. pProc = NULL;
  2849. }
  2850. return pProc;
  2851. }
  2852. /*++ CProcPage::HandleWMCOMMAND
  2853. Routine Description:
  2854. Handles WM_COMMANDS received at the main page dialog
  2855. Arguments:
  2856. id - Command id of command received
  2857. Return Value:
  2858. Revision History:
  2859. Nov-22-95 Davepl Created
  2860. --*/
  2861. void CProcPage::HandleWMCOMMAND( WORD id , HWND hCtrl )
  2862. {
  2863. CProcInfo * pProc = GetSelectedProcess();
  2864. switch(id)
  2865. {
  2866. case IDC_DEBUG:
  2867. case IDM_PROC_DEBUG:
  2868. {
  2869. if (pProc && m_pszDebugger)
  2870. {
  2871. AttachDebugger( pProc->m_UniqueProcessId);
  2872. }
  2873. break;
  2874. }
  2875. case IDC_ENDTASK:
  2876. case IDC_TERMINATE:
  2877. case IDM_PROC_TERMINATE:
  2878. {
  2879. if (pProc)
  2880. {
  2881. KillProcess( pProc->m_UniqueProcessId);
  2882. }
  2883. break;
  2884. }
  2885. case IDM_ENDTREE:
  2886. {
  2887. if (pProc)
  2888. {
  2889. RecursiveKill( pProc->m_UniqueProcessId);
  2890. }
  2891. break;
  2892. }
  2893. case IDM_AFFINITY:
  2894. {
  2895. if (pProc)
  2896. {
  2897. SetAffinity( pProc->m_UniqueProcessId);
  2898. }
  2899. break;
  2900. }
  2901. case IDM_PROC_REALTIME:
  2902. case IDM_PROC_HIGH:
  2903. case IDM_PROC_ABOVENORMAL:
  2904. case IDM_PROC_NORMAL:
  2905. case IDM_PROC_BELOWNORMAL:
  2906. case IDM_PROC_LOW:
  2907. {
  2908. if (pProc)
  2909. {
  2910. SetPriority( pProc, id);
  2911. }
  2912. break;
  2913. }
  2914. case IDC_SHOWALL:
  2915. g_Options.m_bShowAllProcess = SendMessage( hCtrl , BM_GETCHECK , 0 , 0 ) == BST_CHECKED;
  2916. break;
  2917. }
  2918. }
  2919. /*++ ProcPageProc
  2920. Routine Description:
  2921. Dialogproc for the process page.
  2922. Arguments:
  2923. hwnd - handle to dialog box
  2924. uMsg - message
  2925. wParam - first message parameter
  2926. lParam - second message parameter
  2927. Return Value:
  2928. For WM_INITDIALOG, TRUE == user32 sets focus, FALSE == we set focus
  2929. For others, TRUE == this proc handles the message
  2930. Revision History:
  2931. Nov-16-95 Davepl Created
  2932. --*/
  2933. INT_PTR CALLBACK ProcPageProc(
  2934. HWND hwnd, // handle to dialog box
  2935. UINT uMsg, // message
  2936. WPARAM wParam, // first message parameter
  2937. LPARAM lParam // second message parameter
  2938. )
  2939. {
  2940. CProcPage * thispage = (CProcPage *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
  2941. // See if the parent wants this message
  2942. if (TRUE == CheckParentDeferrals(uMsg, wParam, lParam))
  2943. {
  2944. return TRUE;
  2945. }
  2946. switch(uMsg)
  2947. {
  2948. case WM_INITDIALOG:
  2949. {
  2950. SetWindowLongPtr(hwnd, GWLP_USERDATA, lParam);
  2951. CProcPage * thispage = (CProcPage *) lParam;
  2952. thispage->m_hPage = hwnd;
  2953. // Turn on SHOWSELALWAYS so that the selection is still highlighted even
  2954. // when focus is lost to one of the buttons (for example)
  2955. HWND hTaskList = GetDlgItem(hwnd, IDC_PROCLIST);
  2956. SetWindowLong(hTaskList, GWL_STYLE, GetWindowLong(hTaskList, GWL_STYLE) | LVS_SHOWSELALWAYS);
  2957. ListView_SetExtendedListViewStyle(hTaskList, LVS_EX_FULLROWSELECT | LVS_EX_HEADERDRAGDROP | LVS_EX_DOUBLEBUFFER);
  2958. SubclassListView(GetDlgItem(hwnd, IDC_PROCLIST));
  2959. //
  2960. // This was removed from CProcPage::Activate
  2961. //
  2962. HWND hchk = GetDlgItem( hwnd , IDC_SHOWALL );
  2963. if( hchk != NULL )
  2964. {
  2965. if( g_fIsTSEnabled )
  2966. {
  2967. // Disable the IDC_SHOWALL checkbox for non-admin. YufengZ 03/23/98
  2968. ShowWindow(hchk, TRUE);
  2969. if( !IsUserAdmin( ) )
  2970. {
  2971. EnableWindow( hchk, FALSE );
  2972. }
  2973. else
  2974. {
  2975. WPARAM wp = g_Options.m_bShowAllProcess ? BST_CHECKED : BST_UNCHECKED;
  2976. SendMessage( hchk , BM_SETCHECK , wp , 0 );
  2977. //Button_SetCheck( hchk , BST_CHECKED );
  2978. }
  2979. }
  2980. else
  2981. {
  2982. // hide the IDC_SHOWALL checkbox if its not terminal server.
  2983. ShowWindow( hchk , SW_HIDE );
  2984. }
  2985. }
  2986. // We handle focus during Activate(). Return FALSE here so the
  2987. // dialog manager doesn't try to set focus.
  2988. return FALSE;
  2989. }
  2990. case WM_DESTROY:
  2991. thispage->RememberColumnOrder(GetDlgItem(hwnd, IDC_PROCLIST));
  2992. break;
  2993. // We need to fake client mouse clicks in this child to appear as nonclient
  2994. // (caption) clicks in the parent so that the user can drag the entire app
  2995. // when the title bar is hidden by dragging the client area of this child
  2996. case WM_LBUTTONUP:
  2997. case WM_LBUTTONDOWN:
  2998. {
  2999. if (g_Options.m_fNoTitle)
  3000. {
  3001. SendMessage(g_hMainWnd,
  3002. uMsg == WM_LBUTTONUP ? WM_NCLBUTTONUP : WM_NCLBUTTONDOWN,
  3003. HTCAPTION,
  3004. lParam);
  3005. }
  3006. break;
  3007. }
  3008. case WM_NCLBUTTONDBLCLK:
  3009. case WM_LBUTTONDBLCLK:
  3010. {
  3011. SendMessage(g_hMainWnd, uMsg, wParam, lParam);
  3012. break;
  3013. }
  3014. // We have been asked to find and select a process
  3015. case WM_FINDPROC:
  3016. {
  3017. DWORD cProcs = thispage->m_pProcArray->GetSize();
  3018. DWORD dwProcessId;
  3019. for (INT iPass = 0; iPass < 2; iPass++)
  3020. {
  3021. //
  3022. // On the first pass we try to find a WOW
  3023. // task with a thread ID which matches the
  3024. // one given in wParam. If we don't find
  3025. // such a task, we look for a process which
  3026. // matches the PID in lParam.
  3027. //
  3028. for (UINT i = 0; i < cProcs; i++)
  3029. {
  3030. CProcInfo *pProc = (CProcInfo *)thispage->m_pProcArray->GetAt(i);
  3031. dwProcessId = pProc->m_UniqueProcessId;
  3032. if ((!iPass && wParam == (WPARAM) dwProcessId) ||
  3033. ( iPass && lParam == (LPARAM) dwProcessId))
  3034. {
  3035. // TS filters items out of the view so cannot assume
  3036. // that m_pProcArray is in sync with the listview.
  3037. HWND hwndLV = GetDlgItem(hwnd, IDC_PROCLIST);
  3038. LVFINDINFO fi;
  3039. fi.flags = LVFI_PARAM;
  3040. fi.lParam = (LPARAM)pProc;
  3041. int iItem = ListView_FindItem(hwndLV, -1, &fi);
  3042. if (iItem >= 0)
  3043. {
  3044. ListView_SetItemState (hwndLV,
  3045. iItem,
  3046. LVIS_FOCUSED | LVIS_SELECTED,
  3047. 0x000F);
  3048. ListView_EnsureVisible(hwndLV, iItem, FALSE);
  3049. }
  3050. else
  3051. {
  3052. // We found the process but the user isn't allowed
  3053. // to see it; remove the selection
  3054. ListView_SetItemState (hwndLV,
  3055. -1,
  3056. 0,
  3057. LVIS_FOCUSED | LVIS_SELECTED);
  3058. }
  3059. goto FoundProc;
  3060. }
  3061. }
  3062. }
  3063. FoundProc:
  3064. break;
  3065. }
  3066. case WM_COMMAND:
  3067. {
  3068. thispage->HandleWMCOMMAND( LOWORD(wParam) , ( HWND )lParam );
  3069. break;
  3070. }
  3071. case WM_CONTEXTMENU:
  3072. {
  3073. CProcInfo * pProc = thispage->GetSelectedProcess();
  3074. if (pProc && pProc->m_UniqueProcessId)
  3075. {
  3076. if ((HWND) wParam == GetDlgItem(hwnd, IDC_PROCLIST))
  3077. {
  3078. thispage->HandleProcListContextMenu(GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam));
  3079. return TRUE;
  3080. }
  3081. }
  3082. break;
  3083. }
  3084. case WM_NOTIFY:
  3085. {
  3086. return thispage->HandleProcPageNotify((HWND) wParam, (LPNMHDR) lParam);
  3087. }
  3088. // Size our kids
  3089. case WM_SIZE:
  3090. {
  3091. thispage->SizeProcPage();
  3092. return TRUE;
  3093. }
  3094. case WM_SYSCOLORCHANGE:
  3095. SendMessage(GetDlgItem(hwnd, IDC_PROCLIST), uMsg, wParam, lParam);
  3096. return TRUE;
  3097. default:
  3098. return FALSE;
  3099. }
  3100. return FALSE;
  3101. }
  3102. /*++ CProcPage::GetTitle
  3103. Routine Description:
  3104. Copies the title of this page to the caller-supplied buffer
  3105. Arguments:
  3106. pszText - the buffer to copy to
  3107. bufsize - size of buffer, in characters
  3108. Return Value:
  3109. Revision History:
  3110. Nov-16-95 Davepl Created
  3111. --*/
  3112. void CProcPage::GetTitle(LPTSTR pszText, size_t bufsize)
  3113. {
  3114. LoadString(g_hInstance, IDS_PROCPAGETITLE, pszText, static_cast<int>(bufsize));
  3115. }
  3116. /*++ CProcPage::Activate
  3117. Routine Description:
  3118. Brings this page to the front, sets its initial position,
  3119. and shows it
  3120. Arguments:
  3121. Return Value:
  3122. HRESULT (S_OK on success)
  3123. Revision History:
  3124. Nov-16-95 Davepl Created
  3125. --*/
  3126. HRESULT CProcPage::Activate()
  3127. {
  3128. // Make this page visible
  3129. ShowWindow(m_hPage, SW_SHOW);
  3130. SetWindowPos(m_hPage,
  3131. HWND_TOP,
  3132. 0, 0, 0, 0,
  3133. SWP_NOMOVE | SWP_NOSIZE);
  3134. // Change the menu bar to be the menu for this page
  3135. HMENU hMenuOld = GetMenu(g_hMainWnd);
  3136. HMENU hMenuNew = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAINMENU_PROC));
  3137. AdjustMenuBar(hMenuNew);
  3138. if (hMenuNew && SHRestricted(REST_NORUN))
  3139. {
  3140. DeleteMenu(hMenuNew, IDM_RUN, MF_BYCOMMAND);
  3141. }
  3142. g_hMenu = hMenuNew;
  3143. if (g_Options.m_fNoTitle == FALSE)
  3144. {
  3145. SetMenu(g_hMainWnd, hMenuNew);
  3146. }
  3147. if (hMenuOld)
  3148. {
  3149. DestroyMenu(hMenuOld);
  3150. }
  3151. // If the tab control has focus, leave it there. Otherwise, set focus
  3152. // to the listview. If we don't set focus, it may stay on the previous
  3153. // page, now hidden, which can confuse the dialog manager and may cause
  3154. // us to hang.
  3155. if (GetFocus() != m_hwndTabs)
  3156. {
  3157. SetFocus(GetDlgItem(m_hPage, IDC_PROCLIST));
  3158. }
  3159. return S_OK;
  3160. }
  3161. /*++ CProcPage::Initialize
  3162. Routine Description:
  3163. Initializes the process page
  3164. Arguments:
  3165. hwndParent - Parent on which to base sizing on: not used for creation,
  3166. since the main app window is always used as the parent in
  3167. order to keep tab order correct
  3168. Return Value:
  3169. Revision History:
  3170. Nov-16-95 Davepl Created
  3171. --*/
  3172. HRESULT CProcPage::Initialize(HWND hwndParent)
  3173. {
  3174. //
  3175. // Find out what debbuger is configured on this system
  3176. //
  3177. HKEY hkDebug;
  3178. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  3179. TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"),
  3180. 0, KEY_READ, &hkDebug))
  3181. {
  3182. TCHAR szDebugger[MAX_PATH * 2];
  3183. DWORD cbString = sizeof(szDebugger);
  3184. if (ERROR_SUCCESS == RegQueryValueEx(hkDebug, TEXT("Debugger"), NULL,
  3185. NULL, (LPBYTE) szDebugger, &cbString))
  3186. {
  3187. // Find the first token (which is the debugger exe name/path)
  3188. LPTSTR pszCmdLine = szDebugger;
  3189. if ( *pszCmdLine == TEXT('\"') )
  3190. {
  3191. //
  3192. // Scan, and skip over, subsequent characters until
  3193. // another double-quote or a null is encountered.
  3194. //
  3195. while ( *++pszCmdLine && (*pszCmdLine != TEXT('\"')) )
  3196. {
  3197. NULL;
  3198. }
  3199. //
  3200. // If we stopped on a double-quote (usual case), skip
  3201. // over it.
  3202. //
  3203. if ( *pszCmdLine == TEXT('\"') )
  3204. {
  3205. pszCmdLine++;
  3206. }
  3207. }
  3208. else
  3209. {
  3210. while (*pszCmdLine > TEXT(' '))
  3211. {
  3212. pszCmdLine++;
  3213. }
  3214. }
  3215. *pszCmdLine = TEXT('\0'); // Don't need the rest of the args, etc
  3216. // If the doctor is in, we don't allow the Debug action
  3217. if (lstrlen(szDebugger) && lstrcmpi(szDebugger, TEXT("drwtsn32")) && lstrcmpi(szDebugger, TEXT("drwtsn32.exe")))
  3218. {
  3219. m_pszDebugger = (LPTSTR) LocalAlloc( 0, (lstrlen(szDebugger) + 1) * sizeof(TCHAR));
  3220. if (NULL == m_pszDebugger)
  3221. {
  3222. return E_OUTOFMEMORY;
  3223. }
  3224. lstrcpy(m_pszDebugger, szDebugger);
  3225. }
  3226. }
  3227. RegCloseKey(hkDebug);
  3228. }
  3229. //
  3230. // Get basic info like page size, etc.
  3231. //
  3232. NTSTATUS Status = NtQuerySystemInformation(
  3233. SystemBasicInformation,
  3234. &g_BasicInfo,
  3235. sizeof(g_BasicInfo),
  3236. NULL
  3237. );
  3238. if (!NT_SUCCESS(Status))
  3239. {
  3240. return E_FAIL;
  3241. }
  3242. //
  3243. // Create the ptr array used to hold the info on running processes
  3244. //
  3245. m_pProcArray = new CPtrArray(GetProcessHeap());
  3246. if (NULL == m_pProcArray)
  3247. {
  3248. return E_OUTOFMEMORY;
  3249. }
  3250. // Our pseudo-parent is the tab contrl, and is what we base our
  3251. // sizing on. However, in order to keep tab order right among
  3252. // the controls, we actually create ourselves with the main
  3253. // window as the parent
  3254. m_hwndTabs = hwndParent;
  3255. //
  3256. // Create the dialog which represents the body of this page
  3257. //
  3258. m_hPage = CreateDialogParam(
  3259. g_hInstance, // handle to application instance
  3260. MAKEINTRESOURCE(IDD_PROCPAGE), // identifies dialog box template name
  3261. g_hMainWnd, // handle to owner window
  3262. ProcPageProc, // pointer to dialog box procedure
  3263. (LPARAM) this ); // User data (our this pointer)
  3264. if (NULL == m_hPage)
  3265. {
  3266. return GetLastHRESULT();
  3267. }
  3268. // Set up the columns in the listview
  3269. if (FAILED(SetupColumns()))
  3270. {
  3271. DestroyWindow(m_hPage);
  3272. m_hPage = NULL;
  3273. return E_FAIL;
  3274. }
  3275. // Restore the column positions.
  3276. RestoreColumnOrder(GetDlgItem(m_hPage, IDC_PROCLIST));
  3277. // Do one initial calculation
  3278. TimerEvent();
  3279. return S_OK;
  3280. }
  3281. /*++ CProcPage::Destroy
  3282. Routine Description:
  3283. Frees whatever has been allocated by the Initialize call
  3284. Arguments:
  3285. Return Value:
  3286. Revision History:
  3287. Nov-16-95 Davepl Created
  3288. --*/
  3289. HRESULT CProcPage::Destroy()
  3290. {
  3291. if (m_pProcArray)
  3292. {
  3293. INT c = m_pProcArray->GetSize();
  3294. while (c)
  3295. {
  3296. delete (CProcInfo *) (m_pProcArray->GetAt(c - 1));
  3297. c--;
  3298. }
  3299. delete m_pProcArray;
  3300. m_pProcArray = NULL;
  3301. }
  3302. if ( m_pvBuffer != NULL )
  3303. {
  3304. HeapFree( GetProcessHeap( ), 0, m_pvBuffer );
  3305. m_pvBuffer = NULL;
  3306. }
  3307. if (m_hPage != NULL)
  3308. {
  3309. DestroyWindow(m_hPage);
  3310. m_hPage = NULL;
  3311. }
  3312. if (m_pszDebugger != NULL)
  3313. {
  3314. LocalFree(m_pszDebugger);
  3315. m_pszDebugger = NULL;
  3316. }
  3317. return S_OK;
  3318. }
  3319. /*++ CProcPage::SaveColumnWidths
  3320. Routine Description:
  3321. Saves the widths of all of the columns in the global options structure
  3322. Revision History:
  3323. Jan-26-95 Davepl Created
  3324. --*/
  3325. void CProcPage::SaveColumnWidths()
  3326. {
  3327. UINT i = 0;
  3328. LV_COLUMN col = { 0 };
  3329. while (g_Options.m_ActiveProcCol[i] != (COLUMNID) -1)
  3330. {
  3331. col.mask = LVCF_WIDTH;
  3332. if (ListView_GetColumn(GetDlgItem(m_hPage, IDC_PROCLIST), i, &col) )
  3333. {
  3334. g_Options.m_ColumnWidths[i] = col.cx;
  3335. }
  3336. else
  3337. {
  3338. ASSERT(0 && "Couldn't get the column width");
  3339. }
  3340. i++;
  3341. }
  3342. }
  3343. /*++ CProcPage::Deactivate
  3344. Routine Description:
  3345. Called when this page is losing its place up front
  3346. Arguments:
  3347. Return Value:
  3348. Revision History:
  3349. Nov-16-95 Davepl Created
  3350. --*/
  3351. void CProcPage::Deactivate()
  3352. {
  3353. SaveColumnWidths();
  3354. if (m_hPage)
  3355. {
  3356. ShowWindow(m_hPage, SW_HIDE);
  3357. }
  3358. }
  3359. /*++ CProcPage::KillAllChildren
  3360. Routine Description:
  3361. Given a pid, recursively kills it and all of its descendants
  3362. Arguments:
  3363. Return Value:
  3364. Revision History:
  3365. 2-26-01 Bretan Created
  3366. --*/
  3367. BOOL CProcPage::KillAllChildren(
  3368. DWORD dwTaskPid,
  3369. DWORD pid,
  3370. BYTE* pbBuffer,
  3371. LARGE_INTEGER CreateTime
  3372. )
  3373. {
  3374. ASSERT(pbBuffer);
  3375. BOOL rval = TRUE;
  3376. PSYSTEM_PROCESS_INFORMATION ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pbBuffer;
  3377. ULONG TotalOffset = 0;
  3378. for ( ;; ) // ever
  3379. {
  3380. // If we are a child of pid and not pid itself
  3381. // and if we have been created after pid (we can't be a child if we were created first)
  3382. if (PtrToUlong(ProcessInfo->InheritedFromUniqueProcessId) == pid &&
  3383. PtrToUlong(ProcessInfo->UniqueProcessId) != pid &&
  3384. CreateTime.QuadPart < ProcessInfo->CreateTime.QuadPart)
  3385. {
  3386. DWORD newpid = PtrToUlong(ProcessInfo->UniqueProcessId);
  3387. //
  3388. // Recurse down to the next level
  3389. //
  3390. rval = KillAllChildren(dwTaskPid, newpid, pbBuffer, ProcessInfo->CreateTime);
  3391. // Kill it if it is not task manager
  3392. if (newpid != dwTaskPid)
  3393. {
  3394. BOOL tval = KillProcess(newpid, TRUE);
  3395. //
  3396. // If it has failed earlier in the recursion
  3397. // we want to keep that failure (not overwrite it)
  3398. //
  3399. if (rval == TRUE)
  3400. {
  3401. rval = tval;
  3402. }
  3403. }
  3404. }
  3405. if (ProcessInfo->NextEntryOffset == 0)
  3406. {
  3407. break;
  3408. }
  3409. TotalOffset += ProcessInfo->NextEntryOffset;
  3410. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &pbBuffer[ TotalOffset ];
  3411. }
  3412. return rval;
  3413. }
  3414. /*++ CProcPage::RecursiveKill
  3415. Routine Description:
  3416. Given a pid, starts the recursive function that kills all the pid's descendents
  3417. Arguments:
  3418. Return Value:
  3419. Revision History:
  3420. 8-4-98 Davepl Created
  3421. 2-26-01 Bretan Modified
  3422. --*/
  3423. #define MAX_TASKS 4096
  3424. BOOL CProcPage::RecursiveKill(DWORD pid)
  3425. {
  3426. BYTE* pbBuffer = NULL;
  3427. BOOL rval = TRUE;
  3428. DWORD dwTaskPid = GetCurrentProcessId();
  3429. if (IsSystemProcess(pid, NULL))
  3430. {
  3431. return FALSE;
  3432. }
  3433. if (IDYES != QuickConfirm(IDS_WARNING, IDS_KILLTREE))
  3434. {
  3435. return FALSE;
  3436. }
  3437. //
  3438. // get the task list for the system
  3439. //
  3440. pbBuffer = GetTaskListEx();
  3441. if (pbBuffer)
  3442. {
  3443. PSYSTEM_PROCESS_INFORMATION ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pbBuffer;
  3444. ULONG TotalOffset = 0;
  3445. for ( ;; ) // ever
  3446. {
  3447. if (PtrToUlong(ProcessInfo->UniqueProcessId) == pid)
  3448. {
  3449. rval = KillAllChildren(dwTaskPid, pid, pbBuffer, ProcessInfo->CreateTime);
  3450. //
  3451. // Kill the parent process if it is not task manager
  3452. //
  3453. if (pid != dwTaskPid)
  3454. {
  3455. KillProcess(pid, TRUE);
  3456. }
  3457. // We will not run into this pid again (since its unique)
  3458. // so we might as well break outta this for loop
  3459. break;
  3460. }
  3461. //
  3462. // Advance to next task
  3463. //
  3464. if (ProcessInfo->NextEntryOffset == 0)
  3465. {
  3466. break;
  3467. }
  3468. TotalOffset += ProcessInfo->NextEntryOffset;
  3469. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) &pbBuffer[ TotalOffset ];
  3470. }
  3471. }
  3472. else
  3473. {
  3474. rval = FALSE;
  3475. }
  3476. if (rval != TRUE)
  3477. {
  3478. // We failed to kill at least one of the processes
  3479. TCHAR szTitle[MAX_PATH];
  3480. TCHAR szBody[MAX_PATH];
  3481. if (0 != LoadString(g_hInstance, IDS_KILLTREEFAIL, szTitle, ARRAYSIZE(szTitle)) &&
  3482. 0 != LoadString(g_hInstance, IDS_KILLTREEFAILBODY, szBody, ARRAYSIZE(szBody)))
  3483. {
  3484. MessageBox(m_hPage, szBody, szTitle, MB_ICONERROR);
  3485. }
  3486. }
  3487. //
  3488. // Buffer allocated in call to GetTaskListEx
  3489. //
  3490. HeapFree( GetProcessHeap( ), 0, pbBuffer );
  3491. return rval;
  3492. }
  3493. /*++ CProcPage::GetTaskListEx
  3494. Routine Description:
  3495. Provides an API for getting a list of tasks running at the time of the
  3496. API call. This function uses internal NT apis and data structures. This
  3497. api is MUCH faster that the non-internal version that uses the registry.
  3498. Arguments:
  3499. dwNumTasks - maximum number of tasks that the pTask array can hold
  3500. Return Value:
  3501. Number of tasks placed into the pTask array.
  3502. --*/
  3503. BYTE* CProcPage::GetTaskListEx()
  3504. {
  3505. BYTE* pbBuffer = NULL;
  3506. NTSTATUS status;
  3507. DWORD dwBufferSize = sizeof(SYSTEM_PROCESS_INFORMATION) * 100; // start with ~100 processes
  3508. retry:
  3509. ASSERT( NULL == pbBuffer );
  3510. pbBuffer = (BYTE *) HeapAlloc( GetProcessHeap( ), 0, dwBufferSize );
  3511. if (pbBuffer == NULL)
  3512. {
  3513. return FALSE;
  3514. }
  3515. status = NtQuerySystemInformation( SystemProcessInformation
  3516. , pbBuffer
  3517. , dwBufferSize
  3518. , NULL
  3519. );
  3520. if ( status != ERROR_SUCCESS )
  3521. {
  3522. HeapFree( GetProcessHeap( ), 0, pbBuffer );
  3523. pbBuffer = NULL;
  3524. }
  3525. if (status == STATUS_INFO_LENGTH_MISMATCH) {
  3526. dwBufferSize += 8192;
  3527. goto retry;
  3528. }
  3529. if (!NT_SUCCESS(status))
  3530. {
  3531. return FALSE;
  3532. }
  3533. return pbBuffer;
  3534. }