Click here to Skip to main content
Click here to Skip to main content

Calculate Memory (Working Set - Private) Programmatically in Windows XP/2000

, 13 Jun 2010
Rate this:
Please Sign up or sign in to vote.
This will explain the overall process of how to get private working set memory of any process in Windows XP/2000

Introduction

I have seen some questions related to this issue. In this article, I will focus on details of how to programmatically get Memory (Working Set - Private) of any running process in Windows.

Background

First of all, you must know the difference between working set & private working set memory. Working set actually tells you how many virtual pages for the process are mapped into physical memory. Working set memory is the amount of memory in the private working set plus the amount of memory the process is using that can be shared by other running processes. So basically private working set is the subset of working set that specifically describes the amount of memory a process is using that cannot be shared by other processes.

Using the Code

If you notice the task manager in Vista & Windows 7, window shows Memory (Private Working Set) which is the key thing to consider in case of memory issues. But this thing was not popular\supported in Windows 2000 & XP. If you observe Windows XP’s task manager, it shows “Mem Usage” which is basically ‘Memory (Working Set)’ and this is sort of general memory for that specific process. Windows XP do not support\show private working set memory.

Usually, we use performance counters to monitor memory usage and yes there is a process object counter to get the private working set memory of any specific running process with the name “Working Set – Private” but this is not supported in Windows XP/2000 systems. If you try to use this in XP, it throws an exception.

According to MSDN, you can calculate this memory usage in XP using different things but MSDN provides very little information about this.

In the article, I will code the process of calculating memory (Working Set-Private) in Windows XP. You can also verify the memory count you are receiving is correct by running this solution on non XP systems and open the Windows task manager at the same time.

The key function to get this thing done is PSAPI function name QueryWorkingSet(). It gives you the working set information of a process and we can calculate private working set on the base of this.

In the implementation of CalculateWSPrivate() function, we are calculating the Working Set Private memory of any particular process whose process ID we will pass in this function. Outside of this function, we will declare a big array (dWorkingSetPages) which will receive the working set information return from QueryWorkingSet(). No worries, it will not affect the size of our current executable because this is not initialized one. We will also declare the page size (dPageSize) and readjust its value in the function.

In this method (CalculateWSPrivate), our key calling function is QueryWorkingSet(). QueryWorkingSet requires process handle which we can get by calling OpenProcess(). If QueryWorkingSet() succeed, this will return page count in the first entry of passed array. After that, we will iterate all subsequent pages that are contiguous in working set memory and have the same attribute (shared or private or from page table region). At the end, we will have the exact count of shared and private memory areas. So in this way, we can get the working set private memory (which is basically none shared memory) of the particular process. 

As you can see, I implemented this function in C++ Win32 DLL, you can easily utilize this in C# by using P/Invoke:

int Compare( const void * Val1, const void * Val2 )
{
    if ( *(PDWORD)Val1 == *(PDWORD)Val2 )
    return 0;

    return *(PDWORD)Val1 > *(PDWORD)Val2 ? 1 : -1;
}

DWORD dWorkingSetPages[ 1024 * 128 ]; // hold the working set 
				// information get from QueryWorkingSet()
DWORD dPageSize = 0x1000;

extern "C" __declspec(dllexport) DWORD CalculateWSPrivate(DWORD processID)
{
    DWORD dSharedPages = 0;
    DWORD dPrivatePages = 0; 
    DWORD dPageTablePages = 0;
 
    HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | 
		PROCESS_VM_READ, FALSE, processID );

    if ( !hProcess )
    return 0;

    __try
    { 
        char szBuffer[MAX_PATH * 4];

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

        DWORD dPages = dWorkingSetPages[0];

        qsort( &dWorkingSetPages[1], dPages, sizeof(DWORD), Compare );

        for ( DWORD i = 1; i <= dPages; i++ )
        {
            DWORD dCurrentPageStatus = 0; 
            DWORD dCurrentPageAddress;
            DWORD dNextPageAddress;
            DWORD dNextPageFlags;
            DWORD dPageAddress = dWorkingSetPages[i] & 0xFFFFF000;
            DWORD dPageFlags = dWorkingSetPages[i] & 0x00000FFF;

            while ( i <= dPages ) // iterate all pages
            {
                dCurrentPageStatus++;

                if ( i == dPages ) //if last page
                break;
 
                dCurrentPageAddress = dWorkingSetPages[i] & 0xFFFFF000;
                dNextPageAddress = dWorkingSetPages[i+1] & 0xFFFFF000;
                dNextPageFlags = dWorkingSetPages[i+1] & 0x00000FFF;
 
                //decide whether iterate further or exit
                //(this is non-contiguous page or have different flags) 
                if ( (dNextPageAddress == (dCurrentPageAddress + dPageSize)) 
			&& (dNextPageFlags == dPageFlags) )
                {
                    i++;
                }
                else
                break;
            }
 
            if ( (dPageAddress < 0xC0000000) || (dPageAddress > 0xE0000000) )
            {
                if ( dPageFlags & 0x100 ) // this is shared one
                dSharedPages += dCurrentPageStatus;

                else // private one
                dPrivatePages += dCurrentPageStatus;
            }
            else
            dPageTablePages += dCurrentPageStatus; //page table region 
        } 

        DWORD dTotal = dPages * 4;
        DWORD dShared = dSharedPages * 4;
        DWORD WSPrivate = dTotal - dShared;

        return WSPrivate;
    }
    __finally
    {
        CloseHandle( hProcess );
    }
}
[DllImport("AccessWSPrivate.dll")]
public static extern Int32 CalculateWSPrivate(Int32 processID);
CalculateWSPrivate(ReqProcess.Id);

History

  • 13th June, 2010: Initial post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Najam ul Hassan
Software Developer (Senior)
United States United States
I have done Masters in Computer Sciences and for last seven years working in Visual C++ and C# on different domains.
Keep programming... . . .

Comments and Discussions

 
Question64bit PinmemberMember 785489019-Dec-12 21:09 
QuestionWhat can i do on 64bit system? Pinmembertw08027-Jan-12 0:48 
QuestionWhere does it come from Pinmember_clown_13-Jan-12 9:26 
GeneralMy vote of 1 PinmemberAnton Lesnichenko23-Dec-11 9:55 
Generalthks Pinmemberohyeahchenzai7-Jun-11 6:14 
QuestionWhat if the process doesn't respond? PinmemberOgottogottogott18-Aug-10 1:10 
Questionchar szBuffer[MAX_PATH * 4]; Pinmemberlispman16-Jun-10 4:37 
AnswerRe: char szBuffer[MAX_PATH * 4]; PinmemberNajam ul Hassan16-Jun-10 6:05 
QuestionWMI equivalent? PinmemberDaniel Desormeaux14-Jun-10 4:03 
Generalgreat article PinmemberChris-197413-Jun-10 16:08 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web04 | 2.8.140709.1 | Last Updated 13 Jun 2010
Article Copyright 2010 by Najam ul Hassan
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid