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

时间:2011-06-28 17:12:24

标签: c# winapi automation crash

我正在编写一个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>(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));
    }

知道我做错了吗?

4 个答案:

答案 0 :(得分:4)

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

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

如果您使用WM_CLOSE,则需要传递窗口消息(SendMessage,例如WM_WM_RBUTTONDOWN)。

答案 1 :(得分:1)

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

关键问题是,虽然在您正在处理的同一桌面上在后台运行API类型测试没有问题,但基于UI的同样很难做到这一点很好测试

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

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

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

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

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

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

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

如果您拥有正在接受测试的应用,那么您可以将其考虑在内并进行编写,以便对其进行测试&#39;在后台:但请注意,它不再以与实际用户交互一致的方式运行 - 所以可以说它不是一个有效的用户等效测试用例! - 由您决定考虑您的测试要求。

长话短说:在这个特定情况下你可能能够得到一些工作,但要注意潜藏在这里的一大堆问题,并注意到这绝对不是一个UI测试最佳实践!

答案 2 :(得分:0)

您准确发送了哪些消息,是否可以提供其Windows定义?根据MSDN:

#define WM_RBUTTONDOWN                  0x0204
#define WM_RBUTTONUP                    0x0205

答案 3 :(得分:0)

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