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.
If you are looking for a solution to have a pointer-like object that is moved around on the desktop, I might have an idea for a "dirty" solution, but I wish to warn you that this should be your last choice. It is real dirty and ill-advised. (I did something like this when I was a real beginner and I would never do it again.)
So here is the deal:
You might create a topmost little form which is mouse pointer-shaped. It then can be programmatically moved around the screen. The clicks/doubleclicks/hovering can be emulated through accessibility or through windows messages (both solutions have their own caveeats, so choose wisely).
Accessibility provides a somewhat standardized way to access forms and controls, and would be the recommended solution but there is a problem: Not all windows programs support accessibility (especially older software and games), so some of them will not respond to the fake pointer.
Windows messages are the unmanaged Win32 way to go if you want something like that. In that case you can send mouse messages (WM_CAPTURECHANGED WM_LBUTTONDBLCLK WM_LBUTTONDOWN WM_LBUTTONUP WM_MBUTTONDOWN WM_MBUTTONUP WM_MOUSEMOVE WM_MOUSEWHEEL WM_RBUTTONDOWN WM_RBUTTONUP) to the form under your fake cursor. This solution has the caveeat, that you have to invoke the non-managed postmessage API.
Whatever solution form the above listed you are using be prepared that some programs manage the cursor directly, so some software will act weird.