Win32 API讲座--窗口函数(转)

    一、 关于窗口函数

    在上一堂课里,我们已经提出了"句柄"的概念,并为此进行了较深度的讨论。现在来想,我要补充的是,句柄并非是仅仅是窗口才有的。似乎所有的WINDOWS对象都具有句柄。如,GDI对象中的画笔、刷子等,不久即将要学习的设备场景等也有自己的句柄,等等。但,和一些控件不同,这些对象并不属于窗口
    什么是窗口呢?有一句非常有趣的话∶如果它位于屏幕,那么它肯定是在一个窗口里;如果它不在于屏幕,它仍然可能在一个窗口里。窗体也是窗口;滚动条、列表框,文本框,甚至是桌面上的快捷图表也是窗口。更有趣的是,就连作为背景的桌面也是窗口

    很多控件基本上都提供了hWnd属性,但没有提供的也有。对于这些控件可以用SetFocus
方法,将输入焦点设向控件,然后用API函数GetFocus取得当前具有焦点的那个窗口的句柄。当然,这一过程应当写在GoFocus事件中。在我碰到过的问题中有一个有趣的事情是,
VB提供的IE控件的hWnd属性不管用。这个问题我一般都采用上述方法来解决的。
    很多窗口函数都能对系统的任何窗体进行操作。这意味了VB程序可以直接操纵正在运行中的其他窗体。大家知道,如果对VB设计出的程序未做特殊的处理,那么我们可以启动多个该应用程序实例。我们可以利用API窗口函数来判断一个窗体的先例是否在运行当中,从而可以做到如果有先例则停止启动。很多应用程序就是这个样子的。比如四通利方中文平台,在已经启动的情况下再此启动,程序会告诉用户"四通利方已经在运行",并停止启动。

    窗口函数主要可分为四个类型(也许说为"这是为了这次讲课分类出来的"更适合一些)∶
    1、窗口分级函数;
    2、窗口位置与大小函数;
    3、窗口信息函数;
    4、其他窗口函数。
    以下我们就一一讲述。但由于窗口函数比较多,在这里就选择性的进行讨论。关于窗口函数有多少,具体的用法如何,您可以注意"小雁侠"的VB API站点的技术文档,或者本站
程序下载栏目中的WinAPI帮助文。由于帮助文其内容来自"小雁侠"的网站,因此其内容更新比较起来会较晚一些。

    二、窗口分级函数

    系统中运行的窗口是有级别的高低之分的。谁不知道这样?这当然是废话。很多文章都是采用类似的这种废话来做导语,在这里我只不过也是学学而罢。
    一、父子关系。
    每个窗口都可能有自己的父窗口和子窗口。但,系统中运行的窗口是有限的,说明总得有个窗口是没有其父,我们把它叫做顶级窗口。一般把一个应用程序的主窗口就是顶级窗口,VB独立窗体及MDI窗体都是顶级窗口窗口间的父子关系一般遵循以下规则∶
    1、父窗口显示时,所有包容在其中的可见的子窗口会随着父窗口的显示而显示出来。

    2、父窗口隐藏时,所有包容在其中的子窗口会随着父窗口的隐藏而隐藏。
    3、父窗口被卸载时,哈,您已经知道我想说什么了,当然是∶跟着自动卸载。
    4、父窗口移动时,跟着移动。
    二、兄弟关系及Z序列
    当然,一位父亲有好几个儿女,都是常见的事情。同样,一个父窗口可以拥有多个子窗体。比如,位于一个窗体中的各种控件之间以及MDI窗口的各子窗口之间的关系。父窗口与子窗口的显示、隐藏、卸载及移动,其先后顺序是显而易见的。那么各兄弟窗口之间的情况会是如何呢?

    显然,两个互相重叠的两个子窗体不能都同时显示出它的全貌,自然有个显示的顺序规则。这个顺序规则叫做Z序列。有个解释为,如果把屏幕坐标看层X和Y轴组成的平面(事实上正是如此),那么作为三维坐标系统Z轴可看做是垂直于屏幕的坐标轴。这样,可以认为屏幕上的所有窗口是垂直于这个Z轴的。在Z轴上,谁在前,谁在后,就产生了一个Z序列。很生动!可用WINDOWS API函数和Visual Basic Z序列方法对Z序列进行控制。

    有了以上简单的知识以后,我们就不难应用API窗口分级函数,主要有以下及个∶
窗口分级函数

函数名                                 说   明
FindWindow             按类名或窗口名(Caption)查找一个窗口
FindWindowEx         类似于FindWindow提供了更多的功能
GetLastActivePopup 针对指定的窗口,取回上一个活动的弹出式窗口的句柄
GetParent                获得指定窗口窗口的句柄
GetTopWindow        获得指定窗口的第一个子窗口的句柄
GetWindow              如给定一个窗口句柄,该函数能取回具有特定关系的另一个窗口                             的句柄。如,第一个子窗口、父窗口窗口列表内的上一个或下一个窗口
SetParent                改变任何窗口的父窗口
    从我个人的经验来看,我最常用的是GetWindow和SetParent函数。

    三、窗口位置与大小函数

     Windows API函数基本上都是(尤其是USER32.DLL动态连接库内的函数)以屏幕像素为度量单位的。这一点很重要,必须牢记。为此,在使用API函数的时候,我们经常把窗体或图片框控件的ScaleMode属性设置为3,即vbPixels(像素)。
     理解窗口位置及大小函数的关键在于分清屏幕坐标、窗口坐标及客户坐标这三个概念。以下图展示了这三个坐标系统之间的关系。


屏目、窗口与客户区坐标系统

     只要对这些坐标有了明确的概念,对使用窗口位置及大小函数就不难了。关于窗口的位置,有些函数返回的是上一堂课学习到的RECT结构。有关窗口位置及大小函数如下表所列∶
窗口位置及大小函数

函数名                                               说  明
BringWindowToTop         使指定的窗口进入可见窗口列表的顶部,如它被部分或全部隐藏,则令其全部可见。同时,该窗口成为当前活动窗口。只有从前台线程调用时,才生效。
ChildWindowFromPoint   在规定的坐标取得某子窗口的句柄(如果有的话),这儿的坐标是指相对于父窗口的客户区坐标。
ChildWindowFromPointEx 与 ChildWindowFromPoint相同,功能更强。
ClientToScreen                判断指定点在窗口客户区内的屏幕坐标。
GetClientRect                  获得对窗口客户区进行表述的一个矩形(RECT)。这是以像素为单位判断客户区大小的一个简便的方法。
GetWindowPlacement    获得指定窗口的一个WINDOWPLACEMENT结构。该结构说明了窗口的状态。
GetWindowRect              用于获得一个矩形(RECT)结构,它描述了窗体在屏幕坐标系统中的位置。
MapWindowPoints          对某窗口客户区坐标内的一个或多个点进行转换,用另一窗口的客户区坐标表示。
MoveWindow                  移动指定窗口的位置,并能改变它的大小。
OpenIcon                        将一个最小化窗口恢复为原始状态。
ScreenToClient                针对屏幕内一个指定的点,用某个特定窗口内的客户区坐标表示它。
SetWindowsPos              更改窗口的位置和大小,并能修改它在内部窗口列表内的位置(这个列表起着控制窗口先是顺序)。
SetWindowPlacement     在一个WINDOWPLACEMENT结构的基础上,设置某窗口的特征。该结构描述了窗口的状态,以及它在最小化、最大化或正常显示时的位置。
WindowFromPoint           根据屏幕上一个指定的点,判断哪个窗口正位于它的下面。
    以上函数的具体用法均可在WinAPI帮助文中找到。在这些函数当中,SetWindowsPos
函数的使用率比较高,现在很多人都是用这个函数来实现"窗口总在前面"的效果,即通过
HWND_TOPMOST常数把窗口置于列表顶部。如果想把From1置于列表顶部,方法如下∶
     SetWindowPos Form1.hWnd, HWND_TOPMOST, Form1.Left / Screen.TwipsPerPixelX, Fo rm1.Top \ Screen.TwipsPerPixelY, Form1.Width \ Screen.TwipsPerPixelX, Form1.Height \ Screen.TwipsPerPixelY, 0

    您可以把这行代码放在Paint事件中。
    另外,GetWindowRect函数、MoveWindow函数以及下一课堂即将要学到的
GetCursorPos函数的相互配合能够实现一个拖动无标题栏的窗口。这是您必须掌握的技巧之一。感兴趣的朋友,可以到《前线》的《源码解析》栏目,下载第4号演示程序。以下是其主要的代码部分∶
Private MyRect As RECT
Private MyPoint As POINTAPI
Private Movex As Long, Movey As Long

Private Sub Image1_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)

Dim dl&
dl& = GetWindowRect(Form1.hwnd, MyRect)
dl& = GetCursorPos(MyPoint)
If Button = 1 Then
     Movex = MyPoint.X - MyRect.Left
     Movey = MyPoint.Y - MyRect.Top
End If
End Sub

Private Sub Image1_MouseMove(Button As Integer, Shift As Integer, X As Single, Y As Single)
       Dim dl&
       dl& = GetCursorPos(MyPoint)
       If Button = 1 Then
           dl& = MoveWindow(Form1.hwnd, MyPoint.X - Movex, MyPoint.Y - Movey, _

                          MyRect.Right - MyRect.Left, MyRect.Bottom - MyRect.Top, -1)
       End If
End Sub
    在MouseDown事件中,程序首先用 GetWindowRect 函数确定窗口在屏幕中的位置。再次是通过GetCursorPos函数确鼠标在屏幕中的位置。从而可通过计算获得鼠标位置与窗口左上角之间的横向与纵向距离(Movex与Movey)。
    在紧接着发生的MouseMove事件中程序不断地用GetCursorPos函数获得鼠标当前的位置,并按前面已经求得的Movex与Movey判断窗口所应处的位置,而这在MoveWindow函数调用中直接完成。MoveWindow函数将窗体移动到新的位置。

    四、窗口信息函数

    所谓窗口信息函数就是用来获取有关窗口当前状态信息的函数。这类函数主要有∶

函   数                                            说    明
GetClassInfo                          取得指定窗口的类信息结构
GetClassInfoEx                      效果类似于GetClassInfo,但增加了一些功能
GetClassLong,GetClassWord 用于获取窗口类信息
SetClassLong,SetClassWord 用于设置窗口类信息
GetClassName                       获取窗口类名
GetDesktopWindow               获取整个桌面(屏幕)的窗口句柄
GetWindowLong,GetWindowWord  获取与窗口有关的信息
SetWindowLong,SetWindowWord  设置与窗口有关的信息
GetWindowText                     获得窗口文本。它的效果大致等价于窗体或控件的Text属性
GetWindowTextLength          获得窗口文本的长度,用字符数表示。
IsChild                                   判断某窗口是否为另一窗口的子窗口或从属窗口
IsIconic                                  判断某窗口是否处于最小化状态
IsWindow                              判断指定的句柄是否为窗口句柄。
IsWindowEnabled                 判断指定的窗口是否处于活动状态。
IsWindowVisible                    判断某窗口是否可见。
IsZoomed                              判断窗口是否处于最大化状态。
SetWindowText                     设置窗口文本。大致等价于窗体或控件的Text属性。
    大部分窗口信息函数是非常好理解的,按照有关手册中进行的函数说明,按指定数据类型进行调用即可。有必要说明的是,关于类和窗口样式位。Windows是用一个长整形的数据的位设置方式来记录类和窗口样式的。其中,窗口样式由一个32位样式以及另一个32
位扩展样式来构成。类样式操作由上述列表中的GetClassLong以及GetClassLong来进行,窗口样式操作由GetWindowLong 以及SetWindowLong来进行。
    由于样式位的内容较多,我无法在此给出,您可以参考有关手册。这里有必要提醒大家的是,您想改变或获取当前窗口或类的样式,绝大多数情况可以考虑样式位操作。下面,就这个问题举一个简单了例子来说明。

    下面是用BS_LEFTTEXT样式位将VB复选框或选项按钮的文本在左边和右边之间相互移动来、移动去的程序(是附带的Program1.vbp的部分内容)。程序的原理很简单。首先用
GetWindowLong函数获得当前样式位的信息,然后通过位操作来准备新的样式位信息,最后用SetWindowLong实际地去更改。如下∶
    Dim f&, dl&
    f& = GetWindowLong(Option1.hWnd, GWL_STYLE)    '获得当前样式位的信息
    If Index = 0 Then
         f& = f& Or BS_LEFTTEXT

    Else
         f& = f& And Not BS_LEFTTEXT
    End If
    dl& = SetWindowLong(Option1.hWnd, GWL_STYLE, f&)    '设置新的样式
    Option1.Refresh
    (对Or和And位操作不熟悉的朋友,请参考有关技术资料)
    在这里,对样式位不进行更详细讨论,主要有这样一个原因。用SetWindowLong函数改变一个样式位之后,不会导致窗口发生相应的变化(至少不会立即变化)。有些样式位可能在运行时候才会成功变化,而大多数都只在窗口创建时才生效。因为,用API方式创建一个窗体已经超出了本教程的范围,就算我在这里对样式位谈得再多,您可能也没有多大用处。同时,微软公司没有告诉我们哪些样式位在运行期间安全地改变,因此对具体的情况,只好靠自己进行具体试验。而从我个人的实际编程经验来看,没有特别的要求,我们不大会涉及到这些样式位操作,很多都可在VB中很方便地实现。

    本教程还附带了一个Program2.vbp的演示程序。是我本人随便编写的,没什么特别希罕之处。想看就看看好了。
    最后,想简单提一提的是,使用SetWindowLong函数的时候,改变GWL_WNDPROC
数据是非常危险的(系统或VB经常挂死),即更改窗口函数的位置。一般,这种更改在需要进行子类处理的地方应用到。每次试运行程序,都应当习惯性地进行存盘。

    五、其他窗口函数


    API中还有以下本教程未列出的窗口函数,以供大家参考。

函数名                                                说   明
AnyPopup                    判断是否存在可见的弹出式窗口
CascadeWindows        令窗口在一个父窗口内层叠显示
CloseWindow              对指定的窗口进行最小化处理(如果它是个钉级窗口)对弹出式及子窗口无效
DestroyWindow           清除指定的窗口以及下属所有子窗口与包容窗口
DrawAnimatedRects     获得窗口打开或关闭的动画效果
EnableWindow             激活或屏蔽(禁用)指定窗口
FlashWindow               令指定窗口的标题闪烁显示
GetUpdateRect             判断需要更新的那个窗口的位置
GetWindowContextHelpId 取得与窗口关联在一起的帮助场景
InvalidateRect              指定窗口内需要更新的全部或部分客户区
IsWindowUnicode        判断一个窗口是否期望文本消息采用Unicode格式
LockWindowUpdate     允许或禁止描绘指定的窗口
RedrawWindow            一个功能强大的函数,用于控制全部或部分窗口重画
ScrollWindow,ScrollWindowEx 滚动显示窗口的全部或部分客户区
SetWindowContextHelpId 设置与窗口关联在一起的帮助场景
ShowOwnedPopups    隐藏或显示从属于指定窗口的所有保容弹出窗口
ShowWindow              用于设置窗口的状态,其中包括窗口的隐藏、显示、最小化、最大化以及激活等
ShowWindowAsync     类似于ShowWindow,增加了对其他进程内的窗口进行操作的能力
TileWindows                令窗口在一个父窗口内平铺显示
UpdateWindow           立即更新窗口内需要更新的任何部分
ValidateRect                指出全部或部分矩形已经更新,毋需再更新
    其中,FlashWindow函数非常有趣,不妨大家试一试。
posted @ 2009-04-14 14:47  willen  阅读(2823)  评论(0编辑  收藏  举报