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

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

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

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

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

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

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

有帮助吗?

解决方案

而不是MSalter的做法试图DLL注入到每一个运行的应用程序,可以考虑安装一个WH_CBT的Windows钩子。在你CBTProc,返回0,当你得到一个HCBT_MOVESIZE你所关心的。

三个应用程序窗口句柄

读MSDN对于文档上 CBTProc SetWindowsHookEx函数

其他提示

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

接着,通过调用EnumWindows()找到所有窗口,并呼吁每个GetWindowThreadProcessId()以确定它是否属于当前进程(即,一种发现在每个应用程序的主窗口(未完全微不足道的,在理论上可以存在更多)您的DLL中注入)。

现在,你有正确的HWND,你可以连接相关的WndProc和赶上发送给它的任何WM_WINDOWPOSCHANGING。

当您创建两个窗口,你想在上面,给这个窗口,你想成为底部的hWndParentCreateWindow。那么Windows将永远把这些窗口前时,所带来的底部窗口前,让他们永远留在它的前面。

所以,如果您的底部窗口是窗口1.首先创建它,那么当你创建窗口2&3,举窗1的句柄作为hWndParent值。窗口管理器没有休息。

您可以使用 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 功能) :)

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

我想我会与约翰Knoeller的答案达成一致。如果你想在窗口停留在一个特定的Z顺序,然后确定这个顺序是什么,以及用适当的父子关系创建窗口。

::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent);

当你这样做的子窗口会一直留在父母的顶部。

如果你还是坚持抓的消息,你可以尝试在每个窗口醒目WM_ACTIVATE,然后转发该消息到你的窗口管理器,它可以访问所有的窗户和他们正确使用SetWindowPos Z顺序的hwnds。而不是SetWindowPos你可以一次全部为Windows和避免闪烁使用DeferWindowPos来改变Z顺序。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top