Comment fermer correctement Internet Explorer lorsqu'il est lancé depuis PowerShell?
il y avait un ensemble de questions récemment posées sur faire quelque chose avec Internet Explorer via PowerShell. Tous contiennent des codes pour lancer IE à partir de PowerShell en tant qu'objet, via $ie=new-object -comobject InternetExplorer.Application
. Le problème est, la bonne façon de fermeture IE qui consiste à appeler $ie.quit()
ne fonctionne pas-d'abord, si ce IE se serait produit d'avoir plus d'un seul onglet ouvert, IE ne démissionne pas dans son ensemble et seulement l'onglet qui correspond à L'objet COM est fermé, et deuxièmement, s'il n'y a qu'un seul onglet, la fenêtre se ferme mais les processus restent.
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
j'ai essayé de rechercher les méthodes sur la façon de fermer un processus commencé via New-Object -ComObject
, et j'ai trouvé ceci: comment se débarrasser d'un COMObject . L'exemple de ce COMObject est Excel.Application
, qui se comporte en effet comme prévu-appelant quit()
rend la fenêtre fermée, et l'exécution [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ex)
si $ex
est une création COMObject arrête le processus Excel. Mais ce n'est pas le cas avec Internet Explorer.
j'ai aussi trouvé cette question: Comment obtenir un objet COM existant D'un IE en cours D'exécution qui fournit le code pour se connecter à IE via la liste des fenêtres ouvertes, et fonctionne à un degré D'IE lancé d'ailleurs, mais si L'objet COM est créé via PowerShell, ce script n'est pas en mesure d'arrêter complètement les processus D'IE, si modifié comme tel:
$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)
}
}
dans le cas D'IE lancé à L'extérieur de PowerShell, les processus sont arrêtés, mais dans le cas D'IE lancé à L'intérieur de PowerShell, il reste deux processus, et ce code signale n'avoir trouvé aucune fenêtre IE pour atteindre les objets COM, donc les processus IE ne peuvent pas (encore) être arrêtés.
alors, comment atteindre les processus de IE apparemment orphelins sans fenêtre et gracieusement les arrêter? Je suis au courant de Get-Process iexplore | Stop-Process
, mais cela va arrêter n'importe quel et tous les seulement ceux lancés par le script, et si le script est exécuté en tant qu'administrateur ou SYSTEM
sur un, disons, serveur de bureau distant, tout le monde sera arrêté.
environnement: OS Windows 7 x64, PowerShell 4 (installé au-dessus de la version PS 2), IE11 version 11.0.9600.17691 (mis à jour automatiquement). IE défini à "ouvrir la page d'accueil" au début, donc au moins un onglet est toujours ouvert.
6 réponses
appeler simplement la méthode Quit()
devrait normalement suffire pour mettre fin avec élégance aux processus Internet Explorer, qu'ils aient été créés en exécutant iexplore.exe
ou en instanciant un objet COM dans PowerShell.
démonstration:
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:\> _
si vous avez des processus orphelins D'Internet Explorer auxquels vous n'avez pas de poignée, vous pouvez les parcourir comme ceci:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
}
à soyez totalement sur le côté sûr, vous pouvez libérer L'objet COM après avoir appelé Quit()
et puis attendre que le collecteur d'ordures à nettoyer:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
[Runtime.Interopservices.Marshal]::ReleaseComObject($_)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
j'ai eu des problèmes similaires avec les objets COM qui ne se termineraient pas en utilisant la méthode quit (). Interopservices.marshall aussi ne travaille pas souvent. Mon contournement : je fais un get-process pour obtenir une liste de tous les procs avant d'appeler l'objet com et juste après : de cette façon j'ai le PID de mon instance. Après l'exécution de mon script, il tue le processus en utilisant le stop-process.
Pas la meilleure façon de le faire, mais au moins ça marche
avec un rapide coup d'oeil autour dans le ComObject pour IE, il semble que quand il est créé, il vous donne une interface directe aux méthodes qui rendent l'interaction avec IE plus facile, par exemple Navigate()
ou ReadyState
.
j'ai découvert une propriété qui semble être ce que vous cherchez et qui serait Parent
appelant $IE.Parent.Quit()
semblait se débarrasser des instances créées par PowerShell.
$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...
il y a HKCU\Software\Microsoft\Internet Explorer\Main\FrameMerging
clé de Registre qui empêche la fusion IE" frame "processus, expliqué ici . Je ne l'ai pas essayé moi-même, mais je pense que cela pourrait résoudre votre problème si vous définissez avant vous instanciez l'objet COM InternetExplorer.Application
.
si cela n'aide pas, essayez de lancer une nouvelle instance IE avec la ligne de commande suivante, avant pour créer L'objet COM (Je n'ai pas essayé cela, soit):
-
iexplore.exe -noframemerging -private -embedding
il y a une condition de course possible avant que cette instance IE ne devienne disponible en tant que serveur COM, donc vous pouvez vouloir mettre un certain délai avant de créer un objet.
j'ai essayé une expérience avec Powershell launching Excel via 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
dès Que [GC]::collect() a terminé le processus disparu de taskmgr. Cela a du sens pour moi, parce que (d'après ce que j'ai compris) un client COM est responsable de la publication des références au serveur COM. Dans ce cas, .NET (via un Wrapper Callable Runtime) est le client COM.
la situation peut être plus compliquée avec IE, car il peut y avoir d'autres onglets associé à un processus IE donné (et il y a la fusion de cadre que @Noseratio mentionne), mais au moins cela va se débarrasser de la référence créée par le script PS .
cela peut vous être utile:
Get-Process | Where-Object {$_.Name -Match "iexplore"} | Stop-Process