向WPF应用程序消息泵注入消息

Injecting Messages into WPF Application Message Pump

本文关键字:消息 注入 应用程序 WPF      更新时间:2023-10-16

我有一些预编译的WPF应用程序,我需要通过发送各种消息(例如:WM_POINTERMOVE, WM_KEYDOWN等)来控制而不重新编译它们。我尝试的控制方法是将消息注入到应用程序消息泵中。作为测试,我通过获取应用程序的windows句柄,然后使用SendMessage发送消息,尝试了其他一些Win32应用程序。

BOOL CALLBACK EnumWindowsProc(
_In_ HWND   hwnd,
_In_ LPARAM lParam
)
{
    LPWSTR windowName = new TCHAR[256];
    GetWindowText(hwnd, windowName, 256);
    std::wstring windowNameString(windowName);
    int position = windowNameString.find(std::wstring(L"MyApplicationWindowName"));
    if (position > -1) {
        hTargetWindow = hwnd;
        delete windowName;
        return FALSE;//stop enumerating windows
    }
    delete windowName;
    return TRUE;
}
 .....
 EnumWindows(EnumWindowsProc, NULL);;
 SendMessage(hTargetWindow, MY_MESSAGE, 0, 0);

我使用EnumWindows检索了应用程序的HWND(这工作得很好)。然后我使用SendMessagePostMessage发送我的消息。而其他应用程序响应这个WPF应用程序没有。作为另一个测试,我制作了一个WPF应用程序,并添加了一些代码,以便在它接收到特定消息时设置断点。

ComponentDispatcher.ThreadFilterMessage += (ref MSG msg, ref bool handled) =>
{
    if(msg.message == MY_MESSAGE)
        Debug.WriteLine("break");
};

断点永远不会被击中,这表明消息没有进入消息泵。在其他类型的应用程序中尝试同样的事情会获得成功。我注入这些信息做错了什么。

我想在这里给出一个更详细的回应,但由于时间紧迫,我在这里做一个总结。我确实找到了其他一些想做同样事情的人。

处理windows消息的WPF类执行额外的检查。如果收到了一个鼠标消息,而不是处理鼠标消息中的信息,WPF将再次调用查询鼠标光标以查看它的实际位置,它将检查它是否确实有焦点,等等(对其他一些输入消息也会进行类似的检查)。如果根据WPF类库中的注释,这些检查中的任何一个失败,则假定发生了某些条件,导致WPF错误地获取消息。通过确保WPF应用程序具有焦点,我能够使我的场景工作。

另外,我不得不做一些我不建议任何人做的事情,比如做一个代理User32.dll来覆盖一些WPF行为。这对我的目的是有效的,但只适用于单一的操作系统版本。幸运的是,我的目标机器不会连接到互联网,需要执行它的任务一年左右,不会收到任何操作系统更新在它的隔离环境;Windows更新可以很容易地杀死这个解决方案

不幸的是,为了接收Windows消息,您需要显式地覆盖WPF应用程序中的WndProc方法,如下所示。

HwndSource source = HwndSource.FromHwnd(new WindowInteropHelper(this).Handle);
source.AddHook(new HwndSourceHook(WndProc));

显然,如果不重新编译源代码,你就无法做到这一点,所以你需要另一种方法。


我之前建议EasyHook从原始方法处理程序注入重定向到您选择的一些代码。正如评论中正确指出的那样,这并不能解决问题,因为原始应用程序仍然无法接收Windows消息。欢迎使用UI自动化的建议