我有一个运行三个应用程序的Windows框。当应用程序启动时,每个应用程序都会创建一个无边框窗口,该窗口的位置使得它们以特定方式重叠。
此刻,当我点击底部窗口上的一个控件时,它会出现在窗口堆栈的顶部。
我需要确保每个窗口在窗口堆栈中保持其顺序,即使窗口接收输入。
我在想我需要编写一些简单的窗口管理器来维护窗口的正确Z顺序。
问题是,我需要知道特定窗口是否改变了位置。我发现有一条WM_WINDOWPOSCHANGING消息但我的理解是这条消息被发送到位置已经改变的窗口。
我需要我的窗口管理器应用程序以某种方式通知Z顺序已经改变。
是否有某些方法可以捕获所有WM_消息并确定该消息是否适用于我希望控制的其中一个窗口?
答案 0 :(得分:2)
考虑安装WH_CBT Windows挂钩,而不是尝试将DLL注入每个运行应用程序的MSalter方法。在您的CBTProc中,当您获得关注的三个应用程序窗口句柄的HCBT_MOVESIZE时返回0。
在CBTProc和SetWindowsHookEx上阅读有关文档的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顺序,避免闪烁。