智商是硬伤 发表于 2015-8-7 11:22:08

Windows SDK编程之【注册一个窗口类】理解

声明:编辑了别人的帖子,原帖地址:http://blog.sina.com.cn/s/blog_520f749201009bgi.html

一 创建并显示一个窗口的“遐想”。

首先,要显示的窗口在哪里呢?
    要想显示你自己的窗口,显然你得事先创建一个自己的窗口。当你想要一个窗口时,Window系统才会为你创建窗口;不要时,Window再销毁这个窗口。也就是说我们得先创建一个窗口才能显示(否则哪里来的窗口给你显示呢?)?
其次,你想创建什么样的窗口呢?
    那么,创建什么样的窗口呢?创建前,Window系统可不知道你要的是什么类型的窗口啊(比如标题栏上显示什么图标,鼠标形状是什么,窗口背景颜色等等)。这些类型信息应在你创建前事先告诉Window系统。可以采用这种方法:就是我们事先写一份要创建窗口的类型申请表,提交(注册)给Window系统。然后在创建时,可以让Windows按这个申请表来创建你所要的窗口了。也就是说我们还应该先提交一个申请表,申请成功后再根据这个表创建一个窗口。

    依据上述的理由,我假想了以下几个步骤要做:
    第一. 你得先填写一份你想创建的窗口类的“申请表”。
    第二. 然后将这“申请表”通过一个API函数提交给Windows系统(即注册到Windows系统中)。
    第三. 如果提交(注册)成功,就说明Window系统通过了你的“申请表”,Windows系统中就有了一份你所申请的窗口类(注:这个注册成功的已经注册在系统中的“申请表”我们称之为窗口类)。这样你就可以利用这个申请成功的窗口类,通过一个专门的API函数让Windows系统创建一个或多个的同一窗口类的窗口。
    第四. 创建成功后,我们有了窗口。但是,虽然窗口已存在在内存中,并不一定就马上显示在屏幕上(这根据你的意愿了),所以之后的某时你可以用一个API函数来让Windows系统显示刚才创建的窗口。
    Window系统就是这样设计的噢!(呵呵,这样设计不算坏,我可以接受。)
    以上就是创建一个窗口的大致过程。请记住,在Window系统下你的程序要显示一个你想自定的窗口总是得经历如此步骤的。还要记住一点,我们的代码只是通过调用Window系统所提供的API函数来完成对窗口间接的管理。实际上窗口的管理操作都由Window系统直接完成的。
    好,让我们在本篇中先来完成第一、二步骤吧!

二 第一步 填写一份“申请表”

1 用什么来作为这种“申请表”呢?
    我想,C语言中的struct结构体类型的变量来充当这个“申请表”是再合适不过的了。呵呵,真是这样,VC6下早已为我们准备好了这样的“申请表”了。那就是WNDCLASSEX(我们称之为窗口类结构体)。
    看看这个WNDCLASSEX结构体的底细吧!
    在windef.h中已经有定义:(下面所列的与真实文件中会有点不同,但目前你只要理解我这份就可以了。)
typedef struct tagWNDCLASSEX {
UINT      cbSize;              //用来保存本结构体的所占字节数
UINT      style;                   //窗口类型风格。
                                          //比如,可设置“若移动窗口宽度时,则刷新整个窗口。
WNDPROC   lpfnWndProc; //回调函数指针,用以指向前面那个回调函数。
int         cbClsExtra;       //略,我们可不必使用它,只要赋值为0就可
int         cbWndExtra;      //略,同上
HINSTANCE   hInstance;      //窗口所属的应用程序实例句柄。
HICON       hIcon;          //大图标,(这个图标会显示在窗口的哪里呢?)
HICON       hIconSm;       //小图标,(这个又会显示在窗口的哪里呢?)
HCURSOR   hCursor;       //鼠标句柄,用以指定鼠标移入窗口时的样式
HBRUSH      hbrBackground;//用来刷背景颜色的画刷句柄,
                           //窗口的颜色就会按这个显示。
LPCSTR      lpszMenuName;   //用来指向菜单资源名称字符串的指针,
                              //可让你的窗口有一个菜单。
LPCSTR      lpszClassName;   //用来指向窗口类名字符串的指针
                              //作为这个窗口类的标识
} WNDCLASSEX

    哦!是一个有12个成员变量的struct结构体的类型。(看来我们又要一个一个耐心地搞定它。)

2 创建一个“申请表”
现在就先让我用这个WNDCLASSEX结构体申明一个变量吧!(看粗体字部分)
    HWND hWnd;
    MSG msg;
    WNDCLASSEX winclass;   //变量名可由你自己定。
    顺便解释一下:
    这里我顺便地同时申明了hWnd的窗口句柄变量,用以保存将要创建出来的窗口的句柄。
    还申明了一个msg:它的类型是MSG,也是一个已预定义好的结构体,用来保存窗口操作的各种消息信息。
   
3 填写“申请表”
    接下来就到了填写这个“申请表”的时候啦!也就是要对winclass这个结构体变量的各成员赋值。
winclass.cbSize = sizeof (WNDCLASSEX);
    这是将本结构体的大小(占字节数)赋值给其成员变量cbSize,一定要这样做哦!好处是:以后Windows系统只要访问cbSize就可知道wndclass的大小了,就不必每次都要用sizeof(WNDCLASSEX)来获取大小。(哦,不错,这真是一个很值得学习的做法)
winclass.stype=CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLICKS;
    我们这里将这四个值同时赋值给成员变量stype。
    新问题:CS_VREDRAW、CS_HREDRAW、CS_OWNDC、CS_DBLCLICKS是什么啊!
    stype是一个UINT即unsigned int的类型(共32位二进制位)。stype的可能值为如下的组合:

标识             描述
CS_HREDRAW    若移动或改变了窗口宽度,则刷新整个窗口
CS_VREDRAW    若移动或改变了窗口高度,则刷新整个窗口
CS_OWNDC    为该类中的每个窗口分配一个单值的设备描述表
CS_DBLCLKS    当用户双击鼠标时向窗口程序发送一个双击的信息,同时,光标位于属 于该类的窗口中
CS_PARENTDC    略
CS_NOCLOSE    禁止系统菜单上的关闭命令
CS_SAVEBITS    略

    关于更详细的类型标志描述,请自行参考相关书籍。
    你可能看完后仍还不能完全明白这些窗口类的类型标志的意思。没有关系,它不妨碍我们对整个程序框架的理解(我们可是要善于把握轻重缓急噢!),现在你只要按我上面赋值就可以了。
    还有一个问题:符号“|”是按位或的运算符,即表示CS_VREDRAW、CS_HREDRAW、CS_OWNDC、CS_DBLCLICKS的值同时都赋值给stype

winclass.lpfnWndProc=WinProc;
    lpfnWndProc是一个函数指针,它是WNDPROC函数类型,这个函数类型在winuser.h文件中已有定义。如下:
    typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM);
    发现没有,WNDPROC类型的格式与我在第三篇中写的那个自定义窗口过程函数的格式是一样的。现在我们将我们的这个函数WinProc的地址赋值给lpfnWndProc函数指针变量。
    将前面的那个回调函数WinProc的首地址赋值给成员变量lpfnWndProc。这样这个窗口类就与这个回调函数相关联起来了。
    这里,你可能有个疑问:为窗口类指定一个窗口过程函数有什么用处呢?这是Window系统的消息机制的关键。(等到我将整个程序框架建立起来后,我们再来理解它吧。)

wndclass.hInstance = hinstance;
    主函数参数中hinstance的值就是本应用程序实例句柄值,我们现在将这个hinstance的句柄值赋值给wndclass.hInstance。这样,由这个窗口类结构创建的窗口就与本程序实例相关联了。

wndclass.cbClsExtra=0;
wndclass.cbWndExtra =0;
    我们现在只是对cbClsExtra和cbWndExtra两个成员简单赋值为0。你现在也不用管它们是做什么的。可以保证绝大多数的程序只要这样就行。

wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
    hCursor是一个鼠标光标句柄变量,用来为这个窗口类指定一个鼠标句柄值(也就是想让这个窗口显示一个什么样的鼠标形状了)。LoadIcon(NULL, IDC_ARROW);这个函数是加载一个光标给hCursor的。

wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
    hIcon和hIconSm都是图标句柄变量,前一个是用以指定大图标,后一个是用以指定小图标。
    hIconSm中设置的图标会显示在你的窗口的标题栏左边。
    关于LoadIcon函数,它是加载一个图标给hIcon及hIconSm的。现在你只要造就输入就可以了。

wndclass.lpszMenuName =NULL;
    lpszMenuName是一个字符串指针,这是用来指向一个菜单资源名字符串。我们这个窗口暂时不要菜单,所以这里先指定NULL。

wndclass.hbrBackground =(HBRUSH)GetStockObject(WHITE_BRUSH);
    hbrBackground是一个画刷句柄变量(类型HBRUSH)。窗口客户区的背景颜色是用这个变量指定的画刷来刷的。看不明白(HBRUSH)GetStockObject(BLACK_BRUSH)这个函数没有关系,这里它提供了一个白色画刷给我们。你现在只要造就输入就可以。

wndclass.lpszClassName ="WINCLASS1";
    lpszClassName是一个字符串指针,它是为这个窗口类(申请表)指定个字符串,这个字符串作为这个窗口类的名称。这里我们指定”WINCLASS1”字符串作为这个窗口类的名称。(要注意一点的是,一个程序中如果申请(注册)了多个窗口类,那么每个窗口类的这个字段值是不能相同的。)
    到此为止,我们终于(有点稀里糊涂地)结束了这个结构体各成员变量的赋值了。以上成员变量赋值代码汇总如下:

wndclass.cbSize = sizeof(WNDCLASSEX);
wndclass.cbClsExtra=0;
wndclass.cbWndExtra =0;
wndclass.lpfnWndProc = WinProc;
wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
wndclass.hInstance = hinstance;
wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
wndclass.hIcon = LoadIcon(NULL,IDI_APPLICATION);
wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION);
wndclass.lpszMenuName = NULL;
wndclass.hbrBackground =(HBRUSH) GetStockObject(BLACK_BRUSH);
wndclass.lpszClassName ="WINCLASS1";
   
   好了,我们已经填好了我们想要创建的窗口的“申请表”了,现在可以透一口气了!
   然后……再继续,因为我们的任务还没用完成呢!

三 第二步 提交“申请表”给Window系统
    到了提交“申请表”的时候了。我们现在要将wndclass中的各项数据信息提交(注册)到Windows系统中。有一个API函数是专门用来注册窗口类信息的,原型如下:
ATOM WINAPI RegisterClassEx(CONST WNDCLASSEX *);
    参数是WNDCLASSEX *类型的指针,只要将要注册的WNDCLASSEX结构体变量地址代入就行。
    返回值是一个ATOM类型,其实它也是一个unsigned short类型的别名而已。如果注册不成功则程序返回0值,否则表示注册成功。(注:我们暂时不关心其它返回值的意义。)
    所以我们注册窗口类的代码可以如下:
    if (!RegisterClassEx(&winclass))
      return 0;   //不成功程序就直接结束了

四 最后总括代码
    本篇中所增加的全部代码现总括如下(粗体字部分):
WINAPI int WinMain(HINSTANCE hinstance,
               HINSTANCE hprevinstance,
               LPSTR lpcmdline,
               int ncmdshow)
{
    HWND hWnd;   //窗口句柄变量
   MSG msg;      //消息结构体变量
    WNDCLASSEX wndclass;   //窗口类结构体变量
    //以下是对wndclass各成员赋值
    wndclass.cbSize = sizeof(WNDCLASSEX);
    wndclass.cbClsExtra=0;
    wndclass.cbWndExtra =0;
    wndclass.lpfnWndProc = WinProc;
    wndclass.style = CS_VREDRAW | CS_HREDRAW | CS_OWNDC | CS_DBLCLKS;
    wndclass.hInstance = hinstance;
    wndclass.hCursor =LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon =LoadIcon(NULL,IDI_APPLICATION);
         wndclass.hIconSm = LoadIcon(NULL,IDI_APPLICATION) ;
    wndclass.lpszMenuName = NULL;
    wndclass.hbrBackground =(HBRUSH) GetStockObject(BLACK_BRUSH);
    wndclass.lpszClassName =WND_CLS_NAME;
//下面是利用wndcalss结构体的信息注册一个窗口类
    if (!RegisterClassEx(&wndclass))
      return 0;
      //……
return 0;
}
页: [1]
查看完整版本: Windows SDK编程之【注册一个窗口类】理解