?
Solved

Processes Filepaths

Posted on 2004-11-11
25
Medium Priority
?
2,931 Views
Last Modified: 2010-04-05
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
0
Comment
Question by:Hypoviax
  • 15
  • 3
  • 3
  • +2
25 Comments
 
LVL 4

Expert Comment

by:Mikho
ID: 12563378
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
 
LVL 17

Assisted Solution

by:Wim ten Brink
Wim ten Brink earned 664 total points
ID: 12563800
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
 
LVL 26

Accepted Solution

by:
Eddie Shipman earned 672 total points
ID: 12567410
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
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 5

Author Comment

by:Hypoviax
ID: 12570741
Thanks,

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

Regards,

Hypoviax
0
 
LVL 5

Author Comment

by:Hypoviax
ID: 12570842
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12570859
Solved it don't worry, needed ActiveX
0
 
LVL 5

Author Comment

by:Hypoviax
ID: 12570878
Haha, "Access Denied" !

What's this mean ?
0
 
LVL 5

Author Comment

by:Hypoviax
ID: 12571097
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
 
LVL 26

Expert Comment

by:Eddie Shipman
ID: 12571642
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
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12576360
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12576579
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
 
LVL 20

Assisted Solution

by:Madshi
Madshi earned 664 total points
ID: 12587023
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12587839
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
 
LVL 20

Expert Comment

by:Madshi
ID: 12588145
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12588198
Hmmm...that might be the problem with Eddie's code then, let me test...
0
 
LVL 5

Author Comment

by:Hypoviax
ID: 12588233
Yep, that fixed Eddie's problem...

0
 
LVL 5

Author Comment

by:Hypoviax
ID: 12588272
Well Madshi, i think you've fixed the problem enough for me to award points ... :-)

0
 
LVL 20

Expert Comment

by:Madshi
ID: 12588320
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12588346
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12588370
Ok, was typing before i posted that last comment (didn't refresh), I'll make that change

Regards,

Hypoviax
0
 
LVL 17

Expert Comment

by:Wim ten Brink
ID: 12592936
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12597771
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
 
LVL 26

Expert Comment

by:Eddie Shipman
ID: 12599129
Alex,
 Some systems may not have the WMI service running. If I'm not mistaken you can turn it off.
0
 
LVL 5

Author Comment

by:Hypoviax
ID: 12600285
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
 
LVL 5

Author Comment

by:Hypoviax
ID: 12600287
Windows Management Instruction (WMI) service was on and to no effect of the error message
0

Featured Post

Keep up with what's happening at Experts Exchange!

Sign up to receive Decoded, a new monthly digest with product updates, feature release info, continuing education opportunities, and more.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Introduction I have seen many questions in this Delphi topic area where queries in threads are needed or suggested. I know bumped into a similar need. This article will address some of the concepts when dealing with a multithreaded delphi database…
In my programming career I have only very rarely run into situations where operator overloading would be of any use in my work.  Normally those situations involved math with either overly large numbers (hundreds of thousands of digits or accuracy re…
this video summaries big data hadoop online training demo (http://onlineitguru.com/big-data-hadoop-online-training-placement.html) , and covers basics in big data hadoop .
Screencast - Getting to Know the Pipeline
Suggested Courses
Course of the Month14 days, 19 hours left to enroll

840 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