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 );
    }
}