Figure 1   PSAPIWorkingSetDemo

PSAPIWorkingSetDemo.CPP

 //=======================================
// PSAPI Working Set Demo - Matt Pietrek 1996
// Microsoft Systems Journal, November 1996
// FILE: "PSAPIWorkingSetDemo.CPP"
//=======================================
//....
void AddWorkingSetInfo( HWND hDlg, DWORD processID )
{
    HANDLE hProcess = OpenProcess(  PROCESS_QUERY_INFORMATION |
                                    PROCESS_VM_READ,
                                    FALSE, processID );

    if ( !hProcess )
        return;

    HWND hWndLb = GetDlgItem( hDlg, IDC_LB_WORKING_SET_DETAILS );

    __try
    {
        SendMessage( hWndLb, WM_SETREDRAW, FALSE, 0 );  // Disable LB updating

        char szBuffer[MAX_PATH * 4];

        if ( !QueryWorkingSet(  hProcess, g_WorkingSetPages,
                                sizeof(g_WorkingSetPages) ) )
            __leave;

        DWORD cPages = g_WorkingSetPages[0];    // First DWORD is page count
        DWORD cPrivatePages = 0;
        DWORD cSharedPages = 0;
        DWORD cPageTablePages = 0;
                
        qsort( &g_WorkingSetPages[1], cPages, sizeof(DWORD), CompareDWORDs );

        for ( DWORD i = 1; i <= cPages; i++ )
        {
            DWORD pageAddr = g_WorkingSetPages[i] & g_AbovePageMask;
            DWORD pageFlags = g_WorkingSetPages[i] & g_SubPageMask;
            DWORD thisPageAddr, nextPageAddr, nextPageFlags;
            DWORD cPagesThisRange = 0;

            // Loop through all subseqent pages that are contiguous in
            // memory and have the same attributes
            while ( i <= cPages )
            {
                cPagesThisRange++;

                if ( i == cPages )  // Break out if it's the last page
                    break;

                thisPageAddr = g_WorkingSetPages[i] & g_AbovePageMask;
                nextPageAddr = g_WorkingSetPages[i+1] & g_AbovePageMask;
                nextPageFlags = g_WorkingSetPages[i+1] & g_SubPageMask;

                if ( (nextPageAddr == (thisPageAddr + g_PageSize)) &&
                     (nextPageFlags == pageFlags) )
                {
                     i++;       // Keep going
                }
                else            // Noncontiguous page or different flags
                    break;
            }

            // Is it a page from outside of the "page table" region???
            if ( (pageAddr < 0xC0000000) || (pageAddr > 0xE0000000) )
            {
                if ( pageFlags & 0x100 )            // 0x100 == shared
                    cSharedPages+= cPagesThisRange;
                else                                // 0x000 == private
                    cPrivatePages+= cPagesThisRange;
            }
            else    // It's a page from the page table region
                cPageTablePages += cPagesThisRange;

            PSTR pszBuffCurr = szBuffer;    // ptr to working output string

            pszBuffCurr += wsprintf(pszBuffCurr, "%08X  %4uK",
                                    pageAddr, cPagesThisRange * 4 );

            // 0x100 == not private (i.e., shared)
            pszBuffCurr += wsprintf( pszBuffCurr,
                                    pageFlags & 0x100 ? "  S":  "  P" );

            // 0x01 == ReadOnly
            // 0x04 == Writeable
            // 0x05 == Copy On Write (special case)
            if ( 0x5 == (pageFlags & 0x5) )
                pszBuffCurr += wsprintf( pszBuffCurr, " CW" );
            else if ( pageFlags & 0x1 )
                pszBuffCurr += wsprintf( pszBuffCurr, " RO" );
            else if ( pageFlags & 0x4 )
                pszBuffCurr += wsprintf( pszBuffCurr, " RW" );
            else
                pszBuffCurr += wsprintf( pszBuffCurr, " ??" );

            // 0x02 == Executable
            pszBuffCurr += wsprintf( pszBuffCurr,
                                     pageFlags & 0x2 ? " E  " : "    " );

            char    szMod[MAX_PATH];
            char    szSection[IMAGE_SIZEOF_SHORT_NAME+1];
            DWORD   uSection;
            BOOL    fFoundInfo;

            // Get information (such as the module and section name) for the
            // page that was faulted in
            fFoundInfo = GetModuleNameAndSectionInfo(
                            hProcess,
                            (PVOID)pageAddr,
                            szMod, sizeof(szMod),
                            szSection, sizeof(szSection),
                            &uSection );

            if ( fFoundInfo )   // We found Win32 module info!
            {
                if ( uSection ) // We even know which section!
                {
                    pszBuffCurr += wsprintf(pszBuffCurr,"%s!%s(%u)",
                                            szMod,szSection,uSection);
                }
                else            // We have a module, but no section
                {
                    pszBuffCurr += wsprintf( pszBuffCurr, "%s", szMod );
                }
            }
            else
            {
                // Perhaps it's a memory-mapped file!
                char szOwner[MAX_PATH];
                if ( GetMappedFileNameA( hProcess, (PVOID)pageAddr,
                                        szOwner, sizeof(szOwner) )  )
                    pszBuffCurr += wsprintf( pszBuffCurr, "%s", szOwner );
            }

            // Add the output string to the listbox (finally!)
            SendMessage( hWndLb, LB_ADDSTRING, 0, (LPARAM) szBuffer );
        }

        //
        // Fix this!  page size should be determined dynamically!
        //
        wsprintf( szBuffer, "Total: %uK", cPages * 4 );
        SetDlgItemText( hDlg, IDC_TOTAL_MEMORY, szBuffer );
        wsprintf( szBuffer, "Private: %uK", cPrivatePages * 4 );
        SetDlgItemText( hDlg, IDC_PRIVATE_MEMORY, szBuffer );
        wsprintf( szBuffer, "Shared: %uK", cSharedPages * 4 );
        SetDlgItemText( hDlg, IDC_SHARED_MEMORY, szBuffer );
        wsprintf( szBuffer, "Page Tables: %uK", cPageTablePages * 4 );
        SetDlgItemText( hDlg, IDC_PAGE_TABLES, szBuffer );
    }
    __finally
    {
        SendMessage( hWndLb, WM_SETREDRAW, TRUE, 0 );   // Enable LB updating
        CloseHandle( hProcess );
    }
}

PSAPIWorkingSetDelta.CPP

 //=======================================
// PSAPI Working Set Demo - Matt Pietrek 1996
// Microsoft Systems Journal, November 1996
// FILE: "PSAPIWorkingSetDelta.CPP"
//=======================================
// ....
static PSAPI_WS_WATCH_INFORMATION g_WorkingSetChanges[MAX_WS_DELTA_PAGES];

void FillDeltaListbox( HWND hWndLb )
{
    // Create a pointer to the g_WorkingSetChanges array
    PPSAPI_WS_WATCH_INFORMATION pWSDelta
                        = (PPSAPI_WS_WATCH_INFORMATION) g_WorkingSetChanges;

    SendMessage( hWndLb, WM_SETREDRAW, FALSE, 0 );  // Disable LB updating

    // Loop through each entry in the array until we find an element that
    // has both the FaultingPC and FaultingVA set to 0.  This indicates the
    // end of the array.

    while ( pWSDelta->FaultingPc || pWSDelta->FaultingVa )
    {
        // Skip over entries above 2GB.  They can't (easily) be mapped to
        // module names, and the user can't do any about them anyway
        if ( pWSDelta->FaultingPc >= (PVOID)0x80000000 )
        {
            pWSDelta++;
            continue;
        }
            
        //
        // Warning!!! If a long period transpires between the start and end of
        // the WorkingSetDelta, or if memory is extremely low, it's possible
        // to have multiple entries here with the same address.
        //
        char szOutBuffer[ MAX_PATH * 4 ];
        PSTR pszBuffCurr = szOutBuffer;

        pszBuffCurr += wsprintf( pszBuffCurr, "%08X  %08X  ",
                                 pWSDelta->FaultingVa, pWSDelta->FaultingPc );

        char    szMod[MAX_PATH];
        char    szSection[IMAGE_SIZEOF_SHORT_NAME+1];
        DWORD   uSection;
        BOOL    fFoundInfo;

        // Get information (such as the module and section name) for the
        // page that was faulted in
        fFoundInfo = 
            GetModuleNameAndSectionInfo(
                                        g_WorkingSetHProcess,
                                        pWSDelta->FaultingVa,
                                        szMod, sizeof(szMod),
                                        szSection, sizeof(szSection),
                                        &uSection );

        if ( fFoundInfo )   // The address is within a loaded Win32 module
        {
            if ( uSection )
                pszBuffCurr += wsprintf( pszBuffCurr, "%s!%s(%u)",
                                         szMod, szSection, uSection);
            else
                pszBuffCurr += wsprintf( pszBuffCurr, "%s", szMod);
        }
        else    // Not an address in a Win32 module
        {
            // Could be a stack page, a heap page, or whatever!
            pszBuffCurr += wsprintf( pszBuffCurr, "???" );
        }

        // Get information (such as the module and section name) for the
        // page that caused the fault
        fFoundInfo = 
            GetModuleNameAndSectionInfo(
                                        g_WorkingSetHProcess,
                                        pWSDelta->FaultingPc,
                                        szMod, sizeof(szMod),
                                        szSection, sizeof(szSection),
                                        &uSection );

        if ( fFoundInfo )   // The address is within a loaded Win32 module
        {
            if ( uSection )
                pszBuffCurr += wsprintf( pszBuffCurr, " via %s!%s(%u)",
                                         szMod, szSection, uSection);
        }

        // Dump everything out the listbox (finally!)
        SendMessage( hWndLb, LB_ADDSTRING, 0, (LPARAM)szOutBuffer );

        pWSDelta++;     // Advance to next page which was faulted in
    }

    SendMessage( hWndLb, WM_SETREDRAW, TRUE, 0 );   // Enable LB updating
}

//...

BOOL StartWorkingSetDelta( DWORD pid )
{
    // Open a process handle, and store it in (Eeek!) a Global variable
    g_WorkingSetHProcess = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid );

    if ( !g_WorkingSetHProcess )
        return FALSE;

    if ( !InitializeProcessForWsWatch( g_WorkingSetHProcess ) )
    {
        CloseHandle( g_WorkingSetHProcess );
        g_WorkingSetHProcess = 0;
        return FALSE;
    }

    // Warning!  This function can "fail" (return FALSE) if there aren't
    // any new WS pages to report.  It can also fail if you pass it in
    // a bogus HPROCESS.  Unfortunately, there's no way to tell the two
    // situations apart.
    GetWsChanges( g_WorkingSetHProcess, g_WorkingSetChanges,
                        sizeof(g_WorkingSetChanges) );

    return TRUE;
}

void EndWorkingSetDelta( HWND hWndOwner )
{
    if ( !g_WorkingSetHProcess )
        return;

    if ( GetWsChanges(  g_WorkingSetHProcess, g_WorkingSetChanges,
                        sizeof(g_WorkingSetChanges) ) )
    {
        // We got 1 or more WS changes.  Report it
        DialogBox(  GetModuleHandle(0),
                    MAKEINTRESOURCE(IDD_PSAPIWorkingSetDelta),
                    hWndOwner,
                    (DLGPROC)DeltaDlgProc );
    }
    else
    {
        MessageBox( hWndOwner,
                    "No working set changes, or error",
                    "Working set delta",
                    MB_OK );
    }

    CloseHandle( g_WorkingSetHProcess );
    g_WorkingSetHProcess = 0;
}

PSAPIHELPER.CPP

 //=======================================
// PSAPI Working Set Demo - Matt Pietrek 1996
// Microsoft Systems Journal, November 1996
// FILE: PSAPIHELPER.CPP
//=======================================
#include <windows.h>
#pragma hdrstop
#include "psapi.h"
#include "PSAPIhelper.h"

BOOL GetModuleNameAndSectionInfo(
        HANDLE  hProcess,
        PVOID   p,
        PSTR    pszModuleName,
        DWORD   cbModuleName,
        PSTR    pszSectionName,
        DWORD   cbSectionName,
        PDWORD  puSectionNum )
{

    MEMORY_BASIC_INFORMATION mbi;

    if ( !VirtualQueryEx( hProcess, p, &mbi, sizeof(mbi)) )
        return FALSE;

    PVOID hModule = mbi.AllocationBase;

    DWORD cbRead;       
    IMAGE_DOS_HEADER dosHdr;

    if ( !ReadProcessMemory(hProcess, hModule,
                            &dosHdr, sizeof(dosHdr), &cbRead) )
        return FALSE;

    if ( IMAGE_DOS_SIGNATURE != dosHdr.e_magic )
        return FALSE;

    if ( dosHdr.e_lfarlc < 0x40 )
        return FALSE;

    DWORD peHdrOffs = (DWORD)hModule + dosHdr.e_lfanew;

    IMAGE_NT_HEADERS ntHdr;

    if ( !ReadProcessMemory(hProcess, (PVOID)peHdrOffs,
                            &ntHdr, sizeof(ntHdr), &cbRead ) )
        return FALSE;

    if ( IMAGE_NT_SIGNATURE != ntHdr.Signature )
        return FALSE;

    PVOID sectionHdrOffs = (PVOID)(
                    peHdrOffs
                    + FIELD_OFFSET( IMAGE_NT_HEADERS, OptionalHeader )
                    + ntHdr.FileHeader.SizeOfOptionalHeader );

    #define MAX_SECTIONS 128

    IMAGE_SECTION_HEADER sections[ MAX_SECTIONS ];
    PIMAGE_SECTION_HEADER pSection;

    DWORD cSections = min( ntHdr.FileHeader.NumberOfSections, MAX_SECTIONS );

    if ( !ReadProcessMemory(hProcess,
                            sectionHdrOffs,
                            &sections,
                            cSections * IMAGE_SIZEOF_SECTION_HEADER,
                            &cbRead) )
        return FALSE;

    // Get the module name now, since we can't assume that we'll find
    // a section name/index later.  For instance, the RVA might be somewhere
    // in the NT header page, rather than in a code/data section.
    GetModuleBaseName(  hProcess, hModule,
                        pszModuleName, cbModuleName );
    
    pszSectionName[0] = 0;  // Set to void values in case we bail out of the
    *puSectionNum = 0;      // loop below without finding a section.

    DWORD rva = (DWORD)p - (DWORD)hModule;

    pSection = (PIMAGE_SECTION_HEADER)&sections;

    for ( DWORD i = 0; i < cSections; i++, pSection++ )
    {
        DWORD endRVA = pSection->VirtualAddress
                + max(pSection->SizeOfRawData, pSection->Misc.VirtualSize);

        if ( pSection->VirtualAddress > rva )   // Is this section's RVA
            break;                              // past the RVA we're after?

        if ( (rva >= pSection->VirtualAddress) && (rva <= endRVA ) )
        {
            lstrcpyn( pszSectionName, (PSTR)pSection->Name,
                       min(IMAGE_SIZEOF_SHORT_NAME+1, cbSectionName) );
            *puSectionNum = i+1;
            return TRUE;
        }
    }

    return TRUE;
}