mfc+窗口分析+popup+,overlap,child(4)

2019-09-01 15:16

啊?难道前面的规则和推论都是错误的不成?我创建它们的时候,就明明白白地指定了hWndParent参数,而且上面的实验也表明了他们之间的Owner和Owned关系,那是不是GetParent错了?我想是的,你先别对着我扔砖头,想看到正确的情况么?好,我弄给你看。

我们是如何创建A,B和C这几个弹出窗口的?我再把创建它们的语句贴一下吧:

g_hwndX = CreateWindowEx(NULL, TEXT(\TEXT(\WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL); 现在把这个语句改为:

g_hwndX = CreateWindowEx(NULL, TEXT(\TEXT(\WS_POPUP|WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL);

对,就是加上一个WS_POPUP,看看情况变得怎么样?

很惊讶,对不?GetParent这回全部都正确地按照MSDN的描述工作了,这是我发现的popup和Overlapped的第二个差别,第一个差别?在文章开头附近,自己回去找。而spy++显示出来的那个Parent,其实就是GetParent返回的结果。记住,对于非child窗口来说,GetParent返回的并不是Parent,MSDN也是这么说的,你看看这个函数的名字是不是很有误导性?还有spy++也真是的,将错就错。好吧,就让它错去吧,但我们得记住:对非Child窗口来说,Parent一定是桌面。好,再有个问题,看刚刚这个实验,对于有WS_POPUP风格的非Child窗口来说,GetParent能够取回它的Owner,可对于没有WS_POPUP风格的非Child窗口来说,GetParent恒定返回0,那我们如何有效地取得非Child窗口真正的主人呢?方法当然是有的,看: {

DWORD rtn;

HWND hw = GetWindow(hWnd, GW_OWNER); //获取主窗口的Owner if(hw==NULL)

rtn = GetLastError();

hw = GetWindow(g_hwndA, GW_OWNER); //获取A的Owner if(hw==NULL)

rtn = GetLastError();

hw = GetWindow(g_hwndB, GW_OWNER); //获取B的Owner if(hw==NULL)

rtn = GetLastError();

hw = GetWindow(g_hwndC, GW_OWNER); //获取C的Owner if(hw==NULL)

rtn = GetLastError(); }

这么一来,无论是否带有WS_POPUP风格,都能够正常取得其所有者了,这个跟spy++的结果一致,用GetWindow取得的Owner总是正确的,那有没有一种方法,使得取得的Parent总是正确的?很遗憾,没有直接的API,包括使用GetWindowLong(hwnd,

GWL_HWNDPARENT)都不能一直正确返回Parent,BTW,有位高人说,GetWindowLong(hwnd, GWL_HWNDPARENT)和GetParent(hwnd)有时候会得到不同的结果,不过这个我尝试不出来,我观察的,它们总是返回一样的结果,无论对什么窗口,真怀疑GetParent(hwnd)就是return (HWND)GetWindowLong(hwnd, GWL_HWNDPARENT),虽然我们不能直接一步获取正确的Parent,但我们可以写一个简单的函数:

HWND GetTrueParent(HWND hwnd) {

DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE); if((dwStyle & WS_CHILD) == WS_CHILD) return GetParent(hwnd); else

return GetDesktopWindow(); }

你终于憋不住了,对我大吼:“你有什么依据说非Child窗口的Parent一定是Desktop?”我当然是有依据的,首先是这些非child window的绘制,不能超出桌面,超出桌面就什么都看不见了,只能是桌面管理着它们的绘制,如果它们确实存在Parent的话,当然,聪明你认为这个理由并不充分,OK,我们编程来证明,先介绍一个API:

HWND FindWindowEx(

HWND hwndParent, // handle to parent window HWND hwndChildAfter, // handle to child window LPCTSTR lpszClass, // class name

LPCTSTR lpszWindow // window name );

又被你猜对了,我是从MSDN上copy下来的(^_^),看MSDN对这个函数的说明:

hwndParent

[in] Handle to the parent window whose child windows are to be searched.

If hwndParent is NULL, the function uses the desktop window as the parent window. The function searches among windows that are child windows of the desktop.

hwndChildAfter

[in] Handle to a child window. The search begins with the next child window in the Z order. The child window must be a direct child window of hwndParent, not just a descendant window. If hwndChildAfter is NULL, the search begins with the first child window of hwndParent.

lpszClass

窗口类名(我来翻译,简单点)

lpszWindow 窗口标题

关键是看第一个参数,如果hwndParent为NULL,函数就查找desktop的“子窗口”,但这

个“子窗口”是加引号的,因为这里的“子窗口”和本文前面一直提到的子窗口确实不太一样,那就是这里的“子窗口”没有WS_CHILD风格,算是一个特殊吧,也难怪GetParent不愿意告诉我们desktop就是这些非Child的父窗口。好,有这个函数,我们就可以知道刚才创建的那几个弹出窗口的老爸究竟是不是桌面。代码十分简单: {

DWORD rtn;

HWND hw = FindWindowEx(NULL, NULL, TEXT(\TEXT(\从桌面开始查找主窗口 if(hw==NULL)

rtn = GetLastError();

hw = FindWindowEx(NULL, NULL, TEXT(\从桌面开始查找A

if(hw==NULL)

rtn = GetLastError();

hw = FindWindowEx(NULL, NULL, TEXT(\从桌面开始查找B

if(hw==NULL)

rtn = GetLastError();

hw = FindWindowEx(NULL, NULL, TEXT(\从桌面开始查找C

if(hw==NULL)

rtn = GetLastError(); }

结果如何?(是不是偷懒干脆不做,等着我说结果啊?)我的结果是全部找到了,和用spy++查找的结果一样,所以我有充分的理由认为,所有非child窗口其实是desktop的child,spy++的树形结构组织确实也是这么阐述的。你很厉害,你还是能够驳斥我:“根据规则三,Parent被销毁的时候,其Child将被销毁,你证明给我看?”这个??有点难:

HWND hwndDesktop = GetDesktopWindow(); BOOL rtn = DestroyWindow(hwndDesktop); if(!rtn)

DWORD dwErr = GetLastError();

My god,Desktop没了,你说我们还能看到什么呢?当然微软不会没想到这点,DestroyWindow当然不能成功,错误代码为5,“拒绝访问”。好,我有些累了,不能再纠缠了,转入下一节!留个作业如何?尝试使用SetParent这个API,改变窗口的Parent,观察运行情况,并思考这样做有什么不好之处。

三、如何体现WS_CLIPSIBLING和WS_CLIPCHILD?

看了这个标题,应该怎么做?我想你十有八九是打开MSDN,输入这两个关键字去搜索吧?OK,不用了,我把MSDN对这两个窗口风格的说明贴出来:

WS_CLIPCHILDREN Excludes the area occupied by child windows when you draw within

the parent window. Used when you create the parent window.

WS_CLIPSIBLINGS Clips child windows relative to each other; that is, when a particular child window receives a paint message, the WS_CLIPSIBLINGS style clips all other overlapped child windows out of the region of the child window to be updated. (If WS_CLIPSIBLINGS is not given and child windows overlap, when you draw within the client area of a child window, it is possible to draw within the client area of a neighboring child window.) For use with the WS_CHILD style only.

找到是不难,但如果光看这个就明白的话我也不必要写这种文章了,没有适当的代码去实践,估计很多人是不懂这两个风格什么含义的。OK,现在我来带你实践。spy++开着不?哈,别关啊,后面还要用到。用spy++观察各个top-level window(非Child窗口)的属性,是不是都有个WS_CLIPSIBLINGS?想找个没有的都不行,如果你不服气,你要自己创建一个没有WS_CLIPSIBLINGS风格的顶层窗口,好吧,我在这里等你一会儿(??一会儿过去了??),你垂头丧气地回来了:“不行,即便我不指定这个风格,Windows也强制帮我加上。”那??你可以强制剥离掉这个风格啊,这样:

DWORD dwStyle = GetWindowLong(hWnd, GWL_STYLE); dwStyle &= ~(WS_CLIPSIBLINGS); SetWindowLong(hWnd, GWL_STYLE);

执行后用spy++一看,还是没有把WS_CLIPSIBLINGS风格去掉,看来Windows是吃定你的了。嗯,前面说的都是top-level window,那对于child window呢?创建一个MFC对话框,在上面加几个button,然后增加/删除这几个button的WS_CLIPSIBLINGS风格?你除了发现child window对与WS_CLIPSIBLING风格不再是强制的之外,恐怕仍然一无所获吧。还是得Follow me,我还是不用MFC,用最简单的Windows API。模仿第二节的创建几个popup窗口A、B、C的那个例子,只不过现在的CreateWindowEx改成这样:

g_hwndA = CreateWindowEx(NULL, TEXT(\

WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 30, 30, 400, 300, hWnd, NULL, hInst, NULL);

g_hwndB = CreateWindowEx(NULL, TEXT(\

WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 60, 60, 400, 300, hWnd, NULL, hInst, NULL);

g_hwndC = CreateWindowEx(NULL, TEXT(\

WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 90, 90, 400, 300, hWnd, NULL, hInst, NULL);

创建出来的效果如图:

图14

一眼看没什么奇怪的,但尝试拖动里边的窗口就出现些问题了,首先是显示在最前端的C窗口不能拖动(其实是被挡住了),然后你发现B也不能拖动,A可以,A一拖,就出现这种情况:


mfc+窗口分析+popup+,overlap,child(4).doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:消防专项方案 - 图文

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: