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

本文关键字:目标 应用程序 崩溃 单击 模拟 右键 SendMessage | 更新日期: 2023-09-27 18:00:40

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

知道我做错了什么吗?

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

您混淆了您的类型。您使用的是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,应用程序通过调用一些助手函数来响应它。helper函数使用GetKeyState(VK_LBUTTON)来检查鼠标按钮是否真的按下了——注意到它没有按下,所以提前退出。

(此外,发送最终结果消息会绕过应用程序可能依赖的其他东西;如果你直接向窗口发送密钥,你会绕过通常在消息循环中的加速器和对话框处理代码。)

如果应用程序使用SetCapture()(这在按钮等可以点击的东西中很常见),如果应用程序没有焦点,它就会失败。你可能很幸运,应用程序会忽略失败和运气——或者你可能不会。菜单类型控件通常认为应用程序有焦点,如果他们注意到焦点实际上在其他地方,就会自行关闭。。。

如果你拥有正在测试的应用程序,你可能会考虑到这一点,并编写它,以便在后台进行"测试":但要注意,它不再以与实际用户交互一致的方式运行-因此可以说,这不是一个有效的用户等效测试用例!-这取决于你的测试需求。

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

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

#定义WM_RBUTTONDOWN 0x0204#定义WM_RBUTTONUP 0x0205

您没有公布崩溃的详细信息(异常类型?位置?),但我首先怀疑的是可重入性问题。我会尝试使用PostMessage而不是SendMessage。SendMessage是同步的,在返回之前等待消息的处理,所以在它的调用过程中会执行一些事情。PostMessage只是将消息放入队列中,然后返回,然后进行处理。

相关文章: