怎样使用MFC创建一个窗口类

From:http://blog.sina.com.cn/s/blog_51396f890100pkt6.html

1.5 怎样使用MFC创建一个窗口类

(2011-03-17 11:13:14)
标签:

杂谈

 
//

从上一篇我转载如何进行Windows SDK编程开始,我希望可以借此window 希望开始补充一下Windows编程的一些背景知识。之所以这样,是因为在window之所以这样我前面介绍“SW系统的窗口类”时,假设了读者对Windows界面window 前面假设编程已经有一定的了解。

上一篇主要从介绍“如何用”的角度阐述Windows编程。但是我window 主要阐述个人习惯“打破沙锅问到底”,很多东西是靠“悟”,而个人沙锅东西不是“记”。所以这一篇我们聊聊WindowsSDK为何会是如今这个window如今聊聊样子的。

对于一个经典的WinMain函数,通常包含三步:

  • 注册窗口类(RegisterClass)。
  • 创建并显示窗口(CreateWindow and ShowWindow)。
  • 消息循环(MessageLoop)。即:取得消息 -> 分派消息-> 处理消息。messageloop 消息处理
  • 窗口程序需要“创建并显示窗口”,这显而易见。关于“显而易见 ldquo 需要;消息循环”也容易理解,并且我们在“SW系统简介”中描简介系统我们述已经得非常详细。

    我相信最令人迷惑的是:“窗口类”是什么概念?为什么为什么迷惑什么需要RegisterClass?

    有人回答:“窗口类”是同类窗口的公共属性,是这一类同类窗口属性窗口的共享数据
    有人回答:“窗口类”是同类窗口的默认数据(属性)

    回答窗口类数据是共享数据的,错误。因为我们知道每一个窗口都有自己独立的菜单错误自己菜单、图标、窗口过程(WindowProc,这个最重要了)等等。它们并不windowproc图标重要存在共享关系。回答是默认数据(属性)的,正确,但没有回答为什么需要默认属性,更没涉及到为什么需要默认更为重要的原因。

    我们先来看看CreateWindow与CreateDialog的原型:

    HWND CreateWindow(
        LPCTSTR lpClassName,
        LPCTSTR lpWindowName,
        DWORD dwStyle,
        int x,
        int y,
        int nWidth,
        int nHeight,
        HWND hWndParent,
        HMENU hMenu,
        HINSTANCE hInstance,
        LPVOID lpParam
    );HWND CreateDialog(      
        HINSTANCE hInstance,
        LPCTSTR lpTemplate,
        HWND hWndParent,
        DLGPROC lpDialogFunc
    );

    为什么普通窗口(Window)的创建不是象对话框(Dialog)一样,对话框 window为什么直接把窗口过程(WindowProc)传进去,甚至把其他窗口类相关的windowproc其他进去数据全部直接在CreateWindow时传入?也许有人回答说,这是为了减createwindow数据也许少CreateWindow的参数个数。——呵呵,我还真不知道怎么去证明这createwindow参数证明种说法是错的。但是我固执的认为我下面给出的理由要充理由下面固执分些。

    听说过序列化(Searialize)技术吗?有读者马上回答:知道,这searial 序列技术是MFC中把对象写入磁盘和从磁盘中读入并还原出这些对象这些磁盘读入的技术。这个回答我给它4分(满分5分)。是的,序列序列技术是的化(Searialize)是对象持久化和还原的技术。但这不是MFC才开始有,而是DOS下TurboVision就已经有的vision turbo技术一个技术。

    对话框是什么?对话框其实是支持了序列化(Searialize)技术的searial 对话框序列特殊窗口,它在初始化的时候,从资源中还原(创建)出初始化创建资源它的各个子窗口。只是,与普通序列化(Searialize)不太一样的searial只是序列是,对象持久化过程不是对话框做,而是对话框编辑器负编辑器对话框而是责的。

    无疑,基于对话框进行可视化编程是相当友好的。那么,对话框友好相当从支持这种可视化编程的角度来看,我们需要支持序列化需要序列这种(Searialize),需要从磁盘中创建窗口。我们再回头看看CreateWindow函数createwindow searial需要的原型,你将发现,这些参数是“可序列化(可存盘)”rdquo 参数发现;的。而且,你立刻意识到,窗口过程(WindowProc)是不可序列化windowproc 不可立刻的。

    我们知道,序列化技术需要RuntimeClass技术进行对象的动态创建。runtimeclass需要创建而所谓的RuntimeClass技术,无非是建立了类唯一标识(如类名、GUIDruntimeclass guid无非等)到类创建函数(其他的附属数据是比较次要的)的映数据创建其他射而已。

    现在到了关键:为了支持从磁盘还原窗口对象,Windows需要引window 需要关键入窗口类名,并建立它与窗口过程(WindowProc)的映射。——这windowproc建立窗口正是RegisterClass存在的意义。而RegisterClass中其他的窗口类属性是次要的,registerclass其他意义并且也许前面说得没错,这些属性的存在,只是为了减少前面只是也许CreateWindow函数的参数个数。

    补充:点击这里了解RuntimeClass与Searialize的实现机理。

    补充:“绅士亦花心”提到了GDI资源的共享问题。应当承共享问题资源认,我前面说“窗口类数据是共享数据”这种说法错误,是比较武断的说法。是的,Windows必须面对window面对错误GDI资源的共享问题,不同窗口亦存在资源共享的事实。更共享问题资源为合理的说法是:每一个窗口可以独立设置自己的菜单、合理自己菜单图标、窗口过程,但菜单/图标等GDI资源是可以共享的。因共享图标菜单为我们知道,Windows对GDI资源的共享策略是,窗口不拥有GDI资window共享策略源(菜单/图标等)的所有权,用户必须为GDI资源的生命周所有权图标菜单期负责。换句话说,窗口只拥有菜单/图标句柄。

窗口类

  一个应用程序在创建某个类型的窗口前,必须首先注册该“窗口类”(WindowsClass)。注意,这里不是C++类的类。Register Window把窗口过程、窗口类型以及其他类型信息和要登记的窗口类关联起来。  “窗口类”的数据结构  “窗口类”是Windows系统的数据结构,可以把它理解为Windows系统的类型定义,而Windows窗口则是相应“窗口类”的实例。Windows使用一个结构来描述“窗口类”,其定义如下:  typedef struct _WNDCLASSEX {   UINT cbSize; //该结构的字节数   UINTstyle; //窗口类的风格   WNDPROC lpfnWndProc; //窗口过程   int cbClsExtra;  int cbWndExtra;   HANDLE hInstance; //该窗口类的窗口过程所属的应用实例   HICONhIcon; //该窗口类所用的像标   HCURSOR hCursor; //该窗口类所用的光标   HBRUSHhbrBackground; //该窗口类所用的背景刷   LPCTSTR lpszMenuName; //该窗口类所用的菜单资源  LPCTSTR lpszClassName; //该窗口类的名称   HICON hIconSm; //该窗口类所用的小像标  } WNDCLASSEX;  从“窗口类”的定义可以看出,它包含了一个窗口的重要信息,如窗口风格、窗口过程、显示和绘制窗口所需要的信息,等等。关于窗口过程,将在后面消息映射等有关章节作详细论述。  Windows系统在初始化时,会注册(Register)一些全局的“窗口类”,例如通用控制窗口类。应用程序在创建自己的窗口时,首先必须注册自己的窗口类。
查看文章
  
窗口和mfc环境
2007-09-04 22:44

创建mfc窗口是复杂的,首先,创建类CWnd的一个实力,然后调用类CWnd的一个成员函数,改成员函数调用api中的CreateWindow()函数。返回的窗口句柄保存在类

CWnd的成员 变量m_hWnd中。

注意:因为窗口在内存中创建,而内存经常发生变化,窗口地址可能是经常变化的。因此,窗口句柄并非直接指向窗口对象,而是指向跟踪窗口对象地址的另一个指针

销毁窗口同样也是复杂的,必须确保销毁了改窗口对象,以及封装该窗口对象的CWnd实例。虽然CWnd对象知道窗口对象,但是窗口对象并不知道CWnd对象!
怎么应用MFC创建一个窗口:
可以用MFC的CWnd类创建一个窗口。
CWnd wnd;
BOOLb=wnd.CreateEx(ExStyle,ClassName,WindowName,Style,x,y,Width,Height,Parent,Menu,Param);
第一行代码创建了一个CWnd类的对象,第二行通过调用WindowAPI函数CreateWindowEx()创建了真正的窗口。
应为CWnd类只是封装了用于创建窗口windows API函数
(CreateWindowEx())因此,从本质上讲,创建窗口所必需的参数在api环境和MFC环境中都是相同的
参数解说:
@参数Style和ExStyle决定窗口的外观和类型(重叠,弹出,子窗口)
@参数ClassName决定在创建窗口时使用的窗口类。
@参数WindowName决定窗口标题内容
@参数x,y,Width和Height决定窗口的位置和大小。
@参数Parent指向拥有改窗口的窗口指针(如果拥有的话)
@参数Menu指向内存中的一个对象,作为他的菜单使用--除非创建一个子窗口,如果是这样的话,那么该参数是帮助父窗口识别子窗口的IDnumber
@参数Instance之别改窗口十余那个应用程序,以便发送到改窗口的消息能被发送到正确的应用程序消息队列中。CWnd类填入Instance参数。
@参数Param是在创建窗口时由窗口类使用的指针,该指针是指向附加信息的非强制性结构的指针。

现在既然已经了解了有关创建窗口的基本知识,那么我们进一步来看看这些规则。
1.窗口名称规则
该参数是一个零结尾的文本串,用该串指明在窗口标题中显示的内容。如果窗体没有标题库,该参数可以为零(0)。
2.风格和扩展风格参数
这两个是32的参数,用来制定创建什么类型的窗口。可以选择多种类型,如下面的例子所示:
.WS_CHILD\WS_VISIBLE
窗口风格参数的作用包括:
1.用于创建3中不同的基本窗口类型的风格,用WM_CHILD创建一个子窗口;用WM_POPUP创建一个弹出窗口。
2.WM_OVERLAPPED创建一个重叠窗口。如果不为窗口制定任何一种风格,那么窗口风格默认为WM_OVERLAPPED。
3.用以添加窗口的非客户区特色的风格。例如可
()改变窗口风格。可以使用ModifyStyle()和ModifyStyleEx()改变窗口风格。某些风格可能要求重画窗口,这是可以给modifyStyle()函数添加第三个参数,自动激发CWnd

的成员函数SetWindowPos()做重画窗口工作。
CWnd wnd;
wnd.ModifyStyle(0,WS_BORDER,SWP_NOZOREDER);
3.X和Y位置参数
x,y是相对于窗口左上角的位置。而创建子窗口时,x,y是相对于父窗口客户区的左上角位置。如果吧x和y参数都设置为CW_USERDEFAULT,那么系统将自动为窗口选定

一个位置。

创建窗口后,可以用类CWnd成员函数MoveWindow()移动窗口。
4.宽度和高度
这是两个32位的参数用于以像素为单位指定窗口大小。如果将参数width和height都设置成CW_USERDEFAULT,则系统将根据桌面窗口的大小,自动选定窗口大小。
5.Z-Order
当几个窗口占据屏幕上同一区域时,Z-Order决定那个窗口显示在那个窗口之上。最鼎城窗口用WS_EX_TOPMOST窗口风格创
,并显示在所有非最顶层的窗口的上面,而

与窗口是不是当前选中的窗口无关
创建窗口后,可以用CWnd的成员函数SetWindowPos()改变窗口的Z顺序。
6.父窗口或物主窗口参数
该参数是指向CWnd对象的指针,根据创建的窗口标识是父窗口还是物主窗口(Owner):
@如果创建是一个子窗口,那么用该参数来标识它的父窗口(该值不能为NULL)
@如果创建的是重叠窗口或弹出窗口,用该参数来标识物主窗口,如果该值为NULL则桌面窗口成为物主窗口。

8.实例
前面提到。CWnd类将填入该参数。CWnd通过调用AfxGetInstanceHandle()获得该实力(Instance)。一个应用程序的实例从本质上标识了那个程序在内存里。

AfxGetInstanceHandle()是由MFC库提供的静态函数
9.参数
该32位参数(Parameter)是可选的。它通常是指向一个结构或类对象的指针,而该结构或者类对象是创建某种类型的窗口时需要提供的。例如,当用MDICLIENT窗口类

创建窗口时。需要该参数提供一个CLIENTCREATSTRUCT结构
指针。
10.类名参数
类名参数是一个零结尾字符串。当创建一个窗口时,用来标识使用那个窗口类。关于窗口类和窗口处理,将在本章后面的内容中详细讨论,该参数不能为null一个非常

一般的mfc窗口时,使用AfxRegisterWndClass(0)填入该参数。

查看文章
  
mfc和窗口类(续)
2007-09-04 23:46

pWnd->DestroyWindow(); // destroys WindowObject
delete pWnd;   // destorys CwndObject
也可以只删除CWnd对象,因为DestoryWindow()是从CWnd的析构函数中调用的,但是不提倡这种方法。


捆绑到一个已有的窗口
如果一个窗口是调用windowsAPI在应用程序建立之前或外面创建的,并且需要把它封装到CWnd类中,可以用下面的语句实现:
CWnd wnd;
wnd.Attach(hwnd);
这里的hwnd是已有窗口的句柄。Attach()只是把CWnd的成员变量m_hWnd赋给hwnd
也可以使用CWnd::FromHandle(hwnd),它查看是否有CWnd对象已经封装了该窗口句柄并返回该CWnd对象指针。如果不存在这样CWnd对象,将创建一个临时对象,如

果是临时对象,则不需要该指针,因为在应用程序空闲的时候,该指针指向对象将被删除。

窗口类:
一个窗口不是一个c++类,而是早于并存在于c++之外的窗口专有的类。窗口类的作用就像一个模板,可以由此创建其他窗口,并可共享某些特征。
窗口进程:
窗口与环境的交互是通过发送和接受消息来实现的。系统是发送不同的消息,有窗口不同的进程来完成对消息的处理。
怎么使用MFC创建一个窗口类:
当用户创建一个窗口类时,实际上只有在3个操作系统列表之一中注册一个WNDCLASS结构。系统为每个窗口类类型维护一个列表:
@系统全局类(System Global Class) 在系统启动时注册,且必须注册,对所有应用程序都有效。
@应用程序全局类(Application Global Class)由应用程序注册,只对应用程序及应用陈旭线程有效。
@应用程序局部类(Application Local Class)由应用程序注册,并只对注册他们的应用程序或DLL有效
当系统搜索一个窗口类时,从应用程序局部类开始,然后搜索应用程序全局类,最后搜索系统全局类。
要创建一个窗口类,可以创建WNDCLASS结构的实例,然后用MFC类库的AfxRegisterClass()注册它。也可以用MFC类库的AfxRegisterWndClass()来创建一个基于调用参数的

WNDCLASS对象。

使用AfxRegisterWndClass()函数注册一个窗口类
这个函数在使用上非常自动化,一些通常需要你提供的参数都能自己填入,甚至连新的窗口类的名字也能自动产生。

LPCTSTR lpszClassName=AfxRegisterWndClass(UINTnClassStyle,HCURSOR hCursor=0,HBRUSH hbrBackgroud=0,HICONhIcon=0);

使用AfxRegisterClass()函数创建一个窗口类
如果想完全控制一个新窗口类的创建,如制定窗口类的名字,则应该用下面的语句:
BOOL AFXAPI AfxRegisterClass(WNDCLASS *ipWndClass);
可以使用AfxGetAfxWndProc();可以使用他返回该进程,并为该参数使用。

一些重要的MFC窗口类
AfxWnd类   CWnd窗口
AfxFrameOrView类 MFC框架类和视窗
AfxMDIFrame类   MDI框架窗口
AfxControlBar类   MFC控件条窗口


桌面窗口:
桌面窗口,所有其他窗口都显示在他的上面,并最终属于它。桌面窗口自身是一个弹出窗口,并且是最高阶弹出窗口。最高阶窗口列表是由窗口管理器维护的,因此也

叫做窗口管理器列表(Windows Manager List)。窗口管理器应用该列表维护桌面窗口。

桌面窗口的其他信息如下:
@桌面窗口有#32769窗口类所建
@如要获得窗口桌面的句柄,可以用::GetDesktopWindow()
@可以用带SPI_SETDESKWALLPAPER参数的SystemParametersInfo()在桌面上设置一副背景图。
@Shell_NotifyIcon()将图标放置到外壳盘(Shell Tray)里--以后在任务栏里发现的小图标集。
@可以用::FindWindow()搜索桌面上的窗口。
@可以用WindowFromPoint()找到当前窗口在桌面上的点坐标。
@可以用::GetSystemMetrics(SM_CXSCREEN)和::GetSystemMetrics(SM_CYSCREEN)获得屏幕尺寸

ttp://www.hackpig.cn/post/333.html

每个窗口都是一个窗口类的成员,窗口类是一个属性集.MicrosoftWindows系统在创建应用程序的窗口时用它作为一个模板。每个窗门类有一个与之相应的窗口过程,由同类窗口所共享,窗口过程为该类的所有窗口处理消息,从而控制它们的特性和外观。有关窗口过程.参见 “窗口过程”。

应用程序必须在它创建某类窗口之前注册这个窗口类,注册一个窗口类也就是把一个窗口过程、类风格及其它一些类属性与类名联系起来。如果应用程序在函数CreateWindow或CreateWindowEx中指定一个类名.操作系统就创建一个具有与这个类名相应的窗口过程、风格及其它属性的窗口。


窗口类的种类

有三种类型的窗口类:系统全局类、应用程序全局类及应用程序局部类。这些类型的区别在于作用域以及注册和销毁的时机。


系统全局类

在windows系统启动时,它就注册一些系统全局类用于控制框.包括:按钮、组合框、列表框、滚动条、编辑控制框和静态控制框。有关控制框,参见 “控制”和“静态控制框”。任何应用程序都可以随时使用系统全局类,因为windows系统注册的系统全局类就是用于所有应用程序的,应用程序不能销毁这些类。


应用程序全局类

应用程序全局类是一个通过动态链接库(DLL)注册的窗口类,也适用于系统中的所有应用程序。例如,dll调用RegisterClassEx把一个定义定制控制框的窗口类注册成一个应用程序全局类.那么所有的应用程序就可以创建这个定制控制框的实例。

在windows系统中,所有的窗口类都是特定于某一进程的,应用程序通过在一个DLL中创建一个窗口类来创建一个全局类,并要通过下列关键字在注册数据库中列出这个DLL的名字。

只要进程一启动,系统就在调用这个进程的函数main之前,安装最新启动的进程描述表中指定的DLL,这个DLL必须在初始化它的过程时注册这个类,还必须设置CS_GLOBALCLASS风格。

有关类风格,参见“类风格”。在一个类被注册之后,应用程序就可以用它来创建属于该类任意数目的窗口。

windows系统销毁一个应用程序全局类,在用于注册它的DLL被卸下时,基于这个原因,所有的应用程序必须在这个DLL被卸下之前销毁所有属于应用程序全局类的窗口,函数UnregisterClass用于删除应用程序全局类,并释放与之有关的内存空间。


应用程序局部类

应用程序局部类是由应用程序注册的并由它自己专用的窗口类,尽管应用程序可以注册任意数目的局部类,但绝大多数应用程序只注册一个,这个窗口类支持应用程序主窗口的窗口过程。

注册应用程序局部类与注册一个应用程序全局类差不多,只是WNDCLASSEX结构的style成员没有设置成CS_GLOBALCLASS风格。

windows系统销毁一个局部类是在注册它的应用程序关闭时,应用程序也可用函数UnregisterClass来删除一个局部类并释放与之相关的内存空间。


Windows系统如何确定类

windows系统为三种窗口类各管理一个WNDCLASS结构列表,如果应用程序调用函数CreateWindow 或CreateWindowEx来创建一个指定类的窗口,windows系统用下列步骤确定一个类:

1.windows系统按指定类名搜索应用程序局部类列表。

2.如果名字不在应用程序局部类的列表中,windows系统再搜索应用程序全局类列

3.如果名字不在应用程序全局类的列表中,windows系统就再搜索系统全局类列

所有应用程序创建的窗口都是用的上面这个步骤,也包括windows系统代表应用程序创建的窗口,如对话框。屏蔽系统全局类又不影响其它的应用程序是可以的,这就是说,应用程序可以注册一个与一个系统全局类具有相同名字的应用程序局部类,这就替代了应用程序描述表中的系统全局类,但又不影响其它应用程序对这个系统全局类的使用。

类的所属关联

类的属主是注册类的应用程序或DLL,windows系统是根据在注册时传给函数RegisterClassEx的WNDCLASSEX结构的hInstance成员来确定类的所属关系。对windows系统的DLLs,hInstance成员必须是DLL的实例句柄。类在其属主被关闭或被卸下时被销毁,基于这个原因,应用程序必须在它关闭或DLL被卸下之前销毁所有由这个类所创建的窗口

窗口类的元素

窗口类的元素定义了属于这类窗口的默认的特性。注册窗口类的应用程序是通过设置WNDCLASSEX结构中相应的成员来给类赋元素,并把这个结构传给函数RegisterClassEx。函数GetClassInfoEx和GetClassLong用来检取有关给定窗口类的信息。函数SetClassLong用来改变由应用程序已经注册的局部或全局类的元素。

尽管一个完整的窗口类含有许多元素.但windows系统只要求应用程序提供一个类名、窗口过程的地址和实例句柄,其它的元素则用来定义这类窗口默认的属性.如光标的形状,窗口菜单的内容。窗口类的元素列表如下:

元素 用途

类名 区别于其他注册类

窗口过程地址 处理所有发生这类窗口的消息并定义窗口特性的函数的指针.

实例句柄 标志注册类的应用程序或DLL.

类光标 定义这类窗口中的光标的形状.

类图标 定义大图标.

类小图标 定义小图标 (Windows version 4.0 andhigher).

类背景刷 定义窗口打开或绘制时的填充色和图案.

类菜单 为没有显示式的定义菜单的窗口指定默认的菜单

类风格定义在一个窗口被移动或被改变大小以后如何更改它。如何处理鼠标的双击操作,如何为设备描述表及窗口的其

他方面分配空间.

类附加空间指定windows系统为这个类保留附加空间的字节数,附加空间为所有这类窗口所共享,他也能用于任何应用程

序定义的用途。Windows系统把这部分内存定义为0.

窗口附加空间指定windows系统为这个窗口类的每个窗口保留附加空间的字节数,附加空间用于任何应用程序定义的用途。

Windows系统把这部分内存定义为0.

类名

每一个窗口类都需要有一个类名来区别于其它的类。把WNDCLASSEX结构的lpszClassName成员设成一个以NULL作为结束的zi字串的地址来赋予—个类名,这个字符串就是指定的名字。因为windows系统中的窗口类都是特定于进程的,对同一个进程,窗口类名应是唯—的。

函数GetClassName用来检取一个指定窗口所属类的名字。

窗口过程地址

每个类需要一个窗口过程地址来定义窗口过程的入口点,窗口过程用来处理这类窗口的所有消息。windows系统在它要求窗口完成某个任务时向窗口传递消息,如绘制它的客户区或是响应用户的输入。应用程序把窗口过程的地址复制到WNDCLASSEX结构的lpfnWndProc成员中为一个类提供窗口过程,必须在模块定义文件(.DEF)中输出窗口过程,参见‘窗口过程”.

实例句柄

每个窗口类都要求有一个实例句柄来标识注册窗口类的应用程序或DLL,就象个多任务系统,windows系统同时运行几个应用程序或DLL,所以就要求有实例句柄来跟踪它们。windows系统为运行应用程序或DLL的每个拷贝赋一个句柄。

同一个应用程序或DLL的多个实例共用一个代码段,但它们有各自的数据段,windows系统用一个实例句柄来标识应用程序或DLL的某一个实例相应的数据段。

windows系统在应用程序启动的时候把实例句柄传给应用程序或DLL,应用程序或DLL再通过把这个实例句柄复制到WNDCLASSEX结构的hInstance成员中把它赋给类。

类光标

类光标定义这类窗口客户区中光标的形状,在光标进入窗口的客户区时,windows系统自动把光标设置成给定的形状.并保持这个形状直到光标退出客户区。要给某个窗口类赋一个类光标,可通过函数LoadCursor安装一个预定义的光标形状,再把返回的光标句柄赋给WNDCLASSEX结构的hCursor成员。或者是提供一个定制光标资源,再用函数LoadCursor安装应用程序资源中的这个光标。

windows系统并不要求有一个类光标,如果应用程序把WNDCLASSEX结构的hCursor成员置成NULL,那么就没有定义类光标。windows系统假定每次光标移进窗口,窗口都要设置光标的形状,窗口只要一接收到WM_MOUSEMOVE消息就会调用函数SetCursor设置光标的形状。有关光标,参见 “光标”。

类图符

类图符是系统用来再现一个窗口特别类的图,程序可以有两个类图符,一个大的一个小的,当用户按alt+tab系统在切换窗口中系统显示窗口的大图符,在任务栏和资源管理器里可以显示大图标,小图标出现在窗口的标题栏和在任务栏和资源管理器里可以显示小图标.

为分配大图标和小图标给窗口类,在WNDCLASSEX结构中的hIcon 和hIconSm成员中指定图标句柄,图标尺寸必须遵从大小图标类的尺寸要求,你可以通过在调用函数GetSystemMetrics指定SM_CXICON 和 SM_CYICON的值来决定尺寸要求.,对小图标指定SM_CXSMICON和SM_CYSMICON的值,更多信息参考,”图标”

类图符定义了给定类的窗口在最小化时图符的形状,要把一个图符赋给一个窗口类.是通过函数1朋dI咖安装应用程序资源中的图符.再把返回的句柄赋给WNDCLASSEX结构的hIconSm成员。有关图符,参见第23章“图符”。

windows系统并不要求窗口类有一个类图符,如果应用程序把WNDCLASSEX结构的 hIcon和hIconSm成员置成NULL那么就没有定义类图符,在这种情况下,系统就用缺省的程序图标作为大小图标类给窗口类.如果你指定大的没有指定小的,系统就基于大的创建小的,如果指定小的没有指定大的,系统就使用缺省程序图标作为大的图标类,那指定的图标作为小的图标类.

你可以通过消息WM_SETICON为特殊窗口重载大小图标类,你可以通过消息WM_GETICON来查询当前大小图标类.

windows系统在必须绘制图符的背景时向这个类的窗口发送WM_ICONERASEBKGND消息,如果窗口不处理WM_ICONERASEBKGND消息,windows系统就在窗口最小化时,把窗口存户区的内容画进图符。

背景刷类

类背景刷为应用程序曲绘制准备客户区,windows系统用这个画刷把客户区画成同一的颜色或图案,所以也就删除了这个位置上的所有图案,而不管它是否属于该窗口。windows系统通过向这个窗口发送WM_ERASEBKGND消息来通知它要绘制背景。有关画刷,参见 “画刷”。

要把一个背景刷赋给一个类,先要用图形设备接口(GDI)中适当的函数创建一个画刷,再把返回的画刷句柄赋给WNDCLASSEX结构的hbrBackground成员。

应用程序也可以不创建一个画刷,而是把hbrBackground成员设置成一个标准的系统颜色值,SetSysColors.中,有一个标准系统颜色值的列表。

要使用标准的系统颜色,应用程序必须把背景色的值加1,例如,COLOR_BACKGROUND + 1是系统背景颜色。

windows系统并不要求窗口类有一个类背景刷,如果这个参数设置成NULL,窗口就必须在接收到WM_ERASEBKGND消息时绘制它的背景。

类菜单

如果在创建窗口时没有显式地给出菜单,类菜单就为这个类中的窗口定义一个默认的菜单。菜单是一个命令列表,用户可通过它选择某个活动让应用程序来完成。

要给一个类赋一个菜单,需要把WNDCLASSEX结构的lpszMenuName成员设置成—个以NULL结束的字符串的地址,这个字符串指定菜单的资源名字,这个菜单被假设成是应用程序中的一个资源.在需要的时候,windows系统能够自动安装这个菜单。注意,如果菜单是由一个整数而不是名字来标识的,那么应用程序需要在赋值之前,通过宏MAKEINTRESOURCE把lpszMenuName成员设置成为该整数。

windows系统并不要求一个类菜单,如果应用程序把WNDCLASSEX结构的lpszMenuName成员设置成NULL,windows系统就假定这个类中的窗口没有菜单栏。即使没有给出类菜单,应用程序还是能够在创建窗口时为窗口定义一个菜单栏。

windows系统不允许子窗口有菜单栏,如果给窗口类赋予了一个菜单,那么创建这种类的子窗口时,菜单被忽略。有关菜单,参见“菜单”。

类风格

类风格为窗口类定义另外的元素,两个或更多的风格可通过按位或OR(|)操作组合起来使用。把一个风格给一个窗口类,也就是把这个风格赋给WNDCLASSEX结构的style成员。类风格列表如下:

风 格

活 动

CS_BYTEALIGNCLIENT

CS_BYTEALIGNWINDOWCS_CLASSDC


CS_DBLCLKS
CS_GLOBALCLASS
CS_HREDRAW
CS_NOCLOSE
CS_OWNDC
CS_PARENTDC
CS_SAVEBITS
CS_VREDRAW

把窗口的客户区按位(x方向)对齐以增强绘画操作的性能,这个风格影响窗口的宽度及水平位置。

把窗口按位(x方向)对齐以增强移动或改变窗口大小操作的性能,这个风格影响窗口的宽度及水平位置。

分配一个由某宙u类中的所有窗口共享的设备描述表。参见“类和私有设备描述表”及 “设备描述表。

使得windows系统在用户双击某窗口类的窗口中的鼠标时,向窗口过程发送一个双击消息。见 “鼠标输入”。

指定窗口类为应用程序全局类。有关信息,参见 “应用程序全局类”。

指定在移动或调整大小改变了客户区的宽度时整个窗口都要重画。

禁止系统菜单中的close命令。

为某窗口类的每一个窗口分配一个独立的设备描述表。参见设备描述表,类和私有设备描述表描述表。

把父窗口的设备描述表给其子窗口。

把一个窗口所遮掩住的屏幕映象部分用位图保留下来,windows系统用保留住的位图在窗口移走后重建这部分屏幕映象,windows系统把这个位图显示在原来的位置,但如果没有其它的屏幕活动使所保留的位图失效,就不向由这个由口遮掩住的窗口发送WM_PAINT消息。这个风格用于短暂显示的小窗口,在发生其它屏幕活动时被删除掉(如,菜单或对话框)。这种风格增加了显示一个窗口的时间.因为操作系统首先必须为保留的位图分配内存。

指定在移动或调整大小改变了客户区的高度时整个窗口都要重画。


类附加内存空间

windows为系统中的每一个窗口类管理一个WNDCLASSEX结构,在应用程序注册一个窗口类时.它可以让windows系统为WNDCLASSEX结构分配和追加一定字节数的附加内存空间,这部分内存空间称之为类附加内存,由属于这种窗口类的所有窗口所共享,类附加内存空间用于存储类的附加信息。

因为附加的内存是从系统局部堆里分配的,所以程序要节约使用附加内存。如果类附加内存字节超过40字节,那么函数RegisterClassEx调用失败。如果要这么做,就要分配自己的内存并把指向类附加内存的内存指针保存起来。

函数SetClassWord和SetClassLong用来把某—个值复制到类附加内存,要检取类附内存中的值,则用函数GetClassWord和GetClassLong。WNDCLASSEX路结构的cbClsExtra成员指定了所分配的类附加内存的总数,如果应用程序不需要类附加内存.则必须把cbClsExtra贝成员初始化为0。

窗口的附加空间.

windows系统为每一个窗口管理一个内部数据结构,在注册一个窗口类的时候,应用程序能够指定一定字节数的附加内存空间,称之为窗口附加内存。在创建这类窗口时,windows系统就为窗口的结构分配和追加指定数目的窗口附加内存空间,应用程序可用这部分内存存储窗口特有的数据。

因为附加的内存是从系统局部堆里分配的,所以程序要节约使用附加内存。如果附加窗口内存字节超过40字节,那么函数RegisterClassEx调用失败。如果要这么做,就要分配自己的内存并把指向附加窗口内存的内存指针保存起来。

函数SetWindowWord和SetWindowLong把某个值复制到附加内存,函数GetWindowWord和GetWindowLong从窗口附加内存检取一个值。结构WNDCLASSEX的cbWndExtra成员指定所分配的窗口附加内存总数,如果应用程序不用窗口附加内存,则必须把cbWndExtra成员置成0。

类和私有设备描述表

设备描述表是特殊的数据集,应用程序用来在它们窗口的客户区中进行绘画。windows系统为显示每一个窗口要求有一个设备描述表,但允许操作系统在如何存储和对待这个设备描述表方面有一些灵活性。

如果设备描述表没有显式地给出,windows系统假定每个窗口使用一个从windows系统管理的描述表池中检取的设备描述表,在这种情况下,每个窗口必须在绘制之前检取和初始化这个设备描述表,并在绘制后释放它。

为避免每次对窗口内部进行绘制时都要检取设备描述表,应用程序可为窗口类设置CS_OWNDC风格,这个类风格指导windows系统创建一个私有的设备描述表——也就是,为这个窗口类中的每一个窗口分配独立的设备描述表。应用程序仅需检取一次设备描述表,即可用于后面的所有绘制操作。尽管CS_OWNDC风格是很方便的,但也需要小心使用,因为每一个设备描述表都占用系统资源的一个重要部分。

通过指定S_CLASSDC风格,应用程序能够创建一个类设备描述表,类设备描述表是个难得使用的特性,它允许进程中的同一个窗口类所创建的多个窗口使用完全相同的设备描述表进行绘画。

应用程序能够设置CS_PARENTDC风格创建继承其父窗口设备描述表的子窗口.这种风格允许子窗口在其父窗口的客户区中进行绘画。

使用窗口类

在windows系统中,每个应用程序必须注册它自己的窗口类,应用程序可使用函数RegisterClassEx随时注册一个应用程序局部类,必须在应用径序中定义窗口过程.填充WNDCLASSEX结构的成员,再把结构的指针传给函数RegisterClassEx。

下面的范例说明如何注册一个局部窗口类以及怎样使用它来创建应用程序的主窗

#include<windows.h>

// Global variable

HINSTANCE hinst;

// Function prototypes.

int WINAPI WinMain(HINSTANCE,HINSTANCE, LPSTR, int);

InitApplication(HINSTANCE);

InitInstance(HINSTANCE, int);

LRESULT CALLBACK MainWndProc(HWND,UINT, WPARAM, LPARAM);

// Application entry point.

int WINAPI WinMain(HINSTANCEhinstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, intnCmdShow)

{

MSG msg;

if (!InitApplication(hinstance))

return FALSE;

if (!InitInstance(hinstance,nCmdShow))

return FALSE;

while(GetMessage(&msg, (HWND) NULL, 0, 0))

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

UNREFERENCED_PARAMETER(lpCmdLine);

}

BOOL InitApplication(HINSTANCEhinstance)

{

WNDCLASSEX wcx;

// Fill in the window class structurewith parameters

// that describe the main window.

wcx.cbSize = sizeof(wcx); // size ofstructure

wcx.style = CS_HREDRAW | CS_VREDRAW;// redraw if size changes

wcx.lpfnWndProc = MainWndProc; //points to window procedure

wcx.cbClsExtra = 0; // no extra classmemory

wcx.cbWndExtra = 0; // no extra windowmemory

wcx.hInstance = hinstance; // handleof instance

wcx.hIcon = LoadIcon(NULL,IDI_APPLICATION); // predefined app. icon

wcx.hCursor = LoadCursor(NULL,IDC_ARROW); // predefined arrow

wcx.hbrBackground = GetStockObject(WHITE_BRUSH); // white background brush

wcx.lpszMenuName = "MainMenu"; // nameof menu resource

wcx.lpszClassName = "MainWClass"; //name of window class

wcx.hIconSm = LoadImage(hinstance, //small class icon

MAKEINTRESOURCE(5),GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON),

LR_DEFAULTCOLOR);

// Register the window class.

returnRegisterClassEx(&wcx);

}

BOOL InitInstance(HINSTANCE hinstance,int nCmdShow)

{ HWND hwnd;

// Save the application-instancehandle.

hinst = hinstance;

// Create the main window.

hwnd = CreateWindow(

"MainWClass", // name of windowclass

"Sample", // title-bar string

WS_OVERLAPPEDWINDOW, // top-levelwindow

CW_USEDEFAULT, // default horizontalposition

CW_USEDEFAULT, // default verticalposition

CW_USEDEFAULT, // default width

CW_USEDEFAULT, // default height

(HWND) NULL, // no owner window

(HMENU) NULL, // use class menu

hinstance, // handle of applicationinstance

(LPVOID) NULL); // no window-creationdata

if (!hwnd)

return FALSE;

// Show the window and send a WM_PAINTmessage to the window

// procedure.

ShowWindow(hwnd, nCmdShow);

UpdateWindow(hwnd);

return TRUE;

}

注册应用程序全局类与注册应用程序局部类差不多,只是有如下一些不同:

·结构WNDCLASSEX的style成员必须置成CS_GLOBALCLASS风格o

‘类可在应用程序或DLL的描述表中注册。

·应用程序或DLL不需要在注册类之前检查应用程序或DLL的前一个实例。

GetClassInfoEx

GetClassLong


GetClassName

GetClassWord

GetWindowLong

GetWindowWord

RegisterClassEx

SetClassLong


SetClassWord

SetWindowLong

SetWindowWord

UnregisterClass

 

Obsolete Functions

 

GetClassInfo

RegisterClass

WNDCLASS

WNDCLASSEX


窗口过程

MicrosoftWindows系统中的每个窗口都有一个相应的窗口过程,用于处理发送或投递给该类窗口的所有消息的函数,窗口外观和特性的所有方面取决于:窗口过程对这些消息的响应。

每个窗口都是某个窗口类的成员,窗口类决定了窗口用来处理它的消息的窗口过程,属于同一种窗口类的所有窗口使用相同的窗口过程,例如,系统为组合框类(COMBOBOX)定义了一个窗口过程,那么所有的组合框都可使用这个窗口过程。

应用程序通常至少要注册一个新的窗口类及相应的窗口过程,注册了一个类之后,应用程序可创建许多这种类的窗口.而且所有窗门都使用相同的窗口过程。这就意味着几个窗口能够同时调用同—段代码,因此开发人员在修改共享的窗口过程资源时必须很小心。有关窗口类,参见 “窗口类”。

对话框过程与窗口过程具有相同的结构和功能,这一节中涉及到窗口过程的所有方面都可用十对话框过程。有关对话框参见 “对话框”。


窗口过程的结构

窗口过程是一个带有四个参数的函数。它返回个32位有符号数,参数包括窗口句柄、UNIT消息标识以及两个用WPARAM和LPARAM数据类型说明的参数,操作系统把WPARAM定义成一个32位无符号整数,LPARAM被定义成一个32位有符号整数。

下表是对窗口过程参数的描述。

LRESULT CALLBACK WindowProc(

HWND hwnd, // handle of window

UINT uMsg, // message identifier

WPARAM wParam, // first messageparameter

LPARAM lParam // second messageparameter

);

通常消息参数的低位字和高位字都用来存储信息,win32应用程序编程接门(APl)有几个应用程序可用来从消息参数中抽取信息的宏。例如,宏LOWORD从消息参数中抽取低位字(0—15位),其它宏还音HIWORD,LOBYTE, 和 HIBYTE。

窗口函数返回的值被说明成LRESULT数据类型,它是一个32位有符号数,对返回值的解释取决于不同的消息,应根据每条消息的种类以确定适当的返回值。

因为有可能发生对窗口过程的递归调用,所以减少对局部变量的使用显得尤为重要。在处理某条消息时,应用程序应在窗口过程的外面调用函数以避免无节制的使用局部变量,由于深递归而造成的堆溢出。


默认的窗口过程

默认窗口过程函数DefWindowProc定义了一些所有窗口都有的基本特性。默认窗口过程为窗口提供了最基本的功能,应用程序的窗口过程应把它不处理的任何消息传给函数DefWindowProc进行默认处理。


窗口过程子类

如果应用程序创建了一个窗口.操作系统就得分配一块内存来存贮窗口特有的信息,如用于处理消息的窗口过程的地址。如果windows系统需要把一条消息传给窗口,它就搜寻窗口特有信息来得到窗口过程的地址,以便把消息传给过程。

建子类是—种技术,它使得应用程序在窗口能够处理消息之前截取和处理发送或投递给某个窗口的消息。通过给一个窗口建子类,应用程序可增加、修改或监视窗口的特性。应用程序可以为任何窗口建子类,包括属于系统全局类的窗口,如编辑控制框或列表框。例如,应用程序可以通过了类编辑控制框来禁止控制框接收某些字符。

应用程序通过把窗口原窗口过程的地址换成一个叫做子类过程的新窗门过程的地址来为一个窗口建子类,从此,就由此类过程接收任何发送或投递给窗口的消息。

子类过程在它接收到一条消息时可能有三种情况:把消息传给原来的窗口过程;修改这条消息后再传给原来的窗口过程;再一个就是处理这条消息而不传给原来的窗口。如果子类过程处理某条消息,它可在把这条消息传给原来的窗口过程之前、之后或是在这前后都处理这条消息。

windows系统提供两种类型的子类:实例子类和全局子类。对于实例子类,应用程序取代的是一个窗口的某个实例的窗口过程地址.应用程序必须用实例子类来子类一个存在的窗口。对于全局子类,应用程序取代的是窗口类的WNDCLASS结构中窗口过程的地址,后面所有由这个窗口类创建的窗口用的就是子类过程的地址,但由这个窗口类创建的已存在的窗口是不受影响的。


实例子类

应用程序通过函数SetWindowLong来为一个窗口的某个实例建子类.应用程序把GWL_WNDPROC标志、要建子类的窗口的句柄以及子类过程的地址传给SetWindowLong。子类过程可驻留在应用程序的模块小或是一个动态链接库(DLL)中。应用程序必须在应用程序的或DLL的模块定义文件(.DEF)的EXPORTS语句中列出中子类过程的名字。

SetWindowLong返回窗口的原窗口过程的地址,应用程序必须保留这个地址,用在后面对CallWindowProc的调用中,以便把截取的消息传给原窗口过程。应用程序从窗口删除子类时也需要原窗口过程的地址。这就需要再一次调用SetWindowLong,把原窗口过程的地址以及GWL_WNDPROC标志和窗口的句柄传给它。

应用程序能够为系统中的任何一个窗口建子类,但是在子类并不属于它的窗口时,应用程序必须保证子类过程不要破坏窗口的原有特性,因为应用程序并不控制这个窗口,它就不能使用有可能被窗口属主改变的窗口信息。

应用程序在没有完全搞清窗口或类附加字节确切的意思,以及原窗口过程如何使用这些字节之前是不能使用它们的。即使搞情了,如果不是它自己的窗口也不应使用它们。如果应用程序使用属于另一个应用程序的窗口的窗口附加字节,而某些东西被其属主改变了,子类过程就可能失败。基于这个理由,应用程序最好不要为一个属于系统全局控制类的窗口建子类。windows系统管理系统全局类.控制的某些方面往往会随windows系统版本的不同而有所改变。如果应用程序必须给一个系统全局类窗口建子类,开发人员就可能需要在新的windows版本发行后更新应用程序。

因为实例子类是发生在窗口创建之后,应用程序建窗口子类时就不能为窗口追加任何附加字节。子类窗口的应用程序应该使用窗口的属性列表来存储子类窗口的实例所需的任何数据。有关窗口属性,参见“窗口性质”。

如果应用程序为一个子类窗口建子类,就必须按相反的次序删除前面的子类,否则就会发生不可恢复的系统错误。


全局子类

要为一个窗口类建全局子类,应用程序必须使用这个窗口类的某一个窗口的句柄。应用程序也需要用这个句柄来删除子类。要得到句柄,应用程序通常用要建子类的窗口类创建一个隐藏的窗门,有了句柄之后,应用程序调用函数SetClassLong时指定这个句柄、GCL_WNDPROC标志以及子类过程的地址,SetClassLong返回这个窗口类的原窗口过程的地址。

全局子类中原窗口过程地址的用法与实例子类中相同,子类过程调用函数CallWindowProc把消息传给原窗口过程,应用程序从窗口类中删除子类也是调用SetClassLong,向它传送原窗口过程的地址、GCL_WNDPROC标志以及要建子类的窗口类的某一个窗口句柄。为一个控制类建全局子类的应用程序在应用程序结束时必须删除子类.否则也有可能发生不可恢复的系统错误。

全局子类除了具有与实例子类同样的限制,还要加上另外一些限制。应用程序在没有搞清原窗口过程是如何使用类或窗口实例的附加字节之前是不能使用的。如果应用程序必须与窗口的数据发生联系.它就得使用窗口属性列表。

应用程序不能为一个系统全局类建全局子类,如果有不止一个应用程序为一个控制类建全局子类,就会发生不可恢复的系统错误。如果应用程序需要为一个控制类建全局子类.那么就得采用下面章节中所讲的超类技术。


窗口过程超类

超类也是一个技术,它允许应用程序建一个新的窗口类,既具有原窗口类的基本功能,又增加一些由应用程序提供的增强功能。用于产生超类的窗口类称之为基类,通常基类是系统全局窗口类如:编辑控制框,当然它也可以是其它窗口类。

注:应用程序不能为系统全局类SCROLLBAR建超类。

超类有它自己的窗口过程,叫做超类过程。超类过程处理一条消息也有三种情况:把消息传给原窗口过程;修改消息后再传给原窗口过程;再—个就是直接处理这条消息,不把它传给原窗口过程。如果超类过程处理一条消息,它也可以在把它传给原窗口过程之前、之后或者是在这前后都对一条消息进行处理。

与子类过程不同,超类过程是能够处理窗口创建消息的(WM_NCCREATE,WM_CREATE,等等),但它必须还要把这些消息传给原基类窗口过程,以便基类窗口过程能够完成它的初始化过程。

要超类一个窗口类,应用程序首先调用函数GetClassInfo来检取有关基类的信息,GetClassInfo用基类的WNDCLASS结构的值填充一个WNDCLASS结构。下一步是把它自己的实例句柄复制到WNDCLASS结构的hInstance成员中.把超类的名字复制到lpszClassName成员中。如果基类有一个菜单,应用程序还必须用同一个菜单标识提供一个新的菜单,再把菜单名复制到lpszMenuName成员中。如果超类处理WM_COMMAND消息.并且不再把它传给基类的窗口过程,那么菜革中就无需有相应的标识。函数GetClassInfo并不返回WNDCLASS路结构的lpszMenuName, lpszClassName, 或hInstance成员。

应用程序必须也能设置WNDCLASS结构的lpfnWndProc成员,函数GetClassInfo用这个类的原窗口过程的地址填充这个成员,应用程序必须保存这个地址.以便向原窗口过程传递消息.然后再把超类过程的地址复制到lpfnWndProc成员中。如果需要的话,应用程序是能够修改WNDCLASS结构的其它成员的.在它填充了WNDCLASS结构之后,应用程序把结构的地址传给函数RegisterClass注册这个超类,这个超类就可用来创建窗口。

因为超类注册了新的窗口类.应用程序就能够添加类附加字节和窗口附加字节。超类是不能使用基类或窗口的原附加字节的.理由与实例子类或全局子类不能使用它们是一样的。而且,如果应用程序要为类或窗口实例添加它自己使用的附加字节,它就必须是在原基类使用的附加字节的数目基础上添加附加字节。出为用于基类附加字节的数对不同版本的基类有所不同,超类附加字节的起始偏移值也会随着基类版本的不同而不同。


使用窗口过程

这一节讲解怎样完成下列工作

·设计窗口过程

·联系窗口过程和窗口类

·为窗口建子类


设计窗口过程

下面的范例说明了一个典型的窗口过程的结构,这个窗口过程把消息参数用在switch语句中,通过case语句来处理每一条消息。注意每个case为每一条消息返回一个指定值.对于它不处理的消息,这个窗口过程调用函数DefWindowProc

LRESULT CALLBACK MainWndProc(

HWND hwnd, // handle of window

UINT uMsg, // message identifier

WPARAM wParam, // first messageparameter

LPARAM lParam) // second messageparameter

{

switch (uMsg)

{

case WM_CREATE:

// Initialize the window.

return 0;

case WM_PAINT:

// Paint the window's client area.

return 0;

case WM_SIZE:

// Set the size and position of thewindow.

return 0;

case WM_DESTROY:

// Clean up window-specific dataobjects.

return 0;

//

// Process other messages.

//

default:

return DefWindowProc(hwnd, uMsg,wParam, lParam);

}

return 0;

}

WM_NCCREATE当某个窗口第一次被创建时,此消息在WM_CREATE消息发送前发送, 若返回FALSE, 函数CreateWindowEx 调用失败.WM_DESTROY窗口销毁. WM_NCDESTROY 消息在销毁前发送.

至少,窗口过程应该处理WM_PAINT消息画它自己的窗口,通常它还应该处理鼠标和键盘消息,根据每条消息的描述以确定窗口过程是否需要处理它们。

应用程序可以在处理消息时调用函数DefWindowProc,这种情况下,应用程序能够在把消息传给函数DefWindowProc之前修改消息.或在完成它自己的操作之后再进行默认处理。

对话框过程接收的是WM_INITDIALOG消息而不是WM_CREATE消息,也不把没有处理的消息传给函数DefDlgProc,除此以外,对话框过程与窗口过程完全相同。有关对话框过程,“对话框”。


联系窗口过程和窗口类

在注册类时联系窗口过程和窗口类,必须用有关类的信息填充WNDCLASS结构,lpfnWndProc成员必须指定为窗口过程的地址。要进行类注册,也就是把WNDCLASS结构传给函数RegisterClass。一旦窗口类被注册,窗门过程就会自动地与用这个类所创建的窗口联系起来。

下面的范例说明了如何把前一个例子中的窗口过程与一个窗口类联合起来。

int APIENTRY WinMain(

HINSTANCE hinstance, // handle ofcurrent instance

HINSTANCE hinstPrev, // handle ofprevious instance

LPSTR lpCmdLine, // address ofcommand-line string

int nCmdShow) // show-window type

{

WNDCLASS wc;

// Register the main window class.

wc.style = CS_HREDRAW |CS_VREDRAW;

wc.lpfnWndProc = (WNDPROC)MainWndProc;

wc.cbClsExtra = 0;

wc.cbWndExtra = 0;

wc.hInstance = hinstance;

wc.hIcon = LoadIcon(NULL,IDI_APPLICATION);

wc.hCursor = LoadCursor(NULL,IDC_ARROW);

wc.hbrBackground =GetStockObject(WHITE_BRUSH);

wc.lpszMenuName = "MainMenu";

wc.lpszClassName ="MainWindowClass";

if(!RegisterClass(&wc))

return FALSE;

//

// Process other messages.

//

}


为窗口建子类

要为一个窗口的实例建子类,需要调用函数SetWindowLong,并指定GWL_WNDPROC标志、要建子类的窗口的句柄和子类过程的指针。函数SetWindowLong返回原窗口过程的指针;通过这个指针向原窗口过程传递消息。

下面的范例说明了怎样为对话框中一个编辑控制框建子类的实例.在控制框有输入焦点时,这个子类窗口过程允许编辑控制框接收所有理盘输入.包括ENTER和TAB

WNDPROC wpOrigEditProc;

LRESULT APIENTRY EditBoxProc( HWNDhwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

HWND hwndEdit;

switch(uMsg)

{

case WM_INITDIALOG:

// Retrieve the handle of the editcontrol.

hwndEdit = GetDlgItem(hwndDlg,ID_EDIT);

// Subclass the edit control.

wpOrigEditProc = (WNDPROC)SetWindowLong(hwndEdit,

GWL_WNDPROC, (LONG)EditSubclassProc);

//

// Continue the initializationprocedure.

//

return TRUE;

case WM_DESTROY:

// Remove the subclass from the editcontrol.

SetWindowLong(hwndEdit,GWL_WNDPROC,

(LONG) wpOrigEditProc);

//

// Continue the cleanup procedure.

//

break;

}

return FALSE;

UNREFERENCED_PARAMETER(lParam);

}

// Subclass procedure

LRESULT APIENTRY EditSubclassProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)

{

if (uMsg == WM_GETDLGCODE)

return DLGC_WANTALLKEYS;

return CallWindowProc(wpOrigEditProc,hwnd, uMsg, wParam, lParam);

};

CallWindowProc

DefWindowProc

WindowProc



1.5 怎样使用MFC创建一个窗口类

时间:2010-12-0319:00贡献者:淡淡评论:0条我要推荐:
当用户创建一个窗口类时,实际上只是在三个操作系统列表之一中注册一个W N D C L A SS结构。系统为每个窗口类类型维护一个列表:

系统全局类(System Global Class)在系统启动时注册,且必须注册,对所有应用程序都有效。

应用程序全局类(Application Global Class)由应用程序注册,只对应用程序及应用程序的线程有效。

应用程序局部类(Application Local Class) 由应用程序注册,并只对注册它们的应用程序或D LL有效。

当系统搜索一个窗口类时,从应用程序局部类开始,然后搜索应用程序全局类,最后搜索系统全局类。

要创建一个窗口类,可以先创建W N D C L A S S结构的实例,然后用M F C类库的A f x R e g is t e rClass( ) 注册它。也可以用M F C类库的AfxRegisterWndClass( )来创建一个基于调用参数的W N D C L A S S对象。

1.5.1 使用AfxRegisterWndClass( )函数注册一个窗口类

AfxRegisterWndClass()函数在使用上是非常自动化的,一些通常需要你提供的参数都能自己填入,甚至连新的窗口类的名字也能自动产生。

LPCTSTRlpszClassName=AfxRegisterWndClassUINTnClassStyle,HCURSORhCursor=0,HBRUSHhbrBackground=0,HICON hIcon=0);

这些参数的使用规则如下:

1. 类名

根据传给该函数的参数,为新的窗口类产生名字。如果传输的参数完全相同,那么创建的窗口类也将完全相同。如果需要创建新的窗口类,可以用AfxRegisterClass()。

2. 风格

窗口类风格由下面选项列表中一系列标记的或( O R )运算提供:

 

3. 图标( I c o n )

该参数是显示在窗口左上角的图标的句柄,但只适用于使用W S S Y S M E N U的窗口风格。

应用程序主窗口的图标也显示在任务栏上。如果将该参数设置为N U L L ,并且设置了W S S Y S M E NU风格,则系统将提供一个默认的图标。在M F C环境中,绝大部分图标已被处理过,用CWn d的S e t I c o n ()可改变已有的图标。

4. 光标( C u r s o r )

该参数是鼠标移经应用程序窗口的客户区时,将要显示的鼠标光标句柄。如果将该参数设置为N U LL,则显示的是箭头光标。可以用下面的语句装载一个光标:

HICON hIcon=AfxGetApp( )->LoadCursor(xx);

这里的x x是应用程序资源中光标的名字或I D。

这里指定的光标意味着是该窗口的默认光标。如果想动态地改变光标形状,则应该处理该窗口的W M S E T C U R SO R消息,并用SetCursor( )来改变光标形状(见第8章例3 3 )。

5. 背景色

当系统创建窗口时,先在显示窗口的地方画一个矩形区域,以擦除该区域的背景色。该参数指定填充该矩形域时所用画刷的句柄(参见第4章有关画刷的详细内容)。为窗口类创建的画刷对象在该类退出注册时被自动释放。

在指定背景色时,也可以不创建画刷对象,而指定下面所列系统颜色之一:

C O L O R A C T I V E B O R D E R

C O L O R A C T I V E C A P T I O N

C O L O R A P P W O R K S PA C E

C O L O R B A C K G R O U N D

C O L O R B T N FA C E

C O L O R B T N S H A D O W

C O L O R B T N T E X T

C O L O R C A P T I O N T E X T

C O L O R G R AY T E X T

C O L O R H I G H L I G H T

C O L O R H I G H L I G H T T E X T

C O L O R I N A C T I V E B O R D E R

C O L O R I N A C T I V E C A P T I O N

C O L O R M E N U

C O L O R M E N U T E X T

C O L O R S C R O L L B A R

C O L O R W I N D O W

C O L O R W I N D O W F R A M E

C O L O R W I N D O W T E X T

然而,必须分配颜色到一个画刷句柄类型,并加1。

(HBRUSH) (COLOR_WINDOW+1)设置该参数为N U LL,则在画一个窗口之前,系统不对窗口进行擦除。在非客户区的绘制还是同平常一样,但客户区将保持窗口被画前原来屏幕所显示的样子。若该参数设为NU L L,应确认窗口是画全部客户区,还是处理W M E R A S E B R G N D消息以擦除背景颜色。

1.5.2 使用AfxRegisterClass( ) 函数创建一个窗口类

如果想完全控制一个新窗口类的创建,如指定窗口类的名字,则应该用下面的语句:

BOOL AFXAPI AfxRegisterClass (WNDCLASS•lpWndClass);

这里的W N D C L A S S结构定义如下:

typedef  struct _WNDCLASS {

UINT   style; // style of the class

W N D P R OC     l p f n W n d P r o c;          // function called by system when

// it has a message for a window

// created with this class

i nt     c b C l s E x t r a;                                   / extra bytes to add to the

// WNDCLASS structure when

// registering

i nt     c b W n d E x t r a;                                  // extra bytes to add to the Wi n d o w

// Objects created with this class

H A N D LE     h I n s t a n c e;                       // instance that owns this class

H I C ON         h I c o n;                                   //icon to be used when window

// displays an icon

H C U R S OR    h C u r s or;                          // cursor to use when mouse is over

// a window created with this class

H B R U SH        h b r B a c k g r o u n d;     // background color to use when

// erasing the background area under

// a window created with this class

L P C T S TR    l p s z M en u N a m e;        // menu name to be used when

// creating a menu for a window

// created with this class

L P C T S T R   l p s z C l a s s N a m e;       // the class name that identifies

// this WNDCLASS for the system

} WNDCLASS;

有关填入类的“风格”、“图标”、“光标”和“背景色”参数的规则参看前面部分的介绍,其余参数的填入规则如下:

1. 类名

类名参数是用来标识新窗口类的零结尾字符串。可以任意命名窗口类,但是不要与已有的窗口类同名,除非想使该类无效。正如前面所提及的,系统从应用程序局部类开始,在三个列表中寻找类名匹配,如果在该列表中记录了一个与系统全局类同名的类,则应用程序将使用新记录的类。

2. 菜单名

该参数指向应用程序资源中的菜单名。在M F C环境中,大多数情况下由系统载入菜单,但可以在此处指定菜单名。如果使用资源ID,也可以采用下面语句:

MAKEINTRESOURCE (IDR_MENU);

这将成为该窗口的默认菜单。若指定该值为N U L L,系统将使用在类C W n d的CreateEx()中定义的菜单;若在此处没有指定的菜单,该窗口将没有菜单。该参数对子窗口可以忽略,因为它们从来没有菜单。

3. 窗口进程和实例

这两个参数是窗口类中最重要的参数。有关窗口进程在前面已经讨论过了,而实例只是 

用以标识哪个应用程序包含了该窗口进程。在M F C环境中,默认的窗口进程为AfxWndProc( ),

可以使用下面语句返回该进程,并为该参数使用。

AfxGetAfxWndProc( );

可以用AfxGetInstanceHandle( )填入I n s t a n c e参数。

4. Class Extra和Window Extra参数

C l a s s E x t r a和Wi n d o w E x t ra参数提供了一种存储数据的方法,允许应用程序自身将所属的数据存储到窗口对象或注册的窗口类中。注册类时, ClassExtra指定在类的末尾分配的额外字节数;创建窗口对象时, WindowExtra指定添加到该窗口对象尾部的额外字节数。

在这两种情况下,这些额外字节都被应用程序用来存储属于窗口或窗口类的消息。然而,由于C W nd对象与一个窗口关联,并且一个C W nd对象可以存储的信息量若不比额外字节多的话,起码也与之相等;因此,这些额外字节参数实际上并没有多大用处,可以把它们设为0。


  • 1
    点赞
  • 0
    评论
  • 2
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值