2

I'm trying to get the process count starting from an executable full file name.

Here is my code:

function GetPathFromPID(const PID: cardinal): string;
var
  hProcess: THandle;
  path: array[0..MAX_PATH - 1] of char;
begin
  hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, false, PID);
  if hProcess <> 0 then
    try
      if GetModuleFileNameEx(hProcess, 0, path, MAX_PATH) = 0 then
        RaiseLastOSError;
      result := path;
    finally
      CloseHandle(hProcess)
    end
  else
    RaiseLastOSError;
end;

function ProcessCount(const AFullFileName: string): Integer;
var
  ContinueLoop: boolean;
  FSnapshotHandle: THandle;
  FProcessEntry32: TProcessEntry32;
begin
  FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
  ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
  Result := 0;
  while(ContinueLoop) do
  begin
    if ((UpperCase(GetPathFromPID(FProcessEntry32.th32ProcessID)) = UpperCase(AFullFileName)))
    then Result := Result + 1;
    ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
  end;
  CloseHandle(FSnapshotHandle);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Self.Caption := IntToStr(ProcessCount(Application.ExeName));
end;

GetPathFromPID function has been taken from here (Andreas Rejbrand's answer).

Running the application, I got EOSError exception ('System Error. Code: 87.'). As documentation says:

ERROR_INVALID_PARAMETER

87 (0x57)

The parameter is incorrect.

The exception is raised from GetPathFromPID function, because the hProcess <> 0 condition fails and RaiseLastOSError is executed.

Debugging I've noticed that 0 is passed to GetPathFromPID function as PID parameter but I don't understand what's wrong in my code.

Community
  • 1
  • 1
Fabrizio
  • 7,603
  • 6
  • 44
  • 104

2 Answers2

3

OpenProcess returns ERROR_INVALID_PARAMETER when you give it a PID of zero.

But also you may get ERROR_ACCESS_DENIED if the process that you pass it to OpenProcess through GetPathFromPID function requires elevation.

Use this instated to make sure that you'r passing process have the same name only.

  while (ContinueLoop) do
  begin
    if SameText(ExtractFileName(AFullFileName), FProcessEntry32.szExeFile) then
    if ((UpperCase(GetPathFromPID(FProcessEntry32.th32ProcessID)) = UpperCase(AFullFileName)))
     ....
  end;
RepeatUntil
  • 2,272
  • 4
  • 32
  • 57
  • you get my upvote for nice optimization I overlooked. although you did not explain why PID is 0. which is what the OP asked *"but I don't understand what's wrong in my code"* – kobik Nov 03 '16 at 16:20
  • This code also doesn't take short paths vs long paths into account. A short path and a long path can refer to the same file. There are better ways to compare paths for equality. – Remy Lebeau Nov 03 '16 at 16:56
3

It is possible that FProcessEntry32.th32ProcessID entry will be 0.

For example for the first process of [System Process] (check FProcessEntry32.szExeFile) has a PID of 0. I'm pretty sure this is the only entry with th32ProcessID == 0. See "System Idle Process".

So simply check that FProcessEntry32.th32ProcessID <> 0 before you pass it on to GetPathFromPID.

kobik
  • 21,001
  • 4
  • 61
  • 121