在与 chat gpt 和 bing chat 争论了一晚之后,我现在转向(希望)stackoverflow 上更聪明的人。我的问题是这样的:我在 C# 中实现了低级挂钩。我订阅 powershell 类中的事件,然后在 GUI 中使用该事件。订阅事件并启动挂钩工作得很好,但是,我无法从我的类或 GUI 程序中停止挂钩。目前我在想,每当我按“f4”时,挂钩应该停止,但我不断收到“无法在空值表达式上调用方法”,而且我真的不明白为什么它会是空值或如何我会解决它。这是我的代码,我认为没有必要展示钩子或 GUI 的实现,但请让我知道其他情况。
class InputRecorder {
[KeyboardHookExample.KeyboardHook] $kh
[Ikst.MouseHook.MouseHook] $mh
[System.Windows.Forms.ListView] $list
InputRecorder() {
$this.kh = New-Object KeyboardHookExample.KeyboardHook
$this.mh = New-Object Ikst.MouseHook.MouseHook
# Store the reference to the class instance
$self = $this
# Define the event handler for KeyDown
$self.kh.add_KeyDown({
param($sender, $e)
$vkCode = $sender.vkCode
Write-Host $vkCode
if ($vkCode -eq 115) {
$self.kh.Stop()
$self.mh.Stop()
}
$charCode = [Win32.NativeMethods]::MapVirtualKey($vkCode, 2)
$char = [char]$charCode
Write-Host $char
})
# Define the event handler for LeftButtonDown
$self.mh.add_LeftButtonDown({
param($sender, $e)
$mousePosition = $sender.pt
$y = $mousePosition.y
$x = $mousePosition.x
$item = New-Object System.Windows.Forms.ListViewItem
$item.ToolTipText = $global:dict["LeftClickCode"] -f $x, $y
$item.Text = $global:dict["LeftClickDescription"] -f $x, $y
$CMDList.Items.Add($item)
})
# Start the keyboard and mouse hooks
$self.kh.Start()
$self.mh.Start()
}
[System.Collections.Concurrent.ConcurrentBag[string]] getList() {
return $this.list
}
[void] StopHooks() {
$this.mh.Stop()
$this.kh.Stop()
}
}
虽然期望 PowerShell 的动态范围也适用于 PowerShell 的自定义
class
内是可以理解的,但事实并非如此:
一个脚本块作为事件委托传递到.NET方法从类内部:
的局部变量 - 除非您通过在脚本块上调用.GetNewClosure()
来显式捕获它们。
.GetNewClosure()
的情况下,它外部定义的变量,在定义类的范围内(并且来自该范围的祖先)范围),因为它在 that 范围的子范围中运行。
$this
视为指代自身,因为不幸的是,事件委托中的
自动
$this
变量阴影是
class
的$this
级定义,而是指代事件发送者。 并且由于类内部使用
$this
是访问类的以
每种方法定义方法局部变量,例如
$self
.GetNewClosure()
,这允许您访问该脚本中的方法局部变量块。但是,请注意,
.GetNewClosure()
将使您的脚本块无法访问类定义范围中的变量。最好以
使用 (Get-Variable -Scope 1 -ValueOnly this)
$this
的
shadowed版本
这消除了对特定于方法的逻辑的需要,并且还保留了对类定义范围中的变量的访问(如果需要)。