views:

420

answers:

5

I want to make a Task Manager program that displays this information:

  1. Image name
  2. memory usage
  3. PID

How can I do this?

+2  A: 

Use the PSAPI (Process Status API). The open source JCL has a Delphi wrapper for the PSAPI.

There are some more good stackoverflow Delphi PSAPI questions you can check for answers.

--jeroen

Jeroen Pluimers
A: 

In Jwscl there is a class that can do this for you (JwsclTerminalServer):

 var
   ATerminalServer: TJwTerminalServer;
   i: Integer;
 begin
   // Create Terminal Server instance and allocate memory for it
   ATerminalServer := TjwTerminalServer.Create;

   // Set servername (only in case of remote server)
   ATerminalServer.Server := 'TS001';

   // Remember that EnumerateProcesses will automatically connect to the
   // Terminal Server for you. The connect function raises an Exception
   // if the connection attempt was unsuccessfull, so better use try..except
   try
     if ATerminalServer.EnumerateProcesses then
     begin

       // Now loop through the list
       for i := 0 to ATerminalServer.Processes.Count - 1 do
       begin
         Memo1.Lines.Add(ATerminalServer.Processes[i].ProcessName);
       end;

     end;
   except
     on E: EJwsclWinCallFailedException do
     begin
       // Handle Exception here
     end;
   end;

   // Free Memory
   ATerminalServer.Free;
 end;

Although the unit is aimed at Terminal Server this part works both with and without and as a bonus you can use it on remote systems as well.

For each process detailed information is returned, check the docs for details.

For memory usage you can use the ProcessMemUsage and ProcessVirtualSize properties, for the Pid there is the ProcessId property

Remko
+4  A: 

You don't need the J(WS)CL therefore, there is a simple WinAPI call that does almost all you want, and this is CreateToolhelp32Snapshot. To get a snapshot of all running processes, you have to call it as follows:

var
  snapshot: THandle;
begin
  snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 

Now you have a list of all running processes. You can navigate through this list with the Process32First and Process32Next functions, the list entries are PROCESSENTRY32-structures (which contain, amongst others, the process ID and image name).

uses 
  Windows, TLHelp32, SysUtils; 

var 
  snapshot: THandle; 
  ProcEntry: TProcessEntry32; 
  s: String; 
begin
  snapshot := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); 
  if (snapshot <> INVALID_HANDLE_VALUE) then begin 
    ProcEntry.dwSize := SizeOf(ProcessEntry32); 
    if (Process32First(snapshot, ProcEntry)) then begin 
      s := ProcEntry.szExeFile; 
      // s contains image name of the first process 
      while Process32Next(snapshot, ProcEntry) do begin 
        s := ProcEntry.szExeFile; 
        // s contains image name of the current process 
      end; 
    end; 
  end; 
  CloseHandle(snapshot);

However, memory consumption information doesn't seem to be included, but you can get this via another simple API call, GetProcessMemoryInfo

uses
  psAPI;

var
  pmc: TProcessMemoryCounters;
begin
  pmc.cb := SizeOf(pmc) ;
  if GetProcessMemoryInfo(processID, @pmc, SizeOf(pmc)) then
    // Usage in Bytes: pmc.WorkingSetSize
  else
    // fail

You just have to call this function with the process IDs retrieved from the snapshot.

Mef
You should remove the dynamic memory allocation in the last snippet and simply use a `TProcessMemoryCounters` variable on the stack.
mghie
@mghie: If you say so... =)
Mef
Sorry, that came out wrong. s/should/could/.
mghie
Never mind, I like the new version better, too ;-)
Mef
+1; Cool! I didn't know about this; only about the PSAPI.Note that the on-line documentation is here: http://msdn.microsoft.com/en-us/library/ms682489(VS.85).aspx
Jeroen Pluimers
Probably a very, very, small chance it will ever occur here but PID's can be reused so there is no 100% warranty that the memory info returned actually belongs to the PID obtained in the snapshot.
Remko
@Remko: You can put this comment under a n y of the responses here and I seriously doubt that there is any method outside the kernel that can tell you with 100% warranty the exact memory usage of a given process at the exact point of time of the request - you can't be serious about that. (And remember that the PID reuse could **theoretically** happen even **during** a call of the GetProcessMemoryInfo function, so this is in all likelihood as good as it gets).
Mef
@Mef: well I said it was very unlikely ;-) That said you *can* retreive all info in one query using either NtQuerySystemInformation with the SystemProcessInformation infoclass, this returns an array of _SYSTEM_PROCESSES records that contains both process name, pid and Memory Usage. This is what taskmanager does and it's also what WMI, PsApi and the Tool Help functions will do under water.
Remko
@Remko: That very well may be, but even NtQuerySystemInformation isn't an atomic operation, that means that during a call of this function, PIDs can change. There is no guarantee.
Mef
A: 

ProcessInfo provides basic information about running processes in Windows. It is open-source, and contains a demo of a task manager.

vcldeveloper
+1  A: 

you can use the WMI Win32_Process class to get all the running process info. addtionally you can check the Win32_PerfFormattedData_PerfProc_Process class to get the performance counters related to CPU and memory usage.

Check this sample

program WMIProcessInfo;

{$APPTYPE CONSOLE}

uses
  SysUtils
  ,ActiveX
  ,ComObj
  ,Variants;


procedure GetWin32_Process;
var
  objWMIService : OLEVariant;
  colItems      : OLEVariant;
  colItem       : OLEVariant;
  oEnum         : IEnumvariant;
  iValue        : LongWord;

  User          : OLEVariant;
  Domain        : OLEVariant;

  function GetWMIObject(const objectName: String): IDispatch;
  var
    chEaten: Integer;
    BindCtx: IBindCtx;
      Moniker: IMoniker;
  begin
    OleCheck(CreateBindCtx(0, bindCtx));
    OleCheck(MkParseDisplayName(BindCtx, StringToOleStr(objectName), chEaten, Moniker));
    OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
  end;

begin
  objWMIService := GetWMIObject('winmgmts:\\localhost\root\cimv2');
  colItems      := objWMIService.ExecQuery('SELECT * FROM Win32_Process','WQL',0);
  oEnum         := IUnknown(colItems._NewEnum) as IEnumVariant;

      WriteLn(Format('%-20s %6s %10s %10s %10s',['Caption','PID','User','Domain','Working Set ( Kb Memory)']));
  while oEnum.Next(1, colItem, iValue) = 0 do
  begin
      colItem.GetOwner(User,Domain);
      if colItem.GetOwner( User, Domain ) =0 then //get the user and domain
      WriteLn(Format('%-20s %6s %10s %10s %10s',[colItem.Caption,colItem.ProcessId,User,Domain,colItem.WorkingSetSize / 1024]))
      else
      WriteLn(Format('%-20s %6s %10s %10s %10s',[colItem.Caption,colItem.ProcessId,'','',colItem.WorkingSetSize / 1024]));

  end;
end;


begin
 try
    CoInitialize(nil);
    try
      GetWin32_Process;
      Readln;
    finally
    CoUninitialize;
    end;
 except
    on E:Exception do
    Begin
        Writeln(E.Classname, ': ', E.Message);
        Readln;
    End;
  end;
end.
RRUZ
WMI is terribly slow, if NiliDelphi wants to create something like TaskManager that refreshed the process list often I would not recommend WMI.
Remko