ImageVerifierCode 换一换
格式:DOC , 页数:28 ,大小:267KB ,
资源ID:6989103      下载积分:10 金币
快捷注册下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

开通VIP
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.zixin.com.cn/docdown/6989103.html】到电脑端继续下载(重复下载【60天内】不扣币)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

开通VIP折扣优惠下载文档

            查看会员权益                  [ 下载后找不到文档?]

填表反馈(24小时):  下载求助     关注领币    退款申请

开具发票请登录PC端进行申请

   平台协调中心        【在线客服】        免费申请共赢上传

权利声明

1、咨信平台为文档C2C交易模式,即用户上传的文档直接被用户下载,收益归上传人(含作者)所有;本站仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。所展示的作品文档包括内容和图片全部来源于网络用户和作者上传投稿,我们不确定上传用户享有完全著作权,根据《信息网络传播权保护条例》,如果侵犯了您的版权、权益或隐私,请联系我们,核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
2、文档的总页数、文档格式和文档大小以系统显示为准(内容中显示的页数不一定正确),网站客服只以系统显示的页数、文件格式、文档大小作为仲裁依据,个别因单元格分列造成显示页码不一将协商解决,平台无法对文档的真实性、完整性、权威性、准确性、专业性及其观点立场做任何保证或承诺,下载前须认真查看,确认无误后再购买,务必慎重购买;若有违法违纪将进行移交司法处理,若涉侵权平台将进行基本处罚并下架。
3、本站所有内容均由用户上传,付费前请自行鉴别,如您付费,意味着您已接受本站规则且自行承担风险,本站不进行额外附加服务,虚拟产品一经售出概不退款(未进行购买下载可退充值款),文档一经付费(服务费)、不意味着购买了该文档的版权,仅供个人/单位学习、研究之用,不得用于商业用途,未经授权,严禁复制、发行、汇编、翻译或者网络传播等,侵权必究。
4、如你看到网页展示的文档有www.zixin.com.cn水印,是因预览和防盗链等技术需要对页面进行转换压缩成图而已,我们并不对上传的文档进行任何编辑或修改,文档下载后都不会有水印标识(原文档上传前个别存留的除外),下载后原文更清晰;试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓;PPT和DOC文档可被视为“模板”,允许上传人保留章节、目录结构的情况下删减部份的内容;PDF文档不管是原文档转换或图片扫描而得,本站不作要求视为允许,下载前可先查看【教您几个在下载文档中可以更好的避免被坑】。
5、本文档所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用;网站提供的党政主题相关内容(国旗、国徽、党徽--等)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
6、文档遇到问题,请及时联系平台进行协调解决,联系【微信客服】、【QQ客服】,若有其他问题请点击或扫码反馈【服务填表】;文档侵犯商业秘密、侵犯著作权、侵犯人身权等,请点击“【版权申诉】”,意见反馈和侵权处理邮箱:1219186828@qq.com;也可以拔打客服电话:0574-28810668;投诉电话:18658249818。

注意事项

本文(mfc+窗口分析+popup+,overlap,child.doc)为本站上传会员【pc****0】主动上传,咨信网仅是提供信息存储空间和展示预览,仅对用户上传内容的表现方式做保护处理,对上载内容不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知咨信网(发送邮件至1219186828@qq.com、拔打电话4009-655-100或【 微信客服】、【 QQ客服】),核实后会尽快下架及时删除,并可随时和客服了解处理情况,尊重保护知识产权我们共同努力。
温馨提示:如果因为网速或其他原因下载失败请重新下载,重复下载【60天内】不扣币。 服务填表

mfc+窗口分析+popup+,overlap,child.doc

1、窗口分析 (本文尝试通过一些简单的实验,来分析Windows的窗口机制,并对微软的设计理由进行一定的猜测,需要读者具备C++、Windows编程及MFC经验,还得有一定动手能力。文中可能出现一些术语不统一的现象,比如“子窗口”,有时候我写作“child window”,有时候写作“child”,我想应该不会有太大影响,文章太长,不一一更正了) 问题开始于我的最近的一次开发经历,我打算把程序的一部分界面放在DLL中,而这部分界面又需要使用到Tooltip,但DLL中的虚函数PreTranslateMessage无法被调用到,原因大家可以在网上搜索一下,这并不是我这篇文章要讲的。PreTr

2、anslateMessage不能被调,那Tooltip也就不能起作用,因为Tooltip需要在PreTranslateMessage中加入tooltip.RelayEvent(&msg)来触发事件,方可正常显示。解决方法有好几个,我用的是比较麻烦的一个——完全自己手动编写Tooltip,然后用WM_MOUSEMOVE等事件来触发Tooltip显示,写好之后发现些小问题,那就是调试运行时候IDE给了个warning,说我在析构函数中调用了DestroyWindow,这样会导致窗口OnDestry和OnNcDestroy不被正常调用,这个问题我以前遇到过,当然解决方法也是显而易见的,只需要在窗口对

3、象(C++概念,非Windows内核对象,下文同)销毁前,调用DestroyWindow即可。对于要销毁的这个窗口的子窗口,是不需要显式调用DestroyWindow的,因为父窗口在销毁的时候也会销毁掉它们,OK,我把这个过程用个示意图说明一下: 图1 上图表示了App Window及其子窗口的关系,现在假设我们要销毁Parent Window 1(对应的对象指针是m_pWndParent1),我们可以m_pWndParent1->DestroyWindow(),这样Child Window 1,Parent Window 2,Child Window 2都被销毁了,销毁的时候这些窗口

4、的OnDestry和OnNcDestroy都被调用了,最后delete m_pWndParent1,此时m_pWndParent1->m_hWnd已经是NULL,不会再去调用Destroy,在析构的时候也就不会出现Warning。但如果不先执行m_pWndParent1->DestroyWindow()而直接delete m_pWndParent1,那么在CWnd::~CWnd中就会调用DestroyWindow(m_hWnd),这样会产生WM_DESTROY和WM_NCDESTROY,会尝试去调用OnDestry和OnNcDestroy,但由于是在CWnd的函数~CWnd()的内部调用这两个

5、成员,此时的虚函数表指针并不指向派生类的虚函数表,因此调用的其实是CWnd::OnDestroy和CWnd::OnNcDestroy,派生类的OnDestry和OnNcDestroy不被调用,但我们很多时候把释放内存等操作写在派生类的OnDestroy和OnNcDestroy中,这样,就容易导致内存泄露和逻辑混乱了。 上面这些道理我当然是知道的,但Warning还是出现了,而且我用排除法确定了是跟我写的那个Tooltip有关,下面是关于我的Tooltip的截图: 图2 大家看到,Tooltip显示在我的图形窗口上,它是个弹出式(popup)窗口,其内容为当前鼠标光标的坐标

6、值,图形窗口之外,我是不想让它显示的,那么按照我的思路,Tooltip就应该设计是图形窗口的子窗口,它的窗口对象就应该作为图形窗口对象的成员,在图形窗口OnCreate的时候创建,在图形窗口被DestroyWindow的时候自动销毁,前面提到过,父窗口被销毁的时候,其子窗口会被自动销毁,没错吧,所以不需要显式去对Tooltip调用DestroyWindow。可事实证明了这样是有问题的,因为Tooltip的父窗口根本不是,也不能是图形窗口。大家可以看到我的图形窗口是作为一个子窗口嵌入到别的窗口中去的,它的属性包含了WS_CHILD,通过实验,我发现Tooltip的父窗口只能指定为程序主窗口,如果

7、企图指定为那个图形窗口的话,它就自动变为程序主窗口,再进一步研究发现,弹出式窗口的父窗口都不能是带WS_CHILD风格的窗口,然后打开spy++查看,弹出式窗口的上一级都是桌面,可是,通过GetParent函数,得到的弹出式窗口的父窗口却是程序主窗口而不是桌面,为什么?……问题越来越多,我糊涂了,上面说的都是在我深入理解前,所看到的现象,包括了我的一些概念认识方面的错误。 好吧,我们现在开始,一点点地通过实验去攻破这些难题! 一、神秘的WS_OVERLAPPED 我们从WinUser.h头文件中可以看出,窗口可分三种,其Window Styles定义如下: #defin

8、e WS_OVERLAPPED 0x00000000L #define WS_POPUP 0x80000000L #define WS_CHILD 0x40000000L 那么我们很容易得到这个结论:style的最高位是1的,是一个popup窗口,style的次高位是1的,代表是一个child窗口,如果最高位次高位都是0,那这个窗口就是一个overlapped窗口,如果两位都是1,厄……MSDN告诉我们不能这么干,事实呢?我后面再讲。其实这个结论是有点过时的,甚至很能误导人,不是我们的原因,很可能是Windows的历史原因,为什

9、么?具体也是后面讲。嘿嘿。 OK,我们现在开始来尝试,看看这些风格究竟影响窗口几何,对了,准备spy++,这是必备工具。 用VC++的向导创建一个Hello World的Windows程序,注意是Windows程序,不是MFC的Hello World,这样我们可以绕开MFC,专注于查看一些Windows的技术细节,编译,运行。 图3 然后用spy++查看这个窗口的风格,发现其风格显示为“WS_OVERLAPPEDWINDOW|WS_VISIBLE|WS_CLIPSIBLING|WS_OVERLAPPED”。此时它的创建函数为: hWnd = CreateW

10、indow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 只制定了一个WS_OVERLAPPEDWINDOW,但我们很快就找到了WS_OVERLAPPEDWINDOW的定义: #define WS_OVERLAPPEDWINDOW (WS_OVERLAPPED | \ WS_CAPTION | \

11、 WS_SYSMENU | \ WS_THICKFRAME | \ WS_MINIMIZEBOX | \ WS_MAXIMIZEBOX) 原来overlapped窗口就是有标题,系统菜单,最小最大化按钮和可调整大小边框的窗口,这个定义是正确的,但只是个我们认知上的概念的问题,因为popup和child窗口也同样可以拥有这些(后面证明)。由于WS_OVER

12、LAPPED为0,那我们是不是可以把WS_OVERLAPPEDWINDOW定义中的WS_OVERLAPPED拿掉呢?那是肯定的,那也就是说WS_OVERLAPPED什么都不是!我们只作popup和child的区分,是不是这样?也不是,我们继续实验。 很简单,接下去我们只给这个向导生成的代码加一点点东西,就是把CreateWindow改成: hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW|WS_POPUP, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, h

13、Instance, NULL); 对,给窗口风格增一个popup风格,看看会怎么样?运行!这回可不得了,窗口缩到了屏幕的左上角,并且宽度高度都变为了最小,当然,你还是可以用鼠标拖动窗口边缘来调整它的大小的。如图: 图4 这是为什么呢?观察CreateWindow的,第四、第五、第六和第七参数,分别为窗口的x坐标,y坐标,宽度,和高度,CW_USEDEFAULT被define成0,所以窗口被缩到左上角去也就不奇怪了,可没有popup,光是overlapped风格的窗口,为什么不会缩呢?看MSDN的说明,对第四个参数的说明:“If this parameter is set

14、to CW_USEDEFAULT, the system selects the default position for the window's upper-left corner and ignores the y parameter. CW_USEDEFAULT is valid only for overlapped windows; if it is specified for a pop-up or child window, the x and y parameters are set to zero. ”其余几个参数也有类似的描述,这说明了什么?说明Windows对overl

15、apped和popup还是作区分的,而这点,算是我们发现的第一个不同。哦,还有件事情,就是用spy++观察其风格,发现其确实多了一个WS_POPUP,其余没什么变化。 继续,这回还是老地方,把WS_POPUP改为WS_CHILD,试试看,这回创建窗口失败了,返回0,用GetLastError查看具体错误信息,得到的是:“1406:无法创建最上层子窗口。”看来桌面是不让我们随便搞的。继续,还是老地方,这回改成: hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW|WS_POPUP|WS_CHILD, CW_U

16、SEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL); 嗯?有没搞错,又是popup又是child,肯定不能成功吧,不试不知道,居然成功了,这个创建出来的窗口乍一看,跟popup风格的很像,但用起来有些怪异,比如:当它被别的窗口挡住的时候,不能通过点击它的客户区来让它显示在前面,即使点击它的标题栏,也是要松开鼠标左键,它才能显示在前面,还有就是用spy++的“瞄准器”没法准确捕捉到这个窗口,瞄准器对准它的时候,就显示Caption为“Program Manager”,class为“Program”,“Program Mana

17、ger”是什么?其实就是我们所看到的这个桌面(注意,不是桌面,我说的是我们说“看到的桌面”,就是显示桌面图标的这个所能看到的桌面窗口,和前面提到的桌面窗口是有区别的)的父窗口的父窗口,这个窗口一般情况下是不能直接“瞄准”到的,这点可以通过spy++证实,如图: 图5 图6 spy++不能直接“瞄准”这个popup和child并存的怪窗口,但我们有别的办法捕捉到它,+,输入窗口的标题来查找(记得运行程序后刷新一下才能找到),结果见下图: 图7 我们从上图中清楚地看到,popup和child并存!用spy++逐个查看桌面窗口的

18、下属,这种情况还是无独有偶的,但这样的窗口代表了什么意义,我就不清楚了,总之用起来怪怪的,对Microsoft来说,这可能就是Undocumented,OK,我们了解到这里就行了,但一般情况下,我们不要去创建这种奇怪的窗口。这几轮实验给我们什么启示?设计上的启示:一个应用程序的主窗口通常是一个Overlapped类型的窗口,当然有时可以是一个popup窗口,比如基于对话框的程序,但不应该是一个child窗口,尽管上面演示了如何给应用程序主窗口加入child风格。 那还有一个问题,我为什么认为WS_OVERLAPPED神秘呢?这还算是拜spy++所赐,按照我们一般的想法,如果一个窗口的风格

19、的最高两位都是0,它既不是popup也不是child的时候,那它就是Overlapped。事实上spy++的判定不是这样的,就以刚才的实验为例,当使用WS_OVERLAPPEDWINDOW|WS_POPUP风格创建窗口的时候,WS_OVERLAPPED和WS_POPUP属性同时出现了,我做了很多很多的尝试,企图找出其中规律,看看spy++是怎么判定WS_OVERLAPPED的,但至今没结论,我到MSDN上search,未果,有人提起这个问题,但没有令我满意的答复,下面这段文字是我找到的可能有点线索的答复: Actually, Microsoft Spy++ is wrong. Ther

20、e are two bits in the window style that control its type. If the high-order bit of the style DWORD is set, the window is a popup window. If the next bit is set, the window is a child window. If neither is set, the window is overlapped. (If both are set, the result is undocumented.) Look at these

21、definitions from WinUser.h. #define WS_OVERLAPPED 0x00000000L #define WS_POPUP 0x80000000L #define WS_CHILD 0x40000000L Your window style (0x94c00880) has the high-order bit set and the next bit clear so it is a popup window, not an overlapped window. The corre

22、ct way to identify all three types of windows (this is what Spy++ should do) is dwStyle = GetWindowLong(hWnd, GWL_STYLE); if (dwStyle&WS_POPUP) // it's a popup window else if (dwStyle&WS_CHILD) // it's a child window else // it's an overlapped window 这断描述跟我的想法一致。要知道,就算你只给窗口一个WS_PO

23、PUP的风格,WS_OVERLAPPED也会显示在spy++上的,我认为这十分有问题,究竟spy++如何判,估计得请教比尔盖茨了。还有一段有趣的描述,估计也有所帮助: As long as... WS_POPUP | WS_OVERLAPPED ...is absolutelly equivalent with... WS_POPUP ... why do you care if Spy++ lists WS_OVERLAPPED or not? Please stop playing "Thomas Unbeliever" with us. Becomes too e

24、xpensive to use "walking on the water" device here again, and again. ;) 虽然这么说,我还是认为,spy++给了我们不少误导,那么对WS_OVERLAPPED的讨论就暂时告一段落吧,作为一个技术人,很难容忍自己无法理解的逻辑,我就是这么种人……不过如果再扯下去的话这篇文章就不能结束了,所以姑且认为,这是spy++的错,而我们还是认为窗口分3种——popup,child和Overlapped。(Undocumented不在此列,也不在本文讲述之列) 二、Parent与Owner 这是内容最多的一节,做好心理准

25、备。 微软和我们开了个玩笑,告诉我们,窗口和人一样,可以有父母,有主人……我们先来看一个最著名的Windows API: HWND CreateWindowEx( DWORD dwExStyle, // extended window style LPCTSTR lpClassName, // registered class name LPCTSTR lpWindowName, // window name DWORD dwStyle, // window style int x, /

26、/ horizontal position of window int y, // vertical position of window int nWidth, // window width int nHeight, // window height HWND hWndParent, // handle to parent or owner window HMENU hMenu, // menu handle or child identifier

27、 HINSTANCE hInstance, // handle to application instance LPVOID lpParam // window-creation data ); 猜对了,我就是从MSDN上copy下来的,看第九个参数的名字叫hWndParent,顾名思义哦,这就是Parent窗口了,不过我们中国人不喜欢称之“父母窗口”,我们喜欢叫它“父窗口”,简单一点。其实这个名字对我们造成了不少的误导,我只能说,可能也是由于历史原因,比如在Windows 1.0(1985年出的,当时没什么影响力)的时候,只有Parent这个概念,没有Owner

28、的概念。 回头看看文章开始我提起的,我企图将Tooltip的父窗口设置为一个图形窗口,不能成功,Tooltip的父窗口会自动变成应用程序主窗口,这是为什么?好,现在开始讲概念了,都是我花了很多时间在互联网上搜索,筛选,确认,得出来的结论: 规则一:Owner window控制了Owned window的生存,当Owner window被销毁的时候,其所属的Owned window就会被销毁。 规则二:Parent window控制了Child window的绘制,Child window不可能显示在其Parent window的客户区之外。 规则三:Parent window同

29、时控制了Child window的生存,当Parent window被销毁的时候,其所属的Child window就会被销毁。 规则四:Owner window不能是Child window。 规则五:Child window一定有Parent(否则怎么叫Child?),一定没有Owner。 规则六:非Child window的Parent一定是桌面,它们不一定有Owner。 这是比较重要的几点,如果你认为这跟你以前学到的,或者认知的有所不同,先别急着抗议,先看看我是怎么理解的。除了这几条规则,下面我还会逐步给出一些规则。 先说比较好理解的Child window,上文提到了

30、包含了WS_CHILD风格的窗口就叫Child window,我们中文叫“子窗口”。那么我前面提到的我写的那个Tooltip,是不是“子窗口”呢?——当然不是了,它没有WS_CHILD风格啊,它是popup风格的,我想当然地认为在创建它的时候给它指定了那个Parent参数,那它的Parent就是那个参数,其实是错的。这个实验最简单了,随便找些应用程序,比如“附件”里的计算器,用spy++的“瞄准器”观察上面的按钮等“子窗口”,在Styles标签中,我们可以看到WS_CHILD(或者WS_CHILDWINDOW,一样的)属性,然后在Windows标签中,我们可以清楚地看到,凡是包含了WS_CH

31、ILD属性的窗口(子窗口),都没有Owner window,不信还可以继续观察其它应用程序,省去自己编程了。再看它们的Parent window,是不是一定有的?——当然一定有。 前面说了,子窗口不能显示在父窗口客户区之外,我们最常见的子窗口就是那些摆在对话框上的控件,什么button啊,listbox啊,combobox啊……都有个共同特点,不能拖动的,除非你重写它们的window procedure,然后响应WM_MOUSEMOVE等消息,实现所谓“拖动”。那么有没有能够像应用程序主窗口那样有标题栏,能够被自由拖动的子窗口呢?——当然有!要创建是吗?简单,直接用MFC向导创建一个MD

32、I程序即可,MDI的那些View其实就是可以自由拖动的子窗口,可以用spy++查看一下它们的属性,当然,你是不能把它们拖出主窗口的客户区的。也许你跟我一样,觉得MFC封装了过多的技术细节,想完全自己手动创建一个能拖动的子窗口,而且看起来就像个MDI的界面,OK,follow me。 首先当然是用应用程序向导生成最普通的Window应用程序了。然后增加一个窗口处理函数,也就是我们准备创建的子窗口的处理函数了。 LRESULT CALLBACK WndProcDoNothing(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)

33、 { return DefWindowProc(hWnd, message, wParam, lParam); } DoNothing?好名字。注册之: WNDCLASSEX wcex; wcex.cbSize = sizeof(WNDCLASSEX); wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = (WNDPROC)WndProcDoNothing; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0;

34、 wcex.hInstance = hInstance; wcex.hIcon = LoadIcon(hInstance, (LPCTSTR)IDI_ALLWINDOWTEST); wcex.hCursor = LoadCursor(NULL, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wcex.lpszMenuName = NULL; //子窗口不能拥有菜单,指定了也没有用 wcex.lpszClassName = TEXT("child_windo

35、w"); wcex.hIconSm = LoadIcon(wcex.hInstance, (LPCTSTR)IDI_SMALL); RegisterClassEx(&wcex); 最后当然是把它给创建出来了: g_hwndChild = CreateWindowEx(NULL, TEXT("child_window"), TEXT(""), WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL); 关于WS_

36、CLIPSIBLINGS属性,下文将提到。好,就这样,大家看看运行效果: 图8 是不是很少遇到这种窗口组织结构?确实很少人这样用,而且哦,你会发现子窗口的标题栏没办法变为彩色,它一直是灰的,就表示它一直处于未激活状态,你怎么点它,拖它,调它,都没用的,而这个时候程序主窗口一直显示为激活状态,如何激活这个子窗口?我曾经对此苦思冥想,最后才知道,子窗口是无法被激活的,你立即反驳:“那MFC如何做到的?”哈哈,好,你反应够快,我下文会给你演示如何“激活”子窗口。(注意是加引号的)现在尝试移动主窗口,你会发现所有它的子窗口都会跟着主窗口移动的,这就好像我们看苹果落地一样,不会觉得奇

37、怪,但你有没有想过,主窗口移动的时候,其子窗口对屏幕的位置也发生了变化,不变的是相对主窗口的客户区坐标。这就是子窗口的特性。再试试看启用/禁用主窗口,显示/隐藏主窗口看看,就不难得出结论: 规则七:子窗口会随着其父窗口移动,启用/禁用,显示/隐藏。 子窗口我们就暂时讲那么多,接着讲所有者窗口,就是Owner window,由于子窗口一定没有Owner,因此Owner window是对popup和Overlapped而言的,而popup和Overlapped前面也提到了,不一定有Owner,不像Child那样一定有Parent。现在进入我们下一个实验: 还是用向导生成最普通的W

38、indows hello world程序,步骤和上一个实验很相似,仅仅改了一点点东西,改了哪点?就是把CreateWindowEx函数的第四个参数的WS_CHILD拿掉,其余不变,代码我就不贴了,大家编译并运行看看。大家会看到类似这个效果: 图9 弹出窗口的caption是蓝色的,说明它处于激活状态,如果你现在点击程序主窗口,那弹出窗口的标题栏就变灰,而程序主窗口的标题栏变蓝,两个窗口看起来就像并列的关系,但你很快发现它们其实不并列,因为如果它们有重叠部分的话,弹出窗口总是遮挡程序主窗口。用spy++观察之,发现程序主窗口就是弹出窗口的Owner。 规则八:非Chil

39、d window总是显示在它们的Owner之前。 看到了没?这个时候CreateWindowEx的第九个参数的意义就不是Parent window,而是Owner,那把这个参数改为NULL,会有什么效果呢?马上试试看,反正这么容易。 图10 初一看没什么变化,其实变化大了,一是主窗口这回可以显示在弹出窗口之前了,二是任务栏上出现了两个button。 图11 用spy++观察到这两个窗口的Owner都是NULL。 规则九:Owner为NULL的非Child窗口能够(不是一定哦)在任务栏上出现它们的按钮。 这个时候,你应该清楚为什么给一个M

40、essageBox正确指定一个Owner这么重要了吧?我以前有个同事,非常“厉害”,他创建了一个程序,一旦出现点什么问题,就能把MessageBox弹得满屏都是,而且把任务栏霸占得渣都不剩,他大概是没明白这个道理。MessageBox是一个非child窗口,如果不指定一个正确的Owner,那弹出MessageBox之后,Owner还是处于可操作的状态,两个窗口看起来是并列的,都在任务栏上有显示,如果再弹出MessageBox,先关闭那个MessageBox?我看先关哪个都没问题,因为界面操作上没有限制,但这样很容易导致逻辑混乱,如果不幸走入了个死循环,连续弹MessageBox,那就像这位同事

41、写的那个程序那样,满屏皆是消息框了。 我们现在来进行一些稍微复杂点点的实验,就是创建A弹出窗口,其Owner为主窗口,创建B弹出窗口,其Owner为A窗口,创建C弹出窗口,其Owner为B窗口。步骤模仿上面的窗口创建步骤即可,好,编译,运行,效果大致如此: 图12 现在,把主窗口最小化,看看发生了什么事情。你会发现A窗口不见了,而B,C窗口尚在,A窗口究竟是跟随主窗口一起最小化了呢,或者被销毁了呢?还是被隐藏了呢?答案是被隐藏了,我们可以通过spy++找到它,发现它的属性里边没有WS_VISIBLE。那现在将主窗口还原,A这时候出现了,那现在我们最小化A,Oh?Wha

42、t happen?B不见了,主窗口和C都还在,我们还是老办法,用spy++看B,发现它没了WS_VISIBLE属性,现在还原A窗口,方法如下图所示: 图12_x 注意,最小化的A并不显示在任务栏上。还原A后B也出现了。 规则十:Owner窗口最小化后,被它拥有的窗口会被隐藏。 前面测试的是最小化,那我们现在不妨来测试一下,让A隐藏,会怎么样?在主窗口里创建一个button,点这个button,就执行ShowWindow(g_hwndA, SW_HIDE),如图: 图13 你会发现,被隐藏的只有A,A隐藏后主窗口,B和C都是可见的,你可以继续尝试,隐藏

43、B和C,或者主窗口,不过,你隐藏了主窗口的话恐怕就没法通过主窗口的菜单来关闭程序了,只能打开任务管理器结束掉程序。 规则十一:Owner隐藏,不会影响其拥有的窗口。 现在不是最小化,也不是隐藏,而是测试“关闭”,即销毁窗口,尝试关闭A,发现B,C被关闭;尝试关闭B,发现C被关闭。这个规则也就是规则一了,不必再列。 好,我不可能把所有的规则都列出来,但我相信前面所写的这些东西,对大家起到了抛砖引玉的作用了,其它规则,也可以通过类似的实验得出,或者用已有的规则去推导。那在转入下一节前,我提点问题: 为什么子窗口没有Owner?(就是我们来猜猜微软为什么这样设计)试想一个Ch

44、ild既有Parent,又有Owner,Parent控制其绘制,Owner控制其存在,在Owner销毁的时候,子窗口就要被销毁,而其Parent有可能还继续存在,那这个子窗口的消失可能有点不明不白,这是其中一个原因,另一个原因也类似,如果Parent不控制子窗口的存在,只管其绘制,那么在Parent销毁的时候,Owner可以继续存在,这个时候的子窗口是存在,而又不能显示和访问的,这可能会导致别的怪异问题,既然起了Child这个名字,就应该把它全权交给Parent,由Parent来决定它的一切,我想这就是微软的道理。 那我们如何获取一个窗口的Parent和Owner?大家都知道API函数,

45、GetParent,这是用来获取Parent窗口句柄的API——慢!这并不完全正确!大家再仔细点看看MSDN,再仔细点: If the window is a child window, the return value is a handle to the parent window. If the window is a top-level window, the return value is a handle to the owner window. 什么是top-level window?就是非Child window,这个后面再详细谈这个,现在注意看了,GetPare

46、nt返回的有可能不是parent,对于非child窗口来说,返回的就不是parent,为什么?因为非child窗口的parent恒定是Desktop啊(规则6),这还需要获取吗?我们接下去的实验是用来测试GetParent这个函数是否工作正常的,什么?测试M$提供的API,没错,呵呵,当一把微软的测试员吧。接上面那个实验: //在窗口创建完成后,调用下面的代码,在第一个GetParent处设置个断点,查看返回值,如果返回NULL,按照MSDN所说的,用GetLastError看看是否有出错。 { DWORD rtn; HWND hw = GetParent(hWnd);

47、 //获取主窗口的“Parent” if(hw==NULL) rtn = GetLastError(); hw = GetParent(g_hwndA); //获取A的“Parent” if(hw==NULL) rtn = GetLastError(); hw = GetParent(g_hwndB); //获取B的“Parent” if(hw==NULL) rtn = GetLastError(); hw = GetParent(g_hwndC); //获取C的“Parent” if(hw==NULL) rtn = G

48、etLastError(); } 我的实验结果有些令我不解,清一色返回0,包括GetLastError,也就是说没有出错,那GetParent返回0,根据MSDN上的描述,原因只可能是:这些窗口确实没有Owner。不对啊?难道前面的规则和推论都是错误的不成?我创建它们的时候,就明明白白地指定了hWndParent参数,而且上面的实验也表明了他们之间的Owner和Owned关系,那是不是GetParent错了?我想是的,你先别对着我扔砖头,想看到正确的情况么?好,我弄给你看。 我们是如何创建A,B和C这几个弹出窗口的?我再把创建它们的语句贴一下吧: g_hwndX = Crea

49、teWindowEx(NULL, TEXT("child_window"), TEXT("X"), WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300, hWnd, NULL, hInstance, NULL); 现在把这个语句改为: g_hwndX = CreateWindowEx(NULL, TEXT("child_window"), TEXT("X"), WS_POPUP|WS_VISIBLE|WS_OVERLAPPEDWINDOW|WS_CLIPSIBLINGS, 30, 30, 400, 300,

50、hWnd, NULL, hInstance, NULL); 对,就是加上一个WS_POPUP,看看情况变得怎么样? 很惊讶,对不?GetParent这回全部都正确地按照MSDN的描述工作了,这是我发现的popup和Overlapped的第二个差别,第一个差别?在文章开头附近,自己回去找。而spy++显示出来的那个Parent,其实就是GetParent返回的结果。记住,对于非child窗口来说,GetParent返回的并不是Parent,MSDN也是这么说的,你看看这个函数的名字是不是很有误导性?还有spy++也真是的,将错就错。好吧,就让它错去吧,但我们得记住:对非Child窗口来说

移动网页_全站_页脚广告1

关于我们      便捷服务       自信AI       AI导航        抽奖活动

©2010-2026 宁波自信网络信息技术有限公司  版权所有

客服电话:0574-28810668  投诉电话:18658249818

gongan.png浙公网安备33021202000488号   

icp.png浙ICP备2021020529号-1  |  浙B2-20240490  

关注我们 :微信公众号    抖音    微博    LOFTER 

客服