一个窗口由另一个窗口派生出来,那么被派生出来的窗口为子窗口,而派生窗口的窗口为父窗口
HWND SetParent( HWND hWndChild, // handle to window HWND hWndNewParent // new parent window );
HWND GetParent( HWND hWnd // handle to child window );
BOOL IsChild( HWND hWndParent, // handle to parent window HWND hWnd // handle to window to test );
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次序中的位置是通过把它放在一个给定窗口的后面,或是放在窗口堆的顶部或底部.
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次序从上到下排列.
一个程序
多个程序
BOOL BringWindowToTop( HWND hWnd // handle to window );如果hWnd表示的窗口不是子窗口,那么它将被激活,如果hWnd表示的是子窗口,那
HWND GetTopWindow( HWND hWnd // handle to parent window );如果hWnd为NULL返回顶层窗体
HWND GetNextWindow( HWND hWnd, // handle to current window UINT wCmd // direction );wCmd为 GW_HWNDNEXT表示搜索Z次序小于hWnd的下个窗口;为GW_HWNDPREV表示搜索Z次序大于hWnd的上个窗口,如果找不到这样的窗口,则返回NULL
前后就是前台和后台的简称.当前正在执行的线程被称为前台线程,该线程创建的窗口被称为前台窗口;所有其他线程被称为后台线程,而这个线程创建的窗口就是后台窗口
HWND GetForegroundWindow(VOID);
BOOL SetForegroundWindow( HWND hWnd // handle to window );该函数将试图使用创建hWnd窗口为线程成为前台线程并激活该窗口
拥有关系不同于父子关系,它的关系在于WS_OVERLAPPED和WS_POPUP风格的窗口之间.也就是说一个重叠或者弹出串口能被另一个重叠或者弹出窗口所拥有,这种关系一旦建立起来就不能更改
通过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指出哪个线程应该得到这个鼠标消息
对按键事件事件稍微不同,在任何给定的时间,只有一个线程同原始输入线程"连接".这个线程就是前台线程.因为它建立了正在于用户交互的窗口,当切换前台窗口时,原始输入线程将自动切换和当前的前台线程的连接