Windows程序设计--(三)窗口与消息

3.1 窗口的创建

3.1.1 系统结构概述

所谓「Windows给程序发送消息」,是指Windows呼叫程序中的一个函数,该函数的参数描述了这个特定消息。这种位于Windows程序中的函数称为「窗口消息处理程序」。

3.1.2 HELLOWIN

#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("WNDCLASS NAME");//窗口类名称
    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(DKGRAY_BRUSH);//主窗口背景色
    wndclass.lpszMenuName = NULL;//窗口菜单
    wndclass.lpszClassName = szAppName;//窗口类名

    if (!RegisterClass(&wndclass)) {//注册窗口类,如果注册失败弹出窗口
        MessageBox(NULL, TEXT("窗口创建失败!程序需要Windows NT!(传递窗口消息为UNICODE)"), szAppName, MB_ICONERROR);//消息窗口
        
        return 0;
    }

    hwnd = CreateWindow(szAppName,                //Windows类名
                        TEXT("窗口绘制成功!"),        //窗口标题
                        WS_OVERLAPPEDWINDOW,    //窗口风格
                        CW_USEDEFAULT,            //初始化窗口位置的X坐标
                        CW_USEDEFAULT,            //初始化窗口位置的Y坐标
                        CW_USEDEFAULT,            //初始化窗口宽度大小
                        CW_USEDEFAULT,            //初始化窗口长度大小
                        NULL,                    //父类窗口句柄
                        NULL,                    //窗口菜单句柄
                        hInstance,                //程序实例句柄
                        NULL);                    //创建参数
    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;//绘制结构
    /*
    typedef struct tagRECT
    {
        LONG    left;
        LONG    top;
        LONG    right;
        LONG    bottom;
    } RECT
    其中left,top赋为0,因此right和bottom表示客户区的宽度和高度(像素)
    */
    RECT rect;//矩形结构

    switch (message) {//处理得到的消息
    case WM_CREATE://窗口创建发来消息
        MessageBox(hwnd, TEXT("创建成功,音乐播放"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);
        /*
        1.波形文件的名称
        2.只有当声音文件是一个资源时才有用,此处NULL表示不使用
        3.指定一组选项,表示指定了第一个参数为文件名且该段声音是以异步方式播放
        */
        PlaySound(TEXT("hellowin.wav"), NULL, SND_FILENAME | SND_ASYNC);
        return 0;
    case WM_PAINT://处理窗口绘制
        hdc = BeginPaint(hwnd, &ps);//标明窗口绘制开始,设备环境句柄
        GetClientRect(hwnd, &rect);//获取窗口客户区的尺寸
        /*
        1.为环境句柄
        2.为要绘制的文本内容
        3.设置为-1,表示文本字符串以0结尾
        4.传递rect结构体
        5.是一组位标记
        */
        DrawText(hdc, TEXT("Hello Hk_Mayfly!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(hwnd, &ps);//结束窗口绘制
        return 0;
    case WM_LBUTTONDOWN:
        MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
        return 0;
    case WM_RBUTTONDOWN:
        MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
        return 0;
    case WM_DESTROY://处理窗口关闭时的消息
        MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
        PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理
}

3.1.3 通盘考虑

Windows API离线查询下载:http://download.microsoft.com/download/1/f/0/1f07c259-7ff2-4902-9205-ad1dfb87ccab/VS2008SP1MSDNENUX1506188.iso

上面调用了

函数名 函数描述

GetStockObject

获取一个图形对象

LoadIcon

为程序加载图标

LoadCursor

为程序加载光标

RegisterClass

为程序窗口注册一个窗口类

MessageBox

显示消息对话框

CreateWindow

创建一个窗口

ShowWindow

在屏幕上将窗口显示出来

UpdateWindow

重绘窗口客户区

GetMessage

从消息队列获取消息

TranslateMessage

将虚拟键消息转换为字符消息

DispatchMessage

将消息发送给消息处理函数

BeginPaint

准备对窗口进行绘图

GetClientRect

获取窗口客户区尺寸

DrawText

绘制一个文本字符串

EndPaint

结束对窗口的绘图

PostQuitMessage

向消息队列插入"退出"消息 

DefWindowProc

执行系统默认的消息处理 

CS_HREDRAW

CS_VREDRAW

IDI_APPLICATION

IDC_ARROW

MB_OK

MB_ICONERROR

WS_OVERLAPPEDWINDOW

CW_USEDEFAULT

DT_SINGLELINE

DT_CENTER

DT_VCENTER

WM_CREATE

WM_PAINT

WM_LBUTTONDOWN

WM_DESTROY

这些标识符均为常量,在WINUSER.H头文件中有定义,下面是一些前缀的意义

前缀

含义

CS_

类风格选项

CW_

创建窗口选项

DT_

文本绘制选项

IDI_

图标的ID号

IDC_

光标的ID号

MB_

消息框选项

WM_

窗口消息

WS_

窗口风格

 匈牙利标记法

匈牙利命名法是一种编程时的命名规范。基本原则是:变量名=属性+类型+对象描述,其中每一对象的名称都要求有明确含义,可以取对象名字全称或名字的一部分。

例如,szCmdLine中的sz代表「以0结尾的字符串」。在hInstance和hPrevInstance中的h前缀表示「句柄」;在iCmdShow中的i前缀表示「整数」。WndProc的后两个参数也使用匈牙利表示法。正如我在前面已经解释过的,尽管wParam应该更适当地被命名为uiParam(代表「无正负号整数」),但是因为这两个参数是使用数据型态WPARAM和LPARAM定义的,因此保留它们传统的名字。

3.1.4 窗口类的注册

在为程序建立窗口之前,必须首先呼叫RegisterClass注册一个窗口类别。该函数只需要一个参数,即一个指向型态为WNDCLASS的结构指针。此结构包括两个指向字符串的字段,因此结构在WINUSER.H表头文件中定义了两种不同的方式,第一个是ASCII版的WNDCLASSA

typedef struct tagWNDCLASSA
        
{
        
    UINT       style ;
        
    WNDPROC    lpfnWndProc ;
        
    int        cbClsExtra ;
        
    int            cbWndExtra ;
        
    HINSTANCE     hInstance ;
        
    HICON      hIcon ;
        
    HCURSOR       hCursor ;
        
    HBRUSH     hbrBackground ;
        
    LPCSTR        lpszMenuName ;
        
    LPCSTR        lpszClassName ;
        
}
        
WNDCLASSA, * PWNDCLASSA, NEAR * NPWNDCLASSA, FAR * LPWNDCLASSA ;
 

在这里提示一下数据型态和匈牙利表示法:其中的lpfn前缀代表「指向函数的长指标」。(在Win32 API中,长指标和短指标(或者近程指标)没有区别。这只是16位Windows的遗物。)cb前缀代表「字节数」而且通常作为一个常数来表示一个字节的大小。h前缀是一个句柄,而hbr前缀代表「一个画刷的代号」。lpsz前缀代表「指向以0结尾字符串的指针」。

Unicode版的结构定义如下:

typedef struct tagWNDCLASSW
        
{
        
           UINT             style ;
        
           WNDPROC       lpfnWndProc ;
        
           int           cbClsExtra ;
        
           int           cbWndExtra ;
        
           HINSTANCE     hInstance ;
        
           HICON         hIcon ;
        
           HCURSOR       hCursor ;
        
           HBRUSH        hbrBackground ;
        
           LPCWSTR       lpszMenuName ;
        
           LPCWSTR       lpszClassName ;
        
}
        
WNDCLASSW, * PWNDCLASSW, NEAR * NPWNDCLASSW, FAR * LPWNDCLASSW ;
 

与前者唯一的区别在于最后两个字段定义为指向宽字符串常数,而不是指向ASCII字符串常数。

在WinMain中定义WNDCLASS类型结构

WNDCLASS wndclass;

对该结构的10个字段进行初始化,并调用RegisterClass

位标记

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

代码中用到的这两个标识符表示,所有依据此类别建立的窗口,每当窗口的水平方向大小(CS_HREDRAW)或者垂直方向大小(CS_VREDRAW)改变之后,窗口要完全重画。

在表头文件WINUSER.H中,已定义了一整组以CS为前缀的标识符:

#define     CS_VREDRAW               0x0001
        
#define     CS_HREDRAW           0x0002
        
#define     CS_KEYCVTWINDOW          0x0004
        
#define     CS_DBLCLKS           0x0008
        
#define     CS_OWNDC             0x0020
        
#define     CS_CLASSDC           0x0040
        
#define     CS_PARENTDC          0x0080
        
#define     CS_NOKEYCVT         0x0100
        
#define     CS_NOCLOSE           0x0200
        
#define     CS_SAVEBITS          0x0800
        
#define     CS_BYTEALIGNCLIENT       0x1000
        
#define     CS_BYTEALIGNWINDOW       0x2000
        
#define     CS_GLOBALCLASS        0x4000
        
#define     CS_IME                0x00010000
View Code
wndclass.lpfnWndProc = WndProc ;

将窗口消息处理程序设定为WndProc函数(我们自定义的函数)

wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;

这两个字段用于在窗口类别结构和Windows内部保存的窗口结构中预留一些额外空间

wndclass.hInstance = hInstance ;

程序的执行实体句柄(WinMain参数之一)

wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;

第一个参数是取得预先定义图示的句柄。第二个参数代表图示。对于预先定义图示,此参数是以IDI开始的标识符(「ID代表图示」),标识符在WINUSER.H中定义。

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

LoadCursor函数加载一个预先定义的鼠标光标(命名为IDC_ARROW),并传回该游标的句柄。该句柄被设定给WNDCLASS结构的hCursor字段。当鼠标光标在依据这个类别建立的窗口的显示区域上出现时,它变成一个小箭头。

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

依据这个类别建立的窗口背景颜色。这里所示的GetStockObject呼叫将传回一个白色画刷的句柄:

wndclass.lpszMenuName = NULL ;

指定窗口类别菜单。HElLOWIN没有应用程序菜单,所以该字段被设定为NULL

wndclass.lpszClassName = szAppName ;

类别名称

if (!RegisterClass (&wndclass))
        
{
        
    MessageBox (  NULL, TEXT ("This program requires Windows NT!"),
        
                 szAppName, MB_ICONERROR) ;
        
    return 0 ;
        
}

Windows 98不支持Unicode程序

3.1.5 窗口的创建

窗口创建

HWND CreateWindow(
  LPCTSTR lpClassName,               //窗口类名称
  LPCTSTR lpWindowName,              //窗口标题
  DWORD dwStyle,                     //窗口样式
  int x,                             //窗口初始x坐标
  int y,                             //窗口初始y坐标
  int nWidth,                        //窗口初始x方向尺寸
  int nHeight,                       //窗口初始y方向尺寸
  HWND hWndParent,                   //父窗口句柄
  HMENU hMenu,                       //窗口菜单句柄
  HANDLE hlnstance,                  //程序实例句柄
  LPVOID lpParam                     //创建参数
);
hwnd = CreateWindow(szAppName,                //Windows类名
        TEXT("窗口绘制成功!"),     //窗口标题
        WS_OVERLAPPEDWINDOW,     //窗口风格
        CW_USEDEFAULT,           //初始化窗口位置的X坐标
        CW_USEDEFAULT,           //初始化窗口位置的Y坐标
        CW_USEDEFAULT,           //初始化窗口宽度大小
        CW_USEDEFAULT,           //初始化窗口长度大小
        NULL,                    //父类窗口句柄
        NULL,                    //窗口菜单句柄
        hInstance,               //程序实例句柄
        NULL);                   //创建参数

关于窗口风格除了WS_OVERLAPPEDWINDOW还有

#define WS_OVERLAPPED       0x00000000L            //产生一个层叠的窗口。一个层叠的窗口有一个标题条和一个边框。与WS_TILED风格相同。 
#define WS_POPUP            0x80000000L            //创建一个弹出式窗口。该风格不能与WS_CHLD风格同时使用。 
#define WS_CHILD            0x40000000L            //创建一个子窗口。这个风格不能与WS_POPUP风格合用。 
#define WS_MINIMIZE         0x20000000L            //创建一个初始状态为最小化的窗口。仅与WS_OVERLAPPED风格一起使用。
#define WS_VISIBLE          0x10000000L            //创建一个最初可见的窗口。
#define WS_DISABLED         0x08000000L            //创建一个初始状态为禁止的窗口。
#define WS_CLIPSIBLINGS     0x04000000L            //排除子窗口之间的相对区域。
#define WS_CLIPCHILDREN     0x02000000L            //创建一个初始状态为禁止的子窗口。一个禁止状态的窗口不能接受来自用户的输入信息。 
#define WS_MAXIMIZE         0x01000000L            //创建一个初始状态为最大化状态的窗口。 
#define WS_CAPTION          0x00C00000L            //创建一个有标题框的窗口(包括WS_BODER风格)。 
#define WS_BORDER           0x00800000L            //创建一个单边框的窗口。 
#define WS_DLGFRAME         0x00400000L            //创建一个带对话框边框风格的窗口。这种风格的窗口不能带标题条。 
#define WS_VSCROLL          0x00200000L            //创建一个有垂直滚动条的窗口。 
#define WS_HSCROLL          0x00100000L            //创建一个有水平滚动条的窗口。 
#define WS_SYSMENU          0x00080000L            //创建一个在标题条上带有窗口菜单的窗口,必须同时设定WS_CAPTION风格。 
#define WS_THICKFRAME       0x00040000L            //创建一个具有可调边框的窗口,与WS_SIZEBOX风格相同。 
#define WS_GROUP            0x00020000L            //指定一组控制的第一个控制。
#define WS_TABSTOP          0x00010000L            //创建一个控制,这个控制在用户按下Tab键时可以获得键盘焦点。按下Tab键后使键盘焦点转移到下一具有WS_TABSTOP风格的控制。 
#define WS_MINIMIZEBOX      0x00020000L            //创建一个具有最小化按钮的窗口。
#define WS_MAXIMIZEBOX      0x00010000L            //创建一个具有最大化按钮的窗口。
#define WS_TILED            WS_OVERLAPPED          //产生一个层叠的窗口。一个层叠的窗口有一个标题和一个边框。与WS_OVERLAPPED风格相同。
#define WS_ICONIC           WS_MINIMIZE            //创建一个初始状态为最小化的窗口。仅与WS_OVERLAPPED风格一起使用。
#define WS_SIZEBOX          WS_THICKFRAME          //创建一个具有厚边框的窗口,可以通过厚边框来改变窗口大小。
#define WS_TILEDWINDOW      WS_OVERLAPPEDWINDOW    //创建一个具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU,WS_THICKFRAME,WS_MINIMIZEBOX和WS_MAXIMIZEBOX风格的重叠式窗口。

参数使用CW_USEDEFAULT标识符,指示Windows使用重迭窗口的内定位置。(CW_USEDEFAULT定义为0x80000000)表示使用默认的Windows参数值,也可以使用自定义数值。

3.1.6 窗口的显示

程序通过执行一块称之为「消息循环」的程序代码从消息队列中取出消息:

while (GetMessage (&msg, NULL, 0, 0))
{  
    TranslateMessage (&msg) ; 
    DispatchMessage (&msg) ;   
}
 

msg变量是型态为MSG的结构,型态MSG在WINUSER.H中定义如下:

typedef struct tagMSG
{
    HWND   hwnd ;
    UINT   message ;
    WPARAM wParam ;
    LPARAM lParam ;
    DWORD  time ;
    POINT  pt ;
}
MSG, * PMSG ;
 

POINT数据型态也是一个结构,它在WINDEF.H中定义如下:

typedef struct tagPOINT
{
    LONG  x ;
    LONG  y ;
}
POINT, * PPOINT;

消息循环以GetMessage呼叫开始,它从消息队列中取出一个消息:

GetMessage (&msg, NULL, 0, 0)
TranslateMessage (&msg) ;

将msg结构传给Windows,进行一些键盘转换。

DispatchMessage (&msg) ;

又将msg结构回传给Windows。然后,Windows将该消息发送给适当的窗口消息处理程序,让它进行处理。这也就是说,Windows将呼叫窗口消息处理程序。

3.1.8 窗口过程

窗口消息处理程序总是定义为如下形式:

LRESULT CALLBACK WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)

注意,窗口消息处理程序的四个参数与MSG结构的前四个字段是相同的。第一个参数hwnd是接收消息的窗口的句柄,它与CreateWindow函数的传回值相同。对于与HELLOWIN相似的程序(只建立一个窗口),这个参数是程序所知道的唯一窗口句柄。如果程序是依据同一窗口类别(同时也是同一窗口消息处理程序)建立多个窗口,则hwnd标识接收消息的特定窗口。

第二个参数与MSG结构中的message字段相同,它是标识消息的数值。最后两个参数都是32位的消息参数,提供关于消息的更多信息。这些参数包含每个消息型态的详细信息。有时消息参数是两个存放在一起的16位值,而有时消息参数又是一个指向字符串或数据结构的指针。

程序通常不直接呼叫窗口消息处理程序,窗口消息处理程序通常由Windows本身呼叫。通过呼叫SendMessage函数,程序能够直接呼叫它自己的窗口消息处理程序。我们将在后面的章节讨论SendMessage函数。

3.1.9 消息处理

使用switch...case来处理

    switch (message) {//处理得到的消息
    case WM_CREATE://窗口创建发来消息
        MessageBox(hwnd, TEXT("创建成功,音乐播放"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);
        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 Hk_Mayfly!"), -1, &rect, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
        EndPaint(hwnd, &ps);//结束窗口绘制
        return 0;
    case WM_LBUTTONDOWN:
        MessageBox(hwnd, TEXT("左键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
        return 0;
    case WM_RBUTTONDOWN:
        MessageBox(hwnd, TEXT("右键按下!"), TEXT("BIU"), MB_OK | MB_ICONINFORMATION);
        return 0;
    case WM_DESTROY://处理窗口关闭时的消息
        MessageBox(hwnd, TEXT("主窗口关闭"), TEXT("Windows"), MB_OK | MB_ICONINFORMATION);//显示一个文本字符串
        PostQuitMessage(0);//将退出消息插入消息队列,程序从消息循环退出,return msg.wParam
        return 0;
    }
    return DefWindowProc(hwnd, message, wParam, lParam);//执行默认消息处理

3.1.10 声音文件的播放

3.1.11 WM_PAINT消息

当窗口显示区域的一部分显示内容或者全部变为「无效」,以致于必须「更新画面」时,将由这个消息通知程序。‘

对WM_PAINT的处理几乎总是从一个BeginPaint呼叫开始:

hdc = BeginPaint (hwnd, &ps) ;
 

而以一个EndPaint呼叫结束:

EndPaint (hwnd, &ps) ;

在这两个呼叫中,第一个参数都是程序的窗口句柄,第二个参数是指向型态为PAINTSTRUCT的结构指针。

呼叫完BeginPaint之后,WndProc接着呼叫GetClientRect:

GetClientRect (hwnd, &rect) ;
 

第一个参数是程序窗口的句柄。第二个参数是一个指标,指向一个RECT型态的rectangle结构。该结构有四个LONG字段,分别为left、top、right和bottom。GetClientRect将这四个字段设定为窗口显示区域的尺寸。left和top字段通常设定为0,right和bottom字段设定为显示区域的宽度和高度(像素点数)。

WndProc除了将该RECT结构指针作为DrawText的第四个参数传递外,不再对它做其它处理:

DrawText (  hdc, TEXT ("Hello, Windows 98!"), -1, &rect,
        
                  DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
 

DrawText可以输出文字(正如其名字所表明的一样)。由于该函数要输出文字,第一个参数是从BeginPaint传回的设备内容句柄,第二个参数是要输出的文字,第三个参数是 -1,指示字符串是以字节0终结的。DrawText最后一个参数是一系列位旗标

常用的有:

DT_BOTTOM //将正文调整到矩形底部;

DT_CENTER //使正文在矩形中水平居中;

DT_LEFT //正文左对齐;

DT_RIGHT //正文右对齐;

DT_TOP //正文顶端对齐;

DT_VCENTER //使正文在矩形中垂直居中;

DT_WORD_ELLIPSIS //截短不符合矩形的正文,并增加省略号;

3.1.12 WM_DESTROY消息

程序通过呼叫PostQuitMessage以标准方式响应WM_DESTROY消息:

PostQuitMessage (0) ;
 

该函数在程序的消息队列中插入一个WM_QUIT消息。前面提到过,GetMessage对于除了WM_QUIT之外的从消息队列中取出的所有消息都传回非0值。而当GetMessage得到一个WM_QUIT消息时,它传回0。这将导致WinMain退出消息循环,并终止程序。然后程序执行下面的叙述:

return msg.wParam ;

结构的wParam字段是传递给PostQuitMessage函数的值(通常是0)。然后return叙述将退出WinMain并终止程

原文地址:https://www.cnblogs.com/Mayfly-nymph/p/11291960.html