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