• Win 32 编程之按钮消息响应(代码小错误修复)


    最近不想用MFC写东西了,有没有安装Qt和其他图形化开发环境,只能捣鼓API了。于是乎,就有了以下的学习--

    首先,老套的创建个Windows窗口,由于自己有点小懒,就直接用Hello Word的源码了。

    #include <windows.h>
    #include <stdio.h>
    
    // 窗口过程函数
    LRESULT CALLBACK WinSunProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
    
    int WINAPI WinMain(
                       HINSTANCE hInstance,         // handle to current instance
                       HINSTANCE hPrevInstance,     // handle to previous instance
                       LPSTR lpCmdLine,             // command line
                       int nCmdShow                 // show state
                       )
    
    {
        // 窗口类
        WNDCLASS wndcls;
        wndcls.cbClsExtra = 0;                                          // 类附加内存
        wndcls.cbWndExtra = 0;                                          // 窗口附加内存
        wndcls.hbrBackground = (HBRUSH) GetStockObject(COLOR_WINDOW);    // 背景画刷句柄
        wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);                   // 窗口光标句柄
        wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);                       // 窗口图标句柄
        wndcls.hInstance = hInstance;                                   // 包含窗口过程函数的程序实例
        wndcls.lpfnWndProc = WinSunProc;                                // 只想窗口过程函数的指针
        wndcls.lpszClassName = "CRoot";                                // 窗口类名称
        wndcls.lpszMenuName = NULL;                                     // 菜单资源
        wndcls.style = CS_HREDRAW | CS_VREDRAW;                         // 窗口样式
        RegisterClass(&wndcls);
    
        // 创建窗口, 定义一个变量用来保存成功创建窗口后返回的句柄
        HWND hwnd;
        hwnd = CreateWindow(        // 窗口创建成功时返回为窗口分配的句柄 失败时返回NULL
            "CRoot",               // 窗口类名
            "Hello World",          // 窗口名字
            WS_CAPTION|WS_SYSMENU,    // 窗口样式
            CW_USEDEFAULT, CW_USEDEFAULT,                   // 窗口左上角坐标
            200, 120,               // 窗口宽高
            NULL,                   // 父窗口句柄
            NULL,                   // 窗口菜单句柄
            hInstance,              // 窗口所属应用程序实例
            NULL                    // WM_CREATE消息附加参数lParam传入的数据指针
            );
    
        // 显示及刷新窗口
        ShowWindow(hwnd, SW_SHOWNORMAL);
        UpdateWindow(hwnd);
    
        // 定义消息结构体
        MSG msg;
        while (GetMessage(  // WM_QUIT消息返回0 错误返回-1
            &msg,           // 指向消息的结构体
            NULL,           // 指定接收属于哪一窗口的消息 通常设为NULL,用来接收属于调用线程的所有窗口的窗口消息
            0,              // 获取消息的最小值 通常为0
            0))             // 获取消息的最大值 都设为0表示接收所有消息
        {
            TranslateMessage(&msg);     // 将虚拟消息转换为字符消息 投递到调用线程的消息队列中 下次调用GetMessage时被取出
            DispatchMessage(&msg);      // 将消息传递给操作系统 由操作系统调用窗口过程函数对消息进行处理
        }
        return msg.wParam;
    }
    
    // 窗口过程函数
    LRESULT CALLBACK WinSunProc(
        HWND hwnd,          // 窗口句柄
        UINT uMsg,          // 消息代码
        WPARAM wParam,      // 附加参数
        LPARAM lParam
    )
    {
        switch(uMsg)
        {
        case WM_CHAR:
            char szChar[20];
            sprintf(szChar, "char code is %d", wParam);
            MessageBox(hwnd, szChar, "char", 0);
            break;
    
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
    
        return 0;
    }

    然后,我们要在消息循环里面添加创建Button的代码,因为Button是一个子窗口,所以创建方式也如同创建窗口一样,使用CreateWindow()函数。

    而,此窗口要一开始跟随父窗口出现,所以要在父窗口创建时创建该Button。嗯,所以要在窗口过程函数里面监听下WM_CREATE消息。

    代码片段:

        switch(uMsg)
        {
        case WM_CHAR:
            char szChar[20];
            sprintf(szChar, "char code is %d", wParam);
            MessageBox(hwnd, szChar, "char", 0);
            break;
        case WM_CREATE:
            CreateWindowEx(0,"Button","按钮1",WS_VISIBLE|WS_CHILD,0,0,100,50,hwnd,(HMENU)0,0,0);
    
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }

    这里要说明一下啦,一开始我也搞得晕晕乎乎,不知道怎么搞Button的消息事件,这里我们用Button的ID啦。先看下微软的MSDN关于CreateWindow()函数

    HWND WINAPI CreateWindow(
      _In_opt_  LPCTSTR lpClassName,
      _In_opt_  LPCTSTR lpWindowName,
      _In_      DWORD dwStyle,
      _In_      int x,
      _In_      int y,
      _In_      int nWidth,
      _In_      int nHeight,
      _In_opt_  HWND hWndParent,
      _In_opt_  HMENU hMenu,
      _In_opt_  HINSTANCE hInstance,
      _In_opt_  LPVOID lpParam
    );

    关键点在参数HMENU hWndParent,第一眼一看是不是感觉像菜单?不错啦~确实在菜单中用到的。进一步看他的解释

    hMenu [in, optional]

    Type: HMENU

    A handle to a menu, or specifies a child-window identifier depending on the window style. For an overlapped or pop-up window, hMenu identifies the menu to be used with the window; it can be NULL if the class menu is to be used. For a child window, hMenu specifies the child-window identifier, an integer value used by a dialog box control to notify its parent about events. The application determines the child-window identifier; it must be unique for all child windows with the same parent window.

    红色选中大体意思说,当在做一个子窗口的时候,这个参数就是一个整形表示,用于标识这个子窗口,提供父窗口的消息处理。总的来说就是所谓的控件ID啦~

    既然有标识,就好办啦,我们就判断是不是他,若是这个按钮,我们就让他响应相应的操作啦~~

    但是怎么知道按钮被Click了呢?我们又得监听另一个消息WM_COMMAND,它有两个参数,其中一个就传输了所谓的控件ID

    wParam

    For a description of this parameter.

    lParam

    For a description of this parameter.

    我们可以判断他如果是这个按钮的ID,我们就让他干某些事情。我们首先要转换下wParam的类型,否则和我们写的ID的类型不一样的。

    case WM_COMMAND:
    wmId    = LOWORD(wParam); 
    wmEvent = HIWORD(wParam);
         switch (wmId)
         {
             case (0)://之前我们创建按钮时,写的ID为0
    //功能代码 //....... break; //...更多代码 }

    好了,整体框架差不多完工啦,自己再加下代码啥的就没问题了。在这里,我再次浪费下篇幅,给出完整代码:

    关于wmId一开始写程序没有编译,因为逻辑错误,未有注意到,忘记声明他了。为了防止被default给忽略掉,我们把他写在switch(uMsg)之前。

     1 #include <windows.h>
     2 #include <stdio.h>
     3 #include <Windef.h>
     4 
     5 // 窗口过程函数
     6 LRESULT CALLBACK WinSunProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
     7 
     8 int WINAPI WinMain(
     9                    HINSTANCE hInstance,         // handle to current instance
    10                    HINSTANCE hPrevInstance,     // handle to previous instance
    11                    LPSTR lpCmdLine,             // command line
    12                    int nCmdShow                 // show state
    13                    )
    14 
    15 {
    16     // 窗口类
    17     WNDCLASS wndcls;
    18     wndcls.cbClsExtra = 0;                                          // 类附加内存
    19     wndcls.cbWndExtra = 0;                                          // 窗口附加内存
    20     wndcls.hbrBackground = (HBRUSH) GetStockObject(COLOR_WINDOW);    // 背景画刷句柄
    21     wndcls.hCursor = LoadCursor(NULL, IDC_ARROW);                   // 窗口光标句柄
    22     wndcls.hIcon = LoadIcon(NULL, IDI_APPLICATION);                       // 窗口图标句柄
    23     wndcls.hInstance = hInstance;                                   // 包含窗口过程函数的程序实例
    24     wndcls.lpfnWndProc = WinSunProc;                                // 只想窗口过程函数的指针
    25     wndcls.lpszClassName = "CRoot";                                // 窗口类名称
    26     wndcls.lpszMenuName = NULL;                                     // 菜单资源
    27     wndcls.style = CS_HREDRAW | CS_VREDRAW;                         // 窗口样式
    28     RegisterClass(&wndcls);
    29 
    30     // 创建窗口, 定义一个变量用来保存成功创建窗口后返回的句柄
    31     HWND hwnd;
    32     hwnd = CreateWindow(        // 窗口创建成功时返回为窗口分配的句柄 失败时返回NULL
    33         "CRoot",               // 窗口类名
    34         "Hello World",          // 窗口名字
    35         WS_CAPTION|WS_SYSMENU,    // 窗口样式
    36         CW_USEDEFAULT, CW_USEDEFAULT,                   // 窗口左上角坐标
    37         200, 120,               // 窗口宽高
    38         NULL,                   // 父窗口句柄
    39         NULL,                   // 窗口菜单句柄
    40         hInstance,              // 窗口所属应用程序实例
    41         NULL                    // WM_CREATE消息附加参数lParam传入的数据指针
    42         );
    43 
    44     // 显示及刷新窗口
    45     ShowWindow(hwnd, SW_SHOWNORMAL);
    46     UpdateWindow(hwnd);
    47 
    48     // 定义消息结构体
    49     MSG msg;
    50     while (GetMessage(  // WM_QUIT消息返回0 错误返回-1
    51         &msg,           // 指向消息的结构体
    52         NULL,           // 指定接收属于哪一窗口的消息 通常设为NULL,用来接收属于调用线程的所有窗口的窗口消息
    53         0,              // 获取消息的最小值 通常为0
    54         0))             // 获取消息的最大值 都设为0表示接收所有消息
    55     {
    56         TranslateMessage(&msg);     // 将虚拟消息转换为字符消息 投递到调用线程的消息队列中 下次调用GetMessage时被取出
    57         DispatchMessage(&msg);      // 将消息传递给操作系统 由操作系统调用窗口过程函数对消息进行处理
    58     }
    59     return msg.wParam;
    60 }
    61 
    62 // 窗口过程函数
    63 LRESULT CALLBACK WinSunProc(
    64     HWND hwnd,          // 窗口句柄
    65     UINT uMsg,          // 消息代码
    66     WPARAM wParam,      // 附加参数
    67     LPARAM lParam
    68 )
    69 {
    70     WORD wmId    = LOWORD(wParam);
    71     //WORD wmEvent = HIWORD(wParam);
    72     switch(uMsg)
    73     {
    74     /*case WM_CHAR:
    75         char szChar[20];
    76         sprintf(szChar, "char code is %d", wParam);
    77         MessageBox(hwnd, szChar, "char", 0);
    78         break;*/
    79     case WM_CREATE:
    80         CreateWindowEx(0,"Button","按钮1",WS_VISIBLE|WS_CHILD,0,0,70,40,hwnd,(HMENU)0,0,0);
    81         break;
    82     case WM_COMMAND:
    83         switch (wmId)
    84         {
    85             case 0://(HMENU) 和创建中的id对应
    86             //这里写你要让按钮实现的功能
    87                 MessageBoxA(NULL,"Hello Button!","My TestProject",MB_OK);
    88         }
    89         break;
    90 
    91     default:
    92         return DefWindowProc(hwnd, uMsg, wParam, lParam);
    93     }
    94 
    95     return 0;
    96 }
    View Code

    目前已经在GNU GCC Compiler成功!

  • 相关阅读:
    koa学习中的一系列问题-mongodb
    JS基础语法使用
    vue中的this指向问题
    CDN的问题
    vue基本语法及使用
    python自动化读取excel数据,写入excel数据,xlrd、xlutils
    jenkins配置邮件发送功能
    pytest生成的index.html报告发送邮箱后没有样式的解决办法
    pytest命令同时执行多个目录,多个不同目录下的文件
    pytest+jenkins+allure生成报告
  • 原文地址:https://www.cnblogs.com/croot/p/3485636.html
Copyright © 2020-2023  润新知