2015-06-04 21 views
5

Pojawił się zestaw ostatnio zadawanych pytań dotyczących robienia czegoś z Internet Explorerem za pośrednictwem PowerShell. Wszystkie zawierają kody do uruchamiania IE z PowerShell jako obiektu, poprzez $ie=new-object -comobject InternetExplorer.Application. Problem polega na tym, że właściwa metoda blokowania IE, która składa się z wywołania $ie.quit(), nie działa - po pierwsze, jeśli IE miałby mieć więcej niż jedną otwartą kartę, IE nie wychodzi jako całość i tylko karta który odpowiada obiektowi COM jest zamknięty, a po drugie, jeśli ma tylko jedną kartę, okno zostaje zamknięte, ale procesy pozostają.Jak prawidłowo zamknąć Internet Explorer po uruchomieniu z PowerShell?

PS > get-process iexplore 

Handles NPM(K) PM(K)  WS(K) VM(M) CPU(s)  Id ProcessName 
------- ------ -----  ----- ----- ------  -- ----------- 
    352  31  7724  24968 142  0,58 3236 iexplore 
    228  24 22800  15384 156  0,19 3432 iexplore 

Próbowałem metod badawczych, jak zamknąć proces rozpoczęty przez New-Object -ComObject, i odkryli to: How to get rid of a COMObject. Przykładem tego COMObject jest Excel.Application, który rzeczywiście zachowuje się zgodnie z przeznaczeniem - wywołanie quit() powoduje zamknięcie okna, a wykonanie [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ex), jeśli $ex jest utworzonym COMObject, zatrzymuje proces Excel. Ale tak nie jest w przypadku Internet Explorera.

Znalazłem również to pytanie: How to get existing COM Object of a running IE, które zapewnia kod do łączenia się z IE za pośrednictwem listy otwartych okien i działa w pewnym zakresie IE uruchamiany z innego miejsca, ale jeśli obiekt COM jest tworzony za pomocą PowerShell, ten skrypt nie jest w stanie całkowicie powstrzymać procesy tj, w przypadku zmian jako takich:

$shellapp = New-Object -ComObject "Shell.Application" 
$ShellWindows = $shellapp.Windows() 
for ($i = 0; $i -lt $ShellWindows.Count; $i++) 
{ 
if ($ShellWindows.Item($i).FullName -like "*iexplore.exe") 
    { 
    $ie = $ShellWindows.Item($i) 
    $ie.quit() 
    [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie) 
    } 
} 

w przypadku IE uruchomiony poza PowerShell, procesy są zatrzymane, ale w przypadku IE uruchomiony w ciągu PowerShell dwa procesy pozostają, a to raporty kod nie znaleźli okien IE, aby dotrzeć do obiektów COM, dlatego procesy IE (jeszcze) nie mogą zostać zatrzymane.

A zatem, jak osiągnąć pozornie osierocone procesy bez okien IE i z wdziękiem je zatrzymać? Jestem świadomy Get-Process iexplore | Stop-Process, ale to zatrzyma wszystkie IE, nie tylko te uruchomione przez skrypt, a jeśli skrypt zostanie uruchomiony jako administrator lub SYSTEM na, powiedzmy, serwerze zdalnego pulpitu, wszystkie IE zostaną zatrzymane.

Środowisko: System operacyjny Windows 7 x64, PowerShell 4 (zainstalowany powyżej wersji PS 2), wersja IE11 11.0.9600.17691 (automatycznie aktualizowany). IE ustawiony na "Otwórz stronę początkową" po uruchomieniu, więc przynajmniej jedna zakładka jest zawsze otwarta.

Odpowiedz

4

prostu wywołanie metody Quit() normalnie powinny wystarczyć do zakończenia procesów wdzięcznie Internet Explorer, niezależnie od tego, czy zostały one utworzone przez uruchomiony iexplore.exe lub przez utworzenie obiektu COM w PowerShell.

Demonstracja:

 
PS C:\>$env:PROCESSOR_ARCHITECTURE 
AMD64 
PS C:\>(Get-WmiObject -Class Win32_OperatingSystem).Caption 
Microsoft Windows 8.1 Enterprise 
PS C:\>Get-Process | ? { $_.ProcessName -eq 'iexplore' } 
PS C:\>$ie = New-Object -COM 'InternetExplorer.Application' 
PS C:\>Get-Process | ? { $_.ProcessName -eq 'iexplore' } 

Handles NPM(K) PM(K)  WS(K) VM(M) CPU(s)  Id ProcessName 
------- ------ -----  ----- ----- ------  -- ----------- 
    352  20  4244  14164 176  0.05 3460 iexplore 
    407  32  6428  23316 182  0.23 5356 iexplore 

PS C:\>$ie.Quit() 
PS C:\>Get-Process | ? { $_.ProcessName -eq 'iexplore' } 
PS C:\> _ 

Jeśli osierocone procesy Internet Explorer, do którego nie mają uchwyt Można przechodzić nich tak:

(New-Object -COM 'Shell.Application').Windows() | Where-Object { 
    $_.Name -like '*Internet Explorer*' 
} | ForEach-Object { 
    $_.Quit() 
} 

Aby być całkowicie na po bezpiecznej stronie możesz zwolnić obiekt COM po zadzwonieniu pod numer Quit(), a następnie poczekać, aż śmieciarz wyczyści:

(New-Object -COM 'Shell.Application').Windows() | Where-Object { 
    $_.Name -like '*Internet Explorer*' 
} | ForEach-Object { 
    $_.Quit() 
    [Runtime.Interopservices.Marshal]::ReleaseComObject($_) 
} 

[GC]::Collect() 
[GC]::WaitForPendingFinalizers() 
+0

Swoją drogą, właśnie tego próbowałem i zauważyłem interesującą rzecz. Kiedy wywołuję '[Runtime.Interopservices.Marshal] :: ReleaseComObject()' w stosunku do utworzonego COMObject IE zwraca wartość różną od zera - to najwyraźniej wskazuje, że jest więcej linków do tego obiektu COM w systemie. Również w moim przypadku rozpoczęto trzy procesy, a nie dwa. Może się to różnić w zależności od wersji IE i wersji systemu operacyjnego, więc sprawdzę ponownie na innych komputerach. Jak dotąd wygląda jak błąd wprowadzony w IE11 lub błąd w .NET 4.5, jeśli problem ten pojawiłby się również na komputerze z Win8.1 i IE11 (który nie jest dostępny teraz). – Vesper

+0

Hmm, może to być spowodowane tym, że system operacyjny to x64, dwa pozostałe procesy to jeden x64 i jeden x32, a zabicie tylko x32 daje efektowne zatrzymanie x64. Tak naprawdę może to być (jeszcze jeden) błąd w IE jawnie :) – Vesper

+0

@Vesper Wątpię. Demonstracja w mojej odpowiedzi pochodzi także z 64-bitowego systemu Windows 8.1 (z Internet Explorerem 11). –

1

Po krótkim rozejrzeniu się w programie ComObject dla IE wydaje się, że po utworzeniu daje on bezpośredni interfejs do metod ułatwiających interakcję z IE, na przykład Navigate() lub ReadyState.

zrobiłem odkryć właściwość, która wydaje się być to, czego szukasz i że byłoby Parent

Wywołanie $IE.Parent.Quit() wydawało się pozbyć PowerShell tworzone instancje.

$IE = New-Object -ComObject InternetExplorer.Application 
Get-Process | Where-Object {$_.Name -Match "iex"} 

Handles NPM(K) PM(K)  WS(K) VM(M) CPU(s)  Id ProcessName 
------- ------ -----  ----- ----- ------  -- ----------- 
    291  20  5464  14156 200  0.16 1320 iexplore 
    390  30  5804  20628 163  0.14 5704 iexplore 

$IE.Parent.Quit() 
(Get-Process | Where-Object {$_.Name -Match "iex"}).GetType() 
You cannot call a method on a null-valued expression... 
+0

Po prostu próbowałem, nie ma kości, pozostają dwa procesy. Również '$ ie.parent -eq $ ie' zwraca wartość true, więc nie powinno to być' parent'. Jaka jest twoja wersja OS, PS i IE? – Vesper

+0

Zauważyłem, że musiałaś to zrobić kilka sekund przed ponownym sprawdzeniem procesów. – SomeShinyObject

+0

Jedyną rzeczą, która różni się między naszymi konfiguracjami, jest wersja OS. Mam 8,1. Może złamane w 7? – SomeShinyObject

2

Miałem podobne problemy z obiektami COM, które nie zakończyłyby się przy użyciu metody quit(). Interopservices.marshall również nie działa wiele razy. Moje obejście: Wykonuję proces pobierania, aby uzyskać listę wszystkich proców, zanim zadzwonię do obiektu com i zaraz po nim: w ten sposób otrzymuję identyfikator PID mojej instancji. Po uruchomieniu skryptu zabija proces za pomocą stop-process.

nie jest najlepszym sposobem, aby to zrobić, ale przynajmniej działa

+0

Zastanawiam się, jakie systemy zachowują się w ten sposób. Być może istnieje nieodłączny błąd w niektórych wersjach systemu Windows lub ich modułach COM, które można wyprowadzić ze specyfikacji komputera. – Vesper

0

Jest HKCU\Software\Microsoft\Internet Explorer\Main\FrameMerging klucz rejestru, który uniemożliwia łączenie IE procesy „rama”, explained here. Nie próbowałem go samodzielnie, ale myślę, że może rozwiązać problem, jeśli ustawisz go przed tworzenie instancji obiektu COM InternetExplorer.Application.

Jeśli to nie pomoże, spróbuj rozpoczyna nową instancję IE z następującym wierszu poleceń uprzedniej do tworzenia obiektu COM (nie próbowałem, że obaj):

  • iexplore.exe -noframemerging -private -embedding

Istnieje możliwość wyścigu, zanim ta instancja IE stanie się dostępna jako serwer COM, więc przed utworzeniem obiektu może wystąpić pewne opóźnienie.

0

że próbował eksperyment z PowerShell uruchomienie programu Excel przez COM:

$x = New-Object -com Excel.Application 
$x.Visible = $True 
Start-Sleep 5 # make it stay visible for a little while 
$x.Quit() 
$x = 0 # Remove .NET's reference to com object 
[GC]::collect() # run garbage collection 

Zaraz [GC] :: zbierania() zakończeniu procesu zniknął taskmgr. To ma dla mnie sens, ponieważ (w moim rozumieniu) klient COM jest odpowiedzialny za publikowanie odniesień do serwera COM. W tym przypadku .NET (za pomocą Ralfime Callable Wrapper) jest klientem COM.

Sytuacja może być bardziej skomplikowana w IE, ponieważ mogą istnieć inne zakładki związane z danym procesem IE (i tam jest scalenie ramki, o której wspomina @Noseratio), ale przynajmniej pozbędzie się odniesienia stworzonego przez Skrypt PS.

Powiązane problemy