Window程序的结构--学习笔记

学过C系语言编程的都知道,程序的入口一定是main()函数,在windows系统中也有这样的函数,它叫WinMain函数。WinMain函数是所有windows程序的入口,主要负责注册窗口类,创建并初始化窗口,进入消息循环,以及消息循环检索到WM_QIUT消息时,终止程序执行。

下面详细说明

在此之前,请看下面的代码:

int WINAPI WinMain(
              HINSTANCE hInstance,
              HINSTANCE hPrevInstance,
              LPSTR     lpCmdLine,
              int      nCmdShow)
{
   WNDCLASS wndclass;   //声明结构体变量
   RegisterClass (&wndClass);//注册窗口
   CreateWindow  (……);//创建窗口
   ShowWindow   (……);//显示
   UpdateWindow   (.....);//更新
   while(Getmessage(&msg,NULL,0,0))//进入消息循环
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
}

你随便找一本关于windows编程的书籍来看都会看到类似于上面的代码,可能是完整版,这里只是为了理解程序结构,所以不需要完整的可执行程序。

先说一下WinMain函数的参数意义:

1.建立窗口类

WinMain函数的第一步是要建立、登记应用程序的窗口类。就像小米生产手机一样,首先需要对手机进行设计,什么样子,什么配置等等。窗口类是定义窗口属性的模板,这些属性包括窗口样式、鼠标形状,菜单,窗口函数。只有先设计出小米的外观,才能开始生产小米手机,同样,只有先建立窗口类,才能创建Windows应用程序窗口。

窗口类的定义如下:

typedef struct _WNDCLASS {
  UINT style;
  WNDPROC lpfnWndProc;
  int cbClsExtra;
  int cbWndExtra;
  HINSTANCE hInstance;
  HICON hIcon;
  HCURSOR hCursor;
  HBRUSH hbrBackground;
  LPCTSTR lpszMenuName;
  LPCTSTR lpszClassName;
  } WNDCLASS, *PWNDCLASS;

它是一个结构体,里面包含设计一个窗口的各种信息,就像小米手机的设计信息需要包括屏幕尺寸,机身厚度,宽度,高度,颜色,电池容量,内存……关于WNDCLASS的详细信息请点击这儿

1)style用于控制窗口的特性

  

  • CS_BYTEALIGNCLIENT: 在字节边界上(在x方向上)定位窗口的用户区域的位置
  • CS_BYTEALIGNWINDOW: 在字节边界上(在x方向上)定位窗口的位置
  • CS_CLASSDC: 该窗口类的所有窗口实例都共享一个窗口类DC
  • CS_DBLCLKS: 允许向窗口发送双击鼠标键的消息
  • CS_GLOBALCLASS: 当调用CreateWindow 或 CreateWindowEx

    函数来创建窗口时允许它的hInstance参数和注册窗口类时传递给

  • RegisterClass 的 hInstance参数不同。如果不指定该风格,则这两个 hInstance 必须相同。
  • CS_HREDRAW: 当水平长度改变或移动窗口时,重画整个窗口
  • CS_NOCLOSE: 禁止系统菜单的关闭选项

    还有几个特性我们有列出来,这些特性有的是可以用“|”来进行组合的。

lpfnWndproc:是一个指向窗口内消息处理的指针,该消息处理函数通常称为窗口函数,用于接收Windows发送给窗口的消息,并执行相应任务。可以理解为生产小米的代工厂(不是很恰当的比喻)。

所以每当开发一个小米手机的时候,就需要对设计参数重新复制,wndclass也是这样子。如下就是一个窗口结构。

   wndclass.style =0; // 窗口类型为缺省类型CS_     Class Style
  wndclass.lpfnWndProc=WndProc; //定义窗口处理函数
  wndclass.cbClsExtra=0; //窗口类无扩展
  wndclass.cbWndExtra=0; //窗口实例无扩展
  wndclass.hInstance=hInstance; //当前实例句柄
  wndclass.hIcon=LoadIcon(NULL,IDI_APPLICATION); //窗口的最小化图标为缺省图标
  wndclass.hCursor=LoadCursor(NULL,IDC_ARROW); // 窗口采用箭头光标
  wndclass.hbrBackground=(HBRUSH)(GetStockObject(WHITE_BRUSH)); //窗口背景为白色
  wndclass.lpszMenuName=NULL; //窗口无菜单
  wndclass.lpszClassName=lpszClassName; //窗口类名为“窗口”

顺带说一下这里面的2个常用函数

  1. LoadIcon函数 用于加载图标资源,模型如下
HICON
LoadIcon(
  HINSTANCE hInstance,
  LPCTSTR lpIconName
);

参数lpIconName指明程序图标,可以取一些预设值,

2.GetStockObject函数,用于加载对象资源,原型如下:

HGDIOBJ GetStockObject(int fnObject);

参数fnObject指定对象类型,常见类型如下:

2.注册窗口类

窗口设计完成后,必须要注册窗口类,,当对WNDCLASS结构域一一复制后,就可以注册了,一般调用RegisterClass函数实现窗口类的注册。原型如下:

ATOM RegisterClass(CONST WNDCLASS *lpWndClass)

如果注册失败,RegisterClass返回非0值,程序终止,为0则表示注册成功,程序继续进行。

为什么要注册呢?就是要用户按照一定的要求谁窗口类,不能自己天马行空,想怎么来就怎么来。再用小米手机举例子,小米设计了一款手机,需要去工信部认证,才能上市,批量生产,要是你的手机不和规范,如有窃听功能,甚至手机又爆炸可能,政府是不可能让你上市的,对于汽车就更是如此了。


3.创建窗口

窗口类注册完了,就可以开始创建窗口了,就像小米通过了工信部认证,就可以开始批量生产啦。创建窗口有一个重要的函数CreateWindow,原型如下:

HWND CreateWindow(
LPCTSTR lpClassName,//注册窗口类名
LPCTSTR lpWindowName,//窗口标题名
DWORD dwStyle,  //窗口风格
int x,//显示窗口水平位置
int y,//显示窗口垂直位置
int nWidth,//窗口宽度
int nHeight,//窗口高度
HWND hWndParent,//父窗口句柄
HMENU hMenu,//菜单句柄
HANDLE hlnstance,//应用程序句柄
LPVOID lpParam)//指向传递一个窗口的指针型函数
}

4.显示窗口

大家都知道小米手机生产出来后,接下来都要开发布会。窗口创建后,就要把它显示出来,这时需要用到ShowWindow函数,原型如下:

BOOL ShowWIndow(
        HWND hWnd,
        int nCmdShow
        );

其中hWnd是窗口句柄,指定显示哪一个窗口,参数nCmdShow决定窗口的显示模式,这三个模式SW_MINIMIZE(最小化)、SW_SHOWMAXMIZED(最大化)、SW_HIDE(影藏),SW_SHOWNORMAL(普通化)。

WinMain函数调用完ShowWindow后,还需要UpdateWindow函数,将产生一个WM_PAINT消息,该消息使窗口重画,最终把窗口显示出来。

5.创建消息循环

Windows为每个运行程序维护一个消息队列,当单机鼠标按键时,Windows并不是直接报这个时间发送给应用程序,而是翻译成一个消息,并把这个消息,放置到应用程序所属的消息队列中去,形成消息循环,在有应用程序从队列中去获取消息。

同样用小米举个例子,小米手机性价比高,第一批货一抢而空,很多人都要等着买,由于产能限制,只能一批一批的出货,大家只能预定,然后小米根据预定人数决定下次生产订单数。

应用程序从队列中获取消息的方式有2种:意识由应用程序调用GetMessage函数或者PeekMessage函数从消息队列读取一条消息,并将消息放在MSG结构中;二是由Windows调用用户提供的回调函数来获取消息。一般采用的是第一种消息循环:
while(GetMessage(&msg,NULL,0,0))
{
   TranslateMessage(&msg);
   DispatchMessage(&msg);
}

只有当从应用程序列表检索到WM_QUIT消息时,GetMessage函数才会返回FALSE,也就是说一旦进入消息循环,即使没有消息也会一直运行下去。循环中的TranslateMessage函数用于将消息虚拟键转换为字符信息,字符信息再被发送到消息队列中,下一次调用GetMessage函数时将被取出。

例如当键盘上的某个字符键被按下时,系统将产生WM_KEYDOWN和WM_KEYUP消息。这两个消息的wParam和lParam参数包含的是虚拟键代码和扫码信息。而程序中往往需要字符的ASCII码,TranslateMessage函数就是将WM_KEYDOWN和WM_KEYUP消息组合转换为一条WM_CHAR消息,该消息的wParam参数包含了字符的ASCII码,并将转换后的新消息发送到消息队列中。

*TranslateMessage函数不会修改原有消息,只是产生新消息,而DispatchMessage函数则用于将消息传回给操作系统,由操作系统调用窗口函数对消息进行响应,而窗口函数对消息进行处理

6,。窗口函数

终于到了WinProc函数了。

窗口函数是消息处理函数,用于处理特定消息对应的一些代码,定义了应用程序对可能接收到的不同的消息的响应。以后你会发现,windows编程的重点工作就在这个函数里面,因为消息往往都是类似的(无非鼠标,键盘,控件),对这些消息的处理再是我们工作的重点。前面的框架都是固定的。

窗口函数的名字可以随便取,如Win_Fisrt_proc,只要保证前后一致即可。其原型如下:

LRESULT CALLBACK WndProc( //WndProc名称可自由定义
    HWND hwnd,
    UINT uMsg,
    WPARAM wParam,
    LPARAM lParam
);

细心的读者会发现这个函数的阐述和MSG结构的前4个成员完全相同。WinProc函数通常包括一个多分支switch结构语句,每个case语句对应一种消息。

LRESULT WINAPI WndProc( HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
    switch(uMsg)
    {
    case WM_DESTROY:
        PostQuitMessage(NULL);
        break;
    case ..
    .
    .
    .
    }
    return DefWindowProc(hWnd, uMsg, wParam, lParam);
}  

读者可能会由点疑问:为什么没看到WinMain函数显示调用Winproc函数呢?

*请注意,WInProc函数头部有一个CALLBACK标记,表明这是一个回调函数。什么是回调函数呢?简单的说就是等着别人来调用的函数,这些函数的原因都是由调用者设计好,使用的时候只要按照原型重新定义一个函数,然后将函数指针传递过去,我们在前面设计窗口类的时候就有WNDPROC这一项,就是告诉系统我这个类的消息处理函数是谁,因此回调函数是严格按照系统的规定进行说明和定义,函数的调用约定,参数都是固定的。就像系统是最高法院,你的winproc是地方最高法院,专门用来处理当地案件,一般发生了一个案件,不会直接由最高法院处理,而是最高法院让事件发生的所在地的地方法院来处理,但是作为地方法院,一切都有按照规定的流程来。

(注:本文参考书籍博客整理而成)

时间: 2024-10-10 16:15:20

Window程序的结构--学习笔记的相关文章

微信小程序开发:学习笔记[1]——Hello World

微信小程序开发:学习笔记[1]--Hello World 快速开始 1.前往微信公众平台下载微信开发者工具. 地址:https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html 2.打开微信开发者工具,并新建项目 打开微信开发者工具,选择新建小程序项目,我们先不需理解AppID的概念,新建项目时选择无AppID,并取消勾选"建立普通快速启动模板"的选项. 3.在根目录添加代码 1.在根目录创建app.json文件 2.

黑马程序员-OC学习笔记之Foundation框架NSNumber、NSValue和NSDate

---------------------- IOS开发.Android培训.期待与您交流! ---------------------- 一.NSNumber 前几篇笔记中,小桥复习了Foundatio框架中的几个类,这三个是OC学习的尾声了. 小桥已经复习过OC数组类NSArray,也说过它只能存放OC的对象,对于基本的数据类型确无能为力,但是实际编程中经常要把基本的数据如int.float,结构体存放的OC数组中,怎么办?这里的NSNumber就有用了,它能够把基本数据类型包装成OC对象.

黑马程序员-OC学习笔记之NSString与NSMutableString

---------------------- IOS开发.Android培训.期待与您交流! ---------------------- 一.NSString与NSMutableString 相信大家对NSString类都不陌生,它是OC中提供的字符串类,它的对象中的字符串都是不可变的,而它的子类NSMutableString类的对象中的字符串就是可变的.什么是可变与不可变呢?二者的区别就是在已经创建的一个字符串对象中,在程序中能不能更改字符串,比如拼接新的字符串,可变的可以在原字符串中更改,

微信小程序开发:学习笔记[2]——WXML模板

微信小程序开发:学习笔记[2]--WXML模板 快速开始 介绍 WXML 全称是 WeiXin Markup Language,是小程序框架设计的一套标签语言,结合小程序的基础组件.事件系统,可以构建出页面的结构. 打开开发工具的编辑器,在根目录下找到 app.json 文件,双击打开,在 "pages/index/index" 上新增一行 "pages/wxml/index" 保存文件.模拟器刷新后,读者可以在编辑器中找到 pages/wxml/index.wxm

黑马程序员_OC学习笔记之Foundation框架集合类

OC--集合类 1.OC集合类包括NSArray,NSSet,NSDictionary都是以面向对象的方式操作数组,而且OC数组不像C语言中的数组只能存放同一种数据类型,它可以存放任意类型的对象,但是不能存放非OC对象类型如基本数据类型int,struct,enum等 2.OC数组是以对象的方式存在,因此在创建的时候需要为创建的对象前面加* 3.NSArray数组一旦创建就决定了是不是可变,而且永远是可变或不可变 4.NSArray数组和子类NSMutableArray的基本操作: 1>使用NS

黑马程序员_OC学习笔记之description方法和sel

OC--description方法 1.Description方法包括类方法和对象方法.(NSObject类所包含) -description(对象方法) 2.使用NSLog和@%输出某个对象时,会调用对象的description方法,并拿到返回值进行输出. +description(类方法) 3.使用NSLog和@%输出某个对象时,会调用类对象的description方法,并拿到返回值进行输出,把整个对象一次性打印出来,打印对象使用%@. 4.使用@%打印对象如(“@%”,P)默认打印输出为<

黑马程序员_OC学习笔记之@property和@synthesize

[objc] view plaincopyprint? <span style="font-size:24px;">#import <Foundation/Foundation.h> @interface Person : NSObject { int _age; int age; int _height; int height; int _weight; int weight; int _money; int money; } @property int ag

黑马程序员——Foundation学习笔记(NSSet和NSMutableSet)

==========android培训.ios培训.java培训.期待与您交流========== 一.NSSet: NSSet NSSet里面存储的元素没有顺序,NSArray中的元素有顺序. NSSet *s = [NSSet set]; // 创建一个空的Set,永远是空,不可变. NSSet *s2 = [NSSet setWithObjects:@"jack",@"rose",nil]; NSString *str = [s2 anyObject]; //

黑马程序员-OC学习笔记之block

过山车 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 9426    Accepted Submission(s): 4151 Problem Description RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了.可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做par