【问题标题】:How do I programmatically repeat a key while it is held?如何在按住键时以编程方式重复键?
【发布时间】:2019-08-17 08:50:18
【问题描述】:

我试图在按住 LButton 时自动重复它,然后在它释放时停止,我遇到了一个问题,即使它没有被按住它也会不断重复。

是否有任何解决方法?我还需要它来处理其他应用程序,这就是我使用 GetAsyncKeyState 的原因。

这是我目前所拥有的:

Imports System.Threading

Public Class Form1
   Const KeyDownBit As Integer = &H8000
   Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Integer) As Short
   Private Declare Sub mouse_event Lib "user32" (ByVal dwflags As Integer, ByVal dx As Integer, ByVal cbuttons As Integer, ByVal dy As Integer, ByVal dwExtraInfo As Integer)
   Private Const mouseclickup = 4
   Private Const mouseclickdown = 2

   Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
        If (GetAsyncKeyState(Keys.LButton) And KeyDownBit) = KeyDownBit Then
            mouse_event(mouseclickup, 0, 0, 0, 0)
            Thread.Sleep(100)
            mouse_event(mouseclickdown, 0, 0, 0, 0)
        End If
  End Sub

使用此代码,当我左键单击时,即使释放 Lbutton,代码也会不断自动单击,但这并不是我想要的。我想要它,所以当我按住LButton时,它会连续点击,然后当LButton松开时,它会停止点击。

我尝试过使用BackgroundWorker,尽管发生了同样的事情。

我也尝试过在mouse_event(mouseclickup, 0, 0, 0, 0) 之前使用mouse_event(mouseclickdown, 0, 0, 0, 0),但每次按下它时它只会将单击变成双击,然后停止。

【问题讨论】:

  • 我认为您不应该使用GetAsyncKeyState,文档指出:“确定在调用函数时键是向上还是向下,以及该键是否在上一次调用 GetAsyncKeyState 后按下。" (强调我的)。
  • 你试过用GetKeyState代替吗?
  • @Dai :文档还说:“如果设置了最高有效位,key is down,这就是@ 987654328@变量用于检查。计时器中的If-statement 只会在键实际按下时返回True,所以他使用GetAsyncKeyState 没有问题。
  • 这里的问题是你的代码一直在点击和检查LButton。它会一直点击,因为每次执行 If-statement 时也会触发 LButton。我知道避免这种情况的唯一方法是将鼠标单击窗口消息发送到位于鼠标下方的窗口。不过,这种方法有些先进。
  • 我有空的时候看看能不能给你一个解决方案。

标签: vb.net


【解决方案1】:

LMB 不断被点击的原因是因为您每次检测到鼠标点击时都会不断发送新的鼠标点击。

由于GetAsyncKeyState() 读取键盘/鼠标输入流,因此您不能使用任何向流添加点击的方法,因为一切都会像您当前遇到的那样卡住。

为了消除这个问题,我将一个辅助类和一个方法放在一起,该方法将鼠标点击作为窗口消息发送到点击点下方的窗口。通过这样做,我们现在将鼠标点击直接发送到窗口而不是键盘/鼠标输入流,这意味着GetAsyncKeyState() 不会注意到它。

编辑(2019-08-16)

MouseInputHelper 类早已与InputHelper 合并。答案已更新为使用后者。

从 GitHub 下载 InputHelper:
https://github.com/Visual-Vincent/InputHelper/releases

现在您可以在计时器中执行以下操作:

Private Sub Timer1_Tick(sender As Object, e As EventArgs) Handles Timer1.Tick
    If InputHelper.Mouse.IsButtonDown(MouseButtons.Left) Then
        InputHelper.WindowMessages.SendMouseClick(MouseButtons.Left, Cursor.Position)
    End If
End Sub

希望这会有所帮助!

【讨论】:

  • @Joey :所以我们终于解决了,嗯? :) -- 顺便问一下,您的旧帐户怎么了?
【解决方案2】:

我将尝试一下,并做出一些假设。

假设1:如果你对左键Left被按下感兴趣,那么表单有焦点。您可以使用标志来跟踪按键

Public Class Form1

    Dim timer As New System.Threading.Timer(AddressOf timer_tick, Nothing,
                                            System.Threading.Timeout.Infinite,
                                            System.Threading.Timeout.Infinite)
    ' keeps track of whether the key is pressed
    Dim lKeyIsPressed As Boolean = False
    ' how fast do you want the action to be performed?
    Dim interval As Integer = 100

    Private Sub Form1_FormClosed(sender As Object, e As FormClosedEventArgs) Handles Me.FormClosed
        timer.Change(System.Threading.Timeout.Infinite,
                     System.Threading.Timeout.Infinite)
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles Me.Load
        ' enable the timer
        timer.Change(interval, System.Threading.Timeout.Infinite)
    End Sub

    Private Sub timer_tick(state As Object)
        If lKeyIsPressed Then
            ' this is where you will perform your action when the key is pressed
        End If
        timer.Change(interval, System.Threading.Timeout.Infinite)
    End Sub

    Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
        lKeyIsPressed = (e.KeyCode = Keys.Left)
    End Sub

    Private Sub Form1_KeyUp(sender As Object, e As KeyEventArgs) Handles Me.KeyUp
        If e.KeyCode = Keys.Left Then lKeyIsPressed = False
    End Sub

End Class

更改interval 以控制您希望在按键时执行操作的速度

假设2:如果您对按下鼠标左键LMB感兴趣,您应该添加这些处理程序:

Private Sub mouseDownHandler(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
    lKeyIsPressed = (e.Button = Windows.Forms.MouseButtons.Left)
End Sub

Private Sub mouseUpHandler(sender As Object, e As MouseEventArgs) Handles Me.MouseUp
    If e.Button = Windows.Forms.MouseButtons.Left Then lKeyIsPressed = False
End Sub

但您还想跟踪鼠标在子控件(即标签)上的单击情况。所以将这个添加到 Form_Load

For Each c As Control In Me.Controls
    AddHandler c.MouseDown, AddressOf mouseDownHandler
    AddHandler c.MouseUp, AddressOf mouseUpHandler
Next

最后,您应该在使用完所有一次性资源后将其丢弃

Protected Overrides Sub Dispose(ByVal disposing As Boolean)
    Try
        If disposing AndAlso components IsNot Nothing Then
            components.Dispose()
        End If
        timer.Dispose()
    Finally
        MyBase.Dispose(disposing)
    End Try
End Sub

【讨论】:

  • 不要破坏你的好答案,但我相信 OP 是我之前帮助过的人,他现在正在使用一个新帐户......如果我没记错的话,他正试图为一个外部游戏(因此他使用GetAsyncKeyState() 能够在他的应用没有焦点的情况下检查 LMB 状态)。
  • 不用担心。我想也许还有更多的东西,但没有具体说明。这个答案可以帮助其他人解决这个问题,因为它真的不是那么具体。
  • 我实际上设法找到了解决 OP 原始问题的方法。通过将鼠标窗口消息发送到光标下方的窗口,点击将直接发送到窗口而不是鼠标/键盘输入流。因此,GetAsyncKeyState() 不会注意到任何事情。
猜你喜欢
  • 2021-05-11
  • 1970-01-01
  • 1970-01-01
  • 2013-09-16
  • 2013-08-12
  • 2011-06-25
  • 1970-01-01
  • 2022-01-10
  • 1970-01-01
相关资源
最近更新 更多