我努力寻找有关 .Net 事件的任何示例,这些事件揭示了进程(或相应属性)的 MainWindowTitle
的更改。
为了检测文件内容的更改,我可以简单地使用
function Register-FileWatcherEvent {
param (
[string]$folder,
[string]$file,
[string]$SourceIdentifier = 'File_Changed'
)
$watcher = New-Object IO.FileSystemWatcher $folder, $file -Property @{
IncludeSubdirectories = $false
EnableRaisingEvents = $true
}
$watcher.NotifyFilter = 'LastWrite'
Register-ObjectEvent $watcher -EventName 'Changed' -SourceIdentifier $SourceIdentifier
}
但是我应该使用什么对象、属性和通知程序来注册 MainWindowTitle
或相应属性更改的事件?
取2
我尝试摆弄 WinEventHooks(我不知道我在这里做什么;)
某些事件确实已注册,但它会在鼠标悬停时触发,而不是标题更改...
Add-Type -TypeDefinition @"
using System;
using System.Runtime.InteropServices;
public static class User32 {
public delegate void WinEventDelegate(
IntPtr hWinEventHook,
uint eventType,
IntPtr hwnd,
int idObject,
int idChild,
uint dwEventThread,
uint dwmsEventTime);
[DllImport("user32.dll")]
public static extern IntPtr SetWinEventHook(
uint eventMin,
uint eventMax,
IntPtr hmodWinEventProc,
WinEventDelegate lpfnWinEventProc,
uint idProcess,
uint idThread, uint dwFlags);
[DllImport("user32.dll")]
public static extern bool UnhookWinEvent(IntPtr hWinEventHook);
}
"@
$processId = (Get-Process -Name notepad).Id
$WinEventDelegate = [User32+WinEventDelegate]{
param (
$hWinEventHook,
$eventType,
$hwnd,
$idObject,
$idChild,
$dwEventThread,
$dwmsEventTime
)
if ($eventType -eq 0x800C) {
$windowTitle = Get-Process -Id $processId |
Select-Object -ExpandProperty MainWindowTitle
Write-Host "Window title changed to: $windowTitle"
}
}
$hWinEventHook = [User32]::SetWinEventHook(
0x800C,
0x800C,
[IntPtr]::Zero,
$WinEventDelegate,
$processId,
0,
0
)
$handler = {
param (
$sender,
$eventArgs
)
$windowTitle = Get-Process -Id $processId |
Select-Object -ExpandProperty MainWindowTitle
Write-Host "Window title changed to: $windowTitle"
}
Register-ObjectEvent -InputObject $null `
-EventName EventRecord `
-Action $handler `
-SourceIdentifier "WindowTitleChanged" `
-SupportEvent
# To unregister the event, use the following command:
# Unregister-Event -SourceIdentifier "WindowTitleChanged"
0x800C 是 EVENT_OBJECT_NAMECHANGE,但可以是不同类型的对象。
MS Learn - Event Constants (Winuser.h)
所以我认为我需要检查每个事件触发时发生了什么变化?
最佳答案
注意:
- 有关真正基于事件的解决方案,请参阅 this answer ,但是,这需要按需编译使用 P/Invoke WinAPI 调用的 C# 代码。
在 .NET 中,没有专用事件允许您响应进程的 .MainWindowTitle
属性中的更改(仅在 Windows 上可用)。
- help topic for
System.Diagnotics.Process
显示可用的事件,向 Santiago 致敬,从 .NET 7 开始,这些事件为:OutputDataReceived
、ErrorDataReceived
(用于异步接收 stdout 和 stderr 输出)和Exited
(用于对进程终止进行操作)。[1]
作为解决方法,您可以基于 System.Timers.Timer
实现定期轮询方法。实例。
请注意,轮询方法本质上比真实事件更占用 CPU 资源。
您可以通过选择较长的轮询操作间隔来减轻影响,但这可能会导致您错过标题更改和/或响应不够快。
下面的示例代码使用
Register-ObjectEvent
带有-Action
参数,这意味着给定的脚本 block ({ ... }
) 在动态模块中出现事件时处理它们 - 假设 PowerShell 处于前台线程的控制。- 另一种方法是不使用
-Action
,而是使用Get-Event
轮询/等待事件/Wait-Event
稍后。 - 参见this answer两种方法的并置。
- 另一种方法是不使用
以下是一个独立的示例:
# Create an initially disabled timer that fires every 100 msecs. when enabled.
$timer = [System.Timers.Timer] 100
# Get the process whose title should be monitored.
# (The PowerShell instance itself in this example.s)
$process = Get-Process -Id $PID
# Register for the "Elapsed" event.
# Note the hashtable passed to -MessageData with relevant data from the caller's
# state, which the -Action scriptblock can access via $Event.MessageData
$eventJob = Register-ObjectEvent -InputObject $timer -EventName Elapsed -Action {
($process = $Event.MessageData.Process).Refresh()
if (($currTitle = $process.MainWindowTitle) -ne $Event.MessageData.CurrentWindowTitle) {
Write-Verbose -Verbose (
'Title of process {0} changed from "{1}" to "{2}".' -f $process.Id, $Event.MessageData.CurrentWindowTitle, $currTitle
)
$Event.MessageData.CurrentWindowTitle = $currTitle
}
} -MessageData @{
Process = $process
CurrentWindowTitle = $process.MainWindowTitle
}
# Sample code that sleeps a little and changes the window title, twice.
Write-Verbose -vb 'Starting the timer...'
# Note: The first event will fire only
# after the first configured interval has elapsed.
$timer.Start() # alternative: $timer.Enabled = $true
try {
Write-Verbose -vb 'About to change the title twice...'
# Simulate foreground activity.
# Note: Do NOT use Start-Sleep, because event processing is
# BLOCKED during sleep.
# Waiting for a non-existent event with a timeout is
# like sleeping, but WITH event processing in the -Action script block.
Wait-Event -SourceIdentifier NoSuchEvent -Timeout 1
[Console]::Title = 'foo'
Wait-Event -SourceIdentifier NoSuchEvent -Timeout 1
[Console]::Title = 'bar'
Wait-Event -SourceIdentifier NoSuchEvent -Timeout 1
} finally {
Write-Verbose -vb 'Stopping the timer and removing the event job...'
$timer.Dispose()
Remove-Job $eventJob -Force
}
示例输出:
VERBOSE: Starting the timer...
VERBOSE: About to change the title twice...
VERBOSE: Title of process 3144 changed from "Windows PowerShell" to "foo".
VERBOSE: Title of process 3144 changed from "foo" to "bar".
VERBOSE: Stopping timer and removing the event job...
[1] 还有继承的 Dispose
事件,但它与 .NET 实例本身相关,而不是与它代表的进程相关。
关于powershell - 如何使用 Register-ObjectEvent 检测 PowerShell 中进程的 MainWindowTitle 的更改?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/76112956/