【问题标题】:Window handling manager窗口处理管理器
【发布时间】:2011-01-20 22:51:36
【问题描述】:

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

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

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

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

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

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

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

【问题讨论】:

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


    【解决方案1】:

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

    阅读 MSDN 以获取有关 CBTProc 和 SetWindowsHookEx 的文档。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          【解决方案4】:

          您可以使用 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 函数):)

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

          【讨论】:

          • 如果不要求用户与“窗口管理器”应用程序交互,我会更喜欢它。我希望有某种方法可以检测窗口顺序何时更改,然后让“窗口管理器”应用程序自动响应。例如,一个笨拙的方法是设置一个计时器,以便“窗口管理器”应用程序每隔 X 时间段检查窗口顺序并在必要时重新排序。我希望有一些其他消息可用于确定何时检查是否需要重新排序。
          【解决方案5】:

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

          ::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent);
          

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

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-08-23
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多