PowerShell 7中ReadKey()无法跨窗口追踪按键的替代方案咨询
PowerShell 7全局按键追踪的替代方案
我编写了一段用于追踪按键按下与释放的简单PowerShell脚本:
$j = 0 while($j -lt 10000){ $PressedKey = $Host.UI.RawUI.ReadKey("NoEcho,IncludeKeyDown,IncludeKeyUp") $PressedKey continue }运行该脚本时,在当前窗口内按键可正常输出结果,但切换到其他窗口或进程实例后,就无法再追踪按键并输出。请问有哪些可替代
ReadKey()的方案,能实现跨窗口/实例的按键追踪?我使用的是PowerShell 7。
可行的替代方案
1. 调用Windows API实现全局钩子
PowerShell本身没有原生的全局按键捕获能力,但可以通过调用Win32 API的全局键盘钩子来实现。以下是一个示例脚本,通过Add-Type封装必要的API函数:
Add-Type @" using System; using System.Diagnostics; using System.Runtime.InteropServices; using System.Windows.Forms; public class GlobalKeyHook { private const int WH_KEYBOARD_LL = 13; private const int WM_KEYDOWN = 0x0100; private const int WM_KEYUP = 0x0101; private static LowLevelKeyboardProc _proc = HookCallback; private static IntPtr _hookID = IntPtr.Zero; public delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool UnhookWindowsHookEx(IntPtr hhk); [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam); [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] private static extern IntPtr GetModuleHandle(string lpModuleName); public static void StartHook() { _hookID = SetHook(_proc); Application.Run(); } public static void StopHook() { UnhookWindowsHookEx(_hookID); } private static IntPtr SetHook(LowLevelKeyboardProc proc) { using (Process curProcess = Process.GetCurrentProcess()) using (ProcessModule curModule = curProcess.MainModule) { return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0); } } private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) { if (nCode >= 0 && (wParam == (IntPtr)WM_KEYDOWN || wParam == (IntPtr)WM_KEYUP)) { int vkCode = Marshal.ReadInt32(lParam); string keyState = wParam == (IntPtr)WM_KEYDOWN ? "按下" : "释放"; Console.WriteLine($"按键: {(Keys)vkCode} - {keyState}"); } return CallNextHookEx(_hookID, nCode, wParam, IntPtr.Zero); } } "@ -ReferencedAssemblies System.Windows.Forms # 启动全局钩子 [GlobalKeyHook]::StartHook() # 按Ctrl+C停止钩子 try { while ($true) { Start-Sleep 1 } } finally { [GlobalKeyHook]::StopHook() }
注意:这个脚本需要以管理员权限运行,因为全局钩子需要系统级权限;另外,PowerShell 7中需先确保安装System.Windows.Forms组件(可通过Install-Package System.Windows.Forms安装)。
2. 使用第三方PowerShell模块
社区中有专门用于全局按键监听的PowerShell模块,比如GlobalHotkey模块(需从PowerShell Gallery安装):
# 安装模块(首次运行) Install-Module -Name GlobalHotkey -Scope CurrentUser -Force # 导入模块 Import-Module GlobalHotkey # 注册全局按键监听 Register-HotKey -Key "A" -Action { Write-Host "按键A被按下" } Register-HotKey -Key "B" -KeyState Up -Action { Write-Host "按键B被释放" } # 保持脚本运行以监听按键 while ($true) { Start-Sleep 1 }
这类模块封装了底层API,使用起来更简洁,无需自行编写钩子逻辑。
3. 借助AutoHotkey脚本
如果不想直接处理复杂的API调用,可以借助AutoHotkey的全局按键监听能力,让PowerShell调用AHK脚本并获取输出:
- 编写AHK脚本(
KeyListener.ahk):
#Persistent OnMessage(0x0100, "KeyDown") OnMessage(0x0101, "KeyUp") return KeyDown(wParam, lParam) { FileAppend, 按下: %wParam%`n, * } KeyUp(wParam, lParam) { FileAppend, 释放: %wParam%`n, * }
- 在PowerShell中运行AHK脚本并捕获输出:
# 假设AHK已安装在默认路径 Start-Process -FilePath "C:\Program Files\AutoHotkey\AutoHotkey.exe" -ArgumentList "KeyListener.ahk" -RedirectStandardOutput "key_log.txt" -NoNewWindow # 实时读取日志文件 Get-Content "key_log.txt" -Wait
这种方案适合快速实现全局按键监听,且无需深入Win32 API的场景。
内容的提问来源于stack exchange,提问作者A Not Person
相关产品推荐
相关产品推荐

