The Windows Programming Model-----messages

原创 2001年05月22日 04:31:00
 

Messages, Messages, and More Messages

Where do messages come from, and what kinds of information do they convey? Windows defines hundreds of different message types. Most messages have names that begin with the letters "WM" and an underscore, as in WM_CREATE and WM_PAINT. These messages can be classified in various ways, but for the moment classification is not nearly as important as realizing the critical role messages play in the operation of an application. The following table shows 10 of the most common messages. A window receives a WM_PAINT message, for example, when its interior needs repainting. One way to characterize a Windows program is to think of it as a collection of message handlers. To a large extent, it is a program's unique way of responding to messages that gives it its personality.

Common Windows Messages

Message Sent When
WM_CHAR A character is input from the keyboard.
WM_COMMAND The user selects an item from a menu, or a control sends a notification to its parent.
WM_CREATE A window is created.
WM_DESTROY A window is destroyed.
WM_LBUTTONDOWN The left mouse button is pressed.
WM_LBUTTONUP The left mouse button is released.
WM_MOUSEMOVE The mouse pointer is moved.
WM_PAINT A window needs repainting.
WM_QUIT The application is about to terminate.
WM_SIZE A window is resized.

A message manifests itself in the form of a call to a window's window procedure. Bundled with the call are four input parameters: the handle of the window to which the message is directed, a message ID, and two 32-bit parameters known as wParam and lParam. The window handle is a 32-bit value that uniquely identifies a window. Internally, the value references a data structure in which Windows stores relevant information about the window such as its size, style, and location on the screen. The message ID is a numeric value that identifies the message type: WM_CREATE, WM_PAINT, and so on. wParam and lParam contain information specific to the message type. When a WM_LBUTTONDOWN message arrives, for example, wParam holds a series of bit flags identifying the state of the Ctrl and Shift keys and of the mouse buttons. lParam holds two 16-bit values identifying the location of the mouse pointer when the click occurred. Together, these parameters provide the window procedure with all the information it needs to process the WM_LBUTTONDOWN message.

Windows Programming, SDK-Style

If you haven't programmed Windows in C before, it's instructive to see what the source code for a simple program looks like. The program listed in Figure 1-2 creates a window and responds to WM_PAINT messages by drawing an ellipse in the window's upper left corner. This code is similar to the source code you'll find in books such as Charles Petzold's Programming Windows (1998, Microsoft Press) and other books that teach Windows programming in C.

Figure 1-2. C source code for a simple Windows program.

#include <windows.h>

LONG WINAPI WndProc (HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
    LPSTR lpszCmdLine, int nCmdShow)
{
    WNDCLASS wc;
    HWND hwnd;
    MSG msg;

    wc.style = 0;                                   // Class style
    wc.lpfnWndProc = (WNDPROC) WndProc;             // Window procedure address
    wc.cbClsExtra = 0;                              // Class extra bytes
    wc.cbWndExtra = 0;                              // Window extra bytes
    wc.hInstance = hInstance;                       // Instance handle
    wc.hIcon = LoadIcon (NULL, IDI_WINLOGO);        // Icon handle
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);      // Cursor handle
    wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); // Background color
    wc.lpszMenuName = NULL;                         // Menu name
    wc.lpszClassName = "MyWndClass";                // WNDCLASS name

    RegisterClass (&wc);
	

    hwnd = CreateWindow (
        "MyWndClass",               // WNDCLASS name
        "SDK Application",          // Window title
        WS_OVERLAPPEDWINDOW,        // Window style
        CW_USEDEFAULT,              // Horizontal position
        CW_USEDEFAULT,              // Vertical position        
        CW_USEDEFAULT,              // Initial width
        CW_USEDEFAULT,              // Initial height
        HWND_DESKTOP,               // Handle of parent window
        NULL,                       // Menu handle
        hInstance,                  // Application's instance handle
        NULL                        // Window-creation data
    );

    ShowWindow (hwnd, nCmdShow);
    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)
{
    PAINTSTRUCT ps;
    HDC hdc;

    switch (message) {
    
    case WM_PAINT:
        hdc = BeginPaint (hwnd, &ps);
        Ellipse (hdc, 0, 0, 200, 100);
        EndPaint (hwnd, &ps);
        return 0;

    case WM_DESTROY:
        PostQuitMessage (0);
        return 0;
    }
    return DefWindowProc (hwnd, message, wParam, lParam);
}

WinMain begins by calling the API function RegisterClass to register a window class. The window class defines important characteristics of a window such as its window procedure address, its default background color, and its icon. These and other properties are defined by filling in the fields of a WNDCLASS structure, which is subsequently passed to RegisterClass. An application must specify a window class when it creates a window, and a class must be registered before it can be used. That's why RegisterClass is called at the outset of the program. Keep in mind that a WNDCLASS-type window class is not the same as a C++ window class. To avoid confusion, I'll use the term WNDCLASS throughout this book to refer to classes registered with RegisterClass. The term window class will refer to C++ classes derived from MFC's CWnd class.

Once the WNDCLASS is registered, WinMain calls the all-important CreateWindow function to create the application's window. The first parameter to CreateWindow is the name of the WNDCLASS from which the window will be created. The second parameter is the text that will appear in the window's title bar. The third specifies the window style. WS_OVERLAPPEDWINDOW is a commonly used style that creates a top-level window with a resizing border, a title bar, a system menu, and buttons for minimizing, maximizing, and closing the window.

The next four parameters specify the window's initial position and size. CW_USEDEFAULT tells Windows to use default values for both. The final four parameters specify, in order, the handle of the window's parent window (HWND_DESKTOP for an application's main window); the handle of the menu associated with the window, if any; the application's instance handle (a value that lets the programmer differentiate between the program itself and the modules—that is, DLLs—that it loads); and a pointer to application-specific window-creation data. I could easily devote a section of this book to CreateWindow and its parameters, but as you'll see later, MFC hides much of this detail inside the class library. A typical MFC application doesn't have a WinMain function (at least not one you can see), and it doesn't call RegisterClass or CreateWindow.

The window that CreateWindow creates is not initially visible on the screen because it was not created with the WS_VISIBLE style. (Had it been used, WS_VISIBLE would have been combined with WS_OVERLAPPEDWINDOW in the call to CreateWindow.) Therefore, WinMain follows CreateWindow with calls to ShowWindow and UpdateWindow, which make the window visible and ensure that its WM_PAINT handler is called immediately.

Next comes the message loop. In order to retrieve and dispatch messages, WinMain executes a simple while loop that calls the GetMessage, TranslateMessage, and DispatchMessage API functions repeatedly. GetMessage checks the message queue. If a message is available, it is removed from the queue and copied to msg; otherwise, GetMessage blocks on the empty message queue until a message is available. msg is an instance of the structure MSG, whose fields contain pertinent message parameters such as the message ID and the time at which the message was placed in the queue. TranslateMessage converts a keyboard message denoting a character key to an easier-to-use WM_CHAR message, and DispatchMessage dispatches the message to the window procedure. The message loop executes until GetMessage returns 0, which happens only when a WM_QUIT message is retrieved from the message queue. When this occurs, WinMain ends and the program terminates.

Messages dispatched with DispatchMessage generate calls to the window procedure WndProc. The sample program in Figure 1-2 processes just two message types, WM_PAINT and WM_DESTROY; all other messages are passed to DefWindowProc for default processing. A switch-case block inspects the message ID passed in the message parameter and executes the appropriate message handler. The WM_PAINT handler calls the BeginPaint API function to obtain a device context handle before painting begins and the EndPaint API function to release the handle when painting is finished. In between, the Ellipse API function draws an ellipse that is 200 pixels wide and 100 pixels high. A device context handle is the "magic cookie" that permits a Windows application to draw on the screen. Without it, functions such as Ellipse won't work.

The WM_DESTROY handler calls the PostQuitMessage API function to post a WM_QUIT message to the message queue and ultimately cause the program to terminate. The WM_DESTROY message is sent to a window just before it is destroyed. A top-level window must call PostQuitMessage when it receives a WM_DESTROY message, or else the message loop will not fall through and the program will never end.

Hungarian Notation and Windows Data Types

Another aspect of Figure 1-2 that deserves mentioning is the variable naming convention that it uses. Veteran Windows programmers know it as Hungarian notation, in which each variable name begins with one or more lowercase characters identifying the variable's type: h for handle, n for integer, and so on. The table below lists some of the commonly used Hungarian prefixes. Prefixes are often combined to form other prefixes, as when p and sz are joined to form psz, which stands for "pointer to zero-terminated string."

Many of the data types shown in this table aren't standard C/C++ data types but rather are "special" data types defined in the Windows header files. COLORREF, for example, is the Windows data type for 24-bit RGB color values. A BOOL is a Boolean data type that stores TRUE/FALSE values, while a DWORD is a 32-bit unsigned integer. Over time, you'll come to know these data types as well as you know your compiler's native data types.

Common Hungarian Notation Prefixes

Prefix Data Type
b BOOL
c or ch char
clr COLORREF
cx, cy Horizontal or vertical distance
dw DWORD
h Handle
l LONG
n int
p Pointer
sz Zero-terminated string
w WORD

Most MFC programmers use Hungarian notation, too. Glance through the source code for a typical MFC program and you'll see hundreds of hs and lps and other familiar prefixes as well as prefixes representing MFC's own data types (for example, wnd for CWnd variables). It's also common to prefix member variables with m_ so that it's obvious whether a variable is a member of a class. A temporary CString variable created on the stack might have the name strWndClass, but if it's a member variable it will probably be called m_strWndClass. You don't have to abide by these rules yourself, of course, but observing established naming conventions will make your code more readable to other programmers who do.

SDK Programming in Perspective

All this is a lot to digest if you've never programmed Windows before, but it brings to light a few very important concepts. First, Windows is an event-driven, message-based operating system. Messages are the key to everything that goes on in the system, and for an application, very few things happen that aren't the direct result of receiving a message. Second, there are many different API functions and many different message types, which complicates application development and makes it hard to predict all of the scenarios an application might encounter. Third, seeing how Windows programming is done the hard way provides a baseline for evaluating MFC and other class libraries. MFC is not the panacea some of its proponents would have you believe, but it undeniably makes certain aspects of Windows programming easier. And the higher order it lends to Windows programs frees programmers to spend more time developing the structural components of a program and less time worrying about the style bits passed to CreateWindow and other nuances of the API. If you haven't given MFC a look, now is the time to consider it. Windows programming isn't getting any easier, and MFC lets you benefit from tens of thousands of lines of code already written and tested by Microsoft.

(译文)The Linux Programming Interface:第1章(历史和标准)

1 HISTORY AND STANDARDS (译者:鱼时代  校对:fgn)       Linux 是UNIX操作系统家族中的一员,在计算机出现以来,UNXI已经有很长的历史了。在这一章...
  • a82793510
  • a82793510
  • 2015年06月10日 16:58
  • 462

Expert Python Programming, 2nd Edition(读书笔记,似乎对Python 3.5并未着墨强调,但是代码示例容易看懂一点)

Expert Python Programming, 2nd Edition 目录 1 Python当前状态2 语法最佳实践:class级别以下3 语法最...
  • cteng
  • cteng
  • 2016年08月09日 13:41
  • 827

《Programming Hive》读书笔记(一)Hadoop和hive环境搭建

《Programming Hive》读书笔记(一)Hadoop和Hive环境搭建 先把基本的技术和工具学好,才能更高效地思考和工作。...
  • linger2012liu
  • linger2012liu
  • 2014年11月12日 00:26
  • 1965

《The C Programming Language》读书笔记总结 <一>.基础篇

写了这么多年的C代码,回过头来再看《The C Programming Language》这本书,作者Brian W. Kernighan和C语言之父Dennis M. Ritchie。感觉里面的知识...
  • Eastmount
  • Eastmount
  • 2015年10月21日 16:14
  • 2069

五大常用算法 ----DP 动态规划(Dynamic Programming)

一、基本概念     动态规划过程是:每次决策依赖于当前状态,又随即引起状态的转移。一个决策序列就是在变化的状态中产生出来的,所以,这种多阶段最优化决策解决问题的过程就称为动态规划。 二、基本思想...
  • Caroline424
  • Caroline424
  • 2016年07月24日 22:35
  • 2170

动态规划(dynamic programming)初步入门

通过金矿模型介绍动态规划 点击下载01背包测试数据.rar                 对于动态规划,每个刚接触的人都需要一段时间来理解,特别是第一次接触的时候总是想不通为什么这种方法可行,...
  • bit_zcy
  • bit_zcy
  • 2016年03月22日 10:16
  • 3162

针对Android上的ROP攻击剖析

引言        ROP(Return-oriented programming),即“返回导向编程技术”。其核心思想是在整个进程空间内现存的函数中寻找适合指令片断(gadget),并通过精心设计返...
  • L173864930
  • L173864930
  • 2013年11月01日 14:03
  • 13172

最有价值的50道java面试题

1、面向对象的特征有哪些方面? 答:面向对象的特征主要有以下几个方面: 1)抽象:抽象是将一类对象的共同特征总结出来构造类的过程,包括数据抽象和行为抽象两方面。抽象只关注对象有哪些属性和行为,并不...
  • qq_36721257
  • qq_36721257
  • 2018年01月03日 19:13
  • 368

RxJava学习资源

目前由于产品需求 要用到RxJava 所以开始学习和收集RxJava相关资源了,会一直更新。RxJava: Reactive Extensions for the JVM 它是响应式编程基于JVM的...
  • tiankong1206
  • tiankong1206
  • 2015年11月05日 15:51
  • 3182

读经典《C程序设计语言》(The C Programming Language)

作为软工专业的学生,大三下,我重新开始学习C语言,为什么呢?因为我发现在大学生涯里,我花了很多时间去刷GPA,花了很多时间去做了很多无谓的事情,以至于马上就面临找实习、找工作。自己甚至连一门语言都没有...
  • stc_XC
  • stc_XC
  • 2017年05月30日 22:54
  • 744
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:The Windows Programming Model-----messages
举报原因:
原因补充:

(最多只允许输入30个字)