Jump to content

How to get a PID from ShellExecute("iexplore.exe")


Recommended Posts

is there anyway that i can get a PID from using this function at all ShellExecute("iexplore.exe", "about:blank")

I know you can get a PID from $PID = Run("C:\Program Files\Internet Explorer\IEXPLORE.EXE") but how can you use RUN with IE to open up IE to a Certain page like ("about:blank")..??

Link to comment
Share on other sites

The standard WinAPI call *does* return a process handle, with which a process ID can be extracted.

Why the AutoIT developers chose not to return a PID I don't know, but it would have proven useful.

See the below code, adapted from MrCreatoR's function of the same name

-Ascend4nt

; =================================================================================================
; Func _ShellExecuteEx($sCmd, $sParams = "", $sFolder = "", $sVerb = "", $iState = @SW_SHOWNORMAL,$bCloseProcessHandle=True)
;
; Parameters are the same as ShellExecute(), except for the addition of:
;
; $bCloseProcessHandle = If True (recommended unless planning on using Process Handles),
;           then the Process Handle (if received) is closed
;       If False, the Process Handle (if received) is returned - this can be used to do additional work with Processes
;       Usage is mostly recommended for the _ShellExecuteExWait() function and/or getting exit code.
;
; Return is different from ShellExecute() in the following way:
;   Success: @error = 0, and either the process ID (if $bCloseProcessHandle is True, and process ID received) is returned,
;     or a 2-element array (if $bCloseProcessHandle is False):
;       $array[0]=Process ID if new process started (and process handle+ID received),
;       $array[1]=Process Handle if new process started (and process handle received)
;   Failure: @error set and 0 is returned
;       @error = 1 = parameters error
;       @error = 2 = call failed (probably due to parameter error. can use _WinAPI_GetLastError())
;
; NOTE: Recommended to run on Windows 2000 or higher because:
;   According to Microsoft at http://msdn2.microsoft.com/en-us/library/bb762154.aspx,
;   Windows 95/98/Me: ShellExecuteEx is supported by the Microsoft Layer for Unicode (MSLU).
;   To use this, you must add certain files to your application,
;       as outlined in Microsoft Layer for Unicode on Windows Me/98/95 Systems.
;   So it appears it will break on those machines without MSLU(?)
;
;   Initial Code from MrCreatoR on AutoIt Forum Topic:: http://www.autoitscript.com/forum/index.php?showtopic=69868
;   Enhancements/Modifications by: Ascend4nt
;   (including process handle/ID extract & close-handle code, plus Unicode/path enhancements, & CoInitializeEx call)
; =================================================================================================

Func _ShellExecuteEx($sCmd, $sParams = "", $sFolder = "", $sVerb = "", $iState = @SW_SHOWNORMAL,$bCloseProcessHandle=True)
    Local $stINFO,$stVerb,$stPath,$stArgs,$stWDir,$aRet,$hWnd=0,$aProcessArray[2]=[0,0]
    Local $iParamLen,$iCmdLen,$iFolderLen
    

    $iParamLen=StringLen($sParams)
    ; Verify string lengths are less than maximum.
    ; Through testing, 1997 (1996+1 NULL-term) is the maximum parameter length for this call (Unicode)
    If $iParamLen>1996 Then Return SetError(1,0,0)
    
    $iCmdLen=StringLen($sCmd)
    ; Verify string lengths are less than maximum. [MAX_PATH is 260, but Unicode allows exceptions]
    ; 32767 max length for Unicode strings if prefixed with '\\?\'
    If $iCmdLen>259 Then
        ; 32767-NULL=32766 - 4 (\\?\) = 32762
        If $iCmdLen>(32766-4) Then Return SetError(1,0,0)
        $sCmd='\\?\' & $sCmd
    EndIf       
    
    $iFolderLen=StringLen($sFolder)
    ; Verify string lengths are less than maximum. [MAX_PATH is 260, but Unicode allows exceptions]
    ; 32767 max length for Unicode strings if prefixed with '\\?\'
    If $iFolderLen>259 Then
        ; 32767-NULL=32766 - 4 (\\?\) = 32762
        If $iFolderLen>(32766-4) Then Return SetError(1,0,0)
        $sFolder='\\?\' & $sFolder
    EndIf
        
    ; Setup string structures
    $stVerb = DllStructCreate("wchar["&(StringLen($sVerb)+1)&"]")
    $stPath = DllStructCreate("wchar[" &($iCmdLen+1)& "];wchar")
    $stArgs = DllStructCreate("wchar[" &($iParamLen+1)& "];wchar")
    $stWDir = DllStructCreate("wchar[" &($iFolderLen+1)& "];wchar")
    
    ; Initialize string structures (which are then used by pointer in the larger SHELLEXECUTEINFO structure)
    DllStructSetData($stVerb, 1, $sVerb)
    DllStructSetData($stPath, 1, $sCmd)
    DllStructSetData($stWDir, 1, $sFolder)
    DllStructSetData($stArgs, 1, $sParams)
    
    ; SHELLEXECUTEINFO structure
    $stINFO = DllStructCreate("ulong;ulong;long;ptr;ptr;ptr;ptr;long;long;ptr;ptr;long;ulong;long;long")

    ; SHELLEXECUTEINFO structure initialize
    DllStructSetData($stINFO, 1, DllStructGetSize($stINFO)) ; cbSize, size (in bytes) of structure
    ; ------------------------------------------------------------------------------------------------------
    ; fMask Options:
    ;   0x40 = SEE_MASK_NOCLOSEPROCESS. The 15th element in structure (hProcess) will be set with the Process handle
    ;       NOTE: per MSDN, this handle *must* be closed by the caller. (similar to how "OpenProcess" must use "CloseProcess")
    ;   0x400 = SEE_MASK_FLAG_NO_UI = Do not display an error message box if an error occurs.
    ;       This is not default ShellExecute() behavior, which will display the error message box
    ; ------------------------------------------------------------------------------------------------------
    DllStructSetData($stINFO, 2, BitOR(0x40,0x400)) ; fMask
    ; HWND - MSDN: A window handle to any message boxes that the system might produce while executing this function.
    DllStructSetData($stINFO, 3, $hWnd) ; Is this supposed to *receive* instead of send? I have yet to get clarity on this.
    DllStructSetData($stINFO, 4, DllStructGetPtr($stVerb))  ; lpVerb: pointer to the verb string
    DllStructSetData($stINFO, 5, DllStructGetPtr($stPath))  ; lpFile: pointer to the $cmd string
    DllStructSetData($stINFO, 6, DllStructGetPtr($stArgs))  ; lpParameters: pointer to the parameters/arguments string
    DllStructSetData($stINFO, 7, DllStructGetPtr($stWDir))  ; lpDirectory: pointer to working directory string
    DllStructSetData($stINFO, 8, $iState)   ; nShow = state to show window as

#cs     
    ; ------------------------------------------------------------------------------------------------------
    ; Per MSDN Documentation, the following call should be done prior to calling ShellExecuteEx:
    ; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) ; COINIT_APARTMENTTHREADED = 0x2,COINIT_DISABLE_OLE1DDE = 0x4
    ; Reason:
    ;   "Because ShellExecuteEx can delegate execution to Shell extensions (data sources, context menu handlers, verb implementations)
    ;   that are activated using Component Object Model (COM), COM should be initialized before ShellExecuteEx is called.
    ;   Some Shell extensions require the COM single-threaded apartment (STA) type. In that case, COM should be initialized as shown here.
    ;   There are certainly instances where ShellExecuteEx does not use one of these types of Shell extension and those instances would not
    ;   require COM to be initialized at all. Nonetheless, it is good practice to always initalize COM before using this function."
    ; ------------------------------------------------------------------------------------------------------
#ce
    DllCall("ole32.dll", "int", "CoInitializeEx", "ptr", Chr(0), "dword", BitOR(2,4))
    ; I don't care if it succeeds. Doesn't seem to make much difference even to call it.
        
    $aRet=DllCall("shell32.dll", "int", "ShellExecuteExW", "ptr", DllStructGetPtr($stINFO))

    If @error Or Not $aRet[0] Then
        ; DLLStructDelete()'s:
        $stINFO=0
        $stVerb=0
        $stPath=0
        $stArgs=0
        $stWDir=0
        Return SetError(2,@error,0)
    EndIf
    ; Get Process Handle, if one exists (non-NULL if new process started, otherwise
    ;   NULL if app that performs 'verb' is already running, or is perhaps a 'properties' dialog etc)
    $aProcessArray[1]=DllStructGetData($stINFO,15)
    ; Get Process ID from Handle
    If ($aProcessArray[1]) Then
        $aRet=DllCall("Kernel32.dll","dword","GetProcessId","long",$aProcessArray[1])
        If IsArray($aRet) Then $aProcessArray[0]=$aRet[0]
    EndIf
    
    ;ConsoleWrite("Handle passed to function:" & Number($hWnd) & ", Handle AFTER call:" & Number(DllStructGetData($stINFO,3)) & @CRLF)
    ;ConsoleWrite("Process Handle:" & Number($hProcess) & ", Process ID:" & Number($vProcessID) & @CRLF)

    ; Close Handle
    If $bCloseProcessHandle And $aProcessArray[1] Then DllCall('kernel32.dll','ptr', 'CloseHandle','ptr', $aProcessArray[1])
    
    ; DLLStructDelete()'s:
    $stINFO=0
    $stVerb=0
    $stPath=0
    $stArgs=0
    $stWDir=0
    If ($bCloseProcessHandle) Then Return SetError(0,0,$aProcessArray[0])
    SetError(0,0)
    Return $aProcessArray
EndFunc
Link to comment
Share on other sites

  • 10 months later...

How can one use this, is it possible to have an example? I have done this so far but...

#include <EventLog.au3>
#include <Array.au3>

Global $KillApp[4]
$KillApp[1] = "skype.exe"
$KillApp[2] = "skypePM.exe"
$KillApp[3] = "skypenames2.exe"

kill_before_application()
$PID = _ShellExecuteEx("desk.cpl", @SystemDir & "\")
Sleep(1000)
run_application("StarCraft.exe", @WorkingDir & "\")
ProcessClose($PID)

Func kill_before_application()
For $i = 1 To $KillApp[0] Step 1
  If ProcessExists($KillApp[$i]) Then
   ProcessClose($KillApp[$i])
   If @error Then
    _put_event(1, "Application :" & $KillApp[$i] & " not terminated", @error)
   Else
    _put_event(4, "Application :" & $KillApp[$i] & " terminated", @error)
   EndIf
  EndIf
Next
EndFunc   ;==>kill_before_application

Func kill_after_application($PID1)
If ProcessExists($PID1) Then
  ProcessClose($PID1)
  If @error Then
   _put_event(1, "process :" & $PID1 & " not terminated", @error)
  Else
   _put_event(4, "process :" & $PID1 & " terminated", @error)
  EndIf
EndIf
EndFunc   ;==>kill_after_application

Func start_application($process)
If Not ProcessExists($process) Then Run($process)
EndFunc   ;==>start_application

Func run_application($application, $directory)
Local $val = RunWait($application, $directory, @SW_MAXIMIZE)
If @error Then
  _put_event(1, "Application :" & $application & " not started exit code :" & $val, @error)
Else
  _put_event(4, "Application :" & $application & " stared", @error)
EndIf
EndFunc   ;==>run_application

Func _put_event($value, $text, $error_id_code)
;SUCCESS = 0
;ERROR =1
;WARNING =2
;INFORMATION =4
;AUDIT_SUCCESS =8
;AUDIT_FAILURE =16
Local $hEventLog, $aData[4] = [3, 1, 2, 3]

$hEventLog = _EventLog__Open("", "StarCraft Loader")
_EventLog__Report($hEventLog, $value, 0, $error_id_code, @UserName, @CRLF & $text, $aData)
_EventLog__Close($hEventLog)
EndFunc   ;==>_put_event

; =================================================================================================
; Func _ShellExecuteEx($sCmd, $sParams = "", $sFolder = "", $sVerb = "", $iState = @SW_SHOWNORMAL,$bCloseProcessHandle=True)
;
; Parameters are the same as ShellExecute(), except for the addition of:
;
; $bCloseProcessHandle = If True (recommended unless planning on using Process Handles),
;           then the Process Handle (if received) is closed
;       If False, the Process Handle (if received) is returned - this can be used to do additional work with Processes
;       Usage is mostly recommended for the _ShellExecuteExWait() function and/or getting exit code.
;
; Return is different from ShellExecute() in the following way:
;   Success: @error = 0, and either the process ID (if $bCloseProcessHandle is True, and process ID received) is returned,
;   or a 2-element array (if $bCloseProcessHandle is False):
;       $array[0]=Process ID if new process started (and process handle+ID received),
;       $array[1]=Process Handle if new process started (and process handle received)
;   Failure: @error set and 0 is returned
;       @error = 1 = parameters error
;       @error = 2 = call failed (probably due to parameter error. can use _WinAPI_GetLastError())
;
; NOTE: Recommended to run on Windows 2000 or higher because:
;   According to Microsoft at [url="http://msdn2.microsoft.com/en-us/library/bb762154.aspx"]http://msdn2.microsoft.com/en-us/library/bb762154.aspx[/url],
;   Windows 95/98/Me: ShellExecuteEx is supported by the Microsoft Layer for Unicode (MSLU).
;   To use this, you must add certain files to your application,
;       as outlined in Microsoft Layer for Unicode on Windows Me/98/95 Systems.
;   So it appears it will break on those machines without MSLU(?)
;
;   Initial Code from MrCreatoR on AutoIt Forum Topic:: [url="http://www.autoitscript.com/forum/index.php?showtopic=69868"]http://www.autoitscript.com/forum/index.php?showtopic=69868[/url]
;   Enhancements/Modifications by: Ascend4nt
;   (including process handle/ID extract & close-handle code, plus Unicode/path enhancements, & CoInitializeEx call)
; =================================================================================================

Func _ShellExecuteEx($sCmd, $sParams = "", $sFolder = "", $sVerb = "", $iState = @SW_SHOWNORMAL, $bCloseProcessHandle = True)
Local $stINFO, $stVerb, $stPath, $stArgs, $stWDir, $aRet, $hWnd = 0, $aProcessArray[2] = [0, 0]
Local $iParamLen, $iCmdLen, $iFolderLen


$iParamLen = StringLen($sParams)
; Verify string lengths are less than maximum.
; Through testing, 1997 (1996+1 NULL-term) is the maximum parameter length for this call (Unicode)
If $iParamLen > 1996 Then Return SetError(1, 0, 0)

$iCmdLen = StringLen($sCmd)
; Verify string lengths are less than maximum. [MAX_PATH is 260, but Unicode allows exceptions]
; 32767 max length for Unicode strings if prefixed with '\\?\'
If $iCmdLen > 259 Then
  ; 32767-NULL=32766 - 4 ([url="file://\?"]\\?\[/url]) = 32762
  If $iCmdLen > (32766 - 4) Then Return SetError(1, 0, 0)
  $sCmd = '\\?\' & $sCmd
EndIf

$iFolderLen = StringLen($sFolder)
; Verify string lengths are less than maximum. [MAX_PATH is 260, but Unicode allows exceptions]
; 32767 max length for Unicode strings if prefixed with '\\?\'
If $iFolderLen > 259 Then
  ; 32767-NULL=32766 - 4 ([url="file://\?"]\\?\[/url]) = 32762
  If $iFolderLen > (32766 - 4) Then Return SetError(1, 0, 0)
  $sFolder = '\\?\' & $sFolder
EndIf

; Setup string structures
$stVerb = DllStructCreate("wchar[" & (StringLen($sVerb) + 1) & "]")
$stPath = DllStructCreate("wchar[" & ($iCmdLen + 1) & "];wchar")
$stArgs = DllStructCreate("wchar[" & ($iParamLen + 1) & "];wchar")
$stWDir = DllStructCreate("wchar[" & ($iFolderLen + 1) & "];wchar")

; Initialize string structures (which are then used by pointer in the larger SHELLEXECUTEINFO structure)
DllStructSetData($stVerb, 1, $sVerb)
DllStructSetData($stPath, 1, $sCmd)
DllStructSetData($stWDir, 1, $sFolder)
DllStructSetData($stArgs, 1, $sParams)

; SHELLEXECUTEINFO structure
$stINFO = DllStructCreate("ulong;ulong;long;ptr;ptr;ptr;ptr;long;long;ptr;ptr;long;ulong;long;long")

; SHELLEXECUTEINFO structure initialize
DllStructSetData($stINFO, 1, DllStructGetSize($stINFO)) ; cbSize, size (in bytes) of structure
; ------------------------------------------------------------------------------------------------------
; fMask Options:
;   0x40 = SEE_MASK_NOCLOSEPROCESS. The 15th element in structure (hProcess) will be set with the Process handle
;       NOTE: per MSDN, this handle *must* be closed by the caller. (similar to how "OpenProcess" must use "CloseProcess")
;   0x400 = SEE_MASK_FLAG_NO_UI = Do not display an error message box if an error occurs.
;       This is not default ShellExecute() behavior, which will display the error message box
; ------------------------------------------------------------------------------------------------------
DllStructSetData($stINFO, 2, BitOR(0x40, 0x400)) ; fMask
; HWND - MSDN: A window handle to any message boxes that the system might produce while executing this function.
DllStructSetData($stINFO, 3, $hWnd) ; Is this supposed to *receive* instead of send? I have yet to get clarity on this.
DllStructSetData($stINFO, 4, DllStructGetPtr($stVerb)) ; lpVerb: pointer to the verb string
DllStructSetData($stINFO, 5, DllStructGetPtr($stPath)) ; lpFile: pointer to the $cmd string
DllStructSetData($stINFO, 6, DllStructGetPtr($stArgs)) ; lpParameters: pointer to the parameters/arguments string
DllStructSetData($stINFO, 7, DllStructGetPtr($stWDir)) ; lpDirectory: pointer to working directory string
DllStructSetData($stINFO, 8, $iState) ; nShow = state to show window as

#cs
  ; ------------------------------------------------------------------------------------------------------
  ; Per MSDN Documentation, the following call should be done prior to calling ShellExecuteEx:
  ; CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE) ; COINIT_APARTMENTTHREADED = 0x2,COINIT_DISABLE_OLE1DDE = 0x4
  ; Reason:
  ;   "Because ShellExecuteEx can delegate execution to Shell extensions (data sources, context menu handlers, verb implementations)
  ;   that are activated using Component Object Model (COM), COM should be initialized before ShellExecuteEx is called.
  ;   Some Shell extensions require the COM single-threaded apartment (STA) type. In that case, COM should be initialized as shown here.
  ;   There are certainly instances where ShellExecuteEx does not use one of these types of Shell extension and those instances would not
  ;   require COM to be initialized at all. Nonetheless, it is good practice to always initalize COM before using this function."
  ; ------------------------------------------------------------------------------------------------------
#ce
DllCall("ole32.dll", "int", "CoInitializeEx", "ptr", Chr(0), "dword", BitOR(2, 4))
; I don't care if it succeeds. Doesn't seem to make much difference even to call it.

$aRet = DllCall("shell32.dll", "int", "ShellExecuteExW", "ptr", DllStructGetPtr($stINFO))

If @error Or Not $aRet[0] Then
  ; DLLStructDelete()'s:
  $stINFO = 0
  $stVerb = 0
  $stPath = 0
  $stArgs = 0
  $stWDir = 0
  Return SetError(2, @error, 0)
EndIf
; Get Process Handle, if one exists (non-NULL if new process started, otherwise
;   NULL if app that performs 'verb' is already running, or is perhaps a 'properties' dialog etc)
$aProcessArray[1] = DllStructGetData($stINFO, 15)
; Get Process ID from Handle
If ($aProcessArray[1]) Then
  $aRet = DllCall("Kernel32.dll", "dword", "GetProcessId", "long", $aProcessArray[1])
  If IsArray($aRet) Then $aProcessArray[0] = $aRet[0]
EndIf

;ConsoleWrite("Handle passed to function:" & Number($hWnd) & ", Handle AFTER call:" & Number(DllStructGetData($stINFO,3)) & @CRLF)
;ConsoleWrite("Process Handle:" & Number($hProcess) & ", Process ID:" & Number($vProcessID) & @CRLF)

; Close Handle
If $bCloseProcessHandle And $aProcessArray[1] Then DllCall('kernel32.dll', 'ptr', 'CloseHandle', 'ptr', $aProcessArray[1])

; DLLStructDelete()'s:
$stINFO = 0
$stVerb = 0
$stPath = 0
$stArgs = 0
$stWDir = 0
If ($bCloseProcessHandle) Then Return SetError(0, 0, $aProcessArray[0])
SetError(0, 0)
Return $aProcessArray
EndFunc   ;==>_ShellExecuteEx


; T.D.L
; Need to find a way to close tyhe desk.cpl process
; way to automatically make sure that the application compatibility properties are all ticked

But still cannot close the process, when I run sysinternal Process Explorer and look for the number that your system generates it cannot be found, but the process is stll clearly running? am i doing anything wrong..

Edited by PeterAtkin

[topic='115020'] AD Domain Logon Script[/topic]

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...