按一个键,然后释放它

时间:2016-11-29 19:37:17

标签: vb.net keypress simulate

这不是一个问题,更多的解决方案。我一直试图编写一个例程,这个例程允许我按下操纵杆上的一个键(它发送一个DX按钮)并模拟同时按下并按住一个键。基本上,它归结为三行代码:

    10. Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)

    20. If (condition=true) then
    30.     keybd_event(Keys.Scroll, 0, 0, 0)
    40. Else
    50.     keybd_event(Keys.Scroll, 0, 2, 0)
    60. EndIf

为了清晰起见,添加了行号。如您所见,第20行按住SCROLL LOCK键,第30行再次释放它。虽然代码完全符合我的需求(在1小时35分钟的会话中我在Falcon BMS 4.33U2,IVC客户端和FRAPS中没有遇到任何问题),为了使其工作,我必须使用Debug> Exceptions>禁用MDA。托管调试助手> PInvokeStackImbalance(抛出)。

我的问题是 - 这是一个安全"编程的方式,换句话说,我是否曾在某个地方作弊以使其发挥作用?如果它不安全",是否有适当的方法来做同样的事情?

1 个答案:

答案 0 :(得分:0)

正如我在评论keybd_event()中所说的那样,我们已弃用并由SendInput()取代。但是,您收到PInvokeStackImbalance异常的原因是因为函数声明中的最后两个参数是错误的。

几乎所有Declare...Lib示例都是针对VB 6或更低版本而设计的,例如Long与VB.NET的Long数据类型不同。我建议您始终使用DllImport attribute查找P / Invoke代码段。如果找不到VB.NET版本,可以查找C#片段并使用在线转换器进行转换。

要修正错误,最后两个参数应为UIntegerUIntPtr类型。

Public Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As UInteger, ByVal dwExtraInfo As UIntPtr)

但是,正如我所说,建议坚持DllImport

<DllImport("user32.dll")> _
Public Shared Sub keybd_event(bVk As Byte, bScan As Byte, dwFlags As UInteger, dwExtraInfo As UIntPtr)
End Function

您可以查看pinvoke.net以获取P / Invoke代码段。

要使用今天推荐的方法SendInput(),您可以使用我的InputHelper课程:

Imports System.Runtime.InteropServices

Public NotInheritable Class InputHelper
    Private Sub New()
    End Sub

#Region "Methods"
#Region "PressKey()"
    ''' <summary>
    ''' Virtually presses a key.
    ''' </summary>
    ''' <param name="Key">The key to press.</param>
    ''' <param name="HardwareKey">Whether or not to press the key using its hardware scan code.</param>
    ''' <remarks></remarks>
    Public Shared Sub PressKey(ByVal Key As Keys, Optional ByVal HardwareKey As Boolean = False)
        If HardwareKey = False Then
            InputHelper.SetKeyState(Key, False)
            InputHelper.SetKeyState(Key, True)
        Else
            InputHelper.SetHardwareKeyState(Key, False)
            InputHelper.SetHardwareKeyState(Key, True)
        End If
    End Sub
#End Region

#Region "SetKeyState()"
    ''' <summary>
    ''' Virtually sends a key event.
    ''' </summary>
    ''' <param name="Key">The key of the event to send.</param>
    ''' <param name="KeyUp">Whether to push down or release the key.</param>
    ''' <remarks></remarks>
    Public Shared Sub SetKeyState(ByVal Key As Keys, ByVal KeyUp As Boolean)
        Key = ReplaceBadKeys(Key)

        Dim KeyboardInput As New KEYBDINPUT With {
            .wVk = Key,
            .wScan = 0,
            .time = 0,
            .dwFlags = If(KeyUp, KEYEVENTF.KEYUP, 0),
            .dwExtraInfo = IntPtr.Zero
        }

        Dim Union As New INPUTUNION With {.ki = KeyboardInput}
        Dim Input As New INPUT With {
            .type = INPUTTYPE.KEYBOARD,
            .U = Union
        }

        SendInput(1, New INPUT() {Input}, Marshal.SizeOf(GetType(INPUT)))
    End Sub
#End Region

#Region "SetHardwareKeyState()"
    ''' <summary>
    ''' Virtually sends a key event using the key's scan code.
    ''' </summary>
    ''' <param name="Key">The key of the event to send.</param>
    ''' <param name="KeyUp">Whether to push down or release the key.</param>
    ''' <remarks></remarks>
    Public Shared Sub SetHardwareKeyState(ByVal Key As Keys, ByVal KeyUp As Boolean)
        Key = ReplaceBadKeys(Key)

        Dim KeyboardInput As New KEYBDINPUT With {
            .wVk = 0,
            .wScan = MapVirtualKeyEx(CUInt(Key), 0, GetKeyboardLayout(0)),
            .time = 0,
            .dwFlags = KEYEVENTF.SCANCODE Or If(KeyUp, KEYEVENTF.KEYUP, 0),
            .dwExtraInfo = IntPtr.Zero
        }

        Dim Union As New INPUTUNION With {.ki = KeyboardInput}
        Dim Input As New INPUT With {
            .type = INPUTTYPE.KEYBOARD,
            .U = Union
        }

        SendInput(1, New INPUT() {Input}, Marshal.SizeOf(GetType(INPUT)))
    End Sub
#End Region

#Region "ReplaceBadKeys()"
    ''' <summary>
    ''' Replaces bad keys with their corresponding VK_* value.
    ''' </summary>
    ''' <remarks></remarks>
    Private Shared Function ReplaceBadKeys(ByVal Key As Keys) As Keys
        Dim ReturnValue As Keys = Key

        If ReturnValue.HasFlag(Keys.Control) Then
            ReturnValue = (ReturnValue And Not Keys.Control) Or Keys.ControlKey 'Replace Keys.Control with Keys.ControlKey.
        End If

        If ReturnValue.HasFlag(Keys.Shift) Then
            ReturnValue = (ReturnValue And Not Keys.Shift) Or Keys.ShiftKey 'Replace Keys.Shift with Keys.ShiftKey.
        End If

        If ReturnValue.HasFlag(Keys.Alt) Then
            ReturnValue = (ReturnValue And Not Keys.Alt) Or Keys.Menu 'Replace Keys.Alt with Keys.Menu.
        End If

        Return ReturnValue
    End Function
#End Region
#End Region

#Region "WinAPI P/Invokes"
    <DllImport("user32.dll", SetLastError:=True)>
    Private Shared Function SendInput(ByVal nInputs As UInteger, <MarshalAs(UnmanagedType.LPArray)> ByVal pInputs() As INPUT, ByVal cbSize As Integer) As UInteger
    End Function

    <DllImport("user32.dll")> _
    Private Shared Function MapVirtualKeyEx(uCode As UInteger, uMapType As UInteger, dwhkl As IntPtr) As UInteger
    End Function

    <DllImport("user32.dll")> _
    Private Shared Function GetKeyboardLayout(idThread As UInteger) As IntPtr
    End Function

#Region "Enumerations"
    Private Enum INPUTTYPE As UInteger
        MOUSE = 0
        KEYBOARD = 1
        HARDWARE = 2
    End Enum

    <Flags()> _
    Private Enum KEYEVENTF As UInteger
        EXTENDEDKEY = &H1
        KEYUP = &H2
        SCANCODE = &H8
        UNICODE = &H4
    End Enum
#End Region

#Region "Structures"
    <StructLayout(LayoutKind.Explicit)> _
    Private Structure INPUTUNION
        <FieldOffset(0)> Public mi As MOUSEINPUT
        <FieldOffset(0)> Public ki As KEYBDINPUT
        <FieldOffset(0)> Public hi As HARDWAREINPUT
    End Structure

    Private Structure INPUT
        Public type As Integer
        Public U As INPUTUNION
    End Structure

    Private Structure MOUSEINPUT
        Public dx As Integer
        Public dy As Integer
        Public mouseData As Integer
        Public dwFlags As Integer
        Public time As Integer
        Public dwExtraInfo As IntPtr
    End Structure

    Private Structure KEYBDINPUT
        Public wVk As UShort
        Public wScan As Short
        Public dwFlags As UInteger
        Public time As Integer
        Public dwExtraInfo As IntPtr
    End Structure

    Private Structure HARDWAREINPUT
        Public uMsg As Integer
        Public wParamL As Short
        Public wParamH As Short
    End Structure
#End Region
#End Region
End Class

例如:

If condition = True Then
    InputHelper.SetKeyState(Keys.Scroll, False) 'Key down.
Else
    InputHelper.SetKeyState(Keys.Scroll, True) 'Key up.
End If