_WinAPI_ProcessGetFileName

I didn't want to rely on WMI because I hear all the time that it can be disabled, so I decided to write this function.

NOTES (updated): This requires Windows 2000 or higher. The function _WinAPI_ProcessGetFilename() will return filename or full pathname for everything other than running x86 (32-bit) code in x64 OS's. From what I understand, the filename will still return correctly, but for the pathname you need to go a different route. That is what my other UDF _WinAPI_ProcessGetPathname() addresses - which will work no matter what OS is running (erm, except Win2000 - but then that doesn't have a x64 version).
Also keep in mind that if an older version of PSAPI.DLL exists in the same folder this or AutoIT is run in, it will probably fail.

Another issue is the privilege level the program is running under, which is where you will need to set higher privileges (mentioned below), and shown in the example in post 11.

*UPDATED 3/8/09: Now AutoIT v3.3 compatible, or v3.2.12.1 *UNICODE*
*UPDATE 3/18/09: Better @error reporting, implemented some changes based on trancexx, adjusted Open/Close handle method slightly
*Update 8/12/09: Fixed CloseHandle return param

The below function is the simplest method, but as mentioned above the full-path part will not work correctly for x64 OS's - so see _WinAPI_ProcessGetPathname.

Ascend4nt's AutoIT Code License agreement:
While I provide this source code freely, if you do use the code in your projects, all I ask is that:
  1. If you provide source, keep the header as I have put it, OR, if you expand it, then at least acknowledge me as the original author, and any other authors I credit
  2. If the program is released, acknowledge me in your credits (it doesn't have to state which functions came from me, though  again if the source is provided - see #1)
  3. The source on it's own (as opposed to part of a project) can not be posted unless a link to the page(s) where the code were retrieved from is provided and a message stating that the latest updates will be available on the page(s) linked to.
  4. Pieces of the code can however be discussed on the threads where Ascend4nt has posted the code without worrying about further linking.

- _WinAPI_ProcessGetFileName.au3 CODE START -


#include-once

; ==================================================================================================
; <_WinAPI_ProcessGetFileName.au3>
;
; Function:
;   _WinAPI_ProcessGetFilename()
;
; Author: Ascend4nt
; ==================================================================================================

; ==================================================================================================
; Func _WinAPI_ProcessGetFilename($vProcessID,$bFullPath=False)
;
; Function to get the process executable filename, or full path name, for Process
;       using DLL calls rather than WMI.
;
; $vProcessID = either a process name ("explorer.exe") or Process ID (2314)
; $bFullPath = If True, return the full path to the executable. If False, just return the process executable filename.
;
; Returns:
;   Success: String - either full path or just executable name, based on $bFullPath parameter
;   Failure: "" empty string, with @error set:
;       @error = 1 = invalid parameter or process name not found to 'exist'
;       @error = 2 = DLL call error, use _WinAPI_GetLastError()
;       @error = 3 = Couldn't obtain Process handle
;       @error = 4 = empty string returned from call (possible privilege issue)
;
; Author: Ascend4nt
; ==================================================================================================

Func _WinAPI_ProcessGetFilename($vProcessID,$bFullPath=False)
    ; Not a Process ID? Must be a Process Name
    If Not IsNumber($vProcessID) Then
        $vProcessID=ProcessExists($vProcessID)
        ; Process Name not found (or invalid parameter?)
        If Not $vProcessID Then Return SetError(1,0,"")
    EndIf
   
    Local $sDLLFunctionName,$tErr
   
    ; Since the parameters and returns are the same for both of these DLL calls, we can keep it all in one function
    If $bFullPath Then
        $sDLLFunctionName="GetModuleFileNameExW"
    Else
        $sDLLFunctionName="GetModuleBaseNameW"
    EndIf  
   
    ; Get process handle
    Local $hProcess = DllCall('kernel32.dll','ptr', 'OpenProcess','int', BitOR(0x400,0x10),'int', 0,'int', $vProcessID)
    If @error Then Return SetError(2,0,"")
    If Not $hProcess[0] Then Return SetError(3,0,"")
   
    ; Create 'receiving' string buffers and make the call
    ; Path length size maximum in Unicode is 32767 (-1 for NULL)
    Local $stFilename=DllStructCreate("wchar[32767]")
    ; Make the call (same parameters for both)
    Local $aRet=DllCall("Psapi.dll","dword",$sDLLFunctionName,"ptr",$hProcess[0],"ptr",0,"ptr",DllStructGetPtr($stFilename),"dword",32767)
   
    If @error Then
        $tErr=2
    ElseIf Not $aRet[0] Then
        $tErr=4
    Else
        $tErr=0
    EndIf
    ; Close process handle
    DllCall('kernel32.dll','int', 'CloseHandle','ptr', $hProcess[0])
    ; Error above?
    If $tErr Then Return SetError($tErr,0,"")
   
    ;$stFilename should now contain either the filename or full path string (based on $bFullPath)
    Local $sFilename=DllStructGetData($stFilename,1)

    ; DLLStructDelete()'s
    $stFilename=0
    $hProcess=0

    SetError(0)
    Return $sFilename
EndFunc

; ==================================================================================================
; Test:
; ==================================================================================================

ConsoleWrite("Process full path for svchost.exe:" & _WinAPI_ProcessGetFilename("svchost.exe",True) & @CRLF)
Comments