powershell - 如何使用 Register-ObjectEvent 检测 PowerShell 中进程的 MainWindowTitle 的更改?

标签 powershell events ui-automation user32

我努力寻找有关 .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 开始,这些事件为:OutputDataReceivedErrorDataReceived(用于异步接收 stdout 和 stderr 输出)和 Exited(用于对进程终止进行操作)。[1]

作为解决方法,您可以基于 System.Timers.Timer 实现定期轮询方法。实例。

  • 请注意,轮询方法本质上比真实事件更占用 CPU 资源。

  • 您可以通过选择较长的轮询操作间隔来减轻影响,但这可能会导致您错过标题更改和/或响应不够快。

  • 下面的示例代码使用 Register-ObjectEvent带有 -Action 参数,这意味着给定的脚本 block ({ ... }) 在动态模块中出现事件时处理它们 - 假设 PowerShell 处于前台线程的控制。

以下是一个独立的示例:

# 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/

相关文章:

java - 如何在 Java 上使用 Sikuli 保存屏幕截图

powershell - 将函数作为计算属性调用以显示MB和GB Powershell脚本

unit-testing - dotnet test 仅测试解决方案上的项目

Powershell - Windows Server 2008 的 Sku

function - 从脚本 block 动态创建 PowerShell 脚本 block

MySQL 事件不会执行

java - 在 libgdx 上捕获 onClose 窗口事件

ios - 寻找有关使用 Appium 自动化真实 iOS 设备的教程

iphone - uiautomation录制的脚本保存位置

c# - 事件一次只能发出一个调用吗?