Что нового

[Процессы] Отслеживание окна\процесса вызываемого через ShellExecute

ArtInt

Знающий
Сообщения
135
Репутация
18
Собственно интересует вопрос - как это реализовать?

Возможно я подошёл не с той стороны, и вы сможете подсказать как обойти использование ShellExecute. Соль в том, что по нажатию на кнопочку у меня запускается программа (или открывается файл) через вышеупомянутую функцию. Необходимо предотвратить многократный запуск.
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
может обычным Run?
 

kaster

Мой Аватар, он лучший самый
Команда форума
Глобальный модератор
Сообщения
4,020
Репутация
626
попробуй вот эту функцию. по описанию, вроде отслеживает process ID
Код:
; =================================================================================================
; 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

взято отсюда How to get a PID from ShellExecute("iexplore.exe")
 
Верх