I recently searched the web for multi-cursor solutions and found a bunch of possibilities to emulate cursors in a given window, like MultiPoint SDK, but no solutions where one can use multiple cursors on the whole desktop (and thus not restricted to one application).
I almost gave up, thinking the Windows architecture makes it impossible, but then I found MultiMouse, stating in this video on YouTube, it would at least make it possible to use different cursors for different users over the whole desktop. If that is true, it follows that one could emulate multiple users and thereby making a multiple cursors solution possible.
So my questions are:
- Does someone here use Multimouse and is it really working as described?
- How does it work, can someone use the windows api, or needs to reconfigure Windows to do so?
- Have you any ideas on how to draw and command multiple cursors on Windows 7? Problem is, there is only one mouse pointer. Can someone change that, or what are remote applications doing? I mean, it would suffice to just show the cursors (globally) and control them automatically.
- I'm free to use any interop techniques, no matter how dirty they are, since it suffices if my app runs only on Windows 7. Does someone know windows libraries which could provide such functionality? Sadly, the windows api is poorly documented.
Edit: In the following I will explain how windows messages partially work, but why they don't do in general. First, we take a flash client as an example, that is a handle with a child, and it's child is a flash object. Whenever a click is performed anywhere at the flash area, the following windows messages show up:
<000001> 00100354 S WM_PARENTNOTIFY fwEvent:WM_LBUTTON xPos:[x] yPos:[y]
<000002> 00100354 R WM_PARENTNOTIFY
<000003> 001B09D6 S WM_MOUSEACTIVATE hwndTopLevel:00100354 nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<000004> 00100354 S WM_MOUSEACTIVATE hwndTopLevel:00100354 nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN
<000005> 00100354 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
<000006> 001B09D6 R WM_MOUSEACTIVATE fuActivate:MA_ACTIVATE
<000007> 001B09D6 P WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:[x] yPos:[y]
<000008> 001B09D6 P WM_LBUTTONUP fwKeys:0000 xPos:[x] yPos:[y]
In this example 00100354 is the parent and 001B09D6 is the child (flash object). So it happens, that the parent tells the child to click at a certain position ([x],[y]). But a faking click solution working with windows messages could never know which handle it has to click. If the clicks or notifications are only sent to 00100354, nothing happens. What we are trying to accomplish are the messages <000007> and <000008>, but we can't, except by spamming messages to all descendants of the foreground handle, including the foreground handle itself. But this could also lead to clicks we don't want. We cannot perform a hit test since the target application may have e.g. transparent handles in front of the target handle. Note that one also needs to calculate new relative coordinates if a child handle is not positioned at (0,0) of its parent.
To realize communication as shown above, one can use Win32 calls, like this:
[DllImport("User32.dll", SetLastError = true)]
public static extern IntPtr SendMessage(IntPtr hWnd, WM Msg, IntPtr wParam, IntPtr lParam);
[return: MarshalAs(UnmanagedType.Bool)]
[DllImport("user32.dll", SetLastError = true)]
public static extern bool PostMessage(IntPtr hWnd, WM Msg, IntPtr wParam, IntPtr lParam);
[...]
public static IntPtr ToWMCoords(Point pt)
{
return (IntPtr)(((uint)pt.Y << 16) | (ushort)pt.X);
}
[...]
foreach (Point pt in coordinates)
{
Console.Write("Message-Clicking at " + pt + "\n");
IntPtr mousePos = ToWMCoords(pt);
//# Get flash handle
//## Variation, assuming the client has always only 1 child with class name "MacromediaFlashPlayerActiveX"
IntPtr flashHwnd = Interop.GetChildWindows(hwnd).Where(x => Interop.GetClassNameOfHandle(x) == "MacromediaFlashPlayerActiveX").Single();
//# Simulating a click
//*1: Messages getting to handle of class "#32770 (Dialog)"
//*2: Messages getting to handle of class "MacromediaFlashPlayerActiveX"
Interop.User32.SendMessage(hwnd, Interop.WM.PARENTNOTIFY, (IntPtr)0x201, mousePos);//*1
Interop.User32.SendMessage(flashHwnd, Interop.WM.MOUSEACTIVATE, hWnd, (IntPtr)0x2010001);//*2
Interop.User32.SendMessage(hwnd, Interop.WM.MOUSEACTIVATE, hWnd, (IntPtr)0x2010001);//*1
bool lBtnDown = Interop.User32.PostMessage(flashHwnd, Interop.WM.LBUTTONDOWN, (IntPtr)Interop.MK.LBUTTON, mousePos);//*2
bool lBtnUp = Interop.User32.PostMessage(flashHwnd, Interop.WM.LBUTTONUP, IntPtr.Zero, mousePos);//*2
Console.Write("WM_LBUTTONDOWN " + (lBtnDown ? "success" : "fail") + ", WM_LBUTTONUP " + (lBtnDown ? "success" : "fail") + "\n");
}
In summary, faking mouse input using windows messages appears to be improperly or impossible, depending on the target application.