2016-10-01 16 views
0

これは私が以前に遭遇した問題ですが、私はいつも問題を解決することをあきらめて回避策を打ち出しました。今日ではない(うまくいけば)。一部のアプリケーションで何度かセンドキーを受け付けないのはなぜですか?

私は古典的なDoom IIのボットを作ろうとしています。私はボットにエスケープキーでアクセスするメインメニューにアクセスしたい。当然、私は試しました:

sendkeys.send("{ESC}") 

いいえ運がありません。しかし、何か変わったことが起こった。私はすでにメニューに入っていたときに誤ってコードを実行しました...メニューを閉じました(メニューでエスケープすると正常です)。だから明らかにDoom IIはSendkeysに耳を傾ける。

私はsendinput、postmessage、およびsimulateinputを試みました。いずれも機能していません(すべてsendkeysで説明したのと同じ動作をします)。

誰かが白い馬に乗ってこの問題を回避するコードを教えてもらえれば大丈夫ですが、その外に誰かがこの行動を私に説明することができますか?

+1

ああ、運命II ...古い古典の一つ...私は本当にゲームが大好きです:)。 - トピック以外のトピック、Doom IIのDOS版またはWindows 95版を実行していますか?なぜなら、あなたがDOSエミュレータを使用しているなら、この問題はDOSエミュレータにある可能性があるからです。 –

+1

@visualVincentそれは本当に古典的です、それはちょっと残念です、私はそれがとても悪いです!私は実際にZandronumを使って実行しています。私は私の人生ではエスケープがメニューを閉じてもそれを開くことができない理由を理解できません! – FraserOfSmeg

+1

私はZandronumも使用しているので、それは良いです!あなたはDoom95とZanしか持っていないので、私が助けてくれるのはDOSだと言いました。 - 私は 'SendKeys'と' SendInput'の両方で試してみます。フルスクリーンモードまたはウィンドウモードで実行していますか? –

答えて

2

Zandronumは、ゲームが実行されているとき(一時停止されていないとき)に仮想キーを受け取ることはできないようです。私は確信していませんが、それは仮想キーが実際にウィンドウメッセージかもしれないようです、アンドリューモートンのように(または彼らは少なくとも何か類似している...)。これに対する回避策は、virtual key codeの代わりにhardware scan codeを送信することでした。 バーチャルキーコードシステムがスキャンコード(reference)から解釈するキーである

ハードウェアスキャンコードは、キーを押したとき、実際のキーボードによって送信されたコードであると思われます。

だから私はいくつかのWinAPIの関数を使用して(フルスクリーンとウィンドウの両方)Zandronumにキーストロークを送信するために管理:

実際のキーボード入力を送信するために使用され
  • SendInput()
  • MapVirtualKeyEx()キーコードをスキャンコードに変換するために使用され、逆も同様です。
  • GetKeyboardLayout()ユーザーの現在のキーボードレイアウトを取得するために使用されます(たとえば、スウェーデン語のキーボードを使用しています)。私はあなたが今SendKeys.Send()が含まれるものよりもキーの大きな様々な、簡単な方法でキーストローク(ハードウェアかどうか)を送信することが建て:以下、ヘルパークラス(ラッパー以上正しく)を使用することにより

System.Windows.Forms.Keys enumerationの任意のキーを使用できます。

これはZandronumでテストされ、完全に機能した。

InputHelper.PressKey(Keys.Escape, True) 'True = Send key as hardware scan code. 

InputHelper。VB:

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 

楽しみのためだけに、私はまた、MSDN上のスキャンコードのリストを見つけるために管理:私は自分自身と、それがどのように動作するかに精通ドゥームのファンですのでhttps://msdn.microsoft.com/en-us/library/aa299374(v=vs.60).aspx


をおそらく、あなたは(あなたの古い質問につき)あなたが入力する前にNew Gameをメニューで選択したことを確認する必要がありますか?

Zandronumは、メニュー項目の名前を認識しているので、あなたはそれを最初の文字を与える必要があり、それはそれで始まる項目にジャンプします:私は上記のコードをテストしてみた

InputHelper.PressKey(Keys.Escape, True) 'Open the menu. 
System.Threading.Thread.Sleep(100)  'Small delay to let the menu open. 
InputHelper.PressKey(Keys.N, True)  'Jump to the "New Game" menu item. 
InputHelper.PressKey(Keys.Enter, True) 'Go into the "New Game" menu. 
InputHelper.PressKey(Keys.Enter, True) 'Start a new game. 

フルスクリーンモードで実行中のゲームである。魅力のように動作します。

+0

これは私が探していたものだけではありませんが、これはすばらしく素敵な実装です!ありがとう! – FraserOfSmeg

+0

これは私が探していたものです!それは完璧だ!ありがとう! – FraserOfSmeg

+0

私は言った:「これは*ちょうど*正確ではない...」私は、あなたが私に欲しいものを正確に行うコードをくれたことを意味しました。副次的に - あなたはdoom2での座標を取得する方法を知っていますか? – FraserOfSmeg

関連する問題