Win32    程序开发的流程

message based, event driven

Win32程序是message based, event driven。也就是说Win32程序的运行是依靠外部不断发生的事件来驱动的,也就是说,程序不断等待(有一个while循环),等待任何可能的输入,然后做判断,再做适当的处理。因此Win32程序只需要做好如下几件事情就可以了:

1. 定义窗口的外观;

2. 定义当不同的事件发生时,程序做什么样的反应(定义窗口处理函数);

3. 写一个While循环,不断检测新事件的发生,并将其发送给不同的窗口处理函数

程序进入点WinMain

main是一般C程序的进入点:int main( int argc, char *argv[], char *envp[])

Win32程序的进入点是

int CALLBACK WinMain(  __in  HINSTANCE hInstance,  __in  HINSTANCE hPrevInstance,  __in  LPSTR lpCmdLine,  __in  int nCmdShow);

当用户要执行一个程序时,首先是Windows 的Shell(Explorer)调用CreateProcess这个系统调用,CreateProcess为这个进程创建虚拟地址,然后将代码和数据载入,然后系统再创建一个主线程开始执行run time startup函数的代码,run time startup 函数会最终调用入口点函数(main, WinMain)。如果用户执行的是一个Win32程序,那么C startup就会调用WinMain并开始执行程序。

窗口类注册与窗口诞生

如果前面所说,Win32的一个重要的责任是定义窗口外观和窗口处理函数。这是通过窗口类注册来完成的。创建窗口可以使用CreateWindow来完成,但在调用CreateWindow时必须先设定窗口的各种属性和行为。设定窗口属性和行为是通过API 函数 RegisterClass 来完成的。RegisterClass需要一个大型数据结构WNDCLASS,窗口的属性和行为就是在这个数据结构中指定的。

ATOM WINAPI RegisterClass(  __in  const WNDCLASS *lpWndClass);

下面是数据结构WNDCLASS的定义:

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

消息循环是怎么工作的

应用程序可以获得的输入有两种类型:硬件装置产生的消息房子系统队列中、由Windows系统或者其他Windows程序传送过来的消息,放在程序队列中。以应用程序的眼光来看,消息就是消息,来自哪里或放在哪里没有太大区别,程序每次GetMessage就取得一个消息。

每个窗口都应该有一个函数负责处理消息,程序员必须负责设计这个所谓的窗口函数(Window Procedure),如果窗口获得一个消息,则这个窗口必须判断消息的类别,决定处理方式。下面是主消息循环的例子:

// Main message loop: while (GetMessage(&msg, NULL, 0, 0)) {     if (!TranslateAccelerator(msg.hwnd, hAccelTable, &msg))     {         TranslateMessage(&msg);         DispatchMessage(&msg);     } }

GetMessage的原型:

BOOL WINAPI GetMessage(  __out     LPMSG lpMsg,  __in_opt  HWND hWnd,  __in      UINT wMsgFilterMin,  __in      UINT wMsgFilterMax);

调用线程的消息队列中获取一个消息,这个函数会一直等到有一个可用的Message时才会返回;

int WINAPI TranslateAccelerator(  __in  HWND hWnd,  __in  HACCEL hAccTable,  __in  LPMSG lpMsg);

该函数处理菜单命令中的加速键。该函数将一个WM_KEYDOWN或WM_SYSKEYDOWN消息翻译成一个WM_COMMAND或WM_SYSCOMMAND消息(如果在给定的加速键表中有该键的入口),然后将WM_COMMAND或WM_SYSCOMMAND消息直接送到相应的窗口处理过程。

TranslateMessage是用来把虚拟键消息转换为字符消息。由于Windows对所有键盘编码都是采用虚拟键的定义,这样当按键按下时,并不得字符消息,需要键盘映射转换为字符的消息。 TranslateMessage函数用于将虚拟键消息转换为字符消息。字符消息被投递到调用线程的消息队列中,当下一次调用GetMessage函数时被取出。当我们敲击键盘上的某个字符键时,系统将产生WM_KEYDOWN和WM_KEYUP消息。这两个消息的附加参数(wParam和lParam)包含的是虚拟键代码和扫描码等信息,而我们在程序中往往需要得到某个字符的ASCII码,TranslateMessage这个函数就可以将WM_KEYDOWN和WM_ KEYUP消息的组合转换为一条WM_CHAR消息(该消息的wParam附加参数包含了字符的ASCII码),并将转换后的新消息投递到调用线程的消息队列中。注意,TranslateMessage函数并不会修改原有的消息,它只是产生新的消息并投递到消息队列中。 也就是说TranslateMessage会发现消息里是否有字符键的消息,如果有字符键的消息,就会产生WM_CHAR消息,如果没有就会产生什么消息。

windows消息处理机制是这样的: 首先系统(也就是windows)把来自硬件(鼠标,键盘等消息)和来自应用程序的消息放到一个系统消息队列中去。而应用程序需要有自己的消息队列,也就是线程消息队列,每一个线程有自己的消息队列,对于多线程的应用程序就有和线程数目相等的线程消息队列。winsows消息队列把得到的消息发送到线程消息队列,线程消息队列每次取出一条消息发送到指定窗口,不断循环直到程序退出。这个循环就是靠消息环(while(GetMessage()) TranslateMessage();DispatchMessage(); 实现的.GetMessage()只是从线程消息中取出一条消息,而DispatchMessage 则把取出的消息发送到目的窗口。如果收到WM_CLOSE消息则结束循环,发送postqiutmessage(0),处理WM_DESTROY销毁窗口!

其实问题的关键在于DispatchMessage到底干了什么 如果只是去调用相应的窗口,那自己写个switch不就可以了。DispatchMessage与switch不同之处在于DispatchMessage会先调用windows,进入管态(大概是range 0),然后再由windows调用窗口的函数。 为什么这么麻烦? 因为这样windows就可以知道你的程序运行到什么情况了, windows来调用你的窗口,这样你的窗口返回的时候windows就知道你已经处理过一个消息了,如果没有新的消息进入消息队列windows就不再会给你的进程分配时间片如果是你自己写switch的话,windows就不可能这样灵活的分配时间资源利用率就会降低那么还要消息循环干什么,windows直接把消息发给窗口不就可以了吗?因为你要在消息循环里把KEY_DOWN和KEY_UP组合成WM_CHAR,还可以直接屏蔽掉许多对你来说无用的消息,加快速度。

GetMessage: 从线程的消息队列取出一个消息   TranslateMessage: 将msg结构传给Windows,进行一些转换,比如A键按下,转换成WM_CHAR消息等   DispatchMessage():再将msg结构传给Windows,Windows将该消息发给窗口过程,由窗口过程处理.

TranslateMessage是对一些键盘事件做预处理。GetMessage是从系统为每个应用程序自动分配的消息对列的头部得到一个消息。

TranslateMessage是翻译需要翻译的消息

DispatchMessage()则会把翻译好的消息发送到系统的消息处理函数中,而这个函数又会把这个消息传    递到注册窗体时用户指定的消息处理函数中翻译消息不是简单的转换,一个消息被翻译后,可能会产生几个消息。

问题:

系统是如何把一个消息发送到线程消息队列的?

每个程序只有一个UI线程。

Win32 编程 基础的更多相关文章

  1. (转)Windows驱动编程基础教程

    版权声明     本书是免费电子书. 作者保留一切权利.但在保证本书完整性(包括版权声明.前言.正文内容.后记.以及作者的信息),并不增删.改变其中任何文字内容的前提下,欢迎任何读者 以任何形式(包括 ...

  2. win32编程简介

    win32编程简介 复习Win32整理下知识. 为什么学习win32? 我们要编写windos程序.都离不开API. 也就是我们所说的win32程序. 所以学好win32是你能不能再windows下编 ...

  3. 《网络安全编程基础》之Socket编程

    <网络安全编程基础>之Socket编程 我的代码 server.c // server.cpp : Defines the entry point for the console appl ...

  4. 第二章 Matlab面向对象编程基础

    DeepLab是一款基于Matlab面向对象编程的深度学习工具箱,所以了解Matlab面向对象编程的特点是必要的.笔者在做Matlab面向对象编程的时候发现无论是互联网上还是书店里卖的各式Matlab ...

  5. [.net 面向对象编程基础] (1) 开篇

    [.net 面向对象编程基础] (1)开篇 使用.net进行面向对象编程也有好长一段时间了,整天都忙于赶项目,完成项目任务之中.最近偶有闲暇,看了项目组中的同学写的代码,感慨颇深.感觉除了定义个类,就 ...

  6. Android开发4: Notification编程基础、Broadcast的使用及其静态注册、动态注册方式

    前言 啦啦啦~(博主每次开篇都要卖个萌,大家是不是都厌倦了呢~) 本篇博文希望帮助大家掌握 Broadcast 编程基础,实现动态注册 Broadcast 和静态注册 Broadcast 的方式以及学 ...

  7. T-Sql编程基础

    T-sql编程 入门小游戏 T-sql编程基础,包括声明变量,if判断,while循环,以及使用一些基本函数. 记得在学校的时候,写过一个二人对打的文字输出游戏. 上代码 alter proc usp ...

  8. [Java入门笔记] 面向对象编程基础(二):方法详解

    什么是方法? 简介 在上一篇的blog中,我们知道了方法是类中的一个组成部分,是类或对象的行为特征的抽象. 无论是从语法和功能上来看,方法都有点类似与函数.但是,方法与传统的函数还是有着不同之处: 在 ...

  9. 如何夯实(Java)编程基础,并深入学习和提高

    如何夯实(Java)编程基础,并深入学习和提高? 240赞同反对,不会显示你的姓名 匿名用户 240 人赞同 多学习...网上自学的学习网站很多,见以下榜单~一.汇总榜单: 公开课_学习网站导航 收录 ...

随机推荐

  1. 关于scanf一个变量的覆盖问题

    假如你为了省空间,在scanf一个很长的字符串s后,又重复scanf 字符串s, 但是后面的s比前面的s短,那么在s后面一定有没覆盖的原字符串的字符: 那么在取字符串长度时会不会还是原来的s长度而不是 ...

  2. 关于Mongodb的其他知识

    Mongodb支持的数据类型 数据类型 描述 String 字符串.存储数据常用的数据类型.在 MongoDB 中,UTF-8 编码的字符串才是合法的. Integer 整型数值.用于存储数值.根据你 ...

  3. visudo修改编辑器vim

    update-alternatives --config editor

  4. 关于深度学习之TensorFlow简单实例

    1.对TensorFlow的基本操作 import tensorflow as tf import os os.environ[" a=tf.constant(2) b=tf.constan ...

  5. idea无法创建javaclass文件

    一直用pycharm和jupyter. 今天发现打开IDEA 创建一个新的java项目(maven)后无法在里面的module中创建相应的java class文件 解决方案: (1)选择 File—— ...

  6. 各种环境下搭建ruby on rails开发环境

    win10上搭建raby on rails环境: 步骤如下 1.安装ruby (我选择的版本是ruby 2.2.3p173) 2.安装rails gem 在这之前建议先把gem的源换成淘宝的源,速度快 ...

  7. 对数据劫持 OR 数据代理 的研究------------引用

    数据劫持,也叫数据代理. 所谓数据劫持,指的是在访问或者修改对象的某个属性时,通过一段代码拦截这个行为,进行额外的操作或者修改返回结果.比较典型的是 Object.defineProperty() 和 ...

  8. PHP入门(五)

    一.超级全局变量 超级全局变量在PHP 4.1.0之后被启用, 是PHP系统中自带的变量,在一个脚本的全部作用域中都可用. PHP中预定义了几个超级全局变量(superglobals) ,这意味着它们 ...

  9. vuex中mapState、mapMutations、mapAction的理解

    当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余.为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性. // 在单独构建的版本中辅助函数为 Vue ...

  10. JS实现表单全选以及取消全选实例

    实现效果: 全选按钮:点击全选按钮所有的小按钮都会被选中:点掉全选按钮,所有按钮取消选中: 小按钮:只有全部被选中,全选按钮才会被选中 思路分析: 1.全选和取消全选做法:让下面所有复选框的 chec ...