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函数的参数意义:
Window程序的结构--学习笔记_第1张图片

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指明程序图标,可以取一些预设值,
Window程序的结构--学习笔记_第2张图片
2.GetStockObject函数,用于加载对象资源,原型如下:

HGDIOBJ GetStockObject(int fnObject);

参数fnObject指定对象类型,常见类型如下:
Window程序的结构--学习笔记_第3张图片

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,NULL0,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是地方最高法院,专门用来处理当地案件,一般发生了一个案件,不会直接由最高法院处理,而是最高法院让事件发生的所在地的地方法院来处理,但是作为地方法院,一切都有按照规定的流程来。

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

你可能感兴趣的:(windows)