Win32 最简单的窗口模板和常用的5个消息参数使用示例

2023-03-16 11:03:00 浏览数 (1)

一个很常用的生成窗口模板

相比于 vc 默认实例的那个两百行,这个精简很多。

代码语言:javascript复制
#include <windows.h>
#include <stdio.h>

// 不使用 Win98 风格
#pragma comment(linker,""/manifestdependency:type='win32'  name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'"")

// 窗口处理函数(自定义,处理消息)
LRESULT CALLBACK WndProc(HWND hWnd, UINT msgID, WPARAM wParam, LPARAM lParam) {

    // 关闭窗口
    switch (msgID) {
    case WM_DESTROY:
        PostQuitMessage(0); // 可以使 GetMessage 函数返回 0 
        break;
    }

    return DefWindowProc(hWnd, msgID, wParam, lParam);
}

// 入口函数
//int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdShow, int nCmdShow) {  // 旧版本入口用这行
int WINAPI wWinMain(HINSTANCE hIns, HINSTANCE hPreIns, PWSTR lpCmdLine, int nCmdShow){        // vs 2022 用的这行

    // 注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;  // 窗口类的附加数据 buff 的大小
    wc.cbWndExtra = 0;  // 窗口的附加数据 buff 的大小
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW   1);  // 绘制窗口背景的画刷句柄
    wc.hCursor = NULL;  // 鼠标的句柄
    wc.hIcon = NULL;  // 窗口图标的句柄
    wc.hInstance = hIns;  // 当前模块的实例句柄
    wc.lpfnWndProc = WndProc;  // 窗口处理函数
    wc.lpszClassName = "Main";  // 窗口类的名称
    wc.lpszMenuName = NULL;  // 窗口菜单的资源 ID 字符串
    wc.style = CS_HREDRAW | CS_VREDRAW;  // 窗口类的风格(CS_DBLCLICK|CS_NOCLOSE)

    // 将以上全部赋值全部写入操作系统
    RegisterClass(&wc);

    // 在内存中创建窗口
    HWND hWnd = CreateWindow("Main", "window", WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, NULL, hIns, NULL);

    /* CreateWindowEx 是加强版函数
        多了一个dwExStyle: 窗口的扩展风格

        窗口创建的过程:

        1、根据窗口名称,到每一个窗口类中找到相应的窗口。
        2、如果找到,把 HINSTANCE (即 hIns)对比,如果相等,创建窗口。
        3、找不到,到应用程序全局窗口类中寻找,还没找到,到系统窗口类寻找
    */

    // 显示窗口
    ShowWindow(hWnd, SW_SHOW);

    // 刷新窗口
    UpdateWindow(hWnd);

    // 消息循环
    MSG nMsg = { 0 };
    while (GetMessage(&nMsg, NULL, 0, 0)) {
        TranslateMessage(&nMsg);
        DispatchMessage(&nMsg); // 将窗口交给窗口处理函数来处理
    }

    return 0;
}

五个常见消息

这里消息是大写字母常量,真正的消息是数字,这些常量就是代表的数字。

1. WM_DESTROY (销毁)

产生时间:窗口被销毁时(不是关闭按钮)

常用于窗口被销毁前做的善后处理,如资源和内存

2. WM_SYSCOMMAND (系统点击)

产生时间:点击最大化、最小化、关闭等产生

返回参数 wParam:具体点击的位置,如 SC_CLOSE 关闭 lParam:鼠标光标位置 LOWORD(lParam); //水平位置 HIWORD(lParam); //垂直位置

常用于窗口关闭时,提示用户处理

3. WM_CREATE (创建窗口前)

产生时间:在窗口创建成功但还未显示时。

返回参数 wParam 为 0 lParam 类型是CREATETRUCT类型的指针 可获取到CreatWindowEx中的全部12个参数

常用于初始化窗口的参数

4. WM_SIZE (改变大小)

产生时间:窗口大小发生变化后

返回参数 wParam 窗口大小变化的原因 lParam 窗口变化后的大小 LOWORD(lParam); //变化后的宽度 HIWORD(lParam); //变化后的高度

常用于窗口变化后,调整各个部分的布局

5. WM_QUIT (开发者自行销毁进程)

产生时间:程序员发生

返回参数 wParam :PostQuitMessage 函数传递的参数 lParam : 0

用于退出。由 GetMessage 接收


使用实例

代码语言:javascript复制
#include <windows.h>
#include <stdio.h>

HANDLE g_hOutput = 0;  // 接受标准输出句柄

void OnCreate(HWND hWnd, LPARAM lParam){
    /* 下面三行代码,把开发者在创建窗口之前弹出那个自定义的字符串pszTest */
    CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
    char* pszText = (char*)pcs->lpCreateParams;
    // MessageBox(NULL,pszText,"Infor",MB_OK);

    // 还能创建一个子窗口
    CreateWindowEx(0,"EDIT","hello",WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL);
}

// 打印长宽高信息
void OnSize( HWND hWnd, LPARAM lParam ){
    short nWidth = LOWORD(lParam);
    short nHight    = HIWORD(lParam);

    char szText[256] = { 0 };
    sprintf( szText, "WM_SIZE:宽:%d,高:%dn",nWidth,nHight);
    WriteConsole( g_hOutput, szText, strlen(szText), NULL, NULL);
}

// 窗口处理函数(自定义,处理消息)
// 参数:窗口句柄、消息ID,消息参数、消息参数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID, WPARAM wParam,LPARAM lParam){

    // 关闭窗口
    switch(msgID){
    case WM_SIZE:
        OnSize(hWnd, lParam);
        break;
    case WM_CREATE:
        OnCreate(hWnd, lParam);
        break;
    case WM_DESTROY:
        PostQuitMessage(0); // 可以使 GetMessage 函数返回 0 
        /* 
            PostQuitMessage(0) 可以在 GetMessage 函数经常走的道上埋下
                一个叫 WM_QUIT 的雷,当 GetMessage 抓取到这雷,则返回 0 
        */
        break;
    case WM_SYSCOMMAND:
        if(wParam == SC_CLOSE){
            int nRet = MessageBox( hWnd,"是否退出?","Infor",MB_YESNO);
            if(nRet == IDYES){
                // 什么都不写

            }else{
                return 0;
            }
        }
        break;
    }

    return DefWindowProc( hWnd,msgID,wParam,lParam);  // 给各种消息默认处理
}

// 入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdShow, int nCmdShow){
    //增加 DOS 窗口
    AllocConsole();
    g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    // 注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;  // 窗口类的附加数据 buff 的大小
    wc.cbWndExtra = 0;  // 窗口的附加数据 buff 的大小
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW 1);  // 绘制窗口背景的画刷句柄
    wc.hCursor = NULL;  // 鼠标的句柄
    wc.hIcon = NULL;  // 窗口图标的句柄
    wc.hInstance = hIns;  // 当前模块的实例句柄
    wc.lpfnWndProc = WndProc;  // 窗口处理函数
    wc.lpszClassName = "Main";  // 窗口类的名称
    wc.lpszMenuName = NULL;  // 窗口菜单的资源 ID 字符串
    wc.style = CS_HREDRAW|CS_VREDRAW;  // 窗口类的风格(CS_DBLCLICK|CS_NOCLOSE)

    /*
        如果要创建子窗口
        1. 需要设置父窗口的句柄。
        2. 创建风格(即 CreateWindow 第三个参数)要增加 WS_CHILD|WS_VISIBLE
    */
    // 将以上全部赋值全部写入操作系统
    RegisterClass( &wc );

    // 在内存中创建窗口
    char* pszText = "hello data!";
    HWND hWnd = CreateWindow( "Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,pszText);

    /* CreateWindowEx 是加强版函数 
        多了一个dwExStyle: 窗口的扩展风格

        窗口创建的过程:

        1、根据窗口名称,到每一个窗口类中找到相应的窗口。
        2、如果找到,把 HINSTANCE (即 hIns)对比,如果相等,创建窗口。
        3、找不到,到应用程序全局窗口类中寻找,还没找到,到系统窗口类寻找
    */

    // 创建子窗口类
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = DefWindowProc;
    wc.lpszClassName = "Child";
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW|CS_VREDRAW;
    RegisterClass( &wc );

    // 创建子窗口
    // HWND hChild1 = CreateWindowEx(0,"Child","c1",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL);
    // HWND hChild2 = CreateWindowEx(0,"Child","c2",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL);

    // 显示窗口
    ShowWindow( hWnd,SW_SHOW);

    // 刷新窗口
    UpdateWindow( hWnd );

    // 消息循环
    MSG nMsg = { 0 };
    while( GetMessage(&nMsg,NULL,0,0) ){
        TranslateMessage( &nMsg );
        DispatchMessage( &nMsg ); // 将窗口交给窗口处理函数来处理
    }
    /*
        消息的组成

        1.窗口句柄
        2.消息ID
        3.消息的两个参数
        4.消息产生的时间
        5.消息产生时的鼠标位置

        GetMessage : 到系统内抓本进程的消息
        参数:
            LPMSG lpMsg // 存放获取的消息BUFF
            HWND hWnd // 窗口句柄(填某个句柄,只抓取那个窗口的消息,如果填 NULL 则都抓取)
            UINT wMsgFilterMin // 获取消息的最小ID(这两个参数,限定消息的范围,如果都为0,则不限制)
            UINT wMsgFilterMax // 获取消息的最大ID
        返回值:
            直接决定程序能不能退出

        TranslateMessage -翻译消息,将 按键翻译成字符消息
            tips: 内部第一件事先检查消息是不是按键消息
    */

    return 0;
}

自定义消息处理函数

代码语言:javascript复制
#include <windows.h>
#include <stdio.h>

HANDLE g_hOutput = 0;  // 接受标准输出句柄

#define WM_MYMESSAGE WM_USER 1001  //自己定制消息,WM_USER = 0x400

void OnCreate(HWND hWnd, LPARAM lParam){
    /* 下面三行代码,把开发者在创建窗口之前弹出那个自定义的字符串pszTest */
    CREATESTRUCT* pcs = (CREATESTRUCT*)lParam;
    char* pszText = (char*)pcs->lpCreateParams;
    // MessageBox(NULL,pszText,"Infor",MB_OK);

    // 还能创建一个子窗口
    CreateWindowEx(0,"EDIT","hello",WS_CHILD|WS_VISIBLE|WS_BORDER, 0, 0, 200, 200, hWnd, NULL, 0, NULL);

    // 自定义消息
    PostMessage( hWnd,WM_MYMESSAGE, 1, 2);
}

// 打印长宽高信息
void OnSize( HWND hWnd, LPARAM lParam ){
    short nWidth = LOWORD(lParam);
    short nHight    = HIWORD(lParam);

    char szText[256] = { 0 };
    sprintf( szText, "WM_SIZE:宽:%d,高:%dn",nWidth,nHight);
    WriteConsole( g_hOutput, szText, strlen(szText), NULL, NULL);
}

// 自定义消息的处理函数
void OnMyMessage( HWND hWnd, WPARAM wParam, LPARAM lParam ){
    char szText[256] = { 0 };
    sprintf( szText,"自定义消息被处理:wParam=%d,lParam=%dn",wParam,lParam);
    MessageBox( hWnd, szText, "Infor", MB_OK);
}
// 窗口处理函数(自定义,处理消息)
// 参数:窗口句柄、消息ID,消息参数、消息参数
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID, WPARAM wParam,LPARAM lParam){

    // 关闭窗口
    switch(msgID){
    case WM_MYMESSAGE:
        OnMyMessage( hWnd, wParam, lParam);
        break;
    case WM_SIZE:
        OnSize(hWnd, lParam);
        break;
    case WM_CREATE:
        OnCreate(hWnd, lParam);
        break;
    case WM_DESTROY:
        PostQuitMessage(0); // 可以使 GetMessage 函数返回 0 
        /* 
            PostQuitMessage(0) 可以在 GetMessage 函数经常走的道上埋下
                一个叫 WM_QUIT 的雷,当 GetMessage 抓取到这雷,则返回 0 
        */
        break;
    case WM_SYSCOMMAND:
        if(wParam == SC_CLOSE){
            int nRet = MessageBox( hWnd,"是否退出?","Infor",MB_YESNO);
            if(nRet == IDYES){
                // 什么都不写

            }else{
                return 0;
            }
        }
        break;
    }

    return DefWindowProc( hWnd,msgID,wParam,lParam);  // 给各种消息默认处理
}

// 入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns, LPSTR lpCmdShow, int nCmdShow){
    //增加 DOS 窗口
    AllocConsole();
    g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
    // 注册窗口类
    WNDCLASS wc = { 0 };
    wc.cbClsExtra = 0;  // 窗口类的附加数据 buff 的大小
    wc.cbWndExtra = 0;  // 窗口的附加数据 buff 的大小
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW 1);  // 绘制窗口背景的画刷句柄
    wc.hCursor = NULL;  // 鼠标的句柄
    wc.hIcon = NULL;  // 窗口图标的句柄
    wc.hInstance = hIns;  // 当前模块的实例句柄
    wc.lpfnWndProc = WndProc;  // 窗口处理函数
    wc.lpszClassName = "Main";  // 窗口类的名称
    wc.lpszMenuName = NULL;  // 窗口菜单的资源 ID 字符串
    wc.style = CS_HREDRAW|CS_VREDRAW;  // 窗口类的风格(CS_DBLCLICK|CS_NOCLOSE)

    /*
        如果要创建子窗口
        1. 需要设置父窗口的句柄。
        2. 创建风格(即 CreateWindow 第三个参数)要增加 WS_CHILD|WS_VISIBLE
    */
    // 将以上全部赋值全部写入操作系统
    RegisterClass( &wc );

    // 在内存中创建窗口
    char* pszText = "hello data!";
    HWND hWnd = CreateWindow( "Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,pszText);

    /* CreateWindowEx 是加强版函数 
        多了一个dwExStyle: 窗口的扩展风格

        窗口创建的过程:

        1、根据窗口名称,到每一个窗口类中找到相应的窗口。
        2、如果找到,把 HINSTANCE (即 hIns)对比,如果相等,创建窗口。
        3、找不到,到应用程序全局窗口类中寻找,还没找到,到系统窗口类寻找
    */

    // 创建子窗口类
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW 1);
    wc.hCursor = NULL;
    wc.hIcon = NULL;
    wc.hInstance = hIns;
    wc.lpfnWndProc = DefWindowProc;
    wc.lpszClassName = "Child";
    wc.lpszMenuName = NULL;
    wc.style = CS_HREDRAW|CS_VREDRAW;
    RegisterClass( &wc );

    // 创建子窗口

    // HWND hChild1 = CreateWindowEx(0,"Child","c1",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 0, 0, 200, 200, hWnd, NULL, hIns, NULL);
    // HWND hChild2 = CreateWindowEx(0,"Child","c2",WS_CHILD|WS_VISIBLE|WS_OVERLAPPEDWINDOW, 200, 0, 200, 200, hWnd, NULL, hIns, NULL);

    // 显示窗口
    ShowWindow( hWnd,SW_SHOW);

    // 刷新窗口
    UpdateWindow( hWnd );

    /* 消息循环*/
    MSG nMsg = { 0 };

    /* 这种方式效率太低,注释掉 */
    /*while( GetMessage(&nMsg,NULL,0,0) ){ 
        TranslateMessage( &nMsg );
        DispatchMessage( &nMsg ); // 将窗口交给窗口处理函数来处理
    }*/

    /* 使用这个方法,(PeekMessage)可以作为侦察兵 */
    while(1){
        if(PeekMessage(&nMsg,NULL,0,0,PM_NOREMOVE)){
            // 侦察兵侦查到有消息
            if(GetMessage(&nMsg,NULL,0,0)){
                // 消息为真
                TranslateMessage( &nMsg );
                DispatchMessage( &nMsg );
            }else{
                return 0;
            }
        }else{
            // 空闲处理(利用空闲的时间,即没有消息的时间,来做点事)
            WriteConsole( g_hOutput, "OnIdle", strlen("OnIdles"), NULL, NULL);
        }
    }

    /*

    */
    /*
        消息的组成

        1.窗口句柄
        2.消息ID
        3.消息的两个参数
        4.消息产生的时间
        5.消息产生时的鼠标位置

        GetMessage : 到系统内抓本进程的消息
        参数:
            LPMSG lpMsg // 存放获取的消息BUFF
            HWND hWnd // 窗口句柄(填某个句柄,只抓取那个窗口的消息,如果填 NULL 则都抓取)
            UINT wMsgFilterMin // 获取消息的最小ID(这两个参数,限定消息的范围,如果都为0,则不限制)
            UINT wMsgFilterMax // 获取消息的最大ID
        返回值:
            直接决定程序能不能退出

        TranslateMessage -翻译消息,将 按键翻译成字符消息
            tips: 内部第一件事先检查消息是不是按键消息
    */

    return 0;
}

/*
    五个常见消息(都是代表一个数字)。:

        1. WM_DESTROY
            窗口被销毁时(不是关闭按钮)
            常用于窗口被销毁前做的善后处理,如资源和内存

        2. WM_SYSCOMMAND
            点击最大化、最小化、关闭等产生
            附带信息,wParam:具体点击的位置,如 SC_CLOSE 关闭
                          lParam:鼠标光标位置
                            LOWORD(lParam); //水平位置
                            HIWORD(lParam); //垂直位置

            常用于窗口关闭时,提示用户处理

        3. WM_CREATE 
            在窗口创建成功但还未显示时。
                wParam 为 0 
                lParam 类型是CREATETRUCT类型的指针
                    可获取到CreatWindowEx中的全部12个参数
                常用于初始化窗口的参数

        4. WM_SIZE
            窗口大小发生变化后
                wParam 窗口大小变化的原因 
                lParam 窗口变化后的大小
                    LOWORD(lParam); //变化后的宽度
                    HIWORD(lParam); //变化后的高度

                常用于窗口变化后,调整各个部分的布局

            5. WM_QUIT
                程序员发生
                    wParam :PostQuitMessage 函数传递的参数
                    lParam : 0

                用于退出。由 GetMessage 接收
*/

/*
    发送消息的两个函数
        1. SendMessage() - 发送消息 - 像打电话
        2. PostMessage() - 投递消息 - 像投递信件 

        两者的不同是,第一个会等消息处理的结果,无结果会阻塞
            ,第二个发送后立即返回,不等结果

        上面的 
        PostQuitMessage(0); 等同于 PostMessage( hWnd, WM_QUT, 0, 0);

    系统消息
        ID范围 0 - 0x03FF
        由系统定义好的消息,可以在系统中直接使用

    用户自定义消息
        ID范围 0x0400 - 0x7FFF (31743个消息)
            由用户自己定义,满足用户自己的需求。

*/

0 人点赞