窗口处理经理

时间:2010-02-25 10:23:50

标签: c++ windows winapi window-managers z-order

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

此刻,当我点击底部窗口上的一个控件时,它会出现在窗口堆栈的顶部。

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

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

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

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

是否有某些方法可以捕获所有WM_消息并确定该消息是否适用于我希望控制的其中一个窗口?

5 个答案:

答案 0 :(得分:2)

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

CBTProcSetWindowsHookEx上阅读有关文档的MSDN。

答案 1 :(得分: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(..)
    你打电话给Window::wndProc(UINT, WPARAM, LPARAM)。然后你可以在那里进行检查(至于没有找到第一个wndProc函数):)

这个的缺点是,如果应用程序是由其他人编写的,则需要遵守您的窗口类。当您解释它时,用户不需要与您的窗口管理器进行交互,但是,通过这种方法,唯一的交互是让您的窗口管理器为用户创建窗口。
否则我认为你必须使用其他答案中解释的钩子

答案 2 :(得分:0)

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

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

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

答案 3 :(得分:0)

当您创建要置于顶部的两个窗口时,请将您想要位于底部的窗口作为hWndParent值添加到CreateWindow。当底部窗口向前移动时,Windows将始终向前移动这些窗口,以便它们始终保持在它前面。

因此,如果您的底部窗口是窗口1.首先创建它,然后创建Windows 2&amp; 3,将窗口1的句柄作为hWndParent值。窗口管理器完成剩下的工作。

答案 4 :(得分:0)

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

::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent);

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

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