从 PowerShell 启动时如何正确关闭 Internet Explorer?
How to properly close Internet Explorer when launched from PowerShell?
最近有一组关于通过 PowerShell 使用 Internet Explorer 执行某些操作的问题。它们都包含通过 $ie=new-object -comobject InternetExplorer.Application
从 PowerShell 作为对象启动 IE 的代码。问题是,包含调用 $ie.quit()
的 closing IE 的正确方法不起作用 - 首先,如果该 IE 碰巧有多个打开的选项卡, IE 不会作为一个整体退出,只有与 COM 对象对应的选项卡被关闭,其次,如果它只有一个选项卡,window 会关闭但进程仍然存在。
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
我试图研究如何关闭通过 New-Object -ComObject
启动的进程的方法,并找到了这个:How to get rid of a COMObject。该 COMObject 的示例是 Excel.Application
,它确实按预期运行 - 调用 quit()
使 window 关闭,如果 $ex
是创建的 COMObject 则执行 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ex)
停止Excel 过程。但是 Internet Explorer 却不是这样。
我还发现了这个问题:How to get existing COM Object of a running IE 它提供了通过打开 windows 列表连接到 IE 的代码,并且在从其他地方启动的 IE 的范围内工作,但是如果 COM 对象是通过PowerShell创建的,这个脚本不能完全停止IE的进程,如果这样修改:
$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)
}
}
如果 IE 在 PowerShell 之外启动,进程将停止,但如果 IE 在 PowerShell 内启动,则会保留两个进程,并且此代码报告没有找到 IE windows 来访问 COM 对象,因此 IE 进程(还)无法停止。
那么,如何到达明显孤立的 windowless IE 进程并优雅地 停止它们?我知道 Get-Process iexplore | Stop-Process
,但这将停止任何和所有 IE,而不仅仅是脚本启动的那些,如果脚本是 运行 作为管理员或 SYSTEM
在,比方说,远程桌面服务器,大家的IE都要停了
环境:
OS Windows 7 x64,PowerShell 4(安装在 PS 版本 2 之上),IE11 版本 11.0.9600.17691(自动更新)。 IE 在启动时设置为 "Open home page",因此至少有一个选项卡始终打开。
快速浏览一下 IE 的 ComObject,似乎在创建它时,它为您提供了一个直接接口,使您可以更轻松地与 IE 交互,例如 Navigate()
或 ReadyState
.
我确实发现了一个 属性,它似乎就是您要找的东西,那就是 Parent
调用 $IE.Parent.Quit()
似乎摆脱了 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...
只需调用 Quit()
方法通常就足以正常终止 Internet Explorer 进程,无论它们是由 运行 iexplore.exe
创建的还是通过在 PowerShell 中实例化 COM 对象创建的。
示范:
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:\> _
如果您有孤立的 Internet Explorer 进程而您没有句柄,您可以像这样循环访问它们:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
}
为了安全起见,您可以在调用 Quit()
后释放 COM 对象,然后等待垃圾收集器清理:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
[Runtime.Interopservices.Marshal]::ReleaseComObject($_)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
我遇到过使用 quit() 方法无法终止的 COM 对象的类似问题。 Interopservices.marshall 很多时候也不起作用。
我的解决方法:在我调用 com 对象之前和之后,我执行一个 get-process 来获取所有 proc 的列表:这样我就有了我的实例的 PID。在我的脚本运行后,它使用 stop-process 终止了进程。
这不是最好的方法,但至少有效
有 HKCU\Software\Microsoft\Internet Explorer\Main\FrameMerging
注册表项阻止合并 IE "frame" 进程,explained here。我自己没有尝试过,但我认为如果在 实例化 InternetExplorer.Application
COM 对象之前 设置它可能会解决您的问题。
如果这没有帮助,请尝试使用以下命令行启动一个新的 IE 实例,先于 创建 COM 对象(我也没有尝试过) :
iexplore.exe -noframemerging -private -embedding
在此 IE 实例可用作 COM 服务器之前可能存在竞争条件,因此您可能希望在创建对象之前延迟一些时间。
我尝试了一个实验,使用 Powershell 通过 COM 启动 Excel:
$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
一旦 [GC]::collect() 完成,进程就从 taskmgr 中消失了。这对我来说很有意义,因为(在我的理解中)COM 客户端负责释放对 COM 服务器的引用。在这种情况下,.NET(通过运行时可调用包装器)是 COM 客户端。
IE 的情况可能更复杂,因为可能有其他选项卡与给定的 IE 进程关联(并且有@Noseratio 提到的框架合并),但至少这将摆脱由创建的引用PS 脚本 .
这可能对您有用:
Get-Process | Where-Object {$_.Name -Match "iexplore"} | Stop-Process
此命令关闭所有当前 open/running:
的 Internet Explorer windows
Get-Process iexplore | Stop-Process
最近有一组关于通过 PowerShell 使用 Internet Explorer 执行某些操作的问题。它们都包含通过 $ie=new-object -comobject InternetExplorer.Application
从 PowerShell 作为对象启动 IE 的代码。问题是,包含调用 $ie.quit()
的 closing IE 的正确方法不起作用 - 首先,如果该 IE 碰巧有多个打开的选项卡, IE 不会作为一个整体退出,只有与 COM 对象对应的选项卡被关闭,其次,如果它只有一个选项卡,window 会关闭但进程仍然存在。
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
我试图研究如何关闭通过 New-Object -ComObject
启动的进程的方法,并找到了这个:How to get rid of a COMObject。该 COMObject 的示例是 Excel.Application
,它确实按预期运行 - 调用 quit()
使 window 关闭,如果 $ex
是创建的 COMObject 则执行 [System.Runtime.Interopservices.Marshal]::ReleaseComObject($ex)
停止Excel 过程。但是 Internet Explorer 却不是这样。
我还发现了这个问题:How to get existing COM Object of a running IE 它提供了通过打开 windows 列表连接到 IE 的代码,并且在从其他地方启动的 IE 的范围内工作,但是如果 COM 对象是通过PowerShell创建的,这个脚本不能完全停止IE的进程,如果这样修改:
$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)
}
}
如果 IE 在 PowerShell 之外启动,进程将停止,但如果 IE 在 PowerShell 内启动,则会保留两个进程,并且此代码报告没有找到 IE windows 来访问 COM 对象,因此 IE 进程(还)无法停止。
那么,如何到达明显孤立的 windowless IE 进程并优雅地 停止它们?我知道 Get-Process iexplore | Stop-Process
,但这将停止任何和所有 IE,而不仅仅是脚本启动的那些,如果脚本是 运行 作为管理员或 SYSTEM
在,比方说,远程桌面服务器,大家的IE都要停了
环境: OS Windows 7 x64,PowerShell 4(安装在 PS 版本 2 之上),IE11 版本 11.0.9600.17691(自动更新)。 IE 在启动时设置为 "Open home page",因此至少有一个选项卡始终打开。
快速浏览一下 IE 的 ComObject,似乎在创建它时,它为您提供了一个直接接口,使您可以更轻松地与 IE 交互,例如 Navigate()
或 ReadyState
.
我确实发现了一个 属性,它似乎就是您要找的东西,那就是 Parent
调用 $IE.Parent.Quit()
似乎摆脱了 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...
只需调用 Quit()
方法通常就足以正常终止 Internet Explorer 进程,无论它们是由 运行 iexplore.exe
创建的还是通过在 PowerShell 中实例化 COM 对象创建的。
示范:
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:\> _
如果您有孤立的 Internet Explorer 进程而您没有句柄,您可以像这样循环访问它们:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
}
为了安全起见,您可以在调用 Quit()
后释放 COM 对象,然后等待垃圾收集器清理:
(New-Object -COM 'Shell.Application').Windows() | Where-Object {
$_.Name -like '*Internet Explorer*'
} | ForEach-Object {
$_.Quit()
[Runtime.Interopservices.Marshal]::ReleaseComObject($_)
}
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
我遇到过使用 quit() 方法无法终止的 COM 对象的类似问题。 Interopservices.marshall 很多时候也不起作用。 我的解决方法:在我调用 com 对象之前和之后,我执行一个 get-process 来获取所有 proc 的列表:这样我就有了我的实例的 PID。在我的脚本运行后,它使用 stop-process 终止了进程。
这不是最好的方法,但至少有效
有 HKCU\Software\Microsoft\Internet Explorer\Main\FrameMerging
注册表项阻止合并 IE "frame" 进程,explained here。我自己没有尝试过,但我认为如果在 实例化 InternetExplorer.Application
COM 对象之前 设置它可能会解决您的问题。
如果这没有帮助,请尝试使用以下命令行启动一个新的 IE 实例,先于 创建 COM 对象(我也没有尝试过) :
iexplore.exe -noframemerging -private -embedding
在此 IE 实例可用作 COM 服务器之前可能存在竞争条件,因此您可能希望在创建对象之前延迟一些时间。
我尝试了一个实验,使用 Powershell 通过 COM 启动 Excel:
$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
一旦 [GC]::collect() 完成,进程就从 taskmgr 中消失了。这对我来说很有意义,因为(在我的理解中)COM 客户端负责释放对 COM 服务器的引用。在这种情况下,.NET(通过运行时可调用包装器)是 COM 客户端。
IE 的情况可能更复杂,因为可能有其他选项卡与给定的 IE 进程关联(并且有@Noseratio 提到的框架合并),但至少这将摆脱由创建的引用PS 脚本 .
这可能对您有用:
Get-Process | Where-Object {$_.Name -Match "iexplore"} | Stop-Process
此命令关闭所有当前 open/running:
的 Internet Explorer windowsGet-Process iexplore | Stop-Process