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.
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 requires process handle which we can get by calling
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 *(PDWORD)Val1 > *(PDWORD)Val2 ? 1 : -1;
DWORD dWorkingSetPages[ 1024 * 128 ];
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 )
char szBuffer[MAX_PATH * 4];
if ( !QueryWorkingSet(hProcess, dWorkingSetPages, sizeof(dWorkingSetPages)) )
DWORD dPages = dWorkingSetPages;
qsort( &dWorkingSetPages, dPages, sizeof(DWORD), Compare );
for ( DWORD i = 1; i <= dPages; i++ )
DWORD dCurrentPageStatus = 0;
DWORD dPageAddress = dWorkingSetPages[i] & 0xFFFFF000;
DWORD dPageFlags = dWorkingSetPages[i] & 0x00000FFF;
while ( i <= dPages )
if ( i == dPages )
dCurrentPageAddress = dWorkingSetPages[i] & 0xFFFFF000;
dNextPageAddress = dWorkingSetPages[i+1] & 0xFFFFF000;
dNextPageFlags = dWorkingSetPages[i+1] & 0x00000FFF;
if ( (dNextPageAddress == (dCurrentPageAddress + dPageSize))
&& (dNextPageFlags == dPageFlags) )
if ( (dPageAddress < 0xC0000000) || (dPageAddress > 0xE0000000) )
if ( dPageFlags & 0x100 )
dSharedPages += dCurrentPageStatus;
dPrivatePages += dCurrentPageStatus;
dPageTablePages += dCurrentPageStatus;
DWORD dTotal = dPages * 4;
DWORD dShared = dSharedPages * 4;
DWORD WSPrivate = dTotal - dShared;
CloseHandle( hProcess );
public static extern Int32 CalculateWSPrivate(Int32 processID);
- 13th June, 2010: Initial post