zoukankan      html  css  js  c++  java
  • 《Windows程序设计》第三章 窗口和消息


    总体结构
    所谓「Windows给程序发送消息」,是指Windows呼叫程序中的一个函数,该函数的参数描述了这个特定消息。这种位于Windows程序中的函数称为「窗口消息处理程序」。程序建立的每一个窗口都有相关的窗口消息处理程序。这个窗口消息处理程序是一个函数,既可以在程序中,也可以在动态链接库中。Windows通过呼叫窗口消息处理程序来给窗口发送消息。窗口消息处理程序根据此消息进行处理,然后将控制传回给Windows。

    HELLOWIN.C源代码

    #include <windows.h>

    LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;

    int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
         PSTR szCmdLine, int iCmdShow)
    {
         static TCHAR szAppName[] = TEXT ("HelloWin") ;
         HWND hwnd ;
         MSG msg ;
         WNDCLASS wndclass ;
         wndclass.style = CS_HREDRAW | CS_VREDRAW ;
         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 = szAppName ;
         if (!RegisterClass (&wndclass))
         {
              MessageBox ( NULL, TEXT ("This program requires Windows NT!"),
                   szAppName, MB_ICONERROR) ;
              return 0 ;
         }
         hwnd = CreateWindow( szAppName, // window class name
                                                 TEXT ("The Hello Program"), // window caption
                                                 WS_OVERLAPPEDWINDOW, // window style
                                                 CW_USEDEFAULT, // initial x position
                                                 CW_USEDEFAULT, // initial y position
                                                 CW_USEDEFAULT, // initial x size
                                                 CW_USEDEFAULT, // initial y size
                                                 NULL, // parent window handle
                                                 NULL, // window menu handle
                                                 hInstance, // program instance handle
                                                 NULL) ; // creation parameters
          ShowWindow (hwnd, iCmdShow) ;
          UpdateWindow (hwnd) ;
         while (GetMessage (&msg, NULL, 0, 0))
         {
              TranslateMessage (&msg) ;
              DispatchMessage (&msg) ;
         }
         return msg.wParam ;
    }

    LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
    {
         HDC hdc ;
         PAINTSTRUCT ps ;
         RECT rect ;
         switch (message)
         {
              case WM_CREATE:
                   PlaySound (TEXT ("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC) ;
                   return 0 ;
              case WM_PAINT:
                   hdc = BeginPaint (hwnd, &ps) ;
                   GetClientRect (hwnd, &rect) ;
                   DrawText (hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
                        DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
                   EndPaint (hwnd, &ps) ;
                   return 0 ;
              case WM_DESTROY:
                   PostQuitMessage (0) ;
                   return 0 ;
         }
         return DefWindowProc (hwnd, message, wParam, lParam) ;
    }

    全盘考量

    1. Windows函数调用
    • GetStockObject 取得一个图形对象(在这个例子中,是取得绘制窗口背景的画刷对象)。 
    • RegisterClass 为程序窗口注册窗口类别。 
    • CreateWindow 根据窗口类别建立一个窗口。 
    • ShowWindow 在屏幕上显示窗口。 
    • UpdateWindow 指示窗口自我更新。 
    • GetMessage 从消息队列中取得消息。 
    • TranslateMessage 转译某些键盘消息。 
    • DispatchMessage 将消息发送给窗口消息处理程序。  
    • BeginPaint 开始绘制窗口。 
    • GetClientRect 取得窗口显示区域的大小。 
    • DrawText 显示字符串。 
    • EndPaint 结束绘制窗口。 
    • PostQuitMessage 在消息队列中插入一个「退出程序」消息。 
    • DefWindowProc 执行内定的消息处理。
    2. 大写字母标识符

    这些识别字是在Windows表头档案中定义的。有些识别字含有两个字母或者三个字母的字首,
    这些字首後头接著一个底线。

    CS_HREDRAW, CS_VERDRAW:CS-窗口类别样式
    CW_USEDEFAULT:CW-建立窗口
    DT_CENTER, DT_SINGLELINE, DT_VCENTER:DT-绘制文字
    IDC_ARROW:IDC-游标ID
    IDI_APPLICATION:IDI-图示ID
    MB_ICONERROR:MB-消息框
    SND_ASYNC, SND_FILENAME:SND-声音
    WM_CREATE, WM_DESTORY, WM_PAINT:WM-窗口消息
    WS_OVERLAPPEDWINDOW:WS-窗口样式

    3. 新的数据形态
    有时这些新的数据型态只是为了方便缩写。例如,用于WndProc的第二个参数的UINT数据型态只是一个unsigned int (无正负号整数),在Windows 98中,这是一个32位的值。用于WinMain的第三个参数的PSTR数据型态是指向一个字符串的指针,即是一个char *。

    WndProc函数传回一个型态为LRESULT的值,该值简单地被定义为一个LONG。WinMain函数被指定了一个WINAPI型态(在表头文件中定义的所有Windows函数都被指定这种型态),而WndProc函数被指定一个CALLBACK型态。这两个标识符都被定义为_stdcall,表示在Windows本身和使用者的应用程序之间发生的函数呼叫的呼叫参数传递方式。

    HELLOWIN还使用了Windows表头文件中定义的四种数据结构:MSG-消息结构、WNDCLASS-窗口类别结构、PAINTSTRUCT-绘图结构、RECT-矩形结构。
    4. 句柄
    最后,还有三个大写标识符,用于不同型态的「句柄」:HINSTANCE-执行实体(程序自身)句柄、HWND-窗口句柄、HDC-设备内容句柄。
    句柄在Windows中使用非常频繁。句柄是一个(通常为32位的)整数,它代表一个对象。Windows中的句柄类似传统C或者MS-DOS程序设计中使用的文件句柄。程序几乎总是通过呼叫Windows函数取得句柄。程序在其它Windows函数中使用这个句柄,以使用它代表的对象。代号的实际值对程序来说是无关紧要的。但是,向您的程序提供代号的Windows模块知道如何利用它来使用相对应的对象。
    5. 匈牙利表示法
    许多Windows程序写作者使用一种叫做「匈牙利表示法」的变量命名通则。这是为了纪念传奇性的Microsoft程序写作者Charles Simonyi。非常简单,变量名以一个或者多个小写字母开始,这些字母表示变量的数据型态。例如,szCmdLine中的sz代表「以0结尾的字符串」。在hInstance和hPrevInstance中的h前缀表示「句柄」;在iCmdShow中的i前缀表示「整数」。
    在命名结构变量时,可以用结构名(或者结构名的一种缩写)的小写作为变量名的前缀,或者用作整个变量名。例如,在HELLOWIN. C的WinMain函数中,msg变量是MSG型态的结构;wndclass是WNDCLASSEX型态的一个结构。
    由于变量名既描述了变量的作用,又描述了其数据型态,就比较容易避免产生数据型态不合的错误。
    6. 注册窗口类别
    窗口依照某一窗口类别建立,窗口类别用以标识处理窗口消息的窗口消息处理程序。在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向型态为WNDCLASS的结构指针。
    WINUSER.H定义了WNDCLASSA和WNDCLASSW结构(以及指向结构的指针)以后,表头文件依据对UNICODE标识符的解释,定义了WNDCLASS和指向WNDCLASS的指标。
    在WNDCLASS结构中最重要的两个字段是第二个和最后一个,第二个字段(lpfnWndProc) 是依据这个类别来建立的所有窗口所使用的窗口消息处理程序的地址。在HELLOWIN.C中,这个是WndProc函数。最后一个字段是窗口类别的文字名称。程序写作者可以随意定义其名称。在只建立一个窗口的程序中,窗口类别名称通常设定为程序名称。
    如果用定义的UNICODE标识符编译了程序,程序将呼叫RegisterClassW。该程序可以在Microsoft Windows NT中执行良好。但如果此程序在Windows 98上执行,RegisterClassW函数并未真地被执行到。函数有一个进入点,但函数呼叫后只传回0,表明错误。
    GetLastError函数会帮助您确定在这样的情况下产生错误的原因。GetLastError是Windows中常用的函数,它可以在函数呼叫失败时获得更多错误信息。在Windows 98中呼叫RegisterClassW时,GetLastError将传回120。在WINERROR.H中您可以看到,值120与标识符ERROR_CALL_NOT_IMPLEMENTED相等。
    最后,一个老经验是:
         if (!hPrevInstance)
         {
              create and init wndclass;
              RegisterClass(&wndclass);
         }
    这是出于「旧习难改」的原因。在16位的Windows中,如果您启动正在执行的程序的一个新执行实体,WinMain的hPrevInstance参数将是前一个执行实体的执行实体句柄。为节省内存,两个或多个执行实体就可能会共享相同的窗口类别。在32位的Windows中,hPrevInstance总是NULL。此程序代码会正常执行,而实际上也没必要检查hPrevInstance。
    7. 建立窗口
    Windows程序设计新手有时会混淆窗口类别和窗口之间的区别,以及为什么一个窗口的所有特征不能被一次设定好。由窗口类别来负责处理按钮的键盘和鼠标输入,并定义按钮在屏幕上的外观形象。从这一点看来,所有的按钮都是以同样的方式工作的。但是并非所有的按钮都是一样的。它们可以有不同的大小,不同的屏幕位置,以及不同的字符串。后面的这样一些特征是窗口定义的一部分,而不是窗口类别定义的。传递给RegisterClass函数的信息会在一个数据结构中设定好,而传递给CreateWindow函数的信息会在函数单独的参数中设定好。
    CreateWindow传回被建立的窗口的句柄,该句柄存放在变量hwnd中,后者被定义为HWND型态(「窗口句柄型态」)。Windows中的每个窗口都有一个句柄,程序用句柄来使用窗口。许多Windows函数需要使用hwnd作为参数,这样,Windows才能知道函数是针对哪个窗口的。如果一个程序建立了许多窗口,则每个窗口均有一个句柄。窗口句柄是Windows程序所处理最重要的句柄之一。
    8. 显示窗口
    在CreateWindow呼叫传回之后,Windows内部已经建立了这个窗口。这就是说,Windows已经配置了一块内存,用来保存在CreateWindow呼叫中指定窗口的全部信息跟一些其它信息,而Windows稍后就是依据窗口句柄找到这些信息的。
         ShowWindow(hwnd, iCmdShow):第一个参数是刚刚用CreateWindow建立的窗口句柄。第二个参数是作为参数传给WinMain的iCmdShow。它确定最初如何在屏幕上显示窗口,是一般大小、最小化还是最大化。
         UpdateWindow(hwnd):重画显示区域。它经由发送给窗口消息处理程序(即HELLOWIN.C中的WndProc函数)一个WM_PAINT消息做到这一点。
    9. 消息循环
    呼叫UpdateWindow之后,窗口就出现在视讯显示器上。程序现在必须准备读入使用者用键盘和鼠标输入的数据。Windows为当前执行的每个Windows程序维护一个「消息队列」。在发生输入事件之后,Windows将事件转换为一个「消息」并将消息放入程序的消息队列中。程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:
    while (GetMessage(&msg, NULL, 0, 0))
    {
         TranslateMessage(&msg);
         DispatchMessage(&msg);
    }
         GetMessage(&msg, NULL, 0, 0):传给Windows一个指标,指向名为msg的MSG结构。第二、第三和第四个参数设定为NULL或者0,表示程序接收它自己建立的所有窗口的所有消息。只要从消息队列中取出消息的message字段不为WM_QUIT(其值为0x0012),GetMessage就传回一个非零值。
         TranslateMessage(&msg):将msg结构传给Windows,进行一些键盘转换。(关于这一点将在第六章中深入讨论。)
         DispatchMessage(&msg):又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。处理完消息之后,WndProc传回到Windows。此时,Windows还停留在DispatchMessage呼叫中。在结束DispatchMessage呼叫的处理之后,Windows回到HELLOWIN,并且接着从下一个GetMessage呼叫开始消息循环。
    10. 窗口消息处理程序
    一个Windows程序可以包含多个窗口消息处理程序。一个窗口消息处理程序总是与呼叫RegisterClass注册的特定窗口类别相关联。CreateWindow函数根据特定窗口类别建立一个窗口。但依据一个窗口类别,可以建立多个窗口。
         LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM)
    窗口消息处理程序的四个参数与MSG结构的前四个字段是相同的。第一个参数hwnd是接收消息的窗口的句柄,它与CreateWindow函数的传回值相同。第二个参数与MSG结构中的message字段相同,它是标识消息的数值。最后两个参数都是32位的消息参数,提供关于消息的更多信息。
    程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。
    窗口消息处理程序在处理消息时,必须传回0。窗口消息处理程序不予处理的所有消息应该被传给名为DefWindowProc的Windows函数。
    WM_CREATE消息
         当Windows在WinMain中处理CreateWindow函数时,WndProc接收这个消息。就是说,在HELLOWIN呼叫CreateWindow时,Windows呼叫WndProc,将第一个参数设定为窗口句柄,第二个参数设定为WM_CREATE。WndProc处理WM_CREATE消息并将控制传回给Windows。 Windows然后可以从CreateWindow呼叫中传回到HELLOWIN中,继续在WinMain中进行下一步的处理。
    WM_PAINT消息
    当窗口显示区域的一部分显示内容或者全部变为「无效」,以致于必须「更新画面」时,将由这个消息通知程序。
    窗口无效的情况:
         在最初建立窗口的时候,整个显示区域都是无效的,因为程序还没有在窗口上画什么东西。
         在使用者改变HELLOWIN窗口的大小后,显示区域的显示内容重新变得无效。HELLOWIN中wndclass结构的style字段设定为标志CS_HREDRAW和CS_VREDRAW,这样的格式设定指示Windows,在窗口大小改变后,就把整个窗口显示内容当成无效。
         当使用者将HELLOWIN最小化,然后再次将窗口恢复为以前的大小时,Windows将不会保存显示区域的内容。在图形环境下,窗口显示区域涉及的数据量很大。因此,Windows令窗口无效。
         在移动窗口以致其相互重迭时,Windows不保存一个窗口中被另一个窗口所遮盖的内容。在这一部分不再被遮盖之后,它就被标志为无效。
    对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始,而以一个EndPaint呼叫结束。
    在BeginPaint呼叫中,如果显示区域的背景还未被删除,则由Windows来删除。它使用注册窗口类别的WNDCLASS结构的hbrBackground字段中指定的画刷来删除背景。BeginPaint呼叫令整个显示区域有效,并传回一个「设备内容句柄」。设备内容是指实体输出设备(如视讯显示器)及其设备驱动程序。
    WM_DESTROY消息
    这一个消息指示,Windows正在根据使用者的指示关闭窗口。该消息是使用者单击Close按钮或者在程序的系统菜单上选择 Close时发生的。PostQuitMessage(0)在程序的消息队列中插入一个WM_QUIT消息。前面提到过,GetMessage对于除了WM_QUIT之外的从消息队列中取出的所有消息都传回非0值。而当GetMessage得到一个WM_QUIT消息时,它传回0。这将导致WinMain退出消息循环,并终止程序。
    Windows程序设计的难点
    Windows程序所作的一切,都是响应发送给窗口消息处理程序的消息。这是概念上的主要难点之一,在开始写作Windows程序之前,必须先搞清楚。实际上,从程序外呼叫程序内的例程这一种做法,在传统的程序设计中并非前所未闻。C中的signal函数可以拦截Ctrl-C中断或操作系统的其它中断。为MS-DOS编写的老程序中经常有拦截硬件中断的程序代码。但在Windows中,这种概念扩展为包括一切事件。窗口中发生的一切都以消息的形式传给窗口消息处理程序。
    消息能够被分为「队列化的」和「非队列化的」。队列化的消息是由Windows放入程序消息队列中的。在程序的消息循环中,重新传回并分配给窗口消息处理程序。非队列化的消息在Windows呼叫窗口时直接送给窗口消息处理程序。任何情况下,窗口消息处理程序都将获得窗口所有的消息--包括队列化的和非队列化的。
    队列化消息基本上是使用者输入的结果,以击键(如WM_KEYDOWN和WM_KEYUP消息)、击键产生的字符(WM_CHAR)、鼠标移动(WM_MOUSEMOVE)和鼠标按钮(WM_LBUTTONDOWN)的形式给出。队列化消息还包含时钟消息(WM_TIMER)、更新消息(WM_PAINT)和退出消息(WM_QUIT)。
    非队列化消息则是其它消息。在许多情况下,非队列化消息来自呼叫特定的Windows函数。例如,当WinMain呼叫CreateWindow时,Windows将建立窗口并在处理中给窗口消息处理程序发送一个WM_CREATE消息。当WinMain呼叫ShowWindow时,Windows将给窗口消息处理程序发送WM_SIZE和WM_SHOWWINDOW消息。当WinMain呼叫UpdateWindow时,Windows将给窗口消息处理程序发送WM_PAINT消息。

     

  • 相关阅读:
    通过SSH隧道的本地转发实现Django连接远程数据库
    Mysql多数据库备份
    apollo 项目配置中心开源框架部署
    nginx 配置多个 https 域名访问
    beetl 模板语法
    异常:由 spring-session pom 引发
    异常:NoSuchFieldError: BEST_MATCHING_HANDLER_ATTRIBUTE
    【Redis__验证】手机验证和限制登录功能
    【Linux__环境】配置
    【Layui__上传】多图上传
  • 原文地址:https://www.cnblogs.com/xiaomaohai/p/6157919.html
Copyright © 2011-2022 走看看