使用 SendInput 包装器将击键发送到 DirectX 应用程序时出现问题

Problems sending keystroke to DirectX application using SendInput wrapper

一段时间以来,我一直在尝试将按键发送到 DirectX 应用程序,并且一直在敲击。我是 C# 的新手,所以一些更复杂的函数就在我脑海中,但我一直在尽我所能将它们拼凑起来。

SendInput 是我似乎无法理解的事情之一。

我已经尝试使用一些不同的 SendInput 包装器来为我简化操作,包括:

http://inputsimulator.codeplex.com/

http://www.codeproject.com/Articles/117657/InputManager-library-Track-user-input-and-simulate

还有一个叫做拦截器

它们都可以将按键发送到大多数应用程序,但我仍然无法让它们发送到活动游戏 window。

我也尝试过 SendKeys。

我曾尝试自己放置一个键盘挂钩,但它太高级了,我无法自己弄清楚,而且我的代码最终充满了错误。

我希望如果我 post 我的代码在这里(简单的服务器应用程序),有人可能会向我解释在哪里以及如何放置键盘挂钩。

namespace ServerApp
{
public partial class Form1 : Form
{
    private TcpListener tcpListener;
    private Thread listenThread;
    private int connectedClients = 0;
    private delegate void WriteMessageDelegate(string msg);

    public Form1()
    {
        InitializeComponent();
        Server();
    }

    private void Server()
    {
        this.tcpListener = new TcpListener(IPAddress.Any, 8888);
        this.listenThread = new Thread(new ThreadStart(ListenForClients));
        this.listenThread.Start();
    }

    private void ListenForClients()
    {
        this.tcpListener.Start();

        while (true)
        {
            TcpClient client = this.tcpListener.AcceptTcpClient();

            connectedClients++;
            lblNumberOfConnections.Text = connectedClients.ToString();

            Thread clientThread = new Thread(new ParameterizedThreadStart(HandleClientComm));
            clientThread.Start(client);

        }
    }

    private void HandleClientComm(object client)
    {
        TcpClient tcpClient = (TcpClient)client;
        NetworkStream clientStream = tcpClient.GetStream();

        byte[] message = new byte[4096];
        int bytesRead;

        while (true)
        {
            bytesRead = 0;

            try
            {
                bytesRead = clientStream.Read(message, 0, 4096);
            }
            catch
            {
                break;
            }

            if (bytesRead == 0)
            {
                connectedClients--;
                lblNumberOfConnections.Text = connectedClients.ToString();
                break;
            }

            ASCIIEncoding encoder = new ASCIIEncoding();


            //Write message in RichTextBox

            string msg = encoder.GetString(message, 0, bytesRead);
            WriteMessage(msg);

        }

        tcpClient.Close();
    }

    private void WriteMessage(string msg)
    {
        if (this.rtbServer.InvokeRequired)
        {
            WriteMessageDelegate d = new WriteMessageDelegate(WriteMessage);
            this.rtbServer.Invoke(d, new object[] { msg });

        }
        else
        {
            this.rtbServer.AppendText(msg + Environment.NewLine);

            // SEND KEYSTROKES TO ACTIVE WINDOW
            if (msg.Equals("CTRL-T"))
            {
               // Keyboard.KeyDown(Keys.LControlKey);
               // Keyboard.KeyPress(Keys.T);             // I was using this for the InputManager Wrapper
               // Keyboard.KeyUp(Keys.LControlKey);
            }

到处搜索后,以下代码片段似乎是我在 DirectX 应用程序中获得模拟击键的最佳机会,但我无法在我的代码中实现它而没有出现许多错误和无法使用的代码。

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

    [StructLayout(LayoutKind.Sequential)]
    struct MOUSEINPUT
    {
        public int dx;
        public int dy;
        public int mouseData;
        public int dwFlags;
        public int time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct KEYBDINPUT
    {
        public short wVk;
        public short wScan;
        public int dwFlags;
        public int time;
        public IntPtr dwExtraInfo;
    }

    [StructLayout(LayoutKind.Sequential)]
    struct HARDWAREINPUT
    {
        public int uMsg;
        public short wParamL;
        public short wParamH;
    }

    [StructLayout(LayoutKind.Explicit)]
    struct INPUT
    {
        [FieldOffset(0)]
        public int type;
        [FieldOffset(4)]
        public MOUSEINPUT mi;
        [FieldOffset(4)]
        public KEYBDINPUT ki;
        [FieldOffset(4)]
        public HARDWAREINPUT hi;
    }

    const int KEYEVENTF_EXTENDEDKEY = 0x0001;
    const int KEYEVENTF_KEYUP = 0x0002;
    const int KEYEVENTF_UNICODE = 0x0004;
    const int KEYEVENTF_SCANCODE = 0x0008;

我相信以下部分适合我发送 CTRL+T 命令,但我不是 100% 确定

    // SEND KEYSTROKES TO ACTIVE WINDOW

            if (msg.Equals("CTRL-T"))
{
        INPUT[] InputData = new INPUT[1];

        InputData[0].type = 1;
        InputData[0].ki.wScan = 0x1D;
        InputData[0].ki.time = 0;
        InputData[0].ki.dwExtraInfo = IntPtr.Zero;

        InputData[0].type = 1;
        InputData[0].ki.wScan = 0x14;            
        InputData[0].ki.time = 0;
        InputData[0].ki.dwExtraInfo = IntPtr.Zero;

        InputData[0].type = 1;
        InputData[0].ki.wScan = 0x1D;
        InputData[0].ki.dwFlags = 0x0002;
        InputData[0].ki.time = 0;
        InputData[0].ki.dwExtraInfo = IntPtr.Zero;

        SendInput(1, InputData, Marshal.SizeOf(typeof(INPUT)));
}

我在这里真正苦苦挣扎的主要事情是如何将键盘钩子放入我现有的代码中,并且仍然让我的服务器的其余部分正常运行。让这些击键工作是我项目的最后一部分,它开始变得非常令人沮丧,因此非常感谢您的帮助!



更新 我又做了一次尝试,这次使用 keybd_event(),但出于某种原因,我遇到了同样的问题;它在任何地方都有效,但在游戏中除外。

我使用的是 VK 代码,而不是 DirectInput 代码,我不确定这是否可能是这里的问题,但我担心如果我使用 DI 代码而不是代码的另一部分我' d 需要更改我不知道的内容。

这是我这次尝试的片段:

[DllImport("user32.dll", SetLastError = true)]
    static extern void keybd_event(byte bVk, byte bScan, int dwFlags, int dwExtraInfo);

    public const int KEYEVENTF_KEYUP = 0x0002; 
    public const int VK_LCONTROL = 0xA2; 
    public const int SEMICOLON = 0xBA; 
    public const int QUOTE = 0xDE; 
    public const int T = 0x54;
    public const int H = 0x48; 
    public const int R = 0x52; 
    public const int Z = 0x5A; 
    public const int X = 0x58; 
    public const int NUM8 = 0x68; 
    public const int NUM2 = 0x62; 
    public const int NUM4 = 0x64; 
    public const int NUM6 = 0x66; 
    public const int NUM5 = 0x65; 
    public const int UP = 0x26; 
    public const int DOWN = 0x28; 
    public const int LEFT = 0x25; 
    public const int RIGHT = 0x27; 
    public const int RETURN = 0x0D; 

// .....

if (msg.Equals("CTRL-T"))
     {
         keybd_event(VK_LCONTROL, 0, 0, 0);
         keybd_event(T, 0, 0, 0);
         keybd_event(T, 0, KEYEVENTF_KEYUP, 0);
         keybd_event(VK_LCONTROL, 0, KEYEVENTF_KEYUP, 0);
      }

我不知道是我的代码有问题,还是我还在使用错误的方法。



更新 2 我再次尝试,这次使用 SendMessage,但我一直无法构建,因为我在使用 hWnd 时遇到错误。 "The name hWnd does not exist in the current context"

[DllImport("user32.dll")] 
    public static extern IntPtr SetActiveWindow(IntPtr hWnd);

    [DllImport("user32.dll")] 
    public static extern int SendMessage(IntPtr hWnd, int Msg, uint wParam, int lParam);

    public const ushort WM_KEYDOWN = 0x0100;
    public const ushort WM_KEYUP = 0x0101;

    private void WriteMessage(string msg)
    {
        if (this.rtbServer.InvokeRequired)
        {
            WriteMessageDelegate d = new WriteMessageDelegate(WriteMessage);
            this.rtbServer.Invoke(d, new object[] { msg });

        }
        else
        {
            this.rtbServer.AppendText(msg + Environment.NewLine);

            // SEND KEYSTROKES TO ACTIVE WINDOW
            if (msg.Equals("CTRL-T"))
            {

                SetActiveWindow(hWnd);
                SendMessage(hWnd, WM_KEYDOWN, 0x1D, 0);
                SendMessage(hWnd, WM_KEYDOWN, 0x14, 0);
                SendMessage(hWnd, WM_KEYUP, 0x1D, 0);
                SendMessage(hWnd, WM_KEYUP, 0x14, 0);

            }

我最终使用 "InputManager" SendInput 包装器让一切都完美运行。

using InputManager

//......

Keyboard.KeyPress(Keys.YourSelectedKeyHere);

事实证明,我所要做的只是 运行 我作为管理员的代码...