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

Posted

技术标签:

【中文标题】使用 SendInput 包装器向 DirectX 应用程序发送击键时出现问题【英文标题】:Problems sending keystroke to DirectX application using SendInput wrapper 【发布时间】:2015-10-27 00:37:31 【问题描述】:

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

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

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

http://inputsimulator.codeplex.com/

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

还有一个叫拦截器

它们都会向大多数应用程序发送按键,但我仍然无法让它们发送到活动的游戏窗口。

我也尝试过 SendKeys。

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

我希望如果我在这里发布我的代码(简单的服务器应用程序),有人可以向我解释键盘挂钩的位置和方法。

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);
            

在到处搜索之后,以下 sn-p 似乎是让模拟击键在 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 代码而不是我需要的另一部分代码改变我不知道的事情。

这是我这次尝试的一个sn-p:

[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 出现错误。 "当前上下文中不存在名称 hWnd"

[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);

            

【问题讨论】:

有什么方法需要我定义我缺少的 hWnd 吗?或者有没有办法让它自动默认为活动窗口? 看我的回答:***.com/questions/28854034/… 【参考方案1】:

我最终使用“InputManager”SendInput 包装器让一切正常工作。

using InputManager

//......

Keyboard.KeyPress(Keys.YourSelectedKeyHere);

事实证明,我所要做的就是以管理员身份运行我的代码...

【讨论】:

我花了一段时间才找到这个。原来它不是内置的 InputManager,而是External Codeproject Library。不幸的是,它对我不起作用,但我会对其进行调试,看看它在做什么。也许可以改进。 啊,我明白为什么了……它只是在使用 SendInput。好吧,我很高兴它适用于您的项目(3 年前)。仍在为我的项目寻找答案。 +1 的问题。 :)

以上是关于使用 SendInput 包装器向 DirectX 应用程序发送击键时出现问题的主要内容,如果未能解决你的问题,请参考以下文章

DirectX 游戏挂钩

带有 Directx 11 的 WPF

从 .NET 代码中销毁非托管对象

使用 Direct 3D (C++) 的 DIrectX 11 2D 游戏引擎

UWP 硬件视频解码 - DirectX12 与 Media Foundation

使用 SendInput 模拟拖放操作