2016-11-29 20 views
0

Это не вопрос как таковой, скорее решение. Я пытаюсь закодировать процедуру, которая позволит мне нажать клавишу на моем джойстике (который отправляет кнопку 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 Client и FRAPS), чтобы заставить это работать, мне пришлось отключить MDA с помощью Debug> Exceptions> Управляемые помощники отладки> PInvokeStackImbalance (брошен).

Мой вопрос - это «безопасный» способ программирования, или, другими словами, я где-то меня обманул, чтобы заставить его работать? Если это не «безопасно», есть ли способ сделать то же самое?

+0

'keybd_event' устарела, и поэтому' Declare. ..Lib'. Текущий способ отправки событий клавиатуры/мыши - это использовать [** 'SendInput()' **] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms646310 (v = vs. 85) .aspx). См. Мою оболочку ввода клавиатуры [** в этом ответе **] (http://stackoverflow.com/questions/39809095/why-do-some-applications-not-accept-some-sendkeys-at-some-times/39811061 # 39811061). –

+0

Посмотрите на этот пример: https://msdn.microsoft.com/en-us/library/windows/desktop/ms646304(v=vs.85).aspx Это для numloc, но в соответствии с info вы должны использовать KEYEVENTF_EXTENDEDKEY. Во-вторых: что стоит за CO? Это что-то вроде: GetKeyboardState ((LPBYTE) & keyState); Например. для получения текущего состояния ключа? В-третьих, вы можете попробовать использовать SetInput вместо keybg_event, так как keybg_event заменяется – Kiko

+0

Если функция была объявлена ​​правильно, ошибок не должно быть вообще. По умолчанию флажок «Брошенный» не следует отмечать для каких-либо исключений, так как он нарушит приложение, даже если обработано исключение. –

ответ

0

Как я сказал в своем комментарии keybd_event() устарел и заменен на SendInput(). Однако причина, по которой вы получаете исключение PInvokeStackImbalance, заключается в том, что последние два параметра неверны в объявлении вашей функции.

Почти все Declare...Lib примеры из там предназначены для VB 6 или ниже, где, например, Long не такой же, как тип данных Long Vb.net в. Я советую вам всегда Ищите фрагменты P/Invoke, используя DllImport attribute. Если вы не можете найти версию VB.NET, вы можете искать фрагменты C# и конвертировать их с помощью онлайн-конвертера.

Для устранения ошибки последние два параметра должны быть типа UInteger и UIntPtr.

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