Solved

Get CPU Usage of processes

Posted on 2004-08-28
12
3,792 Views
Last Modified: 2012-06-27
How can I get a list of the CPU usage % of each running process on my system? Just like in Windows Tasklist...

Thanks!
Evarest
0
Comment
Question by:Evarest
  • 5
  • 4
  • 2
  • +1
12 Comments
 
LVL 7

Expert Comment

by:DavidBirch2dotCom
ID: 11921857
ARRRRG I want to CRY !!!!!! that was the second time I accedently changed page once  i had writen a large post !!! once i hit a link in google and it opened in the page :( then i hit one of those domains for sale pages and it opened a pop-up in this page :(:(:(:(

ANYWAY now your all feeling sorry for me ;) I am going to write this in notpad...

I have successfuly used process explorer for this task, it gives the memory usage of every process running.  It also gives an indecation of which programs 'own' other processes which is interesting.  It also gives a list of DLL's inuse by that proccess which has given me some suprises!

you may download process explorer from http://www.sysinternals.com/ntw2k/freeware/procexp.shtml the link is at the bottom of the page,

there are also many other applications for this task including
http://www.xmlsp.com/pview/prcview.htm
and many others though I havnt tired any of them

There are also some related threads on this

http://www.experts-exchange.com/Operating_Systems/Win98/Q_21043478.html
http://www.experts-exchange.com/Operating_Systems/Win98/Q_10337673.html

Hope that helps

David

P>S> there I finaly managed to post ;)
0
 
LVL 14

Expert Comment

by:existenz2
ID: 11921988
The following sources/pages give you information about how you can get Windows process information with Delphi. I suggest you play a bit with them.
http://www.howtodothings.com/showarticle.asp?article=142
http://delphi.about.com/library/weekly/aa080304a.htm

An commercial implementation which works as an Delphi plugin is MiTeC System Information Components. Information about that can be found at http://www.mitec.cz. This plugin can collect a lot of information about the pc and also about processes.
0
 
LVL 4

Author Comment

by:Evarest
ID: 11922326
Thanks for your answers! However, to make clearify my question a bit more:

i'm in need of freeware code that enables me to get a list of all processes (which i can), along with the CPU usage of each separate process (which i can't do).

I know that there're more programs out there which can give you this information (beginning with Windows Task List), but i need it in my own program.

Also interesting to know: it should primarily work on WinXP, and if possible (preferably) also on Win2k.

Thanks in advance!
Evarest
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 11922935
Perhaps you can use Windows Machine Instrumentation to get the info you need. WMI can provide a lot of system information including process information, but it's not easy to use.
0
 
LVL 4

Author Comment

by:Evarest
ID: 11923219
Indeed that's maybe a little overkill, as it's not really worthwhile digging around in WMI itself...

The PDH.dll documentation also isn't really what it should be, that's why I decided to try experts-exchange. Maybe they had some wrapper library or some toolbox that could make it more easy to implement this function...

It seems that there isn't really an "easy" way to get this working after all...
0
 
LVL 4

Author Comment

by:Evarest
ID: 11925206
Workshop_Alex, you can disregard my last post: i've further looked into WMI and found that you can extract the CPU from it. However, it seems not to function very well. Translating from C++ and VBScript code, i get:

uses
  ActiveX, WbemScripting_TLB;

var
  WbemServices: ISWbemServices;
  N1, D1: int64;

function ADsEnumerateNext(pEnumVariant: IEnumVARIANT; cElements: ULONG;
  var pvar: OleVARIANT; var pcElementsFetched: ULONG): HRESULT; safecall; external 'activeds.dll';

procedure DumpWMI_Process(Process: SWBemObject);
var
 Enum: IEnumVARIANT;
 varArr: OleVariant;
 lNumElements: ULong;
 SProp: ISWbemProperty;
 Prop: OleVariant;
 PropName: string;
begin
 N1 :=0;
 D1 :=0;
 Enum :=Process.Properties_._NewEnum as IEnumVariant;
 while (Succeeded(ADsEnumerateNext(Enum, 1, VarArr, lNumElements))) and
       (lNumElements > 0) do
  begin
   if Succeeded(IDispatch(varArr).QueryInterface(SWBemProperty, SProp)) and
      Assigned(SProp)
    then
     begin
      try
       if (SProp.Name <> 'PercentProcessorTime')and
          (SProp.Name <> 'Timestamp_Sys100NS')
        then continue;

       PropName  := SProp.Name;
       Prop := SProp.Get_Value;
       if PropName = 'PercentProcessorTime'
        then N1 :=Prop
         else D1 :=Prop;
       Finalize(SProp);
       Finalize(PropName);
       Finalize(Prop);
      except
       on E: Exception do
        begin
         // WriteLn(ErrOutput, PropName, ': ', E.Message);
        end;
      end;
     end;
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
 Enum: IEnumVARIANT;
 varArr: OleVariant;
 lNumElements: ULong;
 A,B,C,D: int64;
begin
 A :=0; B :=0;
 C :=0; D :=0;
 try
  Enum :=WbemServices.ExecQuery( 'Select * from Win32_PerfRawData_PerfProc_Process where name="procalert"',
                                 'WQL',
                                 wbemFlagBidirectional,
                                 nil )._NewEnum as IEnumVariant;
  while (Succeeded(ADsEnumerateNext(Enum, 1, varArr, lNumElements))) and
        (lNumElements > 0) do
   begin
    DumpWMI_Process(IUnknown(varArr) as SWBemObject);
   end;
  A :=N1;
  C :=D1;

  First :=false;
  sleep(1000);
  Enum :=WbemServices.ExecQuery( 'Select * from Win32_PerfRawData_PerfProc_Process where name="procalert"',
                                 'WQL',
                                 wbemFlagBidirectional,
                                 nil )._NewEnum as IEnumVariant;
  while (Succeeded(ADsEnumerateNext(Enum, 1, varArr, lNumElements))) and
        (lNumElements > 0) do
   begin
    DumpWMI_Process(IUnknown(varArr) as SWBemObject);
   end;
  B :=N1;
  D :=D1;
 finally
  Memo1.Lines.Add(FloatToStr((B-A)/(D-C)*100));
 end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 WbemServices :=CoSWbemLocator.Create.ConnectServer( '[PCNAME]', 'root\cimv2', '',
                                                     '', '', '', 0, nil);
end;


If you review this code, you'll notice it's quite slow (sleep(1000) is called). This line is needed to calculate the CPU usage which i show in the Memo1. Is it possible to speed it up by using some other function of WMI?

Also, and more important, it seems to contain a memoryleak. Just run it a few times to see it for yourself... Maybe you see the cause?

I'm not used to WMI, and quite frankly it seems not to be the most convenient way to get the information, but I really can't find any other way...

Evarest
0
What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

 
LVL 7

Assisted Solution

by:DavidBirch2dotCom
DavidBirch2dotCom earned 40 total points
ID: 11925449
0
 
LVL 17

Accepted Solution

by:
Wim ten Brink earned 80 total points
ID: 11929430
WMI isn't the fastest solution, I'll admit that. But it is the easiest, most reliable one. But I also noticed that you're enumerating the properties of the WMI object but I think you can access it faster by using:

function Prop(Obj: ISWbemObject; Name: string): string;
begin
  try
    Result := VarToStr(obj.Properties_.Item(Name, 0).Get_Value);
  except on E: Exception do Result := E.Message;
  end;
end;

Then use: Prop(Process, 'PercentProcessorTime');
to get a property value directly as string, or an string with error message if it doesn't exist. If you look at the Prop function above, I assume you can easily rewrite it. And this might save you a bit of time again.

About this piece of code:
  Finalize(SProp);
  Finalize(PropName);
  Finalize(Prop);

Better replace it with:
  SProp := nil;
  PropName := nil;
  Prop := nil;

Since we're dealing with COM interfaces here. To free a COM object, you just assign nil to those variables. The COM garbage collector will free them again. The same is true for Enum van VarArr which are COM objects too.

I've used WMI once in a simple console application that needed to retrieve all shares. You might be interested in this source since it's a simple example:
----------------------------------------------------
program GetShares;

{$APPTYPE CONSOLE}

uses
  Windows, SysUtils, ActiveX, Variants, WbemScripting_TLB;

function ADsEnumerateNext(pEnumVariant: IEnumVARIANT; cElements: ULONG; var pvar: OleVariant; var pcElementsFetched: ULONG): HRESULT; safecall; external 'activeds.dll';

function Prop(Obj: ISWbemObject; Name: string): string;
begin
  try
    Result := VarToStr(obj.Properties_.Item(Name, 0).Get_Value);
  except on E: Exception do Result := E.Message;
  end;
end;

var
  AFile: TextFile;
  Enum: IEnumVARIANT;
  varArr: OleVariant;
  lNumElements: ULong;
  Obj: ISWbemObject;
begin
  if Succeeded(CoInitializeEx(nil, COINIT_MULTITHREADED or COINIT_DISABLE_OLE1DDE or COINIT_SPEED_OVER_MEMORY)) then begin
    AssignFile(AFile, ChangeFileExt(ParamStr(0), '.txt'));
    Rewrite(AFile);
    try
      Enum := CoSWbemLocator.Create.ConnectServer(ParamStr(1), 'root\cimv2', '', '', '', '', 0, nil).ExecQuery('Select * from Win32_Share', 'WQL', wbemFlagBidirectional, nil)._NewEnum as IEnumVariant;
      while (Succeeded(ADsEnumerateNext(Enum, 1, varArr, lNumElements))) and (lNumElements > 0) do begin
        obj := IUnknown(varArr) as SWBemObject;
        WriteLn('Name: ', Prop(Obj, 'Name'));
        WriteLn(AFile, 'Name: ', Prop(Obj, 'Name'));
        WriteLn(AFile, 'Path: ', Prop(Obj, 'Path'));
        WriteLn(AFile, 'Status: ', Prop(Obj, 'Status'));
        WriteLn(AFile, 'Caption: ', Prop(Obj, 'Caption'));
        WriteLn(AFile, 'Description: ', Prop(Obj, 'Description'));
        WriteLn(AFile, 'AllowMaximum: ', Prop(Obj, 'AllowMaximum'));
        WriteLn(AFile);
        obj := nil;
      end;
    except
      on E: Exception do begin
        WriteLn(E.Message);
        WriteLn(AFile, E.Message);
      end;
    end;
    varArr := Null;
    Enum := nil;
    CoUninitialize;
    CloseFile(AFile);
  end;
end.
----------------------------------------------------
The CoInitializeEx is required since this is a console application. Normally TApplication would call this but console applications don't use TApplication. It has the simple Prop function that I've shown above. And I'm doing a dirty trick here. I haven't used a variable for the ISWbemServices since I only need it once to get access to WMI. I trust the garbage collection here to free it again for me, since it's a COM interface.

This example might be helpful, I think.

And memory leaks? Unfortunately you're not in control over all the memory here. If there's a memory leak in WMI then it's for MS to solve this. Furthermore, since there's a garbage collector at work here, memory might actually be freed later than you expect.

I'm not too fond of WMI either but I do know that it gives me the easiest access to system information. Basically, it's quite version-independant. It's just that older versions of WMI might not support all properties or objects, thus resulting in exceptions but in general most will work just fine. To get the same information through the Windows API would be extremely low-level API programming. You'd have to determine if the Windows version is NT based or not, then load library X with functions XX if it is, or library Y with function YY if it isn't. Then a snapshot must be taken and anazlyzed and this basically sucks in my opinion. You'd be writing lots of code for something that can be easily read through WMI.
Just look at the sample code I've put above. It's not much code anyways...

David also pointed out another ExEx topic where a reference was made to http://www.torry.net/authorsmore.php?id=4424 or http://www.sysextensions.net/inhalt.aspx?inhalt=prodwmidelphi (WMI Components Delphi v.2.0) but personally I think it's a bit expensive for what's basically a wrapper around existing components. Considering the price, I'm not too impressed by it. I just wonder why no freeware developer ever decided to create a freeware alternative anyway.
0
 
LVL 4

Author Comment

by:Evarest
ID: 11929986
Thanks for your code! I've implemented it, and it works fine except for what I said in my last post. The WMI still seems to contain some memoryleak... If you run the following:

uses
  ActiveX, WbemScripting_TLB;

var
  WbemServices: ISWbemServices;
  N1, D1: int64;

function ADsEnumerateNext(pEnumVariant: IEnumVARIANT; cElements: ULONG;
  var pvar: OleVARIANT; var pcElementsFetched: ULONG): HRESULT; safecall; external 'activeds.dll';

function Prop(const Obj: ISWbemObject; Name: string): int64;
begin
 try
  result :=obj.Properties_.Item(Name, 0).Get_Value;
 except
  result :=0;
 end;
end;

procedure TForm1.Timer1Timer(Sender: TObject);
var
 Enum: IEnumVARIANT;
 varArr: OleVariant;
 lNumElements: ULong;
begin
 Enum :=WbemServices.ExecQuery( 'Select * from Win32_PerfRawData_PerfProc_Process where name="taskmgr"',
                                'WQL',
                                wbemFlagBidirectional,
                                nil )._NewEnum as IEnumVariant;
 try
  while (Succeeded(ADsEnumerateNext(Enum, 1, varArr, lNumElements))) and
        (lNumElements > 0) do
   begin
    N1 :=Prop(IUnknown(varArr) as SWBemObject, 'PercentProcessorTime');
    D1 :=Prop(IUnknown(varArr) as SWBemObject, 'Timestamp_Sys100NS');
   end;
 finally
  varArr :=Null;
  Enum :=nil;
 end;
end;

Timer set to 100 or so, you'll see the memory filling up slowly. First it stays around the starting value, but after a while it'll increase. I know, as i'd use the sleep(1000), it would take ages to fill up the memory, but nevertheless, I cannot allow such a memoryleak in my program...

Maybe you find something i forgot to free, but at the moment I'm not using WMI after all :-(

The components are indeed too expensive, as i'm just a student with a programming hobby in my spare time...

For Workshop_Alex:
About this piece of code:
  Finalize(SProp);
  Finalize(PropName);
  Finalize(Prop);

Better replace it with:
  SProp := nil;
  PropName := nil;
  Prop := nil;

I'm not used working with Variants... I use Finalize in all my code, if there's a string in the record. In the help file of Delphi, they say you should use Finalize to free memory allocated to a non-terminated string or a variant...

All this freeing is quite a "blurry" in Delphi i find. I hope that i use it correctly, if i use Dispose to free a record:

Dispose(PMyRec(MyRec));

and Finalize to free any residing strings in MyRec (called before calling Dispose)

Finalize(PMyRec(MyRec)^);

I've increased the points, as I'm going to split them between Workshop_alex who has helped me with the WMI a lot, and DavidBirch2dotCom, who pointed me to the WMI components which can be interesting for other developers (but not for me at the moment as i said earlier on)...

Evarest
0
 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 80 total points
ID: 11930825
Well, it won't hurt to learn a bit more about interfaces and variants since they tend to be very popular these days.

Finalize is used to free dynamically created variables. But if you assign Null to a variant or nil to an interface, the old value will call the related dispose method for the variant or interface, which is the proper way to free them. Variants and Interfaces are maintained by the build-in garbage collector of Delphi. With normal objects you'd have to call Object.Free but with interfaces you just assign another value to the variable.

Interfaces and variants are maintaining a reference counter. This counter increases with one every time a new variable is pointing to the object. It is decreased with one every time a variable is cleared or gets a new value. Once the counter reaches 0, the interface is 0 and the object is destroyed. Very simple example:

var I1, I2: IAnInterface;
begin
  I1 := NewAnInterface; // Create AnInterface object, sets counter to 1.
  I2 := I1; // Increases counter to 2.
  I1 := nil; // Decreases the counter to 1 again.
  I2 := nil; // Sets counter to 0, then frees the object.

You have to understand that there are many ways in which memory gets allocated and freed. For example, New and Dispose are used with pointers to record types. Object.Create and Object.Free are used for classes/objects. Assigning null is used for variants while assigning nil is used for interfaces. Finally, you could use GetMem and Freemem to allocate random blocks of memory. And there are a few API functions too that are memory-related.

Now, why do you have a memory leak? Well, you are generating a ISWbemServices object but it's never freed. Try to create and free it in your code. Thus:

procedure TForm1.Timer1Timer(Sender: TObject);
var
 Enum: IEnumVARIANT;
 varArr: OleVariant;
 lNumElements: ULong;
begin
 WbemServices :=CoSWbemLocator.Create.ConnectServer( '[PCNAME]', 'root\cimv2', '', '', '', '', 0, nil);
 Enum :=WbemServices.ExecQuery( 'Select * from Win32_PerfRawData_PerfProc_Process where name="taskmgr"', 'WQL', wbemFlagBidirectional, nil )._NewEnum as IEnumVariant;
 try
  while (Succeeded(ADsEnumerateNext(Enum, 1, varArr, lNumElements))) and (lNumElements > 0) do
   begin
    N1 :=Prop(IUnknown(varArr) as SWBemObject, 'PercentProcessorTime');
    D1 :=Prop(IUnknown(varArr) as SWBemObject, 'Timestamp_Sys100NS');
   end;
 finally
  varArr :=Null;
  Enum :=nil;
 end
 WbemServices := nil;
end;

The problem could be that the WbemServices object is allocating a bit of memory every time you execute the query. Memory that's only freed when the WbemServices object is freed.
Another problem could be that WMI itself is leaking memory. This is not unusual, though. And if that's the case, there's not much that can be done about it.
0
 
LVL 4

Author Comment

by:Evarest
ID: 11931251
Thanks Workshop_Alex for your explanation! Interfaces still aren't my speciality, as I only started using them when you pointed me to their use a few questions back...

The memoryleak isn't solved though. As you correctly guessed, I created WbemServices in the OnCreate event. This because i found it useless to recreate the same instance over and over.

Using your code, the same memoryleak persisted. It's quite an odd memoryleak, as I pointed out in my last post...

However, i got so many from this WMI question, that I'm quite happy to grant everyone their points :-)

Thanks a lot!
Evarest
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 11932615
Well, about memory leaks, I've created lots of applications already and memoryleaks just happen. They are unavoidable. Your own code might be perfect but all the components you use, the Windows API's you call and whatever else might still be leaking. Best thing to do is just check your code to make sure you're cleaning up everything the proper way, using the correct release mechanism for the creation mechanisms used.
0

Featured Post

Threat Intelligence Starter Resources

Integrating threat intelligence can be challenging, and not all companies are ready. These resources can help you build awareness and prepare for defense.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
Have you ever had your Delphi form/application just hanging while waiting for data to load? This is the article to read if you want to learn some things about adding threads for data loading in the background. First, I'll setup a general applica…
Access reports are powerful and flexible. Learn how to create a query and then a grouped report using the wizard. Modify the report design after the wizard is done to make it look better. There will be another video to explain how to put the final p…
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now