窗体相关概念和相应关系

三个角度窗体含义

  • 从应用程序开发员的角度
    窗体就是图形用户界面句柄,以该句柄作为系统API的參数,就能够对窗体进行各种操作
  • 从MFC程序猿角度
    窗体就是一个CWnd对象
  • 从操作系统角度
    窗体是一块普通内存块.该内存中保存了一个数据结构的信息(WNDCLASS).操作系统利用这个数据结构维护窗体状态

窗体类型

  1. 可重叠窗体
    这样的窗体是有标题栏,边框,客户区域的顶层窗体,它能够充当应用程序的主窗体,这类窗体还能够有窗体菜单,最大和最小化button及滚动栏
  2. 弹出窗体
    这类窗体具有可重叠窗体的所有特性,但其标题栏是可选的,这类窗体是在应用程序的主窗体中出现的暂时窗体.对话框,消息提示框(也是一种对话框)是最定型的弹出窗体
  3. 子窗体
    子窗体具有WS_CHILD风格.并限制在父窗体的客户区绘制.应用程序会经经常使用子窗体将父窗体划分为若干个功能区,多视图就是一个典型的样例,创建子窗体的方法是调用CreateWindowEx时,指定WS_CHILD作为窗体的风格,父窗体參数指向父窗体句柄(必须有)除了特殊指定,子窗体仅有一个客户区而没有其它特性
    父窗体的某些行为会影响到子窗体.
    • 父窗体销毁时候,会自己主动销毁它全部的子窗体,假设子窗体又有子窗体会有连锁反应,导致全部的子窗体都被销毁
    • 子窗体总是相对于父窗体定位的.因此移动父窗体也会导致它的子窗体跟着移动
  4. 消息窗体
    这类窗体仅仅能发送或接收消息,™没有边框,Z次序,不能被枚举,不能接收广播消息.调用CreateWindowEx时将父窗体句柄设置为常量HWND_MESSAGE,就会创建一个消息窗体
    此外,通过对已有的的窗体调用SetParent,并将HWND_MESSAGE传入hWndNewParent參数,也会把一个窗体转换为消息窗体.由于消息窗体不能被枚举,所以查找消息窗体的过程比較麻烦,这时要将FindWindowEx的hwndParent參数设置为HWND_MESSAGE
  5. 兄弟窗体
    共享同一个父窗体的多个子窗体叫兄弟窗体
  6. 活动窗体
    活动窗体是应用程序的顶层窗体,也就是当前使用的窗体.仅仅有一个顶层窗体能够是活动窗体,假设用户使用的是一个子窗体,Windows系统就激活与这个子窗体对应的顶层窗体
    不论什么时候系统中仅仅能有一个顶层窗体是活动的.用户通过单击窗体(或当中的一个子窗体)、使用ALT+TAB或ALT+ESC组合键来激活一个顶层窗体,应用程序则调用函数SetActiveWindow来激活一个顶层窗体
  7. 前台窗体/后台窗体
    在Windows系统中,每个进程可执行多个线程,每个线程都能创建窗体.创建正在使用窗体的线程称之为前台线程,这个窗体就称之为前台窗体.全部其他的线程都是后台线程,由后台线程所创建的窗体叫后台窗体
    用户通过单击一个窗体、使用ALT+TAB或ALT+ESC组合键来设置前台窗体,应用程序则用函数SetForegroundWindow设置前台窗体.假设新的前台窗体是一个顶层窗体,那么Windows系统就激活它,换句话说,Windows系统激活对应的顶层窗体

窗体关系

父子关系

一个窗体由还有一个窗体派生出来,那么被派生出来的窗体为子窗体,而派生窗体的窗体为父窗体

相关函数

  • 通过调用SetParent能够应用程序能够再执行时,对一个已有的窗体改变其父窗体
    (假设SetParent中传入NULL,那么桌面将成为新窗体的父窗体.
    HWND SetParent(
    HWND hWndChild, // handle to window
    HWND hWndNewParent // new parent window
    );
  • 通过调用GetParent获得父窗体
    HWND GetParent(
    HWND hWnd // handle to child window
    );
  • 通过调用IsChild能够推断一个给定的窗体是否为某个窗体的后代窗体
    BOOL IsChild(
    HWND hWndParent, // handle to parent window
    HWND hWnd // handle to window to test
    );
  • 通过EnumChildWindows提供了枚举起始窗体全部后代窗体
    BOOL EnumChildWindows(
    HWND hWndParent, // handle to parent window
    WNDENUMPROC lpEnumFunc, // callback function
    LPARAM lParam // application-defined value
    );
    lpEnumFuc是回调函数指针.每发现一个子窗体,就会通过它回调函数.被回调的函数必须满足以下签名
    BOOL CALLBACK EnumChildProc(
    HWND hwnd, // handle to child window
    LPARAM lParam // application-defined value
    );
    所谓的签名是原型的还有一种表述,对于函数而言,它的签名包含函数名,每一个參数类型以及返回值

Z次序关系

窗体的Z次序表明了重叠窗体堆中窗体的位置,这个窗体堆是按一个假想的轴定位的,这个轴就是从屏幕向外伸展的Z轴.Z次序最上面的窗体覆盖全部其他的窗体,Z次序最底层的窗体被全部其他的窗体覆盖.应用程序设置窗体在Z次序中的位置是通过把它放在一个给定窗体的后面,或是放在窗体堆的顶部或底部.
Windows系统管理三个独立的Z次序——一个用于顶层窗体、一个用于兄弟窗体,另一个是用于最顶层窗体.最顶层窗体覆盖全部其他非最顶层窗体,而无论它是不是活动窗体或是前台窗体.应用程序通过设置WS_EX_TOPMOST风格创建最顶层窗体.
普通情况下,Windows系统把刚刚创建的窗体放在Z次序的顶部,用户可通过激活另外一个窗体来改变Z次序;Windows系统总是把活动的窗体放在Z次序的顶部,应用程序可用函数BringWindowToTop把一个窗体放置到Z次序的顶部.函数SetWindowPos和DeferWindowPos用来重排Z次序.
系统假定用户由Z轴从上往下看窗体,这样Z次序较低的窗体就可能被其它Z次序较低的窗体覆盖,Z次序最低的窗体是桌面,Z次序最高的窗体,我们把它称为顶层窗体.顶层窗体总是可见的
Windows通过一个全局队列维持窗体的Z次序.加入窗体时,它依照以下的规则设定窗体的Z次序:假设待加入的窗体是WS_EX_TOPMOST风格,那么它将会变为顶层窗体,也就是位于所有现有窗体的顶端.这一点跟待加入的窗体是否为活动或后台窗体无关:假设待加入的窗体为子窗体,则它拥有和父窗体同样的Z次序,对于其它窗体则放在同类型窗体的顶部,当一个窗体位于顶部时,它所拥有的所有子窗体也位于Z次序的顶部
父窗体拥有的每一个子窗体的Z次序不同的,这些子窗体依照Z次序从上到下排列.

演示样例

一个程序

多个程序

相关函数

  • 通过调用BringWindowToTop可将一个窗体带到同样窗体的顶端
    BOOL BringWindowToTop(
    HWND hWnd // handle to window
    );
    假设hWnd表示的窗体不是子窗体,那么它将被激活,假设hWnd表示的是子窗体,那
    么拥有它的父窗体会被激活,注意该函数仅仅能改变同种类型也就是同一个CLASS衍生的窗体的Z次序
  • 通过GetTopWindow能够获得Z次序最高的子窗体
    HWND GetTopWindow(
    HWND hWnd // handle to parent window
    );
    假设hWnd为NULL返回顶层窗体
  • 通过调用GetNextWindow获得当前窗体的上一个或下一个子窗体
    HWND GetNextWindow(
    HWND hWnd, // handle to current window
    UINT wCmd // direction
    );
    wCmd为 GW_HWNDNEXT表示搜索Z次序小于hWnd的下个窗体;为GW_HWNDPREV表示搜索Z次序大于hWnd的上个窗体,假设找不到这种窗体,则返回NULL

前后关系

前后就是前台和后台的简称.当前正在运行的线程被称为前台线程,该线程创建的窗体被称为前台窗体;全部其它线程被称为后台线程,而这个线程创建的窗体就是后台窗体

相关函数

  • 通过调用GetForegroundWindow能够获得当前的前台窗体
    HWND GetForegroundWindow(VOID);
    能够通过这个函数的返回值来推断某个窗体是否为前台窗体
  • 通过调用SetForegroundWindow设置前台窗体
    BOOL SetForegroundWindow(
    HWND hWnd // handle to window
    );
    该函数将试图使用创建hWnd窗体为线程成为前台线程并激活该窗体

拥有关系

拥有关系不同于父子关系,它的关系在于WS_OVERLAPPED和WS_POPUP风格的窗体之间.也就是说一个重叠或者弹出串口能被还有一个重叠或者弹出窗体所拥有,这样的关系一旦建立起来就不能更改

特点

  1. 它总是位于它的拥有窗体的Z次序之上
  2. 当窗体被销毁时,它所拥有的窗体也将被自己主动销毁
  3. 当窗体最小化时,它所拥有的窗体被隐藏
  4. 而且在创建它时候CreateWindow中的父窗体句柄为拥有窗体

相关函数

通过GetWindow获得该窗体的拥有窗体

HWND GetWindow(
HWND hWnd, // handle to original window
UINT uCmd // relationship
);

 


线程和窗体

Windows操作系统将一个应用程序的一次运行定义为一个进程.线程表示一条运行路径,Windows操作系统是一种所谓的分时操作系统,它将CPU资源划分为若干个时间片,在不同的时间片能够运行不同的线程.当前线程拥有独立的堆栈和寄存器状态,并能够和同一进程的其它线程共享进程中的代码和数据,一个进程仅仅少拥有一个主线程,此外还能够有多个辅助线程 .而窗体仅仅只是是窗体结构的一个实例,它是一个普通的GUI资源,窗体本身是由线程创建,窗体不会拥有线程,线程则拥有它全部创建的全部窗体.窗体和线程的联系是窗体包括了负责处理消息的窗体过程的代码,而线程运行这些代码.所以在窗体程序中线程对创建的窗体拥有全部权,这样,假设一个线程建立一个窗体后马上结束,操作系统会自己主动销毁线程拥有的全部窗体.

建立窗体的线程必须是为窗体处理全部的消息的线程,假设线程已经结束,不可能被用来使用窗体接收和处理消息.这也意味着每一个线程,假设它至少建立了一个窗体,都应由系统对它分配一个容纳处理的消息队列.这个队列用于窗体消息发送.这个队列就是线程的消息队列,为了使窗体接收这些消息,线程必须有它自己的消息循环

Windows设计目标之中的一个是提供一个强健的应用程序应用环境.为实现这个目标,要保证每一个线程执行在一个相对独立的环境中,在这个环境中每一个线程都相信自己是唯一执行的线程.说的更详细些,就是每一个线程必须有全然不受其它线程影响的消息对列.并且每一个线程必须有一个虚拟环境,使线程维持它自己的键盘焦点,窗体激活,鼠标捕获等

为了降低线程对系统资源的要求,当一个线程第一次被建立时,系统假定线程不会用于不论什么与用户相关的任务,这样就不会创建消息队列,可是,一旦这个线程调用一个与图形用户界面有关的函数,系统就会为该线程奉陪一个THREADINFO结构,并将这个数据结构域线程联系起来,THREADINFO结构中包括一组成员变量,特别地,该结构中包括了该线程的消息队列集合.利用这组成员,线程能够觉得它是在自己独占的环境中执行
除了为每一个线程维持一个消息队列外,系统还维持一个全局的消息队列,用于容纳各种硬件输入消息的系统硬件输入队列,当系统初始化时,要建立一个特殊的线程,即原始输入线程(Raw Input Thread,RIT)同一时候建立系统硬件输入队列(System Hardware Input Queue,SHIQ)

原始输入线程依照以下的方法确定往哪个线程的的消息队列里添加硬件输入消息.对鼠标消息,原始输入线程仅仅是确定哪一个窗体在鼠标光标之下.利用这个窗体,原始输入线程调用GetWindowThreadProc essId来确定哪个线程建立了这个窗体.返回的线程ID指出哪个线程应该得到这个鼠标消息

对按键事件事件略微不同,在不论什么给定的时间,仅仅有一个线程同原始输入线程"连接".这个线程就是前台线程.由于它建立了正在于用户交互的窗体,当切换前台窗体时,原始输入线程将自己主动切换和当前的前台线程的连接


转自:http://www.cnblogs.com/kzang/archive/2012/10/01/2709354.html


posted @ 2017-04-19 20:12  jhcelue  阅读(409)  评论(0编辑  收藏  举报