There was a set of recently asked questions about doing something with Internet Explorer via PowerShell. All of them contain codes to launch IE from PowerShell as an object, via $ie=new-object -comobject InternetExplorer.Application
. The problem is, the proper way of closing IE that consists of calling $ie.quit()
does not work - first, if that IE would have happened to have more than a single open tab, IE doesn't quit as a whole and only the tab that corresponds to the COM object is closed, and second, should it have only one tab, the window gets closed but the processes remain.
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
I have tried to research the methods on how to close a process started via New-Object -ComObject
, and have found this: How to get rid of a COMObject. The example of that COMObject is Excel.Application
, which indeed behaves as intended - calling quit()
makes window close, and executing [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ex)
if $ex
is a created COMObject stops the Excel process. But this is not the case with Internet Explorer.
I have also found this question: How to get existing COM Object of a running IE which provides code to connect to IE via list of open windows, and works to an extent of IE launched from elsewhere, but if the COM object is created via PowerShell, this script is not able to completely stop IE's processes, if modified as such:
$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)
}
}
In case of IE launched outside of PowerShell, the processes are stopped, but in case of IE launched within PowerShell, two processes remain, and this code reports to have found no IE windows to reach COM objects, therefore IE processes are (yet) unable to be stopped.
So, how to reach the apparently orphaned windowless IE processes and gracefully stop them? I am aware of Get-Process iexplore | Stop-Process
, but this will stop any and all IEs, not just those launched by the script, and if the script is run as administrator or SYSTEM
on a, say, remote desktop server, everyone's IEs will be stopped.
Environment: OS Windows 7 x64, PowerShell 4 (installed above PS version 2), IE11 version 11.0.9600.17691 (automatically updated). IE set to "Open home page" upon starting, so at least one tab is always open.
Simply calling the Quit()
method should normally suffice for gracefully terminating Internet Explorer processes, regardless of whether they were created by running iexplore.exe
or by instantiating a COM object in PowerShell.
Demonstration:
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:\> _
If you have orphaned Internet Explorer processes to which you don't have a handle you can cycle through them like this:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
}
To be totally on the safe side you can release the COM object after calling Quit()
and then wait for the garbage collector to clean up:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
[Runtime.Interopservices.Marshal]::ReleaseComObject($_)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()