web-dev-qa-db-fra.com

Comment fermer correctement Internet Explorer lorsqu'il est lancé à partir de PowerShell?

Une série de questions récemment posées sur l'utilisation d'Internet Explorer via PowerShell a été posée. 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 que la méthode correcte de fermeture IE consistant à appeler $ie.quit() ne fonctionne pas - d’abord, si ce IE aurait eu plus d’un Un seul onglet ouvert, IE ne se ferme pas dans son ensemble et seul l'onglet correspondant à l'objet COM est fermé. Deuxièmement, s'il ne comporte 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 démarré 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 comme prévu: l'appel de quit() fait fermer la fenêtre et l'exécution de [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ex) si $ex est créé. COMObject arrête le processus Excel. Mais ce n'est pas le cas avec Internet Explorer.

J'ai également trouvé cette question: Comment obtenir un objet COM existant d'un IE en cours d'exécution qui fournit le code permettant de se connecter à IE via une liste de fenêtres ouvertes et fonctionne dans la mesure où IE est lancé ailleurs, mais si l'objet COM est créé via PowerShell, ce script ne peut pas arrêter complètement les processus d'IE, s'il est modifié en tant que 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 où IE est lancé en dehors de PowerShell, les processus sont arrêtés, mais dans le cas où IE est lancé dans PowerShell, il reste deux processus et ce code indique n'avoir trouvé aucun IE fenêtres pour atteindre les objets COM. Par conséquent, les processus IE ne peuvent pas encore être arrêtés.

Alors, comment atteindre les processus IE sans fenêtre, apparemment orphelins, et gracieusement les arrêter? Je suis au courant de Get-Process iexplore | Stop-Process, mais cela arrêtera tous les IE, pas seulement ceux lancés par le script, et si le script est exécuté en tant qu'administrateur ou SYSTEM sur un serveur de bureau distant, par exemple, les IE de tous les utilisateurs seront arrêtés.

Environnement: SE Windows 7 x64, PowerShell 4 (installé au-dessus de PS version 2), IE11 version 11.0.9600.17691 (mis à jour automatiquement). IE défini sur "Ouvrir la page d'accueil" au démarrage, de sorte qu'au moins un onglet soit toujours ouvert.

6
Vesper

Le simple fait d'appeler la méthode Quit() devrait normalement suffire à mettre fin aux processus Internet Explorer, qu'ils aient été créés en exécutant iexplore.exe ou en instanciant un objet COM dans PowerShell.

Manifestation:

 PS C: \> $ env: PROCESSOR_ARCHITECTURE
 AMD64 
 PS C: \> (Get-WmiObject -Class Win32_OperatingSystem) .Caption
 Microsoft Windows 8.1 Entreprise 
 PS C: \> Get-Process | ? {$ _. ProcessName -eq 'iexplore'}
 PS C: \> $ ie = New-Object -COM 'InternetExplorer.Application'
 PS C: \> Get-Process | ? {$ _. ProcessName -eq 'iexplore'}
 
 Traite 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 Internet Explorer orphelins pour lesquels vous n'avez pas de descripteur, vous pouvez les parcourir de la manière suivante:

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

Pour être totalement sûr, vous pouvez libérer l'objet COM après avoir appelé Quit(), puis attendre le nettoyage du ramasse-miettes:

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

[GC]::Collect()
[GC]::WaitForPendingFinalizers()
6
Ansgar Wiechers

J'ai eu des problèmes similaires avec des objets COM qui ne se terminaient pas à l'aide de la méthode quit (). Interopservices.marshall ne fonctionne pas non plus très souvent. Ma solution de contournement: je fais un processus d'obtention 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, le processus est arrêté à l'aide de stop-process.

Pas la meilleure façon de faire cela, mais au moins ça marche

2
bluuf

En regardant rapidement dans ComObject pour IE, il semble que sa création offre une interface directe avec les méthodes qui facilitent l’interaction avec IE, par exemple Navigate() ou ReadyState.

J'ai découvert une propriété qui semble être ce que vous recherchez et qui serait Parent

L'appel de $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
SomeShinyObject

Il existe une clé de registre HKCU\Software\Microsoft\Internet Explorer\Main\FrameMerging qui empêche la fusion des processus IE "frame", expliqué ici . Je n'ai pas essayé moi-même, mais je pense que cela pourrait résoudre votre problème si vous le définissez avant vous instanciez l'objet InternetExplorer.Application COM.

Si cela ne vous aide pas, essayez de lancer une nouvelle instance IE avec la ligne de commande suivante, prior pour créer l'objet COM (je n'ai pas essayé non plus):

  • iexplore.exe -noframemerging -private -embedding

Il existe une condition de concurrence critique avant que cette instance IE ne devienne disponible en tant que serveur COM. Par conséquent, vous souhaiterez peut-être attendre un peu avant de créer un objet.

0
noseratio

Cela peut vous être utile:

Get-Process | Where-Object {$_.Name -Match "iexplore"} | Stop-Process
0
Andrei C

J'ai essayé une expérience avec Powershell en lançant 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 a disparu de taskmgr. Cela me semble logique, car (à ma connaissance), un client COM est responsable de la publication des références au serveur COM. Dans ce cas, .NET (via un Runtime Callable Wrapper) est le client COM.

La situation peut être plus compliquée avec IE, car il peut y avoir d'autres onglets associés à un processus donné IE (et le cadre fusionnant que @Noseratio mentionne), mais au moins, cela supprimera la référence créée. par le script PS.

0
Χpẘ