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.

5
demandé sur Community 2015-06-04 14:39:25

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()
5
répondu Ansgar Wiechers 2017-07-12 08:00:11

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

2
répondu bluuf 2015-06-04 16:52:39

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...
1
répondu SomeShinyObject 2015-06-04 12:03:10

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.

0
répondu noseratio 2015-06-05 22:29:57

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 .

0
répondu Χpẘ 2015-09-24 18:11:11

cela peut vous être utile:

Get-Process | Where-Object {$_.Name -Match "iexplore"} | Stop-Process
-2
répondu Andrei C 2018-08-01 19:59:42