Win32编程day04 学习笔记

一 Win32消息机制

  1 消息机制
 
    过程驱动:程序是按照我们预先定义好的顺序执行,每执行一步,下一步都已经按照预定的顺序继续执行,直到程序结束。
     
    事件驱动:程序的执行顺序是无序的。某个时间点所执行的代码,是由外界通知。由于我们无法决定用户执行顺序,所以代码的执行也是无序。
     
    Win32的消息机制 - 事件驱动

  2 Win32消息程序
 
    2.1 Win32窗口注册
    2.2 Win32窗口创建
    2.3 WIn32消息循环
     2.3.1 GetMessage

  BOOL GetMessage(
      LPMSG lpMsg,//存放获取到的消息数据
      HWND hWnd,//获取消息的窗口句柄
      UINT wMsgFilterMin,//消息过滤的起始消息
      UINT wMsgFilterMax //消息过滤的终止消息
    );

    返回值BOOL:成功获取消息,返回TRUE,但是当获取到WM_QUIT消息时,返回FALSE。
    可以使用PostQuitMessage向窗口发送WM_QUIT消息
    MSG - 由系统填写关于消息的参数
    hWnd- GetMessage会根据hWnd值,接收由hWnd指定的窗口的消息。
    wMsgFilterMin,wMsgFilterMax - 消息过滤器,要求GetMessage接收指定范围的消息。

     2.3.2 TranslateMessage
    就是将键盘消息转换成字符消息。
    1 首先检查是否是键盘按键消息     
    2 如果发现是按键消息,将根据按键,产生一个字符消息,在下一个GetMessage执行时,会收到这个消息。
     3 如果未发现按键消息,不做任何处理。

     2.3.3 DispatchMessage
    根据消息数据内窗口句柄,找到这个窗口的窗口处理函数, 调用处理函数,进行消息处理。
    如果MSG中,HWND窗口句柄为空,DispatchMessage不做任何处理。
      
    2.4 Win32基本消息
      2.4.1 WM_DESTROY
         窗口销毁时的消息,可以做退出或善后处理。
      2.4.2 WM_CREATE
         窗口创建消息,是在窗口创建后,窗口处理函数收到第一条消息。
         可以在这个消息内做数据初始化/创建子窗口等。
         WPARAM wParam - 不使用
         LPARAM lParam - CREATESTRUCT指针
      2.4.3 WM_SIZE
         当窗口大小发生变化时,会收到这个消息。
         可以在这个消息中调整窗口布局。
         wParam - SIZE发生变化时的标识
     LOWORD(lParam); - 客户区的宽
         HIWORD(lParam); - 客户区的高
      2.4.4 WM_SYSCOMMAND
         系统命令消息,当点击系统菜单和按钮时,会收到。
         可以在这个消息中,提示用户保存数据等。
         wParam - 系统命令类型
         LOWORD(lParam) - 屏幕X坐标
         HIWORD(lParam) - 屏幕Y坐标

      2.4.5 WM_PAINT 绘图消息
      2.4.6 键盘消息
      2.4.7 鼠标消息
      2.4.8 WM_TIMER定时器消息
     
    2.5 消息结构
      MSG - 消息结构

typedef struct tagMSG {     // msg 
    HWND   hwnd; //消息的窗口句柄   
    UINT   message;//消息标示
    WPARAM wParam; //消息的参数,32位
    LPARAM lParam; //消息的参数,32位
    DWORD  time;//消息产生的时间
    POINT  pt; //消息产生时,鼠标的位置
} MSG; 

   2.6 消息的获取和发送
     2.6.1 获取GetMessage/PeekMessage
         GetMessage 获取消息,阻塞函数
         PeekMessage 获取消息,非阻塞函数
     2.6.2 发送SendMessage/PostMessage
         SendMessage 发送消息并等候消息处理结束才返回。
         PostMessage 发送消息后立即返回,不关心消息处理的结果。

        LRESULT SendMessage/PostMessage(
          HWND hWnd,      //处理消息窗口
          UINT Msg,       //消息的ID
          WPARAM wParam,  //消息的参数
          LPARAM lParam );//消息的参数

  3 消息组成和分类
   3.1 消息组成
      窗口句柄/消息ID/消息参数(WPARAM.LPARAM)
   3.2 消息分类
      3.2.1 系统消息 - 由系统定义和使用的消息
         例如:WM_CREATE/WM_SIZE
         消息ID范围为: 0 - 0x03FF(WM_USER-1)
      3.2.2 用户定义消息 - 应用程序可以自己定义和使用的消息, WM_USER(0x0400)
         从WM_USER的ID开始,到0x7FFF,是用户可以定义使用的消息.
      3.2.3 其他消息范围
         WM_APP(0x8000)-0xBFFF:应用程序访问窗口的消息ID
         0xC000-0xFFFF: 应用程序访问消息,使用字符串注册系统产生相应消息ID
      3.2.4 用户定义消息的使用
         1)定义自定义消息ID:

 #define   WM_FIRSTMSG  (WM_USER+1)

         2)在窗口处理函数中,响应消息

 switch( nMsg )
 {
 case WM_FIRSTMSG:
   //处理函数
    break;
 }

         3)SendMessage/PostMessage发送消息

    SendMessage( hWnd, WM_FIRSTMSG, 0, 0 );

  4 消息队列
    4.1 消息队列 - 用于存储消息的内存空间,消息在队列中是先入先出.
    4.2 消息队列的分类
      4.2.1 系统消息队列 - 由系统维护的消息队列.
      4.2.2 应用程序消息队列(线程消息对列) -属于每个线程的各自拥有的消息队列.
 
  5 消息和消息队列
    5.1 根据消息和消息队列关系,将消息分成两种:
      队列消息 - 可以存放在消息队列中的消息.
      非队列消息 - 发送时不进入消息队列.
    5.2 队列消息
      首先存放到消息队列当中,然后由GetMessage/PeekMessage取出,然后进行处理.
      例如: 鼠标消息/键盘消息/WM_PAINT/WM_QUIT/M_TIMER消息
    5.3 非队列消息
      消息发送直接发送给指定的窗口,查找窗口的处理函数,返回处理结果.
 
 6 消息的获取  
    6.1 消息循环
      6.1.1 GetMesssage从队列中获取消息,判断是否是WM_QUIT消息,如果发现是WM_QUIT消息,消息循环结束,否则继续下一步.
      6.1.2 TranslateMessage 翻译按键消息,如果发现有按键消息,产生字符消息放入消息对列, 继续下一步
      6.1.3 DispatchMessage 找到消息所发窗口的处理函数,处理消息.处理完成后,返回6.1.1.
    6.2 GetMesssage和PeekMessage
      6.2.1 从线程消息队列中获取消息,如果找到消息,就返回消息,进行消息处理. 如果未找到消息,执行6.2.2
      6.2.2 查找系统消息队列.通过向系统消息队列查询,如果找到消息,获取消息并返回,进行消息处理.如果未找到消息,执行6.2.3
      6.2.3 检查窗口需要重新绘制的范围,如果发现存在重新绘制的范围,会产生WM_PAINT消息.然后进行消息处理, 如果未找,执行6.2.4
      6.2.4 检查WM_TIMER定时器消息,如果发现存在已经到时的定时器,会产生WM_TIMER消息.进行消息处理. 如果未找,执行6.2.5
      6.2.5 执行内存管理工作.
      6.2.6 根据函数不同,处理结果不同:
        GetMesssage - 阻塞,等候下一条消息
        PeekMessage - 让出控制权,交给后面的代码执行.
       
  7 消息发送 
    7.1 消息发送分两种
       发送(Send)消息 - 直接发送给指定的窗口,并等候结果.
       投递(Post)消息 - 发送到消息队列当中,立刻返回,由消息循环处理.
    7.2 PostMessage和SendMessage
      PostMessage产生队列消息,由于发送后不等候消息处理结果,所以不能确定消息是否被处理成功.
      SendMessage产生非队列消息,可以确定消息是否成功.

View Code
// winmsg.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "stdio.h"

HINSTANCE g_hInst = NULL;
HWND g_hButton = NULL;

#define  WM_FIRSTMSG (WM_USER+1)
#define  WM_SECONDMSG (WM_USER+2)

LRESULT CALLBACK WndProc(HWND hWnd,
                         UINT nMsg,
                         WPARAM wParam,
                         LPARAM lParam);

BOOL RegisterWnd(LPSTR pszClassName)
{
    WNDCLASSEX wce = {0};
    wce.cbClsExtra = 0;
    wce.cbSize = sizeof(wce);
    wce.cbWndExtra = 0;
    wce.hbrBackground = HBRUSH(COLOR_BTNFACE+1);
    wce.hCursor = NULL;
    wce.hIcon = NULL;
    wce.hIconSm = NULL;
    wce.hInstance = g_hInst;
    wce.lpfnWndProc = WndProc;
    wce.lpszClassName = pszClassName;
    wce.lpszMenuName = NULL;
    wce.style = CS_HREDRAW|CS_VREDRAW;

    ATOM nAtom = RegisterClassEx(&wce);
    if(0 == nAtom)
        return FALSE;
    return TRUE;
}

HWND CreateWnd(LPSTR pszClassName)
{
    HWND hWnd = CreateWindowEx(0, pszClassName,
        "MyWnd", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,
        CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
        NULL,NULL,g_hInst,NULL);
    return hWnd;
}

void DisplayWnd(HWND hWnd)
{
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
}

void OnCreate(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    LPCREATESTRUCT pCreateStruct = LPCREATESTRUCT(lParam);
    //MessageBox(NULL, pCreateStruct->lpszName, "OnCreate", MB_OK);

    g_hButton = CreateWindowEx(0, "BUTTON", "Button", WS_CHILD|WS_VISIBLE,
        0, 0, 100, 100, hWnd, NULL, g_hInst, NULL);
    SendMessage(hWnd, WM_FIRSTMSG, 0, 0);
}

void OnSize(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    int nWidth = LOWORD(lParam);
    int nHeight = HIWORD(lParam);
    CHAR szText[256] = {0};
    sprintf(szText, "WIDTH:%d, HEIGHT:%d", nWidth, nHeight);

    //MessageBox(NULL, szText, "OnSize", MB_OK);

    if(NULL != g_hButton)
    {
        int nX = (nWidth - 100)/2;
        int nY = (nHeight - 100)/2;
        MoveWindow(g_hButton, nX, nY, 100, 100, TRUE);
    }
}

BOOL OnSysCommand(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    switch(wParam)
    {
    case SC_CLOSE:
        if(IDOK == MessageBox(NULL, "是否将文件存盘?", "提示", MB_OKCANCEL|MB_ICONWARNING))
        {
            return TRUE;
        }
        return FALSE;
    }
    return FALSE;
}

LRESULT CALLBACK WndProc(HWND hWnd,
                         UINT nMsg,
                         WPARAM wParam,
                         LPARAM lParam)
{
    switch(nMsg)
    {
    case WM_CREATE:
        OnCreate(hWnd, nMsg, wParam, lParam); break;
    case WM_SIZE:
        OnSize(hWnd, nMsg, wParam, lParam); break;
    case WM_SYSCOMMAND:
        if(FALSE == OnSysCommand(hWnd, nMsg, wParam, lParam))
            return 0;
        break;
    case WM_DESTROY:
        //SendMessage(hWnd, WM_QUIT, 0, 0);
        PostMessage(hWnd, WM_QUIT, 0, 0);
        //PostQuitMessage(0);
        return 0;
    case WM_FIRSTMSG:
        MessageBox(NULL, "FirstMGS", "FIRSTMSG", MB_OK); break;
    }
    return DefWindowProc(hWnd, nMsg, wParam, lParam);
}

void Message()
{
    MSG msg = {0};
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     g_hInst = hInstance;
    RegisterWnd("MyWnd");
    HWND hWnd = CreateWnd("MyWnd");
    DisplayWnd(hWnd);
    Message();
    return 0;
}

二 WM_PAINT消息

 1 WM_PAINT的产生
   由于窗口的互相覆盖等,产生需要绘制的区域,那么会产生WM_PAINT消息.
   一般情况下,不直接发送WM_PAINT消息,通过API声明需要绘制区域,来产生WM_PAINT消息.
   例如,可以使用InvalidateRect声明一个需要重新绘制的区域.
 
  2 WM_PAINT的注意点
    2.1 如果一个消息队列中,有多个WM_PAINT消息,只有最后一个WM_PAINT消息会被处理.
    2.2 WM_PAINT消息处理中,要清空需要被绘制的区域. BeginPaint
     
  3 WM_PAINT的使用
    3.1 WM_PAINT开始时,必须调用BeginPaint
    3.2 绘制图形
    3.3 WM_PAINT处理后,必须调用EndPaint

View Code
// winpaint.cpp : Defines the entry point for the application.
//

#include "stdafx.h"

HINSTANCE g_hInst = NULL;

void OnPaint(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    PAINTSTRUCT ps = {0};
    HDC hDC = BeginPaint(hWnd, &ps);

    CHAR szText[] = "Hello WM_PAINT";
    TextOut(hDC, 100, 100, szText, strlen(szText));
    Rectangle(hDC, 200, 200, 300, 300);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    switch(nMsg)
    {
    case WM_PAINT:
        OnPaint(hWnd, nMsg, wParam, lParam); break;
    case WM_DESTROY:
        PostQuitMessage(0); return 0;
    }
    return DefWindowProc(hWnd, nMsg, wParam, lParam);
}

BOOL RegisterWnd(LPSTR lpszClassName)
{
    WNDCLASSEX wce = {0};
    wce.cbClsExtra = 0;
    wce.cbSize = sizeof(wce);
    wce.cbWndExtra = 0;
    wce.hbrBackground = HBRUSH(COLOR_BTNFACE + 1);
    wce.hCursor = NULL;
    wce.hIcon = NULL;
    wce.hIconSm = NULL;
    wce.hInstance = g_hInst;
    wce.lpfnWndProc = WndProc;
    wce.lpszClassName = lpszClassName;
    wce.lpszMenuName = NULL;
    wce.style = CS_HREDRAW|CS_VREDRAW;

    ATOM nAtom = RegisterClassEx(&wce);
    if(0 == nAtom)
    {
        return FALSE;
    }
    return TRUE;
}

HWND CreateWnd(LPSTR lpszClassName)
{
    HWND hWnd = CreateWindowEx(0,
        lpszClassName, "MyWnd", WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
        NULL, NULL, g_hInst, NULL);
    return hWnd;
}

void DisplayWnd(HWND hWnd)
{
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
}

void Message()
{
    MSG msg = {0};
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
     g_hInst = hInstance;
    RegisterWnd("MyWnd");
    HWND hWnd = CreateWnd("MyWnd");
    DisplayWnd(hWnd);
    Message();
    return 0;
}

三 键盘消息

  1 键盘消息
   按键消息
     WM_KEYDOWN 当键被按下时产生
     WM_KEYUP 当键被释放时产生
     WM_SYSKEYDOWN 当系统键被按下时产生 ALT/F10
     WM_SYSKEYUP 当系统键释放时产生
    字符消息
     WM_CHAR 当有字符键被按下时产生
      TranslateMessage会将WM_KEYDOWN消息中,可以显示的按键,转换成WM_CHAR的消息
  2 消息参数
    WPARAM - 虚拟键码
    LPARAM - 相关的按键信息.
 
  3 消息的使用
    3.1 当有按键消息时,首先进入系统消息队列,然后被程序的消息循环获取.
    3.2 消息的处理
  4 键盘消息的顺序
    对于可显示字符: WM_KEYDOWN,WM_CHAR,WM_KEYUP
    对于不可显示字符: WM_KEYDOWN,WM_KEYUP
    对于系统键:WM_SYSKEYDOWN,WM_SYSKEYUP
    如果按键一直不释放,会重复产生
         WM_KEYDOWN(WM_CHAR)

View Code
// winkeboard.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include "stdio.h"

HINSTANCE g_hInst = NULL;
HANDLE g_hStdOut = NULL;

LRESULT CALLBACK WinProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    switch(nMsg)
    {
    case WM_KEYDOWN:
        {
            CHAR szText[] = "WM_KEYDOWN\n";
            switch(wParam)
            {
            case VK_LEFT:
            case VK_RIGHT:
            case VK_UP:
            case VK_DOWN:
                WriteConsole(g_hStdOut, szText, strlen(szText), NULL, NULL);
                break;
            }
        }
        break;
    case WM_KEYUP:
        {
            CHAR szText[] = "WM_KEYUP\n";
            WriteConsole(g_hStdOut, szText, strlen(szText), NULL, NULL); break;
        }
        break;
    case WM_SYSKEYDOWN:
        {
            CHAR szText[] = "WM_SYSKEYDOWN\n";
            WriteConsole(g_hStdOut, szText, strlen(szText), NULL, NULL); break;
        }
        break;
    case WM_SYSKEYUP:
        {
            CHAR szText[] = "WM_SYSKEYUP\n";
            WriteConsole(g_hStdOut, szText, strlen(szText), NULL, NULL); break;
        }
        break; 
    case WM_CHAR:
        {
            CHAR szText[260] = {0};
            sprintf(szText, "WM_CHAR: %c\n", wParam);
            WriteConsole(g_hStdOut, szText, strlen(szText), NULL, NULL); break;
        }
        break;
    case WM_DESTROY:
        PostQuitMessage(0); return 0;
    }
    return DefWindowProc(hWnd, nMsg, wParam, lParam);
}

BOOL ResiterWnd(LPSTR lpszClassName)
{
    WNDCLASSEX wce = {0};
    wce.cbClsExtra = 0;
    wce.cbSize = sizeof(wce);
    wce.cbWndExtra = 0;
    wce.hbrBackground = HBRUSH(COLOR_BTNFACE+1);
    wce.hCursor = NULL;
    wce.hIcon = NULL;
    wce.hIconSm = NULL;
    wce.hInstance = g_hInst;
    wce.lpfnWndProc = WinProc;
    wce.lpszClassName = lpszClassName;
    wce.lpszMenuName = NULL;
    wce.style = CS_HREDRAW|CS_VREDRAW;

    ATOM nAtom = RegisterClassEx(&wce);
    if(0==nAtom)
    {
        return FALSE;
    }
    return TRUE;
}

HWND CreateWnd(LPSTR lpszClassName)
{
    HWND hWnd = CreateWindowEx(0, lpszClassName, "MyWnd",
        WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,CW_USEDEFAULT,
        NULL, NULL, g_hInst, NULL);
    return hWnd;
}

void ShowWnd(HWND hWnd)
{
    ShowWindow(hWnd, SW_SHOW);
    UpdateWindow(hWnd);
}

void Message()
{
    MSG msg = {0};
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
}

void NewConsole()
{
    AllocConsole();
    g_hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    CHAR szText[] = "Debug Message......:\n";
    WriteConsole(g_hStdOut, szText, strlen(szText), NULL, NULL);
}

int APIENTRY WinMain(HINSTANCE hInstance,
                     HINSTANCE hPrevInstance,
                     LPSTR     lpCmdLine,
                     int       nCmdShow)
{
    NewConsole();
     g_hInst = hInstance;
    ResiterWnd("MyWnd");
    HWND hWnd = CreateWnd("MyWnd");
    ShowWnd(hWnd);
    Message();

    return 0;
}      
原文地址:https://www.cnblogs.com/tangzhengyue/p/2634295.html