编者按:
这个问题是关于在系统范围内创建新进程(可能具有给定的可执行文件名)时运行PowerShell代码的基于事件的方法
我做了大量的研究,似乎找不到任何解决方案。
我试过使用Wait-Event -SourceIdentifier "ProcessStarted"
,但它似乎根本不起作用。它只是无限地等待,不会使代码恢复。如果有一种方法可以等待可执行文件启动或等待特定文件启动,请帮助兄弟!
根据我的了解,您有一个脚本,并希望暂停它,直到某个进程开始,一旦它开始,就继续执行脚本的其余部分。
一个快速而肮脏的解决方案是使用get-process
并查询进程名称,在本例中我使用vlc
。Ouput将是$null
,直到进程启动,一旦启动,循环将退出。只有当进程尚未运行时,这才会起作用。例如,如果您已经在运行Chrome,并且正在尝试寻找该进程的新实例,则此代码将不起作用。
# some code
$processName = "vlc"
# wait until process starts
while ((Get-Process -name $processName -ErrorAction SilentlyContinue) -eq $null)
{
write-host "waiting for process to start to continue"
Start-Sleep -Seconds 2
}
# continue code
write-host "Process started"
--
示例输出
.wait_for_process.ps1
waiting for process to start to continue
waiting for process to start to continue
waiting for process to start to continue
waiting for process to start to continue
Process started
操作系统不会神奇地向您宣布进程启动事件-您需要指定想要了解的确切事件,然后注册目标事件:
# define an event polling query - this will poll WMI for new process creations every 2 seconds
$processCreationEventQuery = "SELECT TargetInstance FROM __InstanceCreationEvent WITHIN 2 WHERE TargetInstance ISA 'Win32_Process'"
# add event registration to make PowerShell consume the event from WMI
Register-CimIndicationEvent -Query $processCreationEventQuery -SourceIdentifier ProcessStarted
现在我们已经设置了事件注册,Wait-Event
将工作:
Wait-Event -SourceIdentifier ProcessStarted
如果您想等待更具体的流程启动,请修改查询过滤器,例如:
$processCreationEventQuery = @"
SELECT TargetInstance
FROM __InstanceCreationEvent
WITHIN 2
WHERE TargetInstance ISA 'Win32_Process'
AND TargetInstance.Name LIKE '%RelevantProcessNameGoesHere%'
"@
补充Mathias R.Jessen的有用答案:
此处显示的基于__InstanceCreationEvent
的方法基于定期轮询,这意味着短时间进程可能会错过,即在轮询间隔之间创建并终止的进程(1
秒是最小值)-这在实践中可能重要,也可能无关紧要。
- 此答案显示了一个具有秒以下分辨率的手动轮询循环(不涉及CIM/WMI事件),以及无限期处理事件的
__InstanceCreationEvent
示例代码
但是,__InstanceCreationEvent
方法的优点是它不需要提升(使用管理员权限运行),而不像需要的专用Win32_ProcessStartTrace
事件。
如果使用仰角运行是一个选项
,Win32_ProcessStartTrace
的优点是不会错过事件;此外,它更容易设置。
这是一个基于Register-CimIndicationEvent
和Wait-Event
的自包含Win32_ProcessStartTrace
样本,它无限期地处理事件(按Ctrl-C停止)-如前所述,需要提升:
#requires -RunAsAdmin
Write-Verbose -Verbose "Monitoring for new processes; press Ctrl-C to exit..."
try {
# Note: This registers for *any* new process.
# To limit the scope, pass a -Query argument instead of -ClassName; e.g.:
# -Query 'Select * From Win32_ProcessStartTrace Where ProcessName = "cmd.exe"'
Register-CimIndicationEvent -ClassName Win32_ProcessStartTrace -SourceIdentifier ProcessStarted
# Wait for and process events indefinitly.
while ($true) {
($evt = Wait-Event -SourceIdentifier ProcessStarted) | Remove-Event
# $evt.SourceEventArgs.NewEvent contains the event details.
# For demo purposes, print them here.
$evt.SourceEventArgs.NewEvent | Out-Host
}
}
finally {
UnRegister-Event -SourceIdentifier ProcessStarted
}
这里有一个变体,它使用-Action
参数来在后台执行事件处理,这允许前台活动并行-但请注意,事件处理只在PowerShell语句之间发生;它在长时间运行的命令期间被阻止,包括.NET方法调用:
#requires -RunAsAdmin
Write-Verbose -Verbose "Monitoring for new processes; press Ctrl-C to exit..."
try {
# Pass the event-handler code to the -Action parameter.
# Register-CimIndicationEvent then creates and returns an event job that must later be removed.
# If your -Action block were to produce *data* to communicate to the main thread, you'd have to
# to use Receive-Job in the main thread.
$eventJob =
Register-CimIndicationEvent `
-ClassName Win32_ProcessStartTrace `
-Action {
# For demo purposes, simply print the event details to the console (host).
$EventArgs.NewEvent | Out-Host
}
# You can now perform other operations here in the foreground,
# but note that event processing happens only *between* PowerShell
# statements.
while ($true) {
Write-Host . -NoNewline # simulate foreground activity
Start-Sleep -Milliseconds 100 # sleep a little
}
}
finally {
# Remove the event job, which also unsubscribes from the CIM event.
Remove-Job -Force $eventJob
}