Page 1 of 2

Process Private Memory Usage

Posted: 20 Mar 2019, 19:17
by Blue Kodiak
HI,
I'm try to monitor the memory usage of a running process using the code below
borrowed from a 2014 post by Flipeador.

Code: Select all

ProcessMemoryInfo(ProcessName) {
	ProcessId := InStr(ProcessName, ".")?ProcessExist(ProcessName):ProcessName
	, hProcess := DllCall("Kernel32.dll\OpenProcess", "UInt", 0x0010|0x0400, "UInt", 0, "UInt", ProcessId)
	, nSize := VarSetCapacity(memCounters, A_PtrSize = 8 ? 72 : 40), NumPut(nSize, memCounters)
	if !(DllCall("Psapi.dll\GetProcessMemoryInfo", "Ptr", hProcess, "UInt", &memCounters, "UInt", nSize)) {
		memCounters := "", nSize := VarSetCapacity(memCounters, A_PtrSize = 8 ? 80 : 44), NumPut(nSize, memCounters)
		if !(DllCall("Kernel32.dll\K32GetProcessMemoryInfo", "Ptr", hProcess, "UInt", &memCounters, "UInt", nSize))
			return false, ErrorLevel := true
	} i := {}, i.PageFaultCount := NumGet(memCounters, 4, "UInt")
		, i.PeakWorkingSetSize := NumGet(memCounters, 8, "Ptr")
		, i.WorkingSetSize := NumGet(memCounters, A_PtrSize = 8 ? 16 : 12, "Ptr") 
		, i.QuotaPeakPagedPoolUsage := NumGet(memCounters, A_PtrSize = 8 ? 24 : 16, "Ptr")
		, i.QuotaPagedPoolUsage := NumGet(memCounters, A_PtrSize = 8 ? 32 : 20, "Ptr")
		, i.QuotaPeakNonPagedPoolUsage := NumGet( memCounters, A_PtrSize = 8 ? 40 : 24, "Ptr")
		, i.QuotaNonPagedPoolUsage := NumGet( memCounters, A_PtrSize = 8 ? 48 : 28, "Ptr")
		, i.PagefileUsage := NumGet( memCounters, A_PtrSize = 8 ? 56 : 32, "Ptr")
		, i.PeakPagefileUsage := NumGet( memCounters, A_PtrSize = 8 ? 64 : 36, "Ptr")
		, i.PrivateUsage := NumGet(memCounters, A_PtrSize = 8 ? 72 : 40, "Ptr")
	return i, DllCall("kernel32.dll\CloseHandle", "Ptr", hProcess), ErrorLevel := false
}


Issue 1.
However The the first DllCall {DllCall("Psapi.dll\GetProcessMemoryInfo", "Ptr", hProcess, "UInt", &memCounters, "UInt", nSize)}
always returns True.
Consequently The Kerne32 version is never called and consequently PrivateUsage
is never returned (the PROCESS_MEMORY_COUNTERS structure is only 72 bytes).
I could drop the PSAPI call altogether and call only the Kernel32 version but
on the Microsoft website they recommending using the PSAPI version to ensure
compatibility with older systems.

Issue 2.
If I do call just the Kernel32 version the value returned for private memory
usage does not match the value shown in TaskManager -- but all other
memory usage values do match.
Is there a way to get the same processes private memory usage that TaskManager reports?

Thanks.

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 09:30
by swagfag
this function looks weird, it's like a windows version check should be performed somewhere in there, but isn't.

the psapi call uses the regular struct, whereas the fallback method uses the extended struct

anyway, idk what task manager shows exactly when "memory (active private working set)" for a given process is inspected (that's the default, I think), but i can tell u it's not PagefileUsage or PrivateBytes. those are Commit Size in task manager

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 10:43
by teadrinker
Hi,

If what you mean by «Private Memory Usage» is «Private Working Set» you could get it using WMI:

Code: Select all

PID := 6380  ; specify any existent PID
MsgBox, % GetPrivateWorkingSet(PID) . " KB"

GetPrivateWorkingSet(PID) {
   bytes := ComObjGet("winmgmts:")
           .ExecQuery("Select * from Win32_PerfFormattedData_PerfProc_Process Where IDProcess=" PID)
           .ItemIndex(0).WorkingSetPrivate
   Return bytes//1024
}
I'd also like to know how it can be obtained using winapi.

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 11:14
by wolf_II
This is all news to me, but I had a go at a different script by Drozd
Maybe this is useful:

Code: Select all

    PID := DllCall("GetCurrentProcessId")
    MsgBox, % GetProcessMemoryInfo(PID, "B")
    MsgBox, % GetProcessMemoryInfo(PID, "K")
    MsgBox, % GetProcessMemoryInfo(PID, "M")

ExitApp


;-------------------------------------------------------------------------------
GetProcessMemoryInfo(PID, Units := "M") {
;-------------------------------------------------------------------------------
    is64 := (A_PtrSize = 8)
    Size := (is64 ? 80 : 44)
    VarSetCapacity(MEM, Size, 0) ; make MEM
    hProcess := DllCall("OpenProcess", UInt, 0x410, Int, 0, Ptr, PID, Ptr)

    if (hProcess) {
        if DllCall("psapi.dll\GetProcessMemoryInfo"
                , Ptr, hProcess, Ptr, &MEM, UInt, Size)
            memory := NumGet(MEM, is64 ? 16 : 12)
        DllCall("CloseHandle", Ptr, hProcess)
    }

    return Units == "B" ? (memory)                 " Bytes"
        :  Units == "K" ? (memory // 1024)         " KiBytes"
        :  Units == "M" ? (memory // 1024 // 1024) " MiBytes"
        :  "I am never reached"
}

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 11:51
by teadrinker
wolf_II wrote: Maybe this is useful
It's not a private working set, it's a working set.

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 14:33
by wolf_II
teadrinker wrote:
21 Mar 2019, 11:51
Thank you !! :)
unfortunately, I do not know what the difference is, I did not even know there was a difference.
I am happy to learn bit-by-bit. Thank you for my lesson for the day. :)
I could have remained silent, but I learn quicker when engaging with code.

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 14:49
by teadrinker
wolf_II wrote: I do not know what the difference is
You could read this: How to interpret Windows Task Manager?

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 14:54
by wolf_II
Thank you again !! :)

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 20:09
by Blue Kodiak
swagfag wrote:
21 Mar 2019, 09:30
this function looks weird, it's like a windows version check should be performed somewhere in there, but isn't.
My thought also. I assume it was intended that psapi call would fail for newer versions of windows but it doesn't and it shouldn't because it's supposed to be compatible with older versions of windows.

Re: Process Private Memory Usage

Posted: 21 Mar 2019, 20:19
by Blue Kodiak
teadrinker wrote:
21 Mar 2019, 10:43
you could get it using WMI:
I'd also like to know how it can be obtained using winapi.
That works, https://www.autohotkey.com/boards/posting.php?mode=quote&f=76&p=268808# Thank you; the api discrepancy has been bugging me for a while.
teadrinker wrote:
21 Mar 2019, 10:43
I'd also like to know how it can be obtained using winapi.
Yes me too. It would be nice to get all the metrics in one operation.
I'll give it a week for someone to provide an API solution.
If that doesn't happen then I'll accept your method as the solution.

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 04:23
by jNizM

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 04:38
by teadrinker
Hi, jNizM

Let's see:

Code: Select all

PID := 6264  ; specify any existent PID
MsgBox, % GetPrivateWorkingSet(PID) . " KB`n" . GetProcessMemoryUsage(PID) . " KB"

GetPrivateWorkingSet(PID) {
   bytes := ComObjGet("winmgmts:")
           .ExecQuery("Select * from Win32_PerfFormattedData_PerfProc_Process Where IDProcess=" PID)
           .ItemIndex(0).WorkingSetPrivate
   Return bytes//1024
}

GetProcessMemoryUsage(ProcessID)
{
   static PMC_EX, size := NumPut(VarSetCapacity(PMC_EX, 8 + A_PtrSize * 9, 0), PMC_EX, "uint")

   if (hProcess := DllCall("OpenProcess", "uint", 0x1000, "int", 0, "uint", ProcessID)) {
      if !(DllCall("GetProcessMemoryInfo", "ptr", hProcess, "ptr", &PMC_EX, "uint", size))
         if !(DllCall("psapi\GetProcessMemoryInfo", "ptr", hProcess, "ptr", &PMC_EX, "uint", size))
            return (ErrorLevel := 2) & 0, DllCall("CloseHandle", "ptr", hProcess)
      DllCall("CloseHandle", "ptr", hProcess)
      return NumGet(PMC_EX, 8 + A_PtrSize * 8, "uptr") // 1024
   }
   return (ErrorLevel := 1) & 0
}
Did you get identical results in the message box?

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 04:52
by jNizM
I checked with Process Explorer (https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer)

The Memory Display from the TaskManager is the Working Set (WS) Private

'Working Set' is the amount of memory that the process currently has in physical RAM

So it depends on what do you (or the OP) wants for informations

I started a Process Explorer (AHK) Project, but its far from finsihed (https://github.com/jNizM/AHK_Process_Explorer)

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 05:33
by teadrinker
I don't understand anything. :) Did the result obtained from your script match Working Set (WS) Private from the Process Explorer?

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 05:48
by jNizM
Found a function in my old library.. will post it after rewrite

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 07:27
by jNizM

Code: Select all

MsgBox % GetWorkingSetPrivateSize(9652) " K"

GetWorkingSetPrivateSize(PID)
{
	if (DllCall("ntdll.dll\NtQuerySystemInformation", "int", 0x5, "ptr", 0, "uint", 0, "uint*", size) != 0) {
		size := VarSetCapacity(buf, size << 1, 0)
		if (DllCall("ntdll\NtQuerySystemInformation", "int", 0x5, "ptr", &buf, "uint", size, "uint*", 0) != 0)
			return (ErrorLevel := 2) & 0
		addr := &buf
		while (addr) {
			if (NumGet(addr + 80, "ptr") = PID)
				return NumGet(addr + 8, "int64") // 1024
			if !(NumGet(addr + 0, "uint"))
				break
			addr += NumGet(addr + 0, "uint")
		}
	}
	return (ErrorLevel := 1) & 0
}
Will create a full function for this with all possible information exports in Scripts and Functions later

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 07:39
by teadrinker
It seems to work, thanks!

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 07:41
by teadrinker
You could use NumGet(addr + 80, "ptr") instead of NumGet(addr+0, 80, "ptr"). :)

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 07:44
by jNizM
Yes I know.. this was in my lib from 2015 where I try to understand and decrypt the NtQuerySystemInformation function :D

Re: Process Private Memory Usage

Posted: 22 Mar 2019, 07:55
by teadrinker
teadrinker wrote: It seems to work
But only for 64 bit AHK.