Get Process CPU Usage by its PID or HANDLE?

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

Get Process CPU Usage by its PID or HANDLE?

Postby Ahmed Sayed » Sat Jan 26, 2019 9:19 am

hi, I am trying to monitor a some programs from another main program that will monitor cpu usage for any instance of the other programs.

I tried using PdhAddEnglishCounter but with no luck to get an external (another process) cpu usage. I only managed to get current process and for only 1 instance if another runs i get exceptions so i guess i am doing something wrong. I don't want to enumerate the whole list of processes to get the ones i want just to get the process info by PID or handle or exe name.

Code: Select all
//---------------------------------------------------------------------------

#include <vcl.h>
#pragma hdrstop

#include "MainU.h"
#include "Psapi.h"
#include <memory>
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm"
TMain *Main;
std::auto_ptr<TStringList> Config(new TStringList);
std::auto_ptr<TStringList> Logger(new TStringList);
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner)
   : TForm(Owner)
{
//Extract app file path
AppPath = ExtractFilePath(ParamStr(0));

ConfigFile = AppPath + "Config.txt";

//load config settings to screen
if (FileExists(ConfigFile))
   {
   Config->LoadFromFile(ConfigFile);

   Server->Text = Config->Values["Server"];
   Port->Text = Config->Values["Port"].ToIntDef(25);
   Mail->Text = Config->Values["Mail"];
   }

//init performance counters
InitCPU();
//form server URL for later use
PrepareURL();
}
//---------------------------------------------------------------------------
void TMain::PrepareURL()
{
URL = "http://" + Server->Text.Trim() + ":" + Port->Text.Trim() + "/updateclient";
}
//---------------------------------------------------------------------------
void TMain::InitCPU()
{
//Creates a new query that is used to manage the collection of performance data.
PdhOpenQuery(NULL, NULL, &cpuQuery);
//Adds the specified language-neutral counter to the query.
PdhAddEnglishCounter(cpuQuery, L"\\Process(MachineStatisticsClient)\\% Processor Time", NULL, &cpuTotal);
/*
Collects the current raw data value for all
counters in the specified query and updates the
status code of each counter.
*/
PdhCollectQueryData(cpuQuery);
}
//---------------------------------------------------------------------------
int TMain::GetMemory()
{
//Retrieves information about the system's current usage of both physical and virtual memory.
GlobalMemoryStatus(&MemoryStatus);
//calculate used memory in percentage
double mem = MemoryStatus.dwTotalPhys - MemoryStatus.dwAvailPhys;
return (mem / MemoryStatus.dwTotalPhys) * 100;
}
//---------------------------------------------------------------------------
double TMain::GetCpuUsage()
{
PDH_FMT_COUNTERVALUE counterVal;
/*
Collects the current raw data value for all
counters in the specified query and updates the
status code of each counter.
*/
PdhCollectQueryData(cpuQuery);
//gets computes a displayable value for the specified counter.
PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal);
return counterVal.doubleValue / 100.0;
}
//---------------------------------------------------------------------------
void __fastcall TMain::TimerTimer(TObject *Sender)
{
//Collect system info memory, cpu, processes
Mem = GetMemory();
Cpu = GetCpuUsage();
Pro = ProcessesCount();

//diplay them in labels
Memory->Caption = Format("Memory: %f%%",ARRAYOFCONST((Mem)));
CPUUsage->Caption = Format("CPU: %f%%",ARRAYOFCONST((Cpu)));
Processes->Caption = Format("Processes: %f",ARRAYOFCONST((Pro)));
//Display info on progress bars
MemBar->Position = Mem;
CPUBar->Position = (int)Cpu;
}
//---------------------------------------------------------------------------
int TMain::ProcessesCount()
{
//Retrieves the process identifier for each process object in the system.
if (!EnumProcesses( aProcesses, sizeof(aProcesses), &cbNeeded ) )
   {
   return 1;
   }

// Calculate how many process identifiers were returned.
return cbNeeded / sizeof(DWORD);
}
//---------------------------------------------------------------------------
void __fastcall TMain::StartStopClick(TObject *Sender)
{
//starts or stops the timers
Timer->Enabled = StartStop->Checked;
UpdateTimer->Enabled = StartStop->Checked;
}
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender)
{
Config->Values["Server"] = Server->Text;
Config->Values["Port"] = Port->Text;
Config->Values["Mail"] = Mail->Text;
Config->SaveToFile(ConfigFile);
}
//---------------------------------------------------------------------------
UnicodeString TMain::SendSysInfo(TStrings *ClientData)
{
std::auto_ptr<TMemoryStream> BodyStr(new TMemoryStream);
//sets mail, date, time in client data
ClientData->Values["Mail"] = Mail->Text.Trim();
ClientData->Values["Date"] = DateToStr(Date());
ClientData->Values["Time"] = TimeToStr(Time());
//save client data to memory stream & reset position
ClientData->SaveToStream(BodyStr.get());
BodyStr->Position = 0;

//Execute http POST request to server with ClientData as the request body
return HTTPClient->Post(URL,BodyStr.get())->ContentAsString();
}
//---------------------------------------------------------------------------
void __fastcall TMain::UpdateTimerTimer(TObject *Sender)
{
std::auto_ptr<TStringList> ClientData(new TStringList);
//Collect ClientData
ClientData->Values["ClientKey"] = THashBobJenkins::GetHashString(Mail->Text.Trim());
ClientData->Values["memory"] = Mem;
ClientData->Values["cpu"] = Cpu;
ClientData->Values["processes"] = Pro;

//Execute request and use ClientData to store response. Lazy hah
PrepareURL();
ClientData->Text = SendSysInfo(ClientData.get());

//Make response data to be in one line not 4 as sent by server, then logs the line
ClientData->LineBreak = ",";
LogEntry(ClientData->Text);
}
//---------------------------------------------------------------------------
void __fastcall TMain::HTTPClientRequestCompleted(TObject * const Sender, IHTTPResponse * const AResponse)
{
//on request error adds error message to log
if (AResponse->StatusCode != 200)
   LogEntry(AResponse->ContentAsString());
}
//---------------------------------------------------------------------------
void __fastcall TMain::HTTPClientRequestError(TObject * const Sender, const UnicodeString AError)
{
//on request error adds error message to log
LogEntry(AError);
}
//---------------------------------------------------------------------------
void TMain::LogEntry(UnicodeString Text)
{
//Log Text and adding timestamp to the log entry
Logger->Add("[" + DateTimeToStr(Now()) +"] - " + Text);
Logger->SaveToFile(AppPath + "ClientLog.txt");
}
//---------------------------------------------------------------------------

I have seen other examples to use GetProcessTimes but it gives me a different value from Windows task manager.

Code: Select all
void InitProcessCPUUsage(HANDLE aProcess,
              ULARGE_INTEGER &lastCPU,
              ULARGE_INTEGER &lastSysCPU,
              ULARGE_INTEGER &lastUserCPU)
{
SYSTEM_INFO sysInfo;
FILETIME ftime, fsys, fuser;

GetSystemInfo(&sysInfo);
CurNumProcessors = sysInfo.dwNumberOfProcessors;

GetSystemTimeAsFileTime(&ftime);
memcpy(&lastCPU, &ftime, sizeof(FILETIME));

GetProcessTimes(aProcess, &ftime, &ftime, &fsys, &fuser);
memcpy(&lastSysCPU, &fsys, sizeof(FILETIME));
memcpy(&lastUserCPU, &fuser, sizeof(FILETIME));
}
//---------------------------------------------------------------------------
double GetProcessCPUUsage(HANDLE aProcess,
              ULARGE_INTEGER lastCPU,
              ULARGE_INTEGER lastSysCPU,
              ULARGE_INTEGER lastUserCPU)
{
FILETIME ftime, fsys, fuser;
ULARGE_INTEGER now, sys, user;
double percent;

GetSystemTimeAsFileTime(&ftime);
memcpy(&now, &ftime, sizeof(FILETIME));

GetProcessTimes(aProcess, &ftime, &ftime, &fsys, &fuser);
memcpy(&sys, &fsys, sizeof(FILETIME));
memcpy(&user, &fuser, sizeof(FILETIME));
percent = (sys.QuadPart - lastSysCPU.QuadPart) +
   (user.QuadPart - lastUserCPU.QuadPart);
percent /= (now.QuadPart - lastCPU.QuadPart);
percent /= CurNumProcessors;
lastCPU = now;
lastUserCPU = user;
lastSysCPU = sys;
return percent * 100.0;
}
//---------------------------------------------------------------------------


Thanks in advance.
Any help will be appreciated
Ahmed Sayed
Active Poster
Active Poster
 
Posts: 24
Joined: Thu Nov 08, 2018 4:12 pm

Re: Get Process CPU Usage by its PID or HANDLE?

Postby rlebeau » Sat Jan 26, 2019 8:27 pm

Ahmed Sayed wrote:I tried using PdhAddEnglishCounter but with no luck to get an external (another process) cpu usage. I only managed to get current process and for only 1 instance if another runs i get exceptions so i guess i am doing something wrong.


Well, for starters, you are not doing ANY error handling at all. You need to add that. ALL Win32 API functions have the potential to fail, so you need to be prepared to handle that and act accordingly, not just ignore errors.

WHERE exactly does the access violation occur? What does the exception say exactly? I see nothing in this code that should be causing access violations. Plenty of API calls that can fail, though.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1584
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: Get Process CPU Usage by its PID or HANDLE?

Postby Ahmed Sayed » Sun Jan 27, 2019 4:18 am

My mistake the exception was for something else but the first code listing is for a test app that i am trying to monitor the current app cpu usage but when i run another instance of the same app it still shows me the cpu usage for the first instance how can i get cpu usage for a certain process with its handle or PID because i found a discussion about the same issue here
https://social.msdn.microsoft.com/Forum ... =vcgeneral
and someone suggested this solution but i don't know how to do it. I am not an expert in windows api

Maybe you can examine the next counter: “\Process(iexplore:index)\ID Process”. Increase index in a loop until you find the process by ID. Then use the found index in your other counters. This is probably more efficient, but not more reliable.


How can i examine the next counter and how can i get the exact indexes for my app only not all the processes? Also is there a cross-platform way to do so in RAD Studio because i searched but couldn't find anything. Also is it possible to get this info using the registry? If so how?

I need to get this done and it has to be fast not resource consuming at all.

Thanks in advance
Ahmed Sayed
Active Poster
Active Poster
 
Posts: 24
Joined: Thu Nov 08, 2018 4:12 pm

Re: Get Process CPU Usage by its PID or HANDLE?

Postby HsiaLin » Mon Jan 28, 2019 8:21 am

Sounds like you may need to use WMI.
WMI sucks too and its like the worst thing ever invented.
HsiaLin
BCBJ Master
BCBJ Master
 
Posts: 310
Joined: Sun Jul 08, 2007 6:29 pm

Re: Get Process CPU Usage by its PID or HANDLE?

Postby Ahmed Sayed » Mon Jan 28, 2019 12:20 pm

yeah couldn't agree more WMI is worst think in development i could think of but i found the solution in this discussion
https://groups.google.com/forum/#!searc ... sJiTO9o5kJ

Microsoft always like to make things hard for developers while the same thing can be done on other OSs with minimal coding check this thread
https://stackoverflow.com/questions/631 ... -a-process
Ahmed Sayed
Active Poster
Active Poster
 
Posts: 24
Joined: Thu Nov 08, 2018 4:12 pm

Re: Get Process CPU Usage by its PID or HANDLE?

Postby rlebeau » Tue Jan 29, 2019 5:52 pm

Ahmed Sayed wrote:the first code listing is for a test app that i am trying to monitor the current app cpu usage but when i run another instance of the same app it still shows me the cpu usage for the first instance


Right, because you are not filtering the query by any particular process instance.

Ahmed Sayed wrote:how can i get cpu usage for a certain process with its handle or PID


You already know the answer to that, because you quoted it.

Ahmed Sayed wrote:i found a discussion about the same issue here
https://social.msdn.microsoft.com/Forum ... =vcgeneral
and someone suggested this solution but i don't know how to do it.


Why not? Do you not know how to format a string from variable input?

Ahmed Sayed wrote:am not an expert in windows api


This is not an API issue, it is a string manipulation issue.

Ahmed Sayed wrote:
Maybe you can examine the next counter: “\Process(iexplore:index)\ID Process”. Increase index in a loop until you find the process by ID. Then use the found index in your other counters. This is probably more efficient, but not more reliable.



Exactly. So, first you would query "\\Process(MachineStatisticsClient#0)\\% Processor Time", then query "\\Process(MachineStatisticsClient#1)\\% Processor Time", and so on in a loop, until you run out of indexes to query.

Or, if you want to query just a specific instance, then query "\\Process(MachineStatisticsClient#<index>)\\ID Process" in a loop until you find the ID you are interested in, then you will have its index for use in the CPU query.

Ahmed Sayed wrote:Also is there a cross-platform way to do so in RAD Studio


No.

Ahmed Sayed wrote:Also is it possible to get this info using the registry?


Not from the Registry itself, no. But, the Registry API can access the same performance counter data that the PDH API can access:

Using the Registry Functions to Consume Counter Data

However, the Registry functions are lower level and more difficult to use than the higher level PDH functions.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1584
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: Get Process CPU Usage by its PID or HANDLE?

Postby Ahmed Sayed » Thu Jan 31, 2019 9:01 am

Thanks I manged to get close enough from task manager values using this for now:

Code: Select all
void TMain::GetCPUUsage()
{
ULONGLONG LastCycleTime = NULL;
LARGE_INTEGER LastPCounter;
LastPCounter.QuadPart = 0; // LARGE_INTEGER Init

SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
int numProcessors = sysInfo.dwNumberOfProcessors;

while (Timer->Enabled)
   {
   ULONG64 CycleTime;
    LARGE_INTEGER qpcLastInt;

   if (!QueryProcessCycleTime(aProcess, &CycleTime))
     return;

   ULONG64 cycle = CycleTime - LastCycleTime;

    if (!QueryPerformanceCounter(&qpcLastInt))
     return;

    double Usage = cycle/((double)(qpcLastInt.QuadPart - LastPCounter.QuadPart));

    // Scaling
    Usage *= 1.0/numProcessors;
    Usage *= 0.1;

    LastPCounter = qpcLastInt;
    LastCycleTime = CycleTime;


   SynchronizeUI(FuncBind( &TMain::DisplayCPULoad, this, (int)Usage));

   Sleep(500);
   }
}
//---------------------------------------------------------------------------


This is another code from what posted earlier.
I will look into it to get performance counters from registry.
Thanks
Ahmed Sayed
Active Poster
Active Poster
 
Posts: 24
Joined: Thu Nov 08, 2018 4:12 pm


Return to Technical

Who is online

Users browsing this forum: No registered users and 11 guests

cron