2010-02-25 174 views
2

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

目前,当我点击底部窗口上的一个控件时,它会进入窗口堆栈的顶部。

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

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

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

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

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

回答

2

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

阅读MSDN中关于CBTProcSetWindowsHookEx上的文档。

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_消息,我会建议调用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){ 
    } 
}; 

在这个例子中你的应用程序会从窗口继承,基本上提供了一个稍微修改的WndProc功能(这将是缺少HWND所以这将需要存储在某个地方,除非你选择它向上用户数据)。

  • 每当您收到消息时,Window::wndProc(HWND, UINT, WPARAM, LPARAM)函数都会检测到它。在这里,您可以对任何消息进行检查,包括(但不限于)WM_WINDOWPOSCHANGING

  • 其他的事情将是:
    wndProc(UINT, WPARAM, LPARAM),而不是调用DefWindowProc(..)
    你打电话Window::wndProc(UINT, WPARAM, LPARAM)。然后,你可以做你的支票在那里,而不是(如不cludge第一wndProc功能):)

这个的缺点是,如果应用程序是别人写的,他们需要遵守你的windowclass。正如你解释的那样,用户不需要与窗口管理器交互,但是,采用这种方法,唯一的交互作用就是让窗口管理器为用户创建窗口。
否则我认为你将不得不去解决在其他答案中解释的钩子

+0

我宁愿它,如果用户不应该被要求与'窗口管理器'应用程序进行交互。 我希望能有一些方法来检测窗口顺序何时改变,然后让'窗口管理器'应用程序自动响应。例如,使用kludgey的方法是设置一个计时器,以便'窗口管理器'应用程序每隔X个时间段检查窗口顺序并在必要时重新排序。 我希望能有一些其他的消息可以用来确定何时检查重新排序是否必要。 – JRT 2010-02-25 13:30:14

0

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

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

既然你有正确的HWND,你可以钩住关联的WndProc并捕获发送给它的任何WM_WINDOWPOSCHANGING。

0

当您创建要置顶的两个窗口时,请将您希望置于最下面的窗口设为hWndParent的值为CreateWindow。当底部窗口向前移动时,Windows将始终将这些窗口向前移动,以便它们始终保持在它的前面。

因此,如果您的底部窗口是窗口1.首先创建它,然后当您创建窗口2时,窗口1的句柄作为hWndParent值。窗口管理员完成剩下的工作。

0

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

::SetWindowLong(hwnd_child, GWL_HWNDPARENT, hwnd_parent); 

当你这样做时,子窗口将始终停留在父窗口之上。

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