SendMessage模拟右键单击崩溃目标应用程序

我正在编写一个C#自动化工具。

由于Microsoft UI Automation不提供任何模拟右键单击或提升上下文菜单的方法,因此我使用SendMessage来执行此操作。 我宁愿不使用SendInput因为我不想抓住焦点。

但是,当我调用SendMessage时,它会崩溃目标应用程序。

这是我的代码:

  [DllImport("user32.dll", CharSet = CharSet.Auto)] static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam); public void RightClick(T element) where T: AutomationElementWrapper { const int MOUSEEVENTF_RIGHTDOWN = 0x0008; /* right button down */ const int MOUSEEVENTF_RIGHTUP = 0x0010; /* right button up */ var point = element.Element.GetClickablePoint(); var processId = element.Element.GetCurrentPropertyValue(AutomationElement.ProcessIdProperty); var window = AutomationElement.RootElement.FindFirst( TreeScope.Children, new PropertyCondition(AutomationElement.ProcessIdProperty, processId)); var handle = window.Current.NativeWindowHandle; var x = point.X; var y = point.Y; var value = ((int)x)<<16 + (int)y; SendMessage(new IntPtr(handle), MOUSEEVENTF_RIGHTDOWN, IntPtr.Zero, new IntPtr(value)); SendMessage(new IntPtr(handle), MOUSEEVENTF_RIGHTUP, IntPtr.Zero, new IntPtr(value)); } 

知道我做错了什么吗?

你混淆了你的类型。 你正在使用SendMessage ,它接收一个窗口消息(按照惯例命名为WM_ …),但是你传递的是一个MOUSEINPUT.dwFlags值,它意味着SendInput (名为MOUSEEVENTF_ …)。 你基本上是在乱搞。

您的代码实际上在做的是发送一个窗口消息,其数值为8(在窗口消息中,表示WM_KILLFOCUS ),然后是窗口消息0x10 == 16( WM_CLOSE )。 后者可能会引起你的问题 – 你告诉窗户关闭。 我不确定它为什么会崩溃,但肯定会退出。

如果您正在使用SendMessage ,则需要传递窗口消息( WM_ ,例如WM_RBUTTONDOWNWM_RBUTTONUP )。

这个回复很长,我会把它放在一个答案位。 我认为我的答案基本上是你提出的问题是基于一个不正确的假设。

关键问题是虽然在你正在使用的同一个桌面上在后台运行API类型测试是很好的,但对于基于UI的测试来说,它很难做同样的事情。

对于长时间运行的UI测试,最好的选择是两台机器和一个键盘/监视器开关,或使用终端服务在自己的会话中运行测试应用程序,因此它可以拥有自己的世界视图(焦点,鼠标,键盘状态)不会干扰您正在处理的桌面。

基本问题是某些UI资源 – 尤其是鼠标指针和键盘焦点 – 在桌面上的所有应用程序之间共享。 许多(大多数?所有?)应用程序假设当他们与之交互时,他们可以随心所欲地做这些。

你有时可以放弃“撒谎”到应用程序并发送消息,这通常是输入的最终结果(例如发送WM_LBUTTONDOWN而不是发送输入),但如果应用程序最终查看全局鼠标状态,最终会出现不一致的情况。

例如,应用程序可能会使用作为参数传递的coords来响应WM_LBUTTONDOWN。 或者它可能会忽略它们并改为调用GetCursorPos – 如果鼠标确实超过了您的电子邮件程序而不是应用程序,那么这可能会导致非常奇怪的行为。

或者您可以发送WM_LBUTTONDOWN,应用程序通过调用一些帮助函数来响应它。 辅助函数使用GetKeyState(VK_LBUTTON)来检查鼠标按钮是否实际关闭 – 注意它不是,所以提前保释。

(另外,发送最终结果消息会绕过应用程序可能依赖的其他内容;如果直接将密钥发送到窗口,则会绕过通常在消息循环中的大部分加速器和对话处理代码。)

如果应用程序使用SetCapture() – 这对于可以点击的东西非常常见,例如按钮等 – 如果应用程序没有焦点,它将会失败。 你可能会很幸运,应用程序将忽略失败和运气 – 或者你可能不会。 菜单类型控件通常假设应用程序具有焦点,如果他们注意到焦点实际上在其他地方,则会自行解散…

如果您拥有正在测试的应用程序,您可能会考虑到这一点,并将其编写为可以在后台进行“测试”:但请注意,它不再以与实际用户交互一致的方式运行 – 所以可以说它不是一个有效的用户等效测试用例! – 由您决定考虑您的测试要求。

简而言之:在这个特定情况下你可能会得到一些工作,但要注意潜伏在这里的一大堆问题,并注意这绝对不是一个UI测试最佳实践!

你发送了什么消息,你能给出他们的Windows定义吗? 根据MSDN:

 #define WM_RBUTTONDOWN 0x0204
 #define WM_RBUTTONUP 0x0205

你没有发布崩溃的细节(exception类型?位置?),但我的第一个怀疑是一个重入问题。 我会尝试使用PostMessage而不是SendMessage。 SendMessage是同步的,在返回之前等待处理消息,因此在调用期间会执行操作。 PostMessage只是将消息放入队列然后返回,然后进行处理。