转自:http://anyzixin.blog.163.com/blog/static/365289122007103031657355/

http://anyzixin.blog.163.com/blog/#m=0&t=1&c=fks_080075086083083064085095074064082083083075093087084

尽管windows应用程序千变万化,令人眼花缭乱,但,消息机制和窗口过程却始终它们的基础,掌握了这两项技术,也就相当于把握住了问题的关键。

  如果你以前是c程序员或是mfc的忠实用户,只要你学习过c语言的语法,自己亲手编过一些简短的c程序,理解以下的win32编程基础也不是一件困难的事。

  一个最简单的win32程序

  在以前的c语言编程中,一个最简单的程序可以只有两行。

  而要实现同样功能的windows程序却最少也要写几十行,这并不是说明windows应用程序效率低下,难于掌握,只是说明程序在windows环境下有更丰富的内涵。windows程序的效率其实不低,在所有的windows应用程序中,都有一个程序初始化的过程,这得用上几十条语句,这段初始化的代码对于任何windows应用程序而言,都是大同小异的。下面以一个实现最简单功能的程序easywin为例,说明windows程序的基本框架。

  打开visual c++ 6.0。

  选择file菜单的new,在出现的对话框中,选择projects栏目(新建工程),并点取其下的win32 application项,表示使用win32环境创建应用程序。先在locatin(路径)中填入“c:\”,然后在project name(项目名称)中填入“easywin”,其它按照缺省设置)。单击ok按钮。

  再次选择file菜单的new,在出现的对话框中,选择files栏目(新建文件),并点取其下的c++ source file项,表示新建一个c++源文件。在右边的file栏中输入“easywin”,最后确定让add to project检查框打上勾 )。单击ok按钮。

  在easywin.cpp文件中输入以下源程序代码。  
 

  程序输入完毕,即可编译执行。在窗口中击鼠标键或按esc键时,会弹出一个对话框以表示你的操作。

  其实,这个程序可以看成是所有win32应用程序的框架,在以后所有的程序中,你会发现它们都是在这个程序的基础之上再添加代码。

  winmain()函数 
 
  winmain()函数是应用程序开始执行时的入口点,通常也是应用程序结束任务退出时的出口点。它与dos程序的main()函数起同样的作用,有一点不同的是,winmain()函数必须带有四个参数,它们是系统传递给它的。winmain()函数的原型如下:

  第一个参数hinstance,是标识该应用程序当前的实例的句柄。它是hinstance类型,hinstance是handle of instance的缩写,表示实例的句柄。hinstance是一个很关键的数据,它唯一的代表该应用程序,在后面初始化程序主窗口的过程中需要用到这个参数。

  这里有两个概念,一个是实例,一个是句柄。实例代表的是应用程序执行的整个过程和方法,一个应用程序如果没有被执行,只是存在于磁盘上,那么就说它是没有被实例化的;只要一执行,则说该程序的一个实例在运行。句柄,顾名思义,指的是一个对象的把柄。在windows中,有各种各样的句柄,它们都是32位的指针变量,用来指向该对象所占据的内存区。句柄的使用,可以极大的方便windows管理其内存中的各种对象。

  第二个参数是hprevinstance,它是用来标识该应用程序的前一个实例句柄。对于基于win32的应用程序来说,这个参数总是null。这是因为在win95操作系统中,应用程序的每个实例都有各自独立的地址空间,即使同一个应用程序被执行了两次,在内存中也会为它们的每一个实例分配新的内存空间,所以一个应用程序被执行后,不会有前一个实例存在的可能。也就是说,hprevinstance这个参数是完全没有必要的,只是为了提供与16位windows的应用程序形式上的兼容性,才保留了这个参数。在以前的16位windows环境下(如windows3.2),hprevinstance用来标识与hinstance相关的应用程序的前一个句柄。

  第三个参数是lpcmdline,是指向应用程序命令行参数字符串的指针。如在win95的“开始”菜单中单击“运行”,输入“easywin hello”,则此参数指向的字符串为“hello”。

  最后一个参数是ncmdshow,是一个用来指定窗口显示方式的整数。这个整数值可以是sw_show、sw_hide、sw_showmaximized、sw_showminimized等,关于这些值的含义,将在下一节说明。

  注册窗口类

  一个应用程序可以有许多窗口,但只有一个是主窗口,它是与该应用程序的实例句柄唯一关联的。上面的例程中,创建主窗口的函数是initwindow()。

  通常要对填充一个窗口类结构wndclass,然后调用registerclass()对该窗口类进行注册。每个窗口都有一些基本的属性,如窗口边框、窗口标题栏文字、窗口大小和位置、鼠标、背景色、处理窗口消息函数的名称等等。注册的过程也就是将这些属性告诉系统,然后再调用createwindow()函数创建出窗口。这也就象你去裁缝店订做一件衣服,先要告诉店老板你的身材尺寸、布料颜色、以及你想要的款式,然后他才能为你做出一件让你满意的衣服。

  在vc的帮助中,可以看到wndclass结构是这样定义的:

  在win95和winnt的具有新界面特性的系统中,为了支持新的窗口界面特性,还有一种扩展的窗口类型wndclassex,它的定义如下:

  wndclass和wndclassex这两个结构基本上是一致的,只是wndclassex结构中多了cbsize和hiconsm这两个成员。wndclass结构的各成员中,其注释后打了星号的表示该项应特别注意。

  wndclass结构的第一个成员style表示窗口类的风格,它往往是由一些基本的风格通过位的“或”操作(操作符位“|”)组合而成。下表列出了一些常用的基本窗口风格:

  在easywin应用程序中,是按如下方式对wndclass结构进行填充和注册的:

  可以看到,wc.style被设为cs_vredraw | cs_hredraw,表示只要窗口的高度或宽度发生变化,都会重画整个窗口。

  第二个成员lpfnwndproc的值为(wndproc)winproc。表明该窗口类的消息处理函数是winproc()函数。这里,要指定窗口的消息处理函数的远指针,输入消息处理函数的函数名称即可,必要时应该进行强制类型转换,将其转换成wndproc型。

  接下来的cbclsextra和wc.cbwndextra在大多数情况下都会设为0。

  然后的hinstance成员,给它的值是由winmain()传来的应用程序的实例句柄,表明该窗口与该实例是相关联的。事实上,只要是注册窗口类,该成员的值始终是该程序的实例句柄,你应该象背书一样记住它。

  下面的hicon,是让你给这个窗口指定一个图标,调用 loadicon( hinstance, idi_application ),可以调用系统内部预先定义好的标志符为idc_application的图标作为该窗口的图标。

  同样,调用loadcursor( null, idc_arrow )为该窗口调用系统内部预先定义好的箭头型鼠标。

  hbrbackground成员用来定义窗口的背景画刷颜色,也就是该窗口的背景色。调用getstockobject(white_brush)可以获得系统内部预先定义好的白色画刷作为窗口的背景色。

  上面的loadicon()、loadcursor()、getstockobject()都是windows的api函数,它们的用法可以参看vc的帮助,这里就不多介绍了。

  lpszmenuname成员的值我们给它null,表示该窗口将没有菜单。如果你想让你的窗口拥有菜单,就把lpszmenuname成员赋值为标志菜单资源的字符串。

  wndclass结构的最后一个成员lpszclassname是让你给这个窗口类起一个唯一的名称,因为windows操作系统中有许许多多的窗口类,必须用一个独一无二的名称来代表它们。通常,你可以用你的程序名来命名这个窗口类的名称。这个名称将在创建窗口的createwindow()函数中用到。

  填充完毕后,对于wndclass结构,调用registerclass()函数进行注册;对于wndclassex结构,调用registerclassex()函数进行注册,它们的原型分别如下:

  该函数如调用成功,则返回一个非0值,表明系统中已经注册了一个名为easywin的窗口类。如果失败,则返回0。

  创建窗口

  当窗口类注册完毕之后,并不会有窗口显示出来,因为注册的过程仅仅是为创建窗口所做的准备工作。实际创建一个窗口的是通过调用createwindow()函数完成的。窗口类中已经预先定义了窗口的一般属性,而createwindow()中的参数可以进一步指定一个窗口的更具体的属性,在easywin程序中,是如下调用createwindow()函数来创建窗口的:

  createwindow()函数的参数的含义在上面的注释中已有介绍,注释后打了星号标记的参数应该着重注意,其它的参数都很简单,不多做介绍,可参看vc的帮助。

  第一个参数是创建该窗口所使用的窗口类的名称,注意这个名称应与前面所注册的窗口类的名称一致。

  第三个参数为创建的窗口的风格,下表列出了常用的窗口风格:

  程序中使用了ws_overlappedwindow标志,它是创建一个普通窗口常用的标志。而在directx编程中,我们常用的是ws_popup,用这个标志创建的窗口没有标题栏和系统菜单,如果设定窗口为最大化,客户区可以占满整个屏幕,以满足directx编程的需要。

  createwindow()函数后面的参数中,仍用到了该应用程序的实例句柄hinstance。

  如果窗口创建成功,返回值是新窗口的句柄,否则返回null。

  显示和更新窗口

  窗口创建后,并不会在屏幕上显示出来,要真正把窗口显示在屏幕上,还得使用showwindow()函数,其原型如下:

  参数hwnd指定要显示得窗口的句柄,ncmdshow表示窗口的显示方式,这里指定为从winmain()函数的ncmdshow所传递而来的值。

  由于showwindow()函数的执行优先级不高,所以当系统正忙着执行其它的任务时,窗口不会立即显示出来,此时,调用updatewindow()函数以可以立即显示窗口。其函数原型如下:

  消息循环

  在win32编程中,消息循环是相当重要的一个概念,看似很难,但是使用起来却是非常简单。在winmain()函数中,调用initwindow()函数成功的创建了应用程序主窗口之后,就要启动消息循环,其代码如下:

  windows应用程序可以接收以各种形式输入的信息,这包括键盘、鼠标动作 、记时器产生的消息,也可以是其它应用程序发来的消息等等。windows系统自动监控所有的输入设备,并将其消息放入该应用程序的消息队列中。

  getmessage()函数则是用来从应用程序的消息队列中按照先进先出的原则将这些消息一个个的取出来,放进一个msg结构中去。getmessage()函数原型如下:

  getmessage()将获取的消息复制到一个msg结构中。如果队列中没有任何消息,getmessage()函数将一直空闲直到队列中又有消息时再返回。如果队列中已有消息,它将取出一个后返回。msg结构包含了一条windows消息的完整信息,其定义如下:

  该结构中的主消息表明了消息的类型,例如是键盘消息还是鼠标消息等,副消息的含义则依赖于主消息值,例如:如果主消息是键盘消息,那么副消息中则存储了是键盘的哪个具体键的信息。

  getmessage()函数还可以过滤消息,它的第二个参数是用来指定从哪个窗口的消息队列中获取消息,其它窗口的消息将被过滤掉。如果该参数为null,则getmessage()从该应用程序线程的所有窗口的消息队列中获取消息。

  第三个和第四个参数是用来过滤msg结构中主消息值的,主消息值在wmsgfiltermin和wmsgfiltermax之外的消息将被过滤掉。如果这两个参数为0,则表示接收所有消息。

  当且仅当getmessage()函数在获取到wm_quit消息后,将返回0值,于是程序退出消息循环。

  translatemessage()函数的作用是把虚拟键消息转换到字符消息,以满足键盘输入的需要。dispatchmessage()函数所完成的工作是把当前的消息发送到对应的窗口过程中去。

  开启消息循环其实是很简单的一个步骤,几乎所有的程序都是按照easywin的这个方法。你完全不必去深究这些函数的作用,只是简单的照抄就可以了。 
 

  消息处理函数

  消息处理函数又叫窗口过程,在这个函数中,不同的消息将用switch语句分配到不同的处理程序中去。windows的消息处理函数都有一个确定的样式,即这种函数的参数个数和类型以及其返回值的类型都有明确的规定。在vc的说明书中,消息处理函数的原型是这样定义的:

  如果你的程序中还有其它的消息处理函数,也都必须按照上面的这个样式来定义,但函数名称可以随便取。easywin中的winproc()函数就是这样一个典型的消息处理函数。

  消息处理函数的四个参数是由getmessage()函数从消息队列中获得msg结构,然后分解后得到的。第二个参数umsg和msg结构中的message值是一致的,代表了主消息值。程序中用switch语句来将不同类型的消息分配到不同的处理程序中去。

  winproc()函数明确的处理了4个消息,分别是wm_keydown(击键消息)、wm_rbuttondown(鼠标右键按下消息)、wm_paint(窗口重画消息)、wm_destroy(销毁窗口消息)。

  值得注意的是,应用程序发送到窗口的消息远远不止以上这几条,象wm_size、wm_minimize、wm_create、wm_move等这样频频使用的消息就有几十条。为了减轻编程的负担,windows的api提供了defwindowproc()函数来处理这些最常用的消息,调用了这个函数后,这些消息将按照系统默认的方式得到处理。

  因此,在switch_case语句中,只须明确的处理那些有必要进行特别响应的消息,把其余的消息交给defwindowproc()函数来处理,是一种明智的选择,也是你必须做的一件事。 
 
  结束消息循环

  当用户按alt+f4或单击窗口右上角的退出按钮,系统就向应用程序发送一条wm_destroy的消息。在处理此消息时,调用了postquitmessage()函数,该函数会给窗口的消息队列中发送一条wm_quit的消息。在消息循环中,getmessage()函数一旦检索到这条消息,就会返回false,从而结束消息循环,随后,程序也结束。

  小结

  本章介绍的是win32编程的基础知识,在进行directx编程之前,掌握它们是十分必要的。

  通过本文的学习,你应该学到以下知识:

   如何创建一个win32应用程序工程

   用registerclass()函数注册一个窗口类,再立即调用createwindow()函数创建一个窗口的实例

   设置窗口的类型以及将一个消息处理函数与窗口联系上

   用一固定的模式开启消息循环

   了解消息处理函数的定义规则,如何自己定义一个窗口消息处理函数

   在消息处理函数中,最后必须调用defwindowproc()函数以处理那些缺省的消息

   调用postquitmessage()函数以结束消息循环

win32创建窗口及其消息循环相关推荐

  1. 2.创建适合游戏的窗口和消息循环

    2.创建适合游戏的窗口和消息循环 本章前言: 创建游戏窗口和处理消息循环是很重要的事情,我尝试过几种不同的窗口处理方式,这次打算使用WS_POPUP样式的窗口(无边框).上一次的框架代码把创建窗口和消 ...

  2. C#中发送消息给指定的窗口到消息循环

    public class Note     {         //声明 API 函数         [DllImport("User32.dll", EntryPoint =  ...

  3. QT源码解析(一) QT创建窗口程序、消息循环和WinMain函数

    版权声明 请尊重原创作品.转载请保持文章完整性,并以超链接形式注明原始作者"tingsking18"和主站点地址,方便其他朋友提问和指正. QT源码解析(一) QT创建窗口程序.消 ...

  4. win32程序窗口的创建

    尽管Windows应用程序千变万化,令人眼花缭乱,但,消息机制和窗口过程却始终它们的基础,掌握了这两项技术,也就相当于把握住了问题的关键. 如果你以前是C程序员或是MFC的忠实用户,只要你学习过C语言 ...

  5. win32消息处理和创建窗口

    文章目录 0.说明 1.win32事件和消息 2.系统处理消息 3.win32创建窗口和消息处理 4.源代码 0.说明 观看滴水逆向视频总结(部分截图来自于滴水课件) 编译器:vc++6.0 编写语言 ...

  6. 【转】为控制台窗口建立消息队列

    介绍Windows的窗口.消息.子类化和超类化 这篇文章本来只是想介绍一下子类化和超类化这两个比较"生僻"的名词.为了叙述的完整性而讨论了Windows的窗口和消息,也简要讨论了进 ...

  7. win32汇编-窗口 对话框 (三)

    创建窗口 .386.model flat,stdcalloption casemap:none;>>>>>>>>>>>>> ...

  8. TApplication与主消息循环

    Windows应用程序的每一个窗口都有一个大的消息循环以及一个窗口函数(WndProc)用以分发和处理消息.VCL作为一个Framework,当然会将这些东西隐藏起来,而重新提供一种易用的.易理解的虚 ...

  9. 用python简易编写创建窗口

    一.创建Window窗口 # 创建Window窗口.py import tkinterroot=tkinter.Tk() # 创建Window窗口对象 # 窗口属性 root.geometry('30 ...

最新文章

  1. java和python哪个好就业2020-Python和java哪个就业前景好些?
  2. 渲染管道(1)整体流程
  3. Spring Cloud(二) 配置Eureka Client
  4. 如何用极致业务基础平台做一个通用企业ERP系列之二一览表培训
  5. Struct2中三种获取表单数据的方式
  6. Codeforces Round #636 (Div. 3) E. Weights Distributing 思维 + bfs
  7. c语言明解课后答案,明解C语言 中级篇 第一章答案
  8. python多次输入数据_如何用python3输出重复的数据?
  9. python ios 坐标点击_python点击鼠标获取坐标(Graphics)
  10. python filter函数 字符串_Python实现filter函数实现字符串切分
  11. 老板下死命令:必须将20M文件从30秒压缩到1秒,我是如何做到的?
  12. 小红书推广效果怎么样?如何进行小红书推广?
  13. 微型计算机中的数据总线用来进行什么的传输,汽检2011级汽车车载网络复习题答案...
  14. 微信小程序引入外部icon(阿里巴巴矢量图标)
  15. c语言 simon 游戏,终于发现少儿英语游戏SimonSays(西蒙说)
  16. 超实用后台UI模板有这些就够了!(一)
  17. logrotate 详解
  18. Android基础篇(四)
  19. ffmpeg sws_scale详细分析
  20. 网络层IP协议和数据链路层

热门文章

  1. ‘adb‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件—解决方法
  2. Android新浪微博实训报告,新浪微博产品简析
  3. 在线文本替换工具 、支持正则表达式(博客园文章里添加Javascript或<script>语句)
  4. js对象数组去除重复
  5. ios11非越狱如何安装ipa
  6. 【数据结构】栈的简单理解以及对栈的基本操作
  7. CF1364D Ehab‘s Last Corollary(思维,环,二分图,构造)
  8. win10 音频设备图形隔离 占用CPU
  9. linux 终端命令行的快捷键列表
  10. <转载>主机ping虚拟机ip请求超时解决办法