How to properly close Internet Explorer when launched from PowerShell?

后端 未结 6 1915
长情又很酷
长情又很酷 2020-12-06 06:43

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,

相关标签:
6条回答
  • 2020-12-06 07:12

    This may be useful to you:

    Get-Process | Where-Object {$_.Name -Match "iexplore"} | Stop-Process
    
    0 讨论(0)
  • 2020-12-06 07:13

    With a quick look around in the ComObject for IE, it seems that when it is created, it gives you a direct interface to the methods that make interacting with IE easier, for example Navigate() or ReadyState.

    I did discover a property that seems to be what you are looking for and that would be Parent

    Calling $IE.Parent.Quit() seemed to get rid of the PowerShell created instances.

    $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 讨论(0)
  • 2020-12-06 07:19

    I tried an experiment with 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
    

    As soon as [GC]::collect() finished the process disappeared from taskmgr. This makes sense to me, because (in my understanding) a COM client is responsible for releasing references to the COM server. In this case .NET (via a Runtime Callable Wrapper) is the COM client.

    The situation may be more complicated with IE, since there may be other tabs associated with a given IE process (and there's the frame merging that @Noseratio mentions), but at least this will get rid of the reference created by the PS script .

    0 讨论(0)
  • 2020-12-06 07:21

    There's HKCU\Software\Microsoft\Internet Explorer\Main\FrameMerging registry key that prevents merging IE "frame" processes, explained here. I haven't tried it myself, but I think it might solve your problem if you set it before you instantiate the InternetExplorer.Application COM object.

    If that doesn't help, try launching a new IE instance with the following command line, prior to creating the COM object (I haven't tried that, either):

    • iexplore.exe -noframemerging -private -embedding

    There is a possible race condition before this IE instance becomes available as a COM server, so you may want to put some delay before you create an object.

    0 讨论(0)
  • 2020-12-06 07:22

    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()
    
    0 讨论(0)
  • 2020-12-06 07:29

    I've had similar problems with COM objects that wouldn't terminate using the quit() method. Interopservices.marshall also doesn't work a lot of times. My workaround : I do a get-process to get a list of all procs before I call the com object and right after : this way I have the PID of my instance. After my script runs it kills the process using stop-process.

    Not the best way to do this but at least it works

    0 讨论(0)
提交回复
热议问题