4

I was looking for a way to send mouse clicks to a background application on Windows (ie. by handle), The test window I used to confirm my code was working accepts and processes the clicks, but my target application does not (even though Spy++ shows the messages).

What could be causing this? And is there a work-around?

here's the C# code i'm using.

public enum WMessages : int
{
    WM_LBUTTONDOWN = 0x201,
    WM_LBUTTONUP = 0x202,

    WM_KEYDOWN = 0x100,
    WM_KEYUP = 0x101,

    WH_KEYBOARD_LL = 13,
    WH_MOUSE_LL = 14,
}

[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
private static extern int PostMessage(HandleRef hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);

public void SendClick(WMessages type, Point pos)
{
    switch(type)
    {
        case WMessages.WM_LBUTTONDOWN:
            PostMessage(new HandleRef(null, this.process.MainWindowHandle),
                (UInt32)WMessages.WM_LBUTTONDOWN, (IntPtr)0x1,
                (IntPtr)((pos.Y << 16) | (pos.X & 0xFFFF)));
            return;
        case WMessages.WM_LBUTTONUP:
            PostMessage(new HandleRef(null, this.process.MainWindowHandle),
                (UInt32)WMessages.WM_LBUTTONDOWN, (IntPtr)0x1,
                (IntPtr)((pos.Y << 16) | (pos.X & 0xFFFF)));
            return;
        default:
            return;
    }
}

SendClick(WMessages.WM_LBUTTONDOWN, Cursor.Position);
SendClick(WMessages,WM_LBUTTONUP, Cursor.Position);

Is this possible to achieve? Is there a better way of acheiving this?

Note: The above code doesn't work when the application is active and the mouse is hovered in the correct location, either. I'm also looking specifically send input to a background application, so SendInput and others are out of the question.

Thanks

Idan K
  • 20,443
  • 10
  • 63
  • 83
mina
  • 439
  • 2
  • 7
  • 13
  • How do you know it's not working if you see the messages going through? Could be that your position is wrong and it's getting it but not doing anything because it's a click outside its area – Seth Carnegie Aug 28 '11 at 17:26
  • To translate screen coordinates (i.e. `Cursor.Position`) to client coordinates with an HWND, use http://msdn.microsoft.com/en-us/library/dd162952(v=vs.85).aspx – Seth Carnegie Aug 28 '11 at 17:35
  • Your pinvoke declaration is wrong. Use pinvoke.net to find the correct one. – Hans Passant Aug 28 '11 at 17:35
  • The application would respond to any click in a wide area, the clicks I sent were calculated well, I think. I also recorded physical clicks and cloned/sent back the lParam as a direct value, ie. (UInt32)0x01EC02E8 - that didn't work either. – mina Aug 28 '11 at 17:36
  • @Hans I checked and it is wrong, but how could it be wrong if he's confirmed the messages going through? – Seth Carnegie Aug 28 '11 at 17:37
  • @Seth - not so sure it is useful to reason what might happen. PostMessage() won't fail on a 64-bit operating system, it simply gets junk values for the wparam and lparam arguments. – Hans Passant Aug 28 '11 at 17:47
  • yeah, i've just updated the declaration but i'm still having the same problem, and i'm pretty sure the wParam/lParam values being sent are accurate, since i even sent them as raw values and confirmed it with detective/spy++. – mina Aug 28 '11 at 18:01
  • You should use `SendInput`: http://msdn.microsoft.com/en-us/library/ms646310(v=vs.85).aspx – i_am_jorf Aug 28 '11 at 18:09
  • afaik, SendInput is only for sending input to the active window, this isn't really what i'm looking for if possible – mina Aug 28 '11 at 18:14
  • alright, thanks for the help so far people. i made a test application and confirmed that the code does actually work (when the target is a basic form), however the target application i'm looking to send input to ignores it completely. i've updated the question to reflect my situation, any help appreciated. – mina Aug 28 '11 at 18:58
  • 2
    I don't know if this is true for mouse input, but apparently you can't get away with using PostMessage to fake input: http://blogs.msdn.com/b/oldnewthing/archive/2011/07/28/10190521.aspx – siride Aug 28 '11 at 19:36

2 Answers2

4
     public void SendClick(WMessages type, Point pos)
{
    switch(type)
    {
        case WMessages.WM_LBUTTONDOWN:
            PostMessage(new HandleRef(null, this.process.MainWindowHandle),
                (UInt32)WMessages.WM_LBUTTONDOWN, (IntPtr)0x1,
                (IntPtr)((pos.Y << 16) | (pos.X & 0xFFFF)));
            return;
        case WMessages.WM_LBUTTONUP:
            PostMessage(new HandleRef(null, this.process.MainWindowHandle),
                (UInt32)WMessages.WM_LBUTTONDOWN, (IntPtr)0x1, // <--(2) but you are telling to do WM_LBUTTONDOWN
                (IntPtr)((pos.Y << 16) | (pos.X & 0xFFFF)));
            return;
        default:
            return;
    }
}

SendClick(WMessages.WM_LBUTTONDOWN, Cursor.Position);
SendClick(WMessages.WM_LBUTTONUP, Cursor.Position); // <--(1) you are sending WM_LBUTTONUP

so just read (1) first then (2) and your problem is solved

alexanders916
  • 157
  • 1
  • 16
Pedro Silva
  • 263
  • 2
  • 17
2

Did you try a SendMessage call, instead of PostMessage? SendMessage immediately calls the handler for the Window. PostMessage puts the message on a list for later processing.

  • 1
    SendMessage just freezes the calling process while calling PostMessage-like machinery to send message to the target process. The call is immediate only if windows are in the same process. Besides, SendMessage fails in message handlers called from SendMessage calls from different processes - to prevent deadlocks when processes wait for each other. Keep in mind that COM calls for COM objects in another pocesses use SendMessage internally - so don't call SendMessage directly in COM objects methods.... Gosh, Win32 is a holy mess... – Sergey Skoblikov Dec 31 '11 at 18:13