I've been trying to send key presses to a DirectX application for a little while now, and keep striking out. I'm new to C#, so some of the more complicated functions go right over my head, but I've been trying to piece it together best I can.
SendInput is one of those things that I just can't seem to grasp.
I've tried using a few different SendInput wrappers to simplify things for me, including:
http://inputsimulator.codeplex.com/
http://www.codeproject.com/Articles/117657/InputManager-library-Track-user-input-and-simulate
and another one called interceptor
All of them will send key presses fine to most applications, but I'm still unable to get them to send to an active game window.
I've also tried SendKeys.
I've attempted to place a keyboard hook myself, but it's just too advanced for me to figure out on my own, and my code ends up riddled with errors.
I was hoping if I post my code here (simple server app) someone might be able to explain to me where and how to place the keyboard hook.
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);
}
After searching all over the place, the following snippet seems to be my best chance at getting simulated key strokes to work in a DirectX application, but I haven't been able to implement it into my code with out numerous errors and unusable code.
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;
I believe the following section would work for me to send the CTRL+T
command, but I'm not 100% sure
// 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)));
}
The main thing I'm really struggling with here is how to place the keyboard hook into my existing code, and still have the rest of my server function properly. Getting these keystrokes working is the last part of my project, and it's starting to get quite frustrating, so any help is appreciated!
UPDATE
I've made another attempt, this time using keybd_event()
, but for some reason, I've got the same issue; it's working everywhere but in-game.
I'm using VK codes, not DirectInput codes, I'm not sure if that may be the issue here, but I'm worried if I use the DI codes instead that there's another part of the code I'd need to change that I don't know about.
Here's a snippet of what I tried this time:
[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);
}
I can't figure out if it's something wrong with my code, or if I'm still using the wrong method.
UPDATE 2
I've tried again, this time using SendMessage
, but I've been unable to build, because I'm getting errors with 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);
}