Processes Filepaths

At the moment i use this code to find the filepaths of processes. It is a toolhelp function:

var
  c : cardinal;
  me : TModuleEntry32;
begin
  c := CreateToolhelp32Snapshot(TH32CS_SnapModule, PROCESSPIDHERE);
  me.dwSize := sizeof(me);
  Module32First(c, me);

  ShowMessage(me.szExePath);
  CloseHandle(c);
end;

I have no problem using this function.

The problem is this:

Certain processes filepaths cannot be extracted using this function even if provided the correct PID (i have no problem finding PIDs or enumerating processes). Such processes are svchost.exe, winlogon.exe etc. I have solved this problem by assuming any of these processes reside within %windir%/System32. This is fine for most microsoft programs as this is the case. However, some Norton programs i cannot extract the filepaths using this function and they do not reside in system32. My question is:

Is there other code (i want code no components) that will solve my stated problem?

Regards,

Hypoviax
LVL 5
HypoviaxAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

MikhoCommented:
SearchPath could maybe help?

Declaration taken from the Win32 Programmer's reference.

The SearchPath function searches for the specified file.
DWORD SearchPath(
    LPCTSTR lpPath,      // address of search path
    LPCTSTR lpFileName,      // address of filename
    LPCTSTR lpExtension,      // address of extension
    DWORD nBufferLength,      // size, in characters, of buffer
    LPTSTR lpBuffer,      // address of buffer for found filename
    LPTSTR *lpFilePart       // address of pointer to file component
   );      
 

lpPath

Points to a null-terminated string that specifies the path to be searched for the file. If this parameter is NULL, the function searches for a matching file in the following directories in the following sequence:

1.      The directory from which the application loaded.
      2.      The current directory.
      3.      Windows 95: The Windows system directory. Use the GetSystemDirectory function to get the path of this directory.

Windows NT: The 32-bit Windows system directory. Use the GetSystemDirectory function to get the path of this directory. The name of this directory is SYSTEM32.

4.      Windows NT: The 16-bit Windows system directory. There is no Win32 function that obtains the path of this directory, but it is searched. The name of this directory is SYSTEM.
      5.      The Windows directory. Use the GetWindowsDirectory function to get the path of this directory.
      6.      The directories that are listed in the PATH environment variable.

// end

code example below:

var
   Len     : Integer;
   ShortName,
   TempFilePart : string;
   pDummy: PCHAR;
begin
 Len := SearchPath(NIL,'twain.dll',NIL,0, PChar(TempFilePart),pDummy);
 SetLength(TempFilePart, Len);
 SearchPath(NIL,'twain.dll',NIL, Len, PChar(TempFilePart),pDummy);
 Label9.Caption := 'DLL Version: ' + GetFileVersion(Copy(TempFilePart,1,Length(TempFilePart) -1));
end;

fast and maybe not working out of the box :) but its help on the way
0
Wim ten BrinkSelf-employed developerCommented:
I've said it many times at EE before... These things can easily be done using WMI. The next function, for example, would return the executable path for any given PID:

function ProcessPath( PID: Integer ): string;
const
  sQuery = 'SELECT ExecutablePath FROM Win32_Process WHERE (ProcessId = "%d")';
var
  Item: SWbemObject;
  Locator: ISWbemLocator;
  NumProp: LongWord;
  ObjectSet: ISWbemObjectSet;
  OleProperty: OleVariant;
  Services: ISWbemServices;
begin
  Locator := CoSWbemLocator.Create;
  Services := Locator.ConnectServer( '', 'root\cimv2', '', '', '', '', 0, nil );
  ObjectSet := Services.ExecQuery( Format( sQuery, [ PID ] ), 'WQL', wbemFlagBidirectional, nil );
  if Succeeded( ( ObjectSet._NewEnum as IEnumVariant ).Next( 1, OleProperty, NumProp ) ) and ( NumProp > 0 ) and Succeeded( IDispatch( OleProperty ).QueryInterface( SWBemObject, Item ) ) then begin
    Result := VarToStr( Item.Properties_.Item( 'ExecutablePath', 0 ) );
  end
  else begin
    Result := '';
  end;
end;

You'll need http://www.workshop-alex.org/sources/WbemScripting_TLB/WbemScripting_TLB.html to support these WMI calls. With WMI you could also just enumerate all processes by using a simple query and then walking through the list you get back. Once you know how it works, you'll notice you have found an interesting solution that doesn't add much overhead to the final executable. And it works on any system with Internet Explorer 5 or higher.
And this function does provide the right filepaths of C:\WINNT\system32\svchost.exe and C:\WINNT\system32\winlogon.exe. ;-)
0
Eddie ShipmanAll-around developerCommented:
I took the code from the Jedi Code Library and modified it to work with my application.
I only use it on Win2K, WinXP, or Win2K3. This gets the pathname for all files in the
process list into a memo:

uses .., TlHelp32, PSAPI;

function WinXPor2Kor2K3:Boolean;
begin
  Result := False;
  if ( CheckWin32Version( 5, 0 ) ) or   // Win2K
     ( CheckWin32Version( 5, 1 ) ) or   // WinXP
     ( CheckWin32Version( 5, 2 ) ) then // Win2003
  begin
    Result := True;
  end;
end;
 
function RunningProcessesList(const List: TStrings; FullPath: Boolean): Boolean;
 
  function BuildListTH: Boolean;
  var
    SnapProcHandle: THandle;
    ProcEntry: TProcessEntry32;
    NextProc: Boolean;
    FileName: String;
    PIDName : array [0..MAX_PATH - 1] of char;
    Handle: THandle;
  begin
    SnapProcHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    Result := (SnapProcHandle <> INVALID_HANDLE_VALUE);
    if Result then
    try
      ProcEntry.dwSize := SizeOf(ProcEntry);
      NextProc := Process32First(SnapProcHandle, ProcEntry);
      while NextProc do
      begin
        if ProcEntry.th32ProcessID = 0 then
        begin
          // PID 0 is always the "System Idle Process" but this name cannot be
          // retrieved from the system and has to be fabricated.
          FileName := 'System Idle Process';
        end
        else
        begin
          FileName := ProcEntry.szExeFile;
          if not FullPath then
          begin
            FileName := ExtractFileName(FileName);
          end
          else
          begin
            // do we really need this check???
            if WinXPor2Kor2K3 then
            begin
              Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
                                    False, ProcEntry.th32ProcessID);
              if Handle <> 0 then
              try
                SetLength(FileName, MAX_PATH);
                // Use the psapi function GetModuleFileNameEX to get the full path
                if GetModuleFileNameEx(Handle, 0, PChar(FileName), MAX_PATH) > 0 then
                  SetLength(FileName, StrLen(PChar(FileName)))
                else
                  FileName := '';
              finally
                CloseHandle(Handle);
              end;
            end;
          end;
        end;
        List.AddObject(FileName, Pointer(ProcEntry.th32ProcessID));
        NextProc := Process32Next(SnapProcHandle, ProcEntry);
      end;
    finally
      CloseHandle(SnapProcHandle);
    end;
  end;
 
begin
  Result := BuildListTH;
end;

procedure TFOrm1.Button1Click(Sender: TObject);
begin
  RunningProcessesList(Memo1.Lines, True);
end;

Looks to me like you will have to handle changing 'SystemRoot' to 'C:\Windows'.
The path to winlogon.exe came back as: \??\C:\WINDOWS\system32\winlogon.exe
The path to smss.exe came back as: \SystemRoot\System32\smss.exe
I had four entries for svchost.exe, here's how they were returned:

C:\WINDOWS\system32\svchost.exe
C:\WINDOWS\System32\svchost.exe
svchost.exe
svchost.exe
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

HypoviaxAuthor Commented:
Thanks,

Will test each today (operating on a different time zone)

Regards,

Hypoviax
0
HypoviaxAuthor Commented:
Alex,

I have trouble running your example. The IEnumVariant is an 'undeclared identifier'. Is there some unit i need to add to uses? I already have the compiled WMI unit in my uses list.

Hypoviax
0
HypoviaxAuthor Commented:
Solved it don't worry, needed ActiveX
0
HypoviaxAuthor Commented:
Haha, "Access Denied" !

What's this mean ?
0
HypoviaxAuthor Commented:
Ok, I have tested Eddie's and Alex's code

Both work (both with minor flaws)

Alex, your code for some reason  didn't work on my pc ('Accessed denied') however it worked well on another pc. What is the problem here?

Eddie your code worked well too except some processes the filepath is not returned at all. I am sure i can fix this problem (unless you can ;-) )

Regards,

Hypoviax
0
Eddie ShipmanAll-around developerCommented:
I don't know why but some of the processes that are started by the system don't "have" paths.
I would guess that you would have to check the see who the process owner was.
0
Wim ten BrinkSelf-employed developerCommented:
Hypoviax, I'm not completely sure but it could be because the user account you used might not have proper access rights. At least, that's the first thing I always consider. Or maybe WMI isn't properly installed. (It should be part of Internet Explorer 5 and higher.) Or maybe the WMI service isn't running if you're using any NT-based Windows version. (On 98/ME it should just be running anyway.)
WMI does return the paths for the system processes, though. But I was never able to test it on all kinds of systems. Theoretically you could just test WMI by creating a small VBScript application retrieving the data in a similar way, though. Then you know if it's an application error or something else.
0
HypoviaxAuthor Commented:
It is strange, because i am running WinXp, and am an administrator. The thing is it works on my other XP computer fine. So i think that eliminates the user rights issue. I get the error on the third line:

ObjectSet := Services.ExecQuery( Format( sQuery, [ PID ] ), 'WQL', wbemFlagBidirectional, nil );

Regards,

Hypoviax
0
MadshiCommented:
Does executing this first help?

procedure EnableAllPrivileges;
type TTokenPrivileges = record
       PrivilegeCount : dword;
       Privileges     : array [0..maxInt shr 4 - 1] of TLUIDAndAttributes;
     end;
var c1, c2 : dword;
    i1     : integer;
    ptp    : ^TTokenPrivileges;
begin
  if OpenProcessToken(windows.GetCurrentProcess, TOKEN_ADJUST_PRIVILEGES or TOKEN_QUERY, c1) then
    try
      c2 := 0;
      GetTokenInformation(c1, TokenPrivileges, nil, 0, c2);
      if c2 <> 0 then begin
        ptp := pointer(LocalAlloc(LPTR, c2 * 2));
        if GetTokenInformation(c1, TokenPrivileges, ptp, c2 * 2, c2) then begin
          for i1 := 0 to integer(ptp^.PrivilegeCount) - 1 do
            ptp^.Privileges[i1].Attributes := ptp^.Privileges[i1].Attributes or SE_PRIVILEGE_ENABLED;
          AdjustTokenPrivileges(c1, false, PTokenPrivileges(ptp)^, c2, PTokenPrivileges(nil)^, cardinal(pointer(nil)^));
        end;
        LocalFree(dword(ptp));
      end;
    finally CloseHandle(c1) end;
end;
0
HypoviaxAuthor Commented:
I am afraid not :( ,but thanks for the comment.You're into this stuff Madshi, do you have a possible solution?. I will still continue testing to try to get the codes to work because at the moment both Alex's and Eddie's code have minor flaws that mean that i won't be able to extract *every* filepath. There seems to be nothing wrong with Alex's code except that it won't run on my system (works on my other one) and Eddie's works well except certain processes do not return a filepath at all. I may be able to fix these problems so don't think that i have forgotton about this question and not awarded points ;-).

Thanks,

Hypoviax
0
MadshiCommented:
Here's the list of processes, enumerated on my XP PC by using madKernel, while being logged on as admin user:


Without EnableAllPrivileges:
----------------------------
[System Process]
System
C:\WINDOWS\system32\smss.exe
csrss.exe
C:\WINDOWS\system32\winlogon.exe
C:\WINDOWS\system32\services.exe
C:\WINDOWS\system32\lsass.exe
C:\WINDOWS\system32\Ati2evxx.exe
C:\WINDOWS\system32\svchost.exe
svchost.exe
C:\WINDOWS\System32\svchost.exe
svchost.exe
svchost.exe
C:\WINDOWS\system32\Ati2evxx.exe
C:\WINDOWS\Explorer.EXE
C:\WINDOWS\system32\spoolsv.exe
C:\WINDOWS\system32\CTHELPER.EXE
alg.exe
C:\Programme\Mozilla Firefox\FIREFOX.EXE
C:\Programme\Borland\Delphi 7\Bin\delphi32.exe
C:\Sources\mtsM\Mts.exe


With EnableAllPrivileges:
----------------------------
[System Process]
System
C:\WINDOWS\system32\smss.exe
C:\WINDOWS\system32\csrss.exe
C:\WINDOWS\system32\winlogon.exe
C:\WINDOWS\system32\services.exe
C:\WINDOWS\system32\lsass.exe
C:\WINDOWS\system32\Ati2evxx.exe
C:\WINDOWS\system32\svchost.exe
C:\WINDOWS\system32\svchost.exe
C:\WINDOWS\System32\svchost.exe
C:\WINDOWS\System32\svchost.exe
C:\WINDOWS\System32\svchost.exe
C:\WINDOWS\system32\Ati2evxx.exe
C:\WINDOWS\Explorer.EXE
C:\WINDOWS\system32\spoolsv.exe
C:\WINDOWS\system32\CTHELPER.EXE
C:\WINDOWS\System32\alg.exe
C:\Programme\Mozilla Firefox\FIREFOX.EXE
C:\Programme\Borland\Delphi 7\Bin\delphi32.exe
C:\Sources\mtsM\Mts.exe
0
HypoviaxAuthor Commented:
Hmmm...that might be the problem with Eddie's code then, let me test...
0
HypoviaxAuthor Commented:
Yep, that fixed Eddie's problem...

0
HypoviaxAuthor Commented:
Well Madshi, i think you've fixed the problem enough for me to award points ... :-)

0
MadshiCommented:
When using Eddie's code I'd suggest to replace WinXPor2Kor2K3 with this:

function WinNtFamily : boolean;
begin
  result := GetVersion and $80000000 = 0;
end;

PsApi works on all NT OSs, and it will probably continue to work in Longhorn (at least I'd be surprised if it stopped to work there). Eddie's code is alright, it just limits itself to the known NT OSs, which I don't think is necessary.

P.S: Instead of:

SetLength(FileName, StrLen(PChar(FileName)));

You can do this:

FileName := pchar(FileName);

The wonders of Delphi string handling!   :-)
0
HypoviaxAuthor Commented:
For anyone else this is the modified version of Eddie's code so that a PID can be given and the filepath returned. For the csrss smss and winlogon processes /??/ needs to be stripped from the front on the filepath :

function getfilepath(PID:Integer):String;
var
    FileName: String;
    Handle: THandle;
begin
Handle := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ,
                                    False, PID);
              if Handle <> 0 then
              try
                SetLength(FileName, MAX_PATH);
                // Use the psapi function GetModuleFileNameEX to get the full path
                if GetModuleFileNameEx(Handle, 0, PChar(FileName), MAX_PATH) > 0 then
                  SetLength(FileName, StrLen(PChar(FileName)))
                else
                  FileName := '';   //doesn't exist apparently
              finally
                CloseHandle(Handle);
              end;
result:=filename;
end;

And thanks for everyone for their assistence and input, it is greatly appreciated.

Best Regards,

Hypoviax
0
HypoviaxAuthor Commented:
Ok, was typing before i posted that last comment (didn't refresh), I'll make that change

Regards,

Hypoviax
0
Wim ten BrinkSelf-employed developerCommented:
Still, while psAPI might still be supported by LongHorn, I just wonder why the WMI version failed. It's weird since I would assume that MS would never stop supporting WMI but it might drop psAPI in the future.
0
HypoviaxAuthor Commented:
I agree, it is very strange. It seems there is nothing actually wrong with it but that in some systems (such as mine) a certain access level is required (despite the fact i have administrator rights). But thanks for your efforts Alex, they are much appreciated and i may end up using your code if i can determine what the problem is and how to overcome it

Regards,

Hypoviax
0
Eddie ShipmanAll-around developerCommented:
Alex,
 Some systems may not have the WMI service running. If I'm not mistaken you can turn it off.
0
HypoviaxAuthor Commented:
Is the service called : WMI Performance Adapter.

If so even when i started it (it was not running) i still recieved the Access Denied message. Perhaps i needed to restart ?

Regards,

Hypoviax
0
HypoviaxAuthor Commented:
Windows Management Instruction (WMI) service was on and to no effect of the error message
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.