21

I'm wondering, after fiddling with all sorts of issues with SendInput, SendKeys, PostMessage, SendMessage, SendNotifyMessage, keybd_event, etc and so forth. To find that well... trying to send a keyboard input to another non-foreground process is quite finicky and unreliable.

I tried a method of SendInput where I trick the Z-order (to keep the current window on top) and quickly foreground the 3rd party window, send the input, and re-foreground my window. Of which ultimately failed, and also, somehow, not sure why, managed to also proc the keystrokes on my window as well while not foreground (causing an infinite loop of sending and receiving between two windows until I managed to close the process).

I've tried different combinations of SendMessage and PostMessage. One for down, one for up, as using both for down and up leads to issues, like with PostMessage for both, causing the key to duplicate on the receiving window. or SendMessage for both, which caused issues with text entry, but other functions worked ok. SendMessage for keydown and PostMessage for keyUp worked for all functions, but the reliability rate dropped dramatically, as well as adding latency into key events. Only a combination of PostMessage for keydown, and SendMessage for keyup managed to do anything useful, with a maybe 5-10% fail rate of keyup registering. Same goes for SentNotifyMessage (behaves basically in the same fashion as SendMessage as far as reliability goes).

So essentially, I'm at whit's end, and I wanted to know about directly injecting a hook into the target window, and doing some voodoo to send keystrokes to it that way, bypassing the message queue etc. Doing so in a way that will not proc global key events, and only affect the target window. Only thing is I'm pretty unknowledgeable when it comes to injecting/hooking, etc. So I turn to you, community.

Wut do?

Hydra
  • 275
  • 1
  • 2
  • 10
  • After you send the input, you need to wait a while for it to get delivered before you set your own process back to foreground. Despite the name, SendInput actually *posts* the input to the input queue; if your window takes focus immediately after, then when the input is processed by the system, it could end up going to your window! – BrendanMcK May 02 '12 at 09:30
  • And for what it's worth, sending input to a non-foreground app is really not supported; there's no clean reliable way to do it. Making the target app the foreground and using SendInput is the only reliable way. SendMessage and PostMessage both have problems. What's the larger problem you are trying to solve here - why are you trying to send input in the first place? – BrendanMcK May 02 '12 at 09:32
  • Thanks for the reply, BrendanMcK. I could try and play with the timing of re-foregrounding the window, but I'd basically like to avoid that 100%. The reason I'm needing input, I'm writing a multiplexer for keyboard/mouse input across multiple applications and physical machines on a LAN. Of which is used in multi-boxing video games (typically MMOs and RTS games). The best case scenario for me, if I can figure out how to do it, is to hook directly into a 3rd party process and somehow send input to it that way. What would be my options for doing that? Where should I point my research? – Hydra May 02 '12 at 22:50
  • 1
    Someone mentioned Spy++, and I think it's really given me a bit of a boost in trying to figure out my issue. With this, I may be able to use PostMessage() for both messages and not have issues with key registration. What I can't seem to figure out, however, is how this is occuring: [link](http://imgbin.org/images/7894.png), When I am only using `PostMessage(handle, WM_KEYDOWN, key, 0);` for key down and `PostMessage(handle, WM_KEYUP, key, 0);` – Hydra May 03 '12 at 21:43
  • Faking posted key input is hard to do properly. Check the params to the messages, and you'll see that your 'emulated' messages don't match the 'real' ones exactly: notably the 'fUp' flag (bit 31 in the WM_KEYUP message) is 1 in the real message, indicating a release, but 0 in yours, indicating a keypress. This could be confusing code elsewhere. Bottom line: if you're going to 'fake' messages, you need to try to match the originals as closely as possible. Have fun :) – BrendanMcK May 03 '12 at 21:57
  • 2
    I've managed to iron out the duplication issues with PostMessage(); So now I can use PostMessage(); for both keydown and keyup events, this appears to have boosted reliability immensely. I have yet to experience a key failure in my limited initial testing. Text entry is fine. What happened was I needed to set the lParam to 0xC0000001 in order to have it not not generate a WM_CHAR message after keyup. Also, Spy++ is awesome, I'd have never have figured this out without it. However, I'm still interested in having my original question answered, how would it be possible to do this injection? – Hydra May 03 '12 at 22:28

2 Answers2

16

This is a little code that allows you to send message to a backgrounded application. To send the "A" char for example, simply call sendKeystroke(Keys.A), and don't forget to use namespace System.windows.forms to be able to use the Keys object.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Windows.Forms;

namespace keybound
{
class WindowHook
{
    [DllImport("user32.dll")]
    public static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
    [DllImport("user32.dll")]
    public static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")]
    public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

    public static void sendKeystroke(ushort k)
    {
        const uint WM_KEYDOWN = 0x100;
        const uint WM_SYSCOMMAND = 0x018;
        const uint SC_CLOSE = 0x053;

        IntPtr WindowToFind = FindWindow(null, "Untitled1 - Notepad++");

        IntPtr result3 = SendMessage(WindowToFind, WM_KEYDOWN, ((IntPtr)k), (IntPtr)0);
        //IntPtr result3 = SendMessage(WindowToFind, WM_KEYUP, ((IntPtr)c), (IntPtr)0);
    }
}
}
Louisbob
  • 860
  • 3
  • 9
  • 22
  • 5
    PostMessage worked wonderfully for this. Using Spy++ helped me to structure the messages to look exactly as they did coming from my actual keyboard. – Hydra May 05 '13 at 08:19
  • 2
    Just tried this on Windows 10 and it had no effect. Not sure if I've done something wrong or it doesn't work anymore? – Sam Apr 13 '17 at 23:29
  • I would just like to add that this no longer worked for me. What i had to do was to find the handle for the Edit window, which is a child window of WindowToFind as well as using PostMessage instead of SendMessage. My code ended up being: IntPtr editHandle = (IntPtr)0x00850766; //Found with Spy++ PostMessage(editHandle, 0x100, (IntPtr)0x56, (IntPtr)0); – Victor Chavauty Feb 06 '20 at 02:52
9

You might have to mess around with this but you can send data via process. This might not work for your situation but it is a thought.

using System;
using System.Runtime.InteropServices;
using System.Diagnostics;

    [DllImport("user32.dll", EntryPoint = "FindWindowEx")]
    public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter,          string lpszClass, string lpszWindow);
    [DllImport("User32.dll")]
    public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);

     static void Send(string message)
     {
         Process[] notepads = Process.GetProcessesByName("notepad");

         if (notepads.Length == 0)
             return;

         if (notepads[0] != null)
         {
             IntPtr child = FindWindowEx(notepads[0].MainWindowHandle, 
                 new IntPtr(0), "Edit", null);

             SendMessage(child, 0x000C, 0, message);
         }
     }

If that doesn't work you can always do:

System.Threading.Thread.Sleep(1000);
//User clicks on active form.
System.Windows.Forms.Sendkeys.Sendwait("<Message>");
Community
  • 1
  • 1
Guest
  • 91
  • 1
  • This is exactly what I need. How do I get to know what is lpszClass for my process, though? – FinnTheHuman Mar 02 '17 at 05:13
  • 2
    You can use Spy++ to find out what lpClassName is. If lpClassName is NULL, FindWindow finds any window whose title matches the lpWindowName parameter. (https://msdn.microsoft.com/en-us/library/windows/desktop/ms633499(v=vs.85).aspx) – Derek Johnson Apr 01 '17 at 23:12