2

我有一个运行三个应用程序的 Windows 机器。当应用程序启动时,每个应用程序都会创建一个无边界窗口,该窗口的位置使得它们以特定方式重叠。

目前,当我单击底部窗口上的控件时,它会到达窗口堆栈的顶部。

我需要确保每个窗口在窗口堆栈中保持其顺序,即使窗口接收到输入。

我在想我需要编写一些简单的窗口管理器来维护窗口的正确 Z 顺序。

问题是,我需要知道一个特定的窗口是否改变了位置。我发现有一条 WM_WINDOWPOSCHANGING 消息,但我的理解是该消息被发送到位置已更改的窗口。

我需要以某种方式通知我的窗口管理器应用程序 Z 顺序已更改。

有什么方法可以捕获所有 WM_ 消息并确定该消息是否适用于我希望控制的窗口之一?

4

5 回答 5

2

与其尝试将 DLL 注入到每个正在运行的应用程序中,不如考虑安装一个 WH_CBT Windows 挂钩,而不是 MSalter 的方法。在您的 CBTProc 中,当您为您关心的三个应用程序窗口句柄获得 HCBT_MOVESIZE 时返回 0。

阅读 MSDN 以获取有关CBTProcSetWindowsHookEx的文档。

于 2010-02-28T01:36:04.533 回答
0

您可以使用 SetWindowPos 以您想要的 Z 顺序定位您的窗口。我建议你拦截 WM_FOCUS 消息(当它收到焦点时发送到你的窗口)

在你的 wndProc 函数中,也许你可以尝试这样的事情:

LRESULT wndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam){
    // other stuff..

    switch (msg){
        case WM_FOCUS:
        {
            HWND firstWindow; // get the first window here
            HWND secondWindow; // this would be the second window
            HWND thirdWindow; // this would be the third window
            // TODO: initialize the windows correctly, based on your priority
            SetWindowPos(firstWindow, secondWindow, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); // position the second window below the first window
            SetWindowPos(secondWindow, thirdWindow, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOREDRAW | SWP_NOSIZE); // position the third window below the second window
        }
        return 0;
    }
    // other messages..
}

我不太确定 SetWindowPos 参数的顺序,因为我现在无法测试代码,但也许这可以让你继续前进?


如果您需要拦截所有 WM_ 消息,我会建议应用程序调用的 Window 类(我猜)而不是调用CreateWindowEx自己。例如:

class Window {
public
    Window(){
        ...
        WNDCLASSEX wc;
        ZeroMemory(&wc, sizeof(WNDCLASSEX));
        wc.cbSize        = sizeof(WNDCLASSEX);
        wc.lpfnWndProc   = wndProc;            // <- Note this one
        ...
    }

    static LRESULT WINAPI wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam){
        // reference: http://www.gamedev.net/community/forums/topic.asp?topic_id=303854 - Evil Steve  [Moderator]
        Window* parent;

        // Get pointer to window
        if(msg == WM_CREATE){
            parent = (Window*)((LPCREATESTRUCT)lParam)->lpCreateParams;
            SetWindowLongPtr(hwnd,GWL_USERDATA,(LONG_PTR)parent);
        }
        else{
            parent = (Window*)GetWindowLongPtr(hwnd,GWL_USERDATA);
            if(!parent) return DefWindowProc(hwnd,msg,wParam,lParam);
        }
        HWND prev = parent->mWin;
        parent->mWin = hwnd;
        LRESULT ret = parent->wndProc(msg,wParam,lParam);
        parent->mWin = prev;
        return ret;
    }

    virtual LRESULT wndProc(UINT msg, WPARAM wParam, LPARAM lParam){
    }
};

在此示例中,您的应用程序将从 Window 继承,基本上提供了稍微修改的 wndProc 函数(它将缺少 HWND,因此需要将其存储在某个地方,除非您从 Userdata 中获取它)。

  • 每次您收到消息时,该Window::wndProc(HWND, UINT, WPARAM, LPARAM)功能都会将其接收。在这里,您可以检查任何消息,包括(但不限于)WM_WINDOWPOSCHANGING

  • 另一件事是:
    wndProc(UINT, WPARAM, LPARAM), 而不是打电话给DefWindowProc(..)
    你 call Window::wndProc(UINT, WPARAM, LPARAM)。然后你可以在那里进行检查(以免堵塞第一个wndProc功能):)

这样做的缺点是,如果应用程序是由其他人编写的,他们将需要遵守您的窗口类。正如您所解释的,用户不需要与您的窗口管理器交互,但是,使用这种方法,唯一的交互是让您的窗口管理器为用户创建窗口。
否则,我认为您将不得不使用其他答案中解释的钩子

于 2010-02-25T11:31:20.640 回答
0

最简单的方法可能是将 DLL 注入三个应用程序中的每一个。这确保您只需要处理您真正关心的窗口消息子集。

EnumWindows()接下来,通过调用查找所有窗口,在每个应用程序中找到主窗口(并非完全微不足道,理论上可能还有更多) ,并调用GetWindowThreadProcessId()每个窗口以确定它是否属于当前进程(即注入您的 DLL 的那个进程) )。

现在您有了正确的 HWND,您可以挂钩关联的 WndProc 并捕获发送给它的任何 WM_WINDOWPOSCHANGING。

于 2010-02-25T15:12:52.853 回答
0

当您创建两个想要位于顶部的窗口时,将您想要位于底部的窗口作为 的hWndParentCreateWindow。然后,当底部窗口向前移动时,Windows 将始终将这些窗口向前移动,以便它们始终保持在它的前面。

因此,如果您的底部窗口是窗口 1。首先创建它,然后在创建窗口 2 和 3 时,将窗口 1 的句柄作为 hWndParent 值。窗口管理器完成其余的工作。

于 2010-02-28T01:01:20.913 回答
0

我想我会同意 John Knoeller 的回答。如果您希望窗口保持特定的 z 顺序,请确定该顺序是什么,并使用适当的父子关系创建您的窗口。

::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent);

当您这样做时,子窗口将始终位于父窗口之上。

如果您仍然坚持要捕获消息,您可以尝试在每个窗口中捕获 WM_ACTIVATE,然后将该消息转发到您的窗口管理器,该窗口管理器将可以访问所有窗口的 hwnd,并使用 SetWindowPos 正确地对它们进行 z-order。而不是 SetWindowPos,您可以使用 DeferWindowPos 一次性更改窗口的 z-order 并避免闪烁。

于 2010-04-29T16:59:20.323 回答