Figure 1 PSAPIDEM.CPP
//=======================================
// PSAPIDEM - Matt Pietrek 1996
// Microsoft Systems Journal, August 1996
// FILE: PSAPIDEM.CPP
//=======================================
#include <windows.h>
#include <commctrl.h>
#pragma hdrstop
#include "psapi.h"
#include "psapidem.h"
// Helper function prototypes
void Handle_WM_COMMAND(HWND hDlg, WPARAM wParam, LPARAM lParam);
void Handle_WM_INITDIALOG(HWND hDlg);
void Handle_WM_CLOSE( HWND hDlg );
BOOL CALLBACK PSApiDemDlgProc( HWND, UINT, WPARAM, LPARAM );
HTREEITEM TVAppendString( HWND hWnd, HTREEITEM hTi, PSTR psz );
void GetSetPositionInfoFromRegistry( BOOL fSave, POINT *lppt );
// ======================= String literals ===============================
char gszRegistryKey[] = "Software\\WheatyProductions\\PSAPIDEM";
char g_AboutMsgTxt[] =
"PSApiDem shows processes and device driver information obtained "
"with PSAPI.DLL, from the Win32 SDK";
char g_AboutTitleTxt[] = "PSApiDem - Matt Pietrek 1996, for MSJ";
// ======================= Start of code ===============================
void AddProcessDetails( HWND hWnd, HTREEITEM hTreeItem, HANDLE hProcess )
{
//
// Fills in the details for one specific process
//
HMODULE hMods[1024];
DWORD cbNeeded;
//
// Get a list of all the HMODULEs in this process. Add a "Module"
// subnode, and add the full path for each HMODULE to it.
//
if ( EnumProcessModules(hProcess, hMods, sizeof(hMods), &cbNeeded) )
{
HTREEITEM hTiModules = TVAppendString( hWnd, hTreeItem, "Modules" );
for ( unsigned i = 0; i < (cbNeeded / sizeof(HMODULE)); i++ )
{
char szModName[MAX_PATH];
char szItem[MAX_PATH+64];
// Get the full path to the module's file
if ( GetModuleFileNameEx( hProcess, hMods[i], szModName,
sizeof(szModName)))
{
wsprintf( szItem, "%s (0x%08X)", szModName, hMods[i] );
TVAppendString( hWnd, hTiModules, szItem );
}
}
}
//
// Next, use PSAPI.DLL to collect information about the memory usage
// of the process. Add a "Memory" node to the process, and add
// subnodes for each field in the PROCESS_MEMORY_COUNTERS structure.
//
PROCESS_MEMORY_COUNTERS pmc;
if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) )
{
char szItem[128];
HTREEITEM hTiMem = TVAppendString( hWnd, hTreeItem, "Memory" );
wsprintf( szItem, "%s 0x%08X", "PageFaultCount", pmc.PageFaultCount );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "PeakWorkingSetSize",
pmc.PeakWorkingSetSize );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "WorkingSetSize", pmc.WorkingSetSize );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "QuotaPeakPagedPoolUsage",
pmc.QuotaPeakPagedPoolUsage );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "QuotaPagedPoolUsage",
pmc.QuotaPagedPoolUsage );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "QuotaPeakNonPagedPoolUsage",
pmc.QuotaPeakNonPagedPoolUsage );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "QuotaNonPagedPoolUsage",
pmc.QuotaNonPagedPoolUsage );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "PagefileUsage", pmc.PagefileUsage );
TVAppendString( hWnd, hTiMem, szItem );
wsprintf( szItem, "%s 0x%08X", "PeakPagefileUsage",
pmc.PeakPagefileUsage );
TVAppendString( hWnd, hTiMem, szItem );
}
//
// Finally, use the KERNEL32 GetProcessTimes function to see how much
// time the process has spent in user and kernel modes. Add this
// information (in milliseconds) to the process node
//
FILETIME ftCreate, ftExit, ftKernel, ftUser;
if ( GetProcessTimes( hProcess, &ftCreate, &ftExit, &ftKernel, &ftUser));
{
// Horrible, disgusting hack! The two lines below basically grab the
// contents of a FILETIME structure and store it in a 64 bit integer.
LONGLONG tUser64 = *(LONGLONG *)&ftUser;
LONGLONG tKernel64 = *(LONGLONG *)&ftKernel;
DWORD tUser, tKernel;
// The LONGLONGs contain the time in 100 nanosecond intervals (now
// there's a useful unit of measurement...). Divide each of them by
// 10000 to convert into milliseconds, and store the results in a
// DWORD. This means that the max time before overflowing is around
// 4 Million seconds (about 49 days)
tUser = (DWORD)(tUser64 / 10000);
tKernel = (DWORD)(tKernel64 / 10000);
// Format the user and kernel times, and add to the process node
char szItem[128];
wsprintf( szItem, "User mode: %u ms", tUser );
TVAppendString( hWnd, hTreeItem, szItem );
wsprintf( szItem, "Kernel mode: %u ms", tKernel );
TVAppendString( hWnd, hTreeItem, szItem );
}
}
void AddProcessToList( HWND hWnd, DWORD processID )
{
//
// Adds the process name and ID to the treeview control, then calls
// AddProcessDetails to fill in the details about the process
//
char szProcessName[MAX_PATH] = "unknown";
char szItemString[MAX_PATH+64];
HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
PROCESS_VM_READ,
FALSE, processID );
if ( hProcess )
{
HMODULE hMod;
DWORD cbNeeded;
if ( EnumProcessModules( hProcess, &hMod, sizeof(hMod), &cbNeeded) )
{
GetModuleBaseName( hProcess, hMod, szProcessName,
sizeof(szProcessName) );
}
}
wsprintf( szItemString, "%s (ID:%u)", szProcessName, processID );
HTREEITEM hTiProcess = TVAppendString( hWnd, TVI_ROOT, szItemString);
if ( hProcess && hTiProcess )
AddProcessDetails( hWnd, hTiProcess , hProcess );
CloseHandle( hProcess );
}
void UpdateProcessList( HWND hDlg )
{
//
// Clears the treeview, obtains a list of process IDs, and shows them
//
// Get the treeview's HWND, then clear it
HWND hWnd = GetDlgItem(hDlg,IDC_TREE_PROCESS);
TreeView_DeleteAllItems( hWnd );
// Get the list of process IDs
DWORD aProcesses[1024], cbNeeded;
if ( !EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
return;
// Calculate how many process IDs were returned
DWORD cProcesses = cbNeeded / sizeof(DWORD);
// Spit out the information for each ID
for ( unsigned i = 0; i < cProcesses; i++ )
AddProcessToList( hWnd, aProcesses[i] );
}
void UpdateDriverList( HWND hDlg )
{
//
// Clears the treeview, obtains a list of drivers, and shows them
//
// Get the treeview's HWND, then clear it
HWND hWnd = GetDlgItem( hDlg, IDC_TREE_DRIVERS );
TreeView_DeleteAllItems( hWnd );
// Get the list of device driver base addresses
PVOID aDrivers[1024];
DWORD cbNeeded;
if ( !EnumDeviceDrivers( aDrivers, sizeof(aDrivers), &cbNeeded ) )
return;
// Calculate how many drivers were returned
DWORD cDrivers = cbNeeded / sizeof(aDrivers[0]);
// Spit out the information for each driver
for ( unsigned i = 0; i < cDrivers; i++ )
{
char szBaseName[MAX_PATH]= "";
char szDriverFileName[MAX_PATH] = "";
// Get the driver's base name
if ( GetDeviceDriverBaseName( aDrivers[i], szBaseName,
sizeof(szBaseName) ) )
{
char szItem[MAX_PATH+64];
wsprintf( szItem, "%s (0x%08X)", szBaseName, aDrivers[i] );
HTREEITEM hTiDriver = TVAppendString( hWnd, TVI_ROOT, szItem );
// Get the full path to the driver
GetDeviceDriverFileName( aDrivers[i], szDriverFileName,
sizeof(szDriverFileName) );
TVAppendString( hWnd, hTiDriver, szDriverFileName );
}
}
}
void RefreshViews( HWND hDlg )
{
UpdateProcessList( hDlg ); // Should be self-explanatory!
UpdateDriverList( hDlg );
}
// ======================= Start of UI code ===============================
int PASCAL WinMain( HANDLE hInstance, HANDLE hPrevInstance,
PSTR lpszCmdLine, int nCmdShow )
{
// Gotta do this for those lovely tree view controls
InitCommonControls();
// Bring up the user interface (A dialog box? What a surprise!)
DialogBox( hInstance, MAKEINTRESOURCE(IDD_PSAPIDEM_DLG),
0, (DLGPROC)PSApiDemDlgProc );
return 0;
}
BOOL CALLBACK PSApiDemDlgProc(HWND hDlg,UINT msg,WPARAM wParam,LPARAM lParam)
{
//
// The dialog procedure for the main window
//
switch ( msg )
{
case WM_COMMAND:
Handle_WM_COMMAND( hDlg, wParam, lParam ); return TRUE;
case WM_INITDIALOG:
Handle_WM_INITDIALOG( hDlg ); return TRUE;
case WM_CLOSE:
Handle_WM_CLOSE( hDlg ); break;
// let everything else fall through
}
return FALSE;
}
void Handle_WM_COMMAND( HWND hDlg, WPARAM wParam, LPARAM lParam )
{
switch ( LOWORD(wParam) )
{
case IDC_BUTTON_REFRESH: RefreshViews( hDlg ); break;
case IDC_BUTTON_EXIT: Handle_WM_CLOSE( hDlg ); break;
case IDC_BUTTON_ABOUT:
MessageBox( hDlg, g_AboutMsgTxt, g_AboutTitleTxt, MB_OK );
break;
}
}
void Handle_WM_INITDIALOG(HWND hDlg)
{
// Get the window coordinates where the program was last running
// and move the window to that spot.
POINT pt;
GetSetPositionInfoFromRegistry( FALSE, &pt );
SetWindowPos(hDlg, 0, pt.x, pt.y, 0, 0,
SWP_NOSIZE | SWP_NOREDRAW | SWP_NOZORDER | SWP_NOACTIVATE);
RefreshViews( hDlg ); // Fill tree views with initial values
}
void Handle_WM_CLOSE( HWND hDlg )
{
// Save off the window's X,Y coordinates for next time
RECT rect;
GetWindowRect( hDlg, &rect );
GetSetPositionInfoFromRegistry( TRUE, (LPPOINT)&rect );
EndDialog(hDlg, 0);
}
HTREEITEM TVAppendString( HWND hWnd, HTREEITEM hTi, PSTR psz )
{
//
// A helper function that appends a string to the end of the list
// of subnodes under a specified node (hTi).
//
TV_INSERTSTRUCT tvis;
memset( &tvis, 0, sizeof(tvis) );
tvis.hParent = hTi;
tvis.hInsertAfter = TVI_LAST;
tvis.item.mask = TVIF_TEXT;
tvis.item.pszText = psz;
return TreeView_InsertItem( hWnd, &tvis );
}
void GetSetPositionInfoFromRegistry( BOOL fSave, POINT *lppt )
{
//
// Function that saves or restores the coordinates of a dialog box
// in the system registry. Handles the case where there's nothing there.
//
HKEY hKey;
DWORD dataSize, err, disposition;
char szKeyName[] = "DlgCoordinates";
if ( !fSave ) // In case the key's not there yet, we'll
lppt->x = lppt->y = 0; // return 0,0 for the coordinates
// Open the registry key (or create it if the first time being used)
err = RegCreateKeyEx( HKEY_CURRENT_USER, gszRegistryKey, 0, 0,
REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
0, &hKey, &disposition );
if ( ERROR_SUCCESS != err )
return;
if ( fSave ) // Save out coordinates
{
RegSetValueEx( hKey, szKeyName, 0, REG_BINARY,
(PBYTE)lppt, sizeof(*lppt) );
}
else // read in coordinates
{
dataSize = sizeof(*lppt);
RegQueryValueEx( hKey, szKeyName, 0, 0, (PBYTE)lppt, &dataSize );
}
}