2010-09-05 123 views
15

我試圖模擬自定義遊戲控制器應用程序的鍵盤命令。因爲我需要在DirectInput環境中模擬命令,所以大多數通常的方法都不起作用。我知道使用鉤子可以工作100%,但我試圖找到一個更容易的實現。在DirectInput應用程序中使用SendInput API模擬鍵盤

我已經做了相當多的搜索,發現通過使用SendInput API與Scancodes而不是虛擬鍵應該可以工作,但它似乎表現得像鍵是「粘住」。我已經發送了KEYDOWN和KEYUP事件,但是當我嘗試在DirectInput環境中發送消息時,遊戲的行爲就好像按住了鍵。例如,如果我模擬一個「W」按鍵並將該鍵映射到第一人稱射擊遊戲中的「向前移動」動作,則一旦我在遊戲中,下面的功能將導致角色向前移動。然而,只要發佈一次命令,它就會無限期地向前移動角色。

下面是SendInput功能我打電話代碼段(在C#):

[DllImport("user32.dll")] 
static extern UInt32 SendInput(UInt32 nInputs, [MarshalAs(UnmanagedType.LPArray, SizeConst = 1)] INPUT[] pInputs, Int32 cbSize); 

public static void Test_KeyDown() 
{ 
    INPUT[] InputData = new INPUT[2]; 
    Key ScanCode = Microsoft.DirectX.DirectInput.Key.W; 

    InputData[0].type = 1; //INPUT_KEYBOARD 
    InputData[0].wScan = (ushort)ScanCode; 
    InputData[0].dwFlags = (uint)SendInputFlags.KEYEVENTF_SCANCODE; 

    InputData[1].type = 1; //INPUT_KEYBOARD 
    InputData[1].wScan = (ushort)ScanCode; 
    InputData[1].dwFlags = (uint)(SendInputFlags.KEYEVENTF_KEYUP | SendInputFlags.KEYEVENTF_UNICODE); 

    // send keydown 
    if (SendInput(2, InputData, Marshal.SizeOf(InputData[1])) == 0) 
    { 
     System.Diagnostics.Debug.WriteLine("SendInput failed with code: " + 
     Marshal.GetLastWin32Error().ToString()); 
    } 
} 

我不知道這方法是一個失敗的事業,或者如果有隻是一些無聊的我我錯過了。如果我不需要使用鉤子,我不想使代碼複雜化,但這對我來說也是一個新的領域。

任何人都可以給的幫助是非常感謝。

謝謝!

+4

什麼是'INPUT []'?是否應該來自''user32.dll「? – 2015-10-23 18:00:04

回答

21

我發現瞭解決我自己的問題。所以,我認爲我會在這裏發帖以幫助任何未來可能有類似問題的人。

keyup命令無法正常工作,因爲只有在發送掃描碼時,keyup必須與掃描碼標誌進行或運算(有效啓用兩個標誌)才能告訴SendInput()API,它既是一個KEYUP和SCANCODE命令。

例如,下面的代碼將正確地發出掃描碼鍵起:

INPUT[] InputData = new INPUT[1]; 

InputData[0].Type = (UInt32)InputType.KEYBOARD; 
//InputData[0].Vk = (ushort)DirectInputKeyScanCode; //Virtual key is ignored when sending scan code 
InputData[0].Scan = (ushort)DirectInputKeyScanCode; 
InputData[0].Flags = (uint)KeyboardFlag.KEYUP | (uint)KeyboardFlag.SCANCODE; 
InputData[0].Time = 0; 
InputData[0].ExtraInfo = IntPtr.Zero; 

// Send Keyup flag "OR"ed with Scancode flag for keyup to work properly 
SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT))) 

感謝漢斯響應。我做了一些調查併發回兩封郵件,就像原來的例子確實模擬了「按鍵」,但速度非常快。這對於移動命令來說效果不佳,但是當動作鍵被「輕敲」而不是被按下時,這將是理想的。

此外,虛擬鍵字段在發送掃描碼時被忽略。 MSDN對此主題有如下說明:

「設置KEYEVENTF_SCANCODE標誌以根據掃描代碼定義鍵盤輸入,無論當前使用哪個鍵盤,這對模擬物理鍵盤都很有用,虛擬鍵根據當前鍵盤佈局或其他按鍵的不同,鍵的值可能會有所不同,但掃描代碼將始終保持不變。「

http://msdn.microsoft.com/en-us/library/ms646271%28v=VS.85%29.aspx

+3

在你的解決方案中發現你提到了一個DirectInputKeyScanCode,我無法使用它,你能幫我嗎? – Fede 2013-09-10 01:43:38

+0

你可以發佈一個完整的解決方案,按下鍵和鍵?我不確定我做錯了什麼,以及在發送的第一個代碼段中需要替換的內容。謝謝! – 2014-04-15 03:43:29

3

我試圖模擬鍵盤在Flash演示,並且也有同樣的問題。它確實適用於記事本等應用程序,但不適用於Flash。經過幾個小時的谷歌搜索,我終於使它的工作:

public static void GenerateKey(int vk, bool bExtended) 
    { 
     INPUT[] inputs = new INPUT[1]; 
     inputs[0].type = INPUT_KEYBOARD; 

     KEYBDINPUT kb = new KEYBDINPUT(); //{0}; 
     // generate down 
     if (bExtended) 
      kb.dwFlags = KEYEVENTF_EXTENDEDKEY; 

     kb.wVk = (ushort)vk; 
     inputs[0].ki = kb; 
     SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0])); 

     // generate up 
     //ZeroMemory(&kb, sizeof(KEYBDINPUT)); 
     //ZeroMemory(&inputs,sizeof(inputs)); 
     kb.dwFlags = KEYEVENTF_KEYUP; 
     if (bExtended) 
      kb.dwFlags |= KEYEVENTF_EXTENDEDKEY; 

     kb.wVk = (ushort)vk; 
     inputs[0].type = INPUT_KEYBOARD; 
     inputs[0].ki = kb; 
     SendInput(1, inputs, System.Runtime.InteropServices.Marshal.SizeOf(inputs[0])); 
    }