|
|
/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
MEMMON.c
Abstract:
This module contains the NT/Win32 Pool Monitor
Author:
Lou Perazzoli (loup) 13-Sep-1993
Revision History:
--*/
#include "perfmtrp.h"
#include <search.h>
#include <malloc.h>
#include <limits.h>
#include <stdlib.h>
#define BUFFER_SIZE 64*1024
#define MAX_BUFFER_SIZE 10*1024*1024
PCHAR buffer; ULONG CurrentBufferSize = BUFFER_SIZE;
#define CPU_USAGE 0
#define QUOTAS 1
#define TAG 0
#define ALLOC 1
#define FREE 2
#define DIFF 3
#define BYTES 4
#define EACH 5
#define LIGHT 6
#define NONPAGED 0
#define PAGED 1
#define BOTH 2
CHAR *PoolType[] = { "Nonp ", "Paged" };
CHAR LargeBuffer1[BUFFER_SIZE]; CHAR LargeBuffer2[BUFFER_SIZE];
#define NONAME_STRING_SIZE 14
CHAR NoName[] = {"No Name Found\0"}; ULONG TotalNoNameFound;
#define NOFILE_STRING_SIZE 13
CHAR NoFileName[] = {"No File Name\0"}; ULONG TotalNoFileFound;
#define META_FILE_STRING_SIZE 13
CHAR MetaFile[] = {"Fs Meta File\0"}; ULONG TotalFsMetaFile;
#define OUT_STRING_SIZE 60
typedef struct _MEMMON_OUT { ULONG Valid; ULONG Standby; ULONG Modified; ULONG PageTable; CHAR String[OUT_STRING_SIZE]; WCHAR Null; } MEMMON_OUT, *PMEMMON_OUT;
MEMMON_OUT OutBuffer[2000];
ULONG DisplayType = BOTH; ULONG SortBy = TAG; ULONG Paren;
ULONG DelayTimeMsec = 5000;
BOOLEAN Interactive; ULONG NumberOfInputRecords; INPUT_RECORD InputRecord; HANDLE InputHandle; HANDLE OriginalOutputHandle; HANDLE OutputHandle; DWORD OriginalInputMode; WORD NormalAttribute; WORD HighlightAttribute; ULONG NumberOfCols; ULONG NumberOfRows; ULONG NumberOfDetailLines; ULONG FirstDetailLine; CONSOLE_SCREEN_BUFFER_INFO OriginalConsoleInfo; ULONG NoHighlight;
BOOLEAN DisplayTotals = FALSE; MEMMON_OUT Totals[2];
typedef struct _FILTER { union { UCHAR Tag[4]; ULONG TagUlong; }; BOOLEAN Exclude; } FILTER, *PFILTER;
#define MAX_FILTER 64
FILTER Filter[MAX_FILTER]; ULONG FilterCount = 0;
VOID ShowHelpPopup( VOID );
int __cdecl ulcomp(const void *e1,const void *e2);
int __cdecl ulcomp(const void *e1,const void *e2) { ULONG u1;
switch (SortBy) { case TAG:
u1 = (strcmp (((PMEMMON_OUT)e1)->String, ((PMEMMON_OUT)e2)->String)); return u1; break;
case ALLOC: u1 = ((PMEMMON_OUT)e2)->Valid - ((PMEMMON_OUT)e1)->Valid; return (u1); break;
case FREE: u1 = ((PMEMMON_OUT)e2)->Standby - ((PMEMMON_OUT)e1)->Standby; return (u1); break;
case BYTES: u1 = ((PMEMMON_OUT)e2)->Modified - ((PMEMMON_OUT)e1)->Modified; return (u1); break;
case DIFF: u1 = ((PMEMMON_OUT)e2)->PageTable - ((PMEMMON_OUT)e1)->PageTable; return (u1); break;
case EACH: return (0); break;
default: return(0); break; } }
BOOLEAN CheckSingleFilter ( PCHAR Tag, PCHAR Filter ) { ULONG i; CHAR tc; CHAR fc;
for ( i = 0; i < 4; i++ ) { tc = *Tag++; fc = *Filter++; if ( fc == '*' ) return TRUE; if ( fc == '?' ) continue; if ( tc != fc ) return FALSE; } return TRUE; }
BOOLEAN CheckFilters ( PSYSTEM_POOLTAG TagInfo ) { BOOLEAN pass; ULONG i; PCHAR tag;
//
// If there are no filters, all tags pass.
//
if ( FilterCount == 0 ) { return TRUE; }
//
// There are filters. If the first filter excludes tags, then any
// tag not explicitly mentioned passes. If the first filter includes
// tags, then any tag not explicitly mentioned fails.
//
if ( Filter[0].Exclude ) { pass = TRUE; } else { pass = FALSE; }
tag = TagInfo->Tag; for ( i = 0; i < FilterCount; i++ ) { if ( CheckSingleFilter( tag, (PCHAR)&Filter[i].Tag ) ) { pass = !Filter[i].Exclude; } }
return pass; }
VOID AddFilter ( BOOLEAN Exclude, PCHAR FilterString ) { PFILTER f; PCHAR p; ULONG i;
if ( FilterCount == MAX_FILTER ) { printf( "Too many filters specified. Limit is %d\n", MAX_FILTER ); return; }
f = &Filter[FilterCount]; p = f->Tag;
for ( i = 0; i < 4; i++ ) { if ( *FilterString == 0 ) break; *p++ = *FilterString++; } for ( ; i < 4; i++ ) { *p++ = ' '; }
f->Exclude = Exclude; FilterCount++;
return; }
VOID ParseArgs ( int argc, char *argv[] ) { char *p; BOOLEAN exclude;
argc--; argv++;
while ( argc-- > 0 ) { p = *argv++; if ( *p == '-' || *p == '/' ) { p++; exclude = TRUE; switch ( tolower(*p) ) { case 'i': exclude = FALSE; case 'x': p++; if ( strlen(p) == 0 ) { printf( "missing filter string\n" ); ExitProcess( 1 ); } else if ( strlen(p) <= sizeof(ULONG) ) { AddFilter( exclude, p ); } else { printf( "filter string too long: %s\n", p ); ExitProcess( 1 ); } break; case 'e': DisplayTotals = TRUE; break; case 't': SortBy = TAG; break; case 'a': SortBy = ALLOC; break; case 'u': case 'b': SortBy = BYTES; break; case 'f': SortBy = FREE; break; case 'd': SortBy = DIFF; break; case 'm': SortBy = EACH;
case 'l': NoHighlight = 1 - NoHighlight; break;
case 'p': DisplayType += 1; if (DisplayType > BOTH) { DisplayType = NONPAGED; } break; case '(': case ')': Paren += 1; break; default: printf( "unknown switch: %s\n", p ); ExitProcess( 2 ); } } else { printf( "unknown switch: %s\n", p ); ExitProcess( 2 ); } }
return; }
BOOL WriteConsoleLine( HANDLE OutputHandle, WORD LineNumber, LPSTR Text, BOOL Highlight ) { COORD WriteCoord; DWORD NumberWritten; DWORD TextLength;
WriteCoord.X = 0; WriteCoord.Y = LineNumber; if (!FillConsoleOutputCharacter( OutputHandle, ' ', NumberOfCols, WriteCoord, &NumberWritten ) ) { return FALSE; }
if (!FillConsoleOutputAttribute( OutputHandle, (WORD)((Highlight && !NoHighlight) ? HighlightAttribute : NormalAttribute), NumberOfCols, WriteCoord, &NumberWritten ) ) { return FALSE; }
if (Text == NULL || (TextLength = strlen( Text )) == 0) { return TRUE; } else { return WriteConsoleOutputCharacter( OutputHandle, Text, TextLength, WriteCoord, &NumberWritten ); } }
int __cdecl main( argc, argv ) int argc; char *argv[]; {
NTSTATUS Status; ULONG LastCount = 0; SYSTEM_BASIC_INFORMATION BasicInfo; SYSTEM_PERFORMANCE_INFORMATION PerfInfo; PSYSTEM_POOLTAG_INFORMATION PoolInfo; PSYSTEM_POOLTAG_INFORMATION PoolInfoOld; PUCHAR PreviousBuffer; PUCHAR CurrentBuffer; PUCHAR TempBuffer; BOOLEAN DoHelp; BOOLEAN DoQuit; int NumberOfPoolTags; int i; UCHAR LastKey; PMEMMON_OUT Out; LONG ScrollDelta; WORD DisplayLine, LastDetailRow; CHAR OutputBuffer[ 512 ];
NTSTATUS status; PSYSTEM_MEMORY_INFORMATION MemInfo; PSYSTEM_MEMORY_INFO Info; PSYSTEM_MEMORY_INFO InfoEnd; PUCHAR String; ULONG TotalValid; ULONG TotalPageTable; ULONG TotalModified; ULONG TotalStandby; SYSTEMTIME Time; ULONG PageKb;
DoHelp = FALSE; DoQuit = FALSE; Interactive = TRUE;
buffer = VirtualAlloc (NULL, MAX_BUFFER_SIZE, MEM_RESERVE, PAGE_READWRITE);
if (buffer == NULL) { printf("Memory allocation failed\n"); return 0; }
buffer = VirtualAlloc (buffer, BUFFER_SIZE, MEM_COMMIT, PAGE_READWRITE);
if (buffer == NULL) { printf("Memory commit failed\n"); return 0; }
CurrentBufferSize = BUFFER_SIZE; ParseArgs( argc, argv );
InputHandle = GetStdHandle( STD_INPUT_HANDLE ); OriginalOutputHandle = GetStdHandle( STD_OUTPUT_HANDLE ); if (Interactive) { if (InputHandle == NULL || OriginalOutputHandle == NULL || !GetConsoleMode( InputHandle, &OriginalInputMode ) ) { Interactive = FALSE; } else { OutputHandle = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); if (OutputHandle == NULL || !GetConsoleScreenBufferInfo( OriginalOutputHandle, &OriginalConsoleInfo ) || !SetConsoleScreenBufferSize( OutputHandle, OriginalConsoleInfo.dwSize ) || !SetConsoleActiveScreenBuffer( OutputHandle ) || !SetConsoleMode( InputHandle, 0 ) ) { if (OutputHandle != NULL) { CloseHandle( OutputHandle ); OutputHandle = NULL; }
Interactive = FALSE; } else { NormalAttribute = 0x1F; HighlightAttribute = 0x71; NumberOfCols = OriginalConsoleInfo.dwSize.X; NumberOfRows = OriginalConsoleInfo.dwSize.Y; NumberOfDetailLines = NumberOfRows; } } }
NtQuerySystemInformation( SystemBasicInformation, &BasicInfo, sizeof(BasicInfo), NULL );
if (GetPriorityClass(GetCurrentProcess()) == NORMAL_PRIORITY_CLASS) { SetPriorityClass(GetCurrentProcess(),HIGH_PRIORITY_CLASS); }
PageKb = BasicInfo.PageSize / 1024;
PreviousBuffer = NULL; CurrentBuffer = LargeBuffer1;
while(TRUE) {
Status = NtQuerySystemInformation( SystemPerformanceInformation, &PerfInfo, sizeof(PerfInfo), NULL );
if ( !NT_SUCCESS(Status) ) { printf("Query perf Failed %lx\n",Status); break; }
retry01: status = NtQuerySystemInformation (SystemFullMemoryInformation, buffer, CurrentBufferSize, NULL);
if ((status == STATUS_INFO_LENGTH_MISMATCH) || (status == STATUS_DATA_OVERRUN)) {
//
// Increase buffer size.
//
CurrentBufferSize += 8192;
buffer = VirtualAlloc (buffer, CurrentBufferSize, MEM_COMMIT, PAGE_READWRITE); if (buffer == NULL) { printf("Memory commit failed\n"); ExitProcess(0); } goto retry01; } if (!NT_SUCCESS (status)) { printf("query system information failed %lx\n",status); return 1; }
TotalValid = 0; TotalPageTable = 0; TotalStandby = 0; TotalModified = 0; MemInfo = (PSYSTEM_MEMORY_INFORMATION)buffer; Info = &MemInfo->Memory[0]; InfoEnd = (PSYSTEM_MEMORY_INFO)MemInfo->StringStart;
//
// Calculate pool tags and display information.
//
PoolInfo = (PSYSTEM_POOLTAG_INFORMATION)CurrentBuffer; i = PoolInfo->Count; PoolInfoOld = (PSYSTEM_POOLTAG_INFORMATION)PreviousBuffer;
DisplayLine = 0; sprintf( OutputBuffer, " Memory:%8ldK Avail:%8ldK PageFlts:%6ld InRam Krnl:%5ldK P:%5ldK", BasicInfo.NumberOfPhysicalPages*(BasicInfo.PageSize/1024), PerfInfo.AvailablePages*(BasicInfo.PageSize/1024), PerfInfo.PageFaultCount - LastCount, (PerfInfo.ResidentSystemCodePage + PerfInfo.ResidentSystemDriverPage)*(BasicInfo.PageSize/1024), (PerfInfo.ResidentPagedPoolPage)*(BasicInfo.PageSize/1024) ); WriteConsoleLine( OutputHandle, DisplayLine++, OutputBuffer, FALSE );
LastCount = PerfInfo.PageFaultCount; sprintf( OutputBuffer, " Commit:%7ldK Limit:%7ldK Peak:%7ldK Pool N:%5ldK P:%5ldK", PerfInfo.CommittedPages*(BasicInfo.PageSize/1024), PerfInfo.CommitLimit*(BasicInfo.PageSize/1024), PerfInfo.PeakCommitment*(BasicInfo.PageSize/1024), PerfInfo.NonPagedPoolPages*(BasicInfo.PageSize/1024), PerfInfo.PagedPoolPages*(BasicInfo.PageSize/1024) ); WriteConsoleLine( OutputHandle, DisplayLine++, OutputBuffer, FALSE );
WriteConsoleLine( OutputHandle, DisplayLine++, " Valid Transition Modified PageTables Name ", FALSE ); WriteConsoleLine( OutputHandle, DisplayLine++, NULL, FALSE );
Out = &OutBuffer[3]; if (DisplayTotals) { RtlZeroMemory( Totals, sizeof(MEMMON_OUT)*2 ); }
TotalNoNameFound = 0; TotalFsMetaFile = 0; Out[0].Valid = 0; Out[0].PageTable = 0; Out[0].Standby = 0; Out[0].Modified = 0; RtlCopyMemory (Out[0].String, NoName, NONAME_STRING_SIZE); Out[1].Valid = 0; Out[1].PageTable = 0; Out[1].Standby = 0; Out[1].Modified = 0; RtlCopyMemory (Out[1].String, MetaFile, META_FILE_STRING_SIZE); Out[2].Valid = 0; Out[2].PageTable = 0; Out[2].Standby = 0; Out[2].Modified = 0; RtlCopyMemory (Out[2].String, NoFileName, NOFILE_STRING_SIZE);
while (Info < InfoEnd) {
// if ( !CheckFilters(&PoolInfo->TagInfo[i]) ) {
// continue;
// }
Out->Valid = Info->ValidCount*PageKb * PageKb; Out->Modified = Info->PageTableCount*PageKb; Out->Standby = Info->TransitionCount*PageKb; Out->PageTable = Info->ModifiedCount*PageKb;
TotalValid += Info->ValidCount; TotalPageTable += Info->PageTableCount; TotalStandby += Info->TransitionCount; TotalModified += Info->ModifiedCount;
RtlZeroMemory (Out->String, OUT_STRING_SIZE); if (Info->StringOffset != 0) { if (*(PUCHAR)(Info->StringOffset + 1) == 0) { WideCharToMultiByte (CP_ACP, 0, (LPCWSTR)Info->StringOffset, -1, (LPSTR)Out->String, OUT_STRING_SIZE, NULL, NULL); } else {
if (!strncmp (Info->StringOffset, MetaFile, META_FILE_STRING_SIZE)) { TotalNoNameFound += 1; Out[1].Valid += Info->ValidCount*PageKb * PageKb; Out[1].PageTable += Info->PageTableCount*PageKb; Out[1].Standby += Info->TransitionCount*PageKb; Out[1].Modified += Info->ModifiedCount*PageKb; Out -= 1; } else if (!strncmp (Info->StringOffset, NoFileName, NOFILE_STRING_SIZE)) { TotalNoNameFound += 1; Out[2].Valid += Info->ValidCount*PageKb * PageKb; Out[2].PageTable += Info->PageTableCount*PageKb; Out[2].Standby += Info->TransitionCount*PageKb; Out[2].Modified += Info->ModifiedCount*PageKb; Out -= 1; } else { RtlCopyMemory (Out->String, Info->StringOffset, OUT_STRING_SIZE); } } } else { TotalNoNameFound += 1; Out[0].Valid += Info->ValidCount*PageKb * PageKb; Out[0].PageTable += Info->PageTableCount*PageKb; Out[0].Standby += Info->TransitionCount*PageKb; Out[0].Modified += Info->ModifiedCount*PageKb; Out -= 1; } Out += 1; Info += 1; i++; } //end for
//
// Sort the running working set buffer
//
NumberOfPoolTags = Out - &OutBuffer[0]; qsort((void *)&OutBuffer, (size_t)NumberOfPoolTags, (size_t)sizeof(MEMMON_OUT), ulcomp);
LastDetailRow = (WORD)(NumberOfRows - (DisplayTotals ? (DisplayType == BOTH ? 3 : 2) : 0)); for (i = FirstDetailLine; i < NumberOfPoolTags; i++) { if (DisplayLine >= LastDetailRow) { break; }
sprintf( OutputBuffer, " %8ld %8ld %8ld %8ld %s", OutBuffer[i].Valid, OutBuffer[i].Standby, OutBuffer[i].Modified, OutBuffer[i].PageTable, OutBuffer[i].String ); WriteConsoleLine( OutputHandle, DisplayLine++, OutputBuffer, FALSE ); }
if (DisplayTotals) { WriteConsoleLine( OutputHandle, DisplayLine++, NULL, FALSE ); for (i = 0; i < 2; i++) { if ( (int)DisplayType == i || DisplayType == BOTH ) { sprintf( OutputBuffer, "Total %9ld %9ld %8ld %7ld", TotalValid, TotalStandby, TotalModified, TotalPageTable ); WriteConsoleLine( OutputHandle, DisplayLine++, OutputBuffer, FALSE ); } } }
if (PreviousBuffer == NULL) { PreviousBuffer = LargeBuffer2; } TempBuffer = PreviousBuffer; PreviousBuffer = CurrentBuffer; CurrentBuffer = TempBuffer;
while (WaitForSingleObject( InputHandle, DelayTimeMsec ) == STATUS_WAIT_0) { //
// Check for input record
//
if (ReadConsoleInput( InputHandle, &InputRecord, 1, &NumberOfInputRecords ) && InputRecord.EventType == KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown ) { LastKey = InputRecord.Event.KeyEvent.uChar.AsciiChar; if (LastKey < ' ') { ScrollDelta = 0; if (LastKey == 'C'-'A'+1) { DoQuit = TRUE; } else switch (InputRecord.Event.KeyEvent.wVirtualKeyCode) { case VK_ESCAPE: DoQuit = TRUE; break;
case VK_PRIOR: ScrollDelta = -(LONG)(InputRecord.Event.KeyEvent.wRepeatCount * NumberOfDetailLines); break;
case VK_NEXT: ScrollDelta = InputRecord.Event.KeyEvent.wRepeatCount * NumberOfDetailLines; break;
case VK_UP: ScrollDelta = -InputRecord.Event.KeyEvent.wRepeatCount; break;
case VK_DOWN: ScrollDelta = InputRecord.Event.KeyEvent.wRepeatCount; break;
case VK_HOME: FirstDetailLine = 0; break;
case VK_END: FirstDetailLine = NumberOfPoolTags - NumberOfDetailLines; break; }
if (ScrollDelta != 0) { if (ScrollDelta < 0) { if (FirstDetailLine <= (ULONG)-ScrollDelta) { FirstDetailLine = 0; } else { FirstDetailLine += ScrollDelta; } } else { FirstDetailLine += ScrollDelta; if (FirstDetailLine >= (NumberOfPoolTags - NumberOfDetailLines)) { FirstDetailLine = NumberOfPoolTags - NumberOfDetailLines; } } } } else { switch (toupper( LastKey )) { case 'Q': //
// Go to the bottom of the current screen when
// we quit.
//
DoQuit = TRUE; break;
case 'T': SortBy = TAG; FirstDetailLine = 0; break;
case 'A': SortBy = ALLOC; FirstDetailLine = 0; break;
case 'U': case 'B': SortBy = BYTES; FirstDetailLine = 0; break;
case 'F': SortBy = FREE; FirstDetailLine = 0; break;
case 'D': SortBy = DIFF; FirstDetailLine = 0; break;
case 'M': SortBy = EACH; FirstDetailLine = 0; break;
case 'L':
NoHighlight = 1 - NoHighlight; break;
case 'P': DisplayType += 1; if (DisplayType > BOTH) { DisplayType = NONPAGED; } FirstDetailLine = 0; break;
case 'X': case '(': case ')':
Paren += 1; break;
case 'E': DisplayTotals = !DisplayTotals; FirstDetailLine = 0; break;
case 'H': case '?': DoHelp = TRUE; break;
} }
break; } }
if (DoQuit) { break; }
if (DoHelp) { DoHelp = FALSE; ShowHelpPopup(); } }
if (Interactive) { SetConsoleActiveScreenBuffer( OriginalOutputHandle ); SetConsoleMode( InputHandle, OriginalInputMode ); CloseHandle( OutputHandle ); }
ExitProcess( 0 ); return 0; }
VOID ShowHelpPopup( VOID ) { HANDLE PopupHandle; WORD n;
PopupHandle = CreateConsoleScreenBuffer( GENERIC_READ | GENERIC_WRITE, FILE_SHARE_WRITE | FILE_SHARE_READ, NULL, CONSOLE_TEXTMODE_BUFFER, NULL ); if (PopupHandle == NULL) { return; }
SetConsoleActiveScreenBuffer( PopupHandle );
n = 0;
WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, " Poolmon Help", FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, " columns:", FALSE ); WriteConsoleLine( PopupHandle, n++, " Tag is the 4 byte tag given to the pool allocation", FALSE ); WriteConsoleLine( PopupHandle, n++, " Type is paged or nonp(aged)", FALSE ); WriteConsoleLine( PopupHandle, n++, " Allocs is count of all alloctions", FALSE ); WriteConsoleLine( PopupHandle, n++, " ( ) is difference in Allocs column from last update", FALSE ); WriteConsoleLine( PopupHandle, n++, " Frees is count of all frees", FALSE ); WriteConsoleLine( PopupHandle, n++, " ( ) difference in Frees column from last update", FALSE ); WriteConsoleLine( PopupHandle, n++, " Diff is (Allocs - Frees)", FALSE ); WriteConsoleLine( PopupHandle, n++, " Bytes is the total bytes consumed in pool", FALSE ); WriteConsoleLine( PopupHandle, n++, " ( ) difference in Bytes column from last update", FALSE ); WriteConsoleLine( PopupHandle, n++, " Per Alloc is (Bytes / Diff)", FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, " switches: ", FALSE ); WriteConsoleLine( PopupHandle, n++, " ? or h - gives this help", FALSE ); WriteConsoleLine( PopupHandle, n++, " q - quits", FALSE ); WriteConsoleLine( PopupHandle, n++, " p - toggles default pool display between both, paged, and nonpaged", FALSE ); WriteConsoleLine( PopupHandle, n++, " e - toggles totals lines on and off", FALSE ); WriteConsoleLine( PopupHandle, n++, " l - toggles highlighting of changed lines on and off", FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, " sorting switches:", FALSE ); WriteConsoleLine( PopupHandle, n++, " t - tag a - allocations", FALSE ); WriteConsoleLine( PopupHandle, n++, " f - frees d - difference", FALSE ); WriteConsoleLine( PopupHandle, n++, " b - bytes m - per alloc", FALSE ); WriteConsoleLine( PopupHandle, n++, " (u is the same as b)", FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, " ) - toggles sort between primary tag and value in ( )", FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, " command line switches", FALSE ); WriteConsoleLine( PopupHandle, n++, " -i<tag> - list only matching tags", FALSE ); WriteConsoleLine( PopupHandle, n++, " -x<tag> - list everything except matching tags", FALSE ); WriteConsoleLine( PopupHandle, n++, " <tag> can include * and ?", FALSE ); WriteConsoleLine( PopupHandle, n++, " -peltafdbum) - as listed above", FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE ); WriteConsoleLine( PopupHandle, n++, NULL, FALSE );
while (TRUE) { if (WaitForSingleObject( InputHandle, DelayTimeMsec ) == STATUS_WAIT_0 && ReadConsoleInput( InputHandle, &InputRecord, 1, &NumberOfInputRecords ) && InputRecord.EventType == KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown && InputRecord.Event.KeyEvent.wVirtualKeyCode == VK_ESCAPE ) { break; } }
SetConsoleActiveScreenBuffer( OutputHandle ); CloseHandle( PopupHandle ); return; }
|