C# 如何在一个Windows客户端上使用多个游标?

C# 如何在一个Windows客户端上使用多个游标?,c#,cursor,mouse,desktop,multipoint,C#,Cursor,Mouse,Desktop,Multipoint,我最近在网上搜索了多光标解决方案,发现了一系列在给定窗口中模拟光标的可能性,例如,但没有一种解决方案可以在整个桌面上使用多个光标,从而不局限于一个应用程序 我几乎放弃了,认为Windows架构让它变得不可能,但后来我发现,在YouTube上的视频中,它至少可以让不同的用户在整个桌面上使用不同的光标。如果这是真的,那么就可以模拟多个用户,从而使多游标解决方案成为可能 因此,我的问题是: 这里有人使用多鼠标吗?它是否真的像描述的那样工作? 它是如何工作的,是否有人可以使用windows api,或者

我最近在网上搜索了多光标解决方案,发现了一系列在给定窗口中模拟光标的可能性,例如,但没有一种解决方案可以在整个桌面上使用多个光标,从而不局限于一个应用程序

我几乎放弃了,认为Windows架构让它变得不可能,但后来我发现,在YouTube上的视频中,它至少可以让不同的用户在整个桌面上使用不同的光标。如果这是真的,那么就可以模拟多个用户,从而使多游标解决方案成为可能

因此,我的问题是:

这里有人使用多鼠标吗?它是否真的像描述的那样工作? 它是如何工作的,是否有人可以使用windows api,或者需要重新配置windows才能这样做? 你对如何在Windows7上绘制和命令多个光标有什么想法吗?问题是,只有一个鼠标指针。有人能改变这一点吗,或者远程应用程序在做什么?我的意思是,只需全局显示游标并自动控制它们就足够了。 我可以自由使用任何互操作技术,不管它们有多脏,因为如果我的应用程序只在Windows7上运行就足够了。有人知道可以提供这种功能的windows库吗?遗憾的是,windows api的文档记录很差。 编辑: 在下文中,我将解释windows消息是如何部分工作的,但为什么它们一般不工作。首先,我们以一个flash客户端为例,它是一个带有子对象的句柄,它的子对象是一个flash对象。每当在闪存区域的任何位置执行单击时,都会显示以下windows消息:

00100354 S WM_PARENTNOTIFY fwEvent:WM_LBUTTON xPos:[x]YPO:[y]

00100354 R WM_PARENTNOTIFY

001B09D6 S WM_MOUSEACTIVATE hwndTopLevel:00100354 nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN

00100354 S WM_MOUSEACTIVATE hwndTopLevel:00100354 nHittest:HTCLIENT uMsg:WM_LBUTTONDOWN

00100354 R WM_MOUSEACTIVATE FUACTIVE:手动激活

001B09D6 R WM_鼠标激活FUACTIVE:MA_激活

001B09D6 p WM_LBUTTONDOWN fwKeys:MK_LBUTTON xPos:[x]YPO:[y]

001B09D6 p WM_LBUTTONUP fwKeys:0000 xPos:[x]YPO:[y]

在本例中,00100354是父对象,001B09D6是子对象。因此,家长会告诉孩子点击某个位置[x],[y]。但是,一个使用windows消息的假点击解决方案永远不可能知道它必须点击哪个句柄。如果单击或通知仅发送到00100354,则不会发生任何事情。我们试图实现的是消息和,但我们不能,除非通过向前景句柄的所有后代(包括前景句柄本身)发送垃圾邮件。但这也可能导致我们不想要的点击。我们无法执行命中测试,因为目标应用程序可能在目标句柄前面有透明句柄。请注意,如果子控制柄未定位在其父控制柄的0,0处,则还需要计算新的相对坐标

要实现如上所示的通信,可以使用Win32调用,如下所示:

[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);
[……]


总之,根据目标应用程序的不同,使用windows消息伪造鼠标输入似乎是不正确的或不可能的。

如果您正在寻找一种在桌面上移动类似指针的对象的解决方案,我可能会想到一种脏的解决方案,但我想警告您,这应该是您的最后选择。这是非常肮脏和不明智的。当我还是一个真正的初学者的时候,我做过这样的事情,我再也不会做了

这就是交易:

您可以创建一个最上面的小表单,它是鼠标指针形状的。然后可以通过编程在屏幕上移动它。点击/双击/悬停可以通过可访问性或windows消息来模拟。两种解决方案都有自己的警告,因此请明智地选择

可访问性为访问表单和控件提供了某种程度的标准化方法,这是推荐的解决方案,但存在一个问题:并非所有windows程序都支持可访问性,尤其是较旧的软件和游戏,因此其中一些程序不会响应假指针

如果您想要类似的东西,Windows消息是非托管Win32方式。在这种情况下,您可以发送鼠标消息WM_capture更改WM_lbuttondblck WM_lbuttonown WM_LBUTTONUP WM_MBUTTONUP WM_MBUTTONUP WM_鼠标移动WM_鼠标滚轮WM_rbuttonown WM_RBUTTONUP到假光标下的窗体。此解决方案有一个警告,即您必须调用非托管的PostMessageAPI


无论您使用上面列出的解决方案是什么,都要准备好让一些程序直接管理光标,这样一些软件就会表现得很怪异。

但实际上。。。只有在没有其他解决方案时才这样做。我不知道windows是否支持多个游标,而我的解决方案只帮助伪造一个游标。谢谢你的回答,但是
这不是我想要的。我想通过问这个问题来避免这个琐碎的假解决方案。该解决方案还有更多的问题,例如Windows消息无法模拟Windows输入,正如所解释的那样。@xamid-这就是为什么我建议使用任何其他解决方案(如果有的话)。据我所知没有。另外,您链接的旧的新事物文章只提到字符输入,这是有原因的。大多数应用程序通过windows消息处理鼠标事件,因此可以使用我的解决方案。直接处理键盘输入是一种非常常见的做法,但直接处理鼠标却不常见。@xamid-如果有人想出更好的解决方案,你能注意到我吗?我只是变得好奇起来。提前谢谢。这与我的问题中编辑的鼠标输入类似。当然,如果我找到更好的解决方案,我会在这里发布我自己的答案。顺便说一下:如果用光标单击,则会发布消息,而不是发送消息。这意味着,要伪造鼠标点击,您需要使用PostMessage,而不是SendMessage,至少在我的Windows 7 x64系统上是这样。在阅读Multimouse手册时——请注意,我没有使用实际的产品——我强烈怀疑,它只是使用我在本手册中描述的技术之一伪造了额外的游标。但这又一次只是一种怀疑。我也开始想:但这会让他们的工具变得很糟糕。我认为检查这一点的一个好方法是,使用mspaint这样的绘图工具,并一次通过多个光标在其中绘制。。即使mspaint由于输入信息太多而表现怪异,该工具也不会假装工作正常。如果它仅适用于单用户图形,则可能会在单用户执行操作时使用真正的光标。如果它不起作用,那就是一个很大的假象,只使用windows消息。我认为这个想法总体上还不错,但我会使用其他工具,因为我不确定MsPaint是否使用可以处理多个鼠标输入的,但是,它们对您没有任何用处,因为它们的作用域是它们内置的应用程序,即使在那里它们也非常糟糕:例如,文本框的滚动条之类的嵌入式控件不起作用。
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");
  }