win32按钮

扫码查看
1.按钮是什么
在win32窗口中,经常可以看到按钮,点击按钮可以触发各种事件;
 
创建按钮的函数:
void CreateButton(HWND hwnd)    //参数为父窗口句柄,按钮必须属于一个父窗口,因此该函数只要在父窗口创建完成即父窗口的CreateWindow函数调用完成之后就能调用;                                    
{
    HWND hwndPushButton;

    hwndPushButton = CreateWindow (
        TEXT("button"),
        TEXT("普通按钮"),
        //WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,                            
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,
        10, 10,
        80, 20,
        hwnd,
        (HMENU)1001,        //子窗口ID                    
        hAppInstance,         //应用程序的句柄,也就是winmian中的参数hinstance                        
        NULL);
}
可以看到创建按钮和创建普通窗口一样,都是调用CreateWindow函数来实现;
创建窗口需要的步骤:1、创建窗口类;2、注册窗口类;3、创建窗口;
创建按钮直接CreateWindow,省去了前两步;
 
总结:
    按钮本质上就是一个窗口;
    按钮是系统定义好的窗口,创建按钮时只需在CreateWindow的第一个参数也就是窗口类名设为TEXT("button")即可;
 
单选框和多选框也是按钮;
不过是调用CreateWindow时传入的窗口外观样式不同而已;
按钮的窗口外观样式可以在msdn文档中查得到;
hwndCheckBox = CreateWindow (
    TEXT("button"),
    TEXT("复选框"),
    //WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_AUTOCHECKBOX,                        
    WS_CHILD | WS_VISIBLE | BS_CHECKBOX |BS_AUTOCHECKBOX ,
    10, 40,
    80, 20,
    hwnd,
    (HMENU)1002,        //子窗口ID                
    hAppInstance,
    NULL);

hwndRadio = CreateWindow (
    TEXT("button"),
    TEXT("单选按钮"),
    //WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON | BS_AUTORADIOBUTTON,                        
    WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON  ,
    10, 70,
    80, 20,
    hwnd,
    (HMENU)1003,        //子窗口ID                
    hAppInstance,
    NULL);     
子窗口id:
    在winmain中创建的窗口CreateWindow函数的倒数第三个参数为菜单句柄;
    菜单句柄的作用是给窗口添加菜单;
    如果是子窗口,则该参数表示子窗口id,用来在程序中唯一标识该窗口;
    将一个整数强转为HMENU类型来传入;
 
2.按钮事件的处理
1)关于按钮消息处理的原理
按钮本质上是一个窗口;
窗口通过注册的WNDCLASS结构中回调函数来处理事件;
按钮用来点击触发一些事件,就是通过回调函数来实现的;
按钮是系统创建的窗口,其回调函数也是系统创建的,因此并不需要给它提供回调函数;
 
按钮的WNDCLASS不是我们定义的,是系统预定义好的。
如果我们想知道,系统预定义的WNDCLASS都包含什么样的信息怎么做?                                        
TCHAR szBuffer[0x20];
GetClassName(hwndPushButton,szBuffer,0x20);     //获取wndclass结构的类名;参数:1.目标按钮的句柄、2.存结果的缓冲区、3.缓冲区字节数               

WNDCLASS wc;
GetClassInfo(hAppInstance,szBuffer,&wc);         //这个函数可以获取wndclass结构的所有属性; 参数:1.应用程序句柄、2.目标wndclass结构的类名,用上一个函数获取、3.存结果的缓冲区                   
OutputDebugStringF("-->%s\n",wc.lpszClassName);
OutputDebugStringF("-->%x\n",wc.lpfnWndProc);        
2)按钮消息处理机制
按钮是一种特殊的窗体,并不需要提供单独的窗口回调函数.                            
当按钮有事件产生时,会给父窗口消息处理程序发送一个WM_COMMAND消息    
 
也就是说当点击按钮时:
    因为点击的是按钮这个窗口,会触发按钮的回调函数,也就是系统提供的WinProc;
    系统回调函数处理该消息的方式为:将点击事件转换为WM_COMMAND消息;
    父窗口的接收到WM_COMMAND消息后,调用父窗口回调函数来处理;
 
如果有多个按钮,父窗口的回调函数在处理WM_COMMAND消息时,需要区分到底是哪个按钮被点击;
WM_COMMAND消息会在消息附加信息wParam的低2字节中带上按钮的Id;
按钮的id用来唯一标识程序中的按钮,为按钮创建时的createwindow函数的倒数第三个参数;
WM_COMMAND消息处理:
case WM_COMMAND:
{
    switch(LOWORD(wParam))
    {
        case 1001:
            MessageBox(hwnd,"Hello Button 1","Demo",MB_OK);
            return 0;
        case 1002:
            MessageBox(hwnd,"Hello Button 2","Demo",MB_OK);
            return 0;
        case 1003:
            MessageBox(hwnd,"Hello Button 3","Demo",MB_OK);
            return 0;
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}  

                      

3)带按钮的窗口程序实例
代码:
#include<stdio.h>
#include<windows.h>
#include "windebug.h"


HINSTANCE hAppInstance;    //应用程序句柄,因为要使用的地方太多需要定义成全局变量;在winmain函数中给它赋值


//创建按钮
void CreateButton(HWND hwnd)
{
    HWND hwndPushButton;
    HWND hwndCheckBox;
    HWND hwndRadio;

    hwndPushButton = CreateWindow (
        TEXT("button"),
        TEXT("普通按钮"),
        //WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,                            
        WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON | BS_DEFPUSHBUTTON,
        10, 10,
        80, 20,
        hwnd,
        (HMENU)1001,        //子窗口ID                    
        hAppInstance,         //应用程序的句柄,                        
        NULL);

    hwndCheckBox = CreateWindow (
        TEXT("button"),
        TEXT("复选框"),
        //WS_CHILD | WS_VISIBLE | BS_CHECKBOX | BS_AUTOCHECKBOX,                            
        WS_CHILD | WS_VISIBLE | BS_CHECKBOX |BS_AUTOCHECKBOX ,
        10, 40,
        80, 20,
        hwnd,
        (HMENU)1002,        //子窗口ID                    
        hAppInstance,
        NULL);

    hwndRadio = CreateWindow (
        TEXT("button"),
        TEXT("单选按钮"),
        //WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON | BS_AUTORADIOBUTTON,                            
        WS_CHILD | WS_VISIBLE | BS_RADIOBUTTON  ,
        10, 70,
        80, 20,
        hwnd,
        (HMENU)1003,        //子窗口ID                    
        hAppInstance,
        NULL);
}


//声明回调函数
LRESULT CALLBACK WindowProc(
    IN  HWND hwnd,
    IN  UINT uMsg,
    IN  WPARAM wParam,
    IN  LPARAM lParam

);


int CALLBACK WinMain(                        //CALLBACK是一个宏,表示__stdcall,也就是内平栈,win32所有api函数都是该调用约定
            HINSTANCE hInstance,
            HINSTANCE hPrevInstance,
            LPSTR lpCmdLine,
            int nCmdShow
            ){
    hAppInstance = hInstance;    //给全局变量赋值


    //1.告诉window要画一个什么样的窗口                                    
    TCHAR className[] = "My First Window";     //窗口的类名
    // 创建窗口类的对象                                     
    WNDCLASS wndclass = {0};                        //一定要先将所有值赋值,否则RegisterClass函数无法起作用;            
    wndclass.hbrBackground = (HBRUSH)COLOR_MENU;                        //窗口的背景色            
    wndclass.lpfnWndProc =  WindowProc;                        //窗口过程函数            
    wndclass.lpszClassName = className;                        //窗口类的名字            
    wndclass.hInstance = hInstance;                        //定义窗口类的应用程序的实例句柄


    //2.注册窗口类
    RegisterClass(&wndclass);


    //3.创建窗口类
    HWND hwnd = CreateWindow(
        className,                //类名,可以用自己定义的窗口My First Window,也可用系统定义好的窗口例如按钮button        
        TEXT("我的第一个窗口"),                //窗口标题        
        WS_OVERLAPPEDWINDOW,                //窗口外观样式         
        10,                //相对于父窗口的X坐标        
        10,                //相对于父窗口的Y坐标        
        600,                //窗口的宽度          
        300,                //窗口的高度          
        NULL,                //父窗口句柄,为NULL          
        NULL,                //菜单句柄,为NULL          
        hInstance,                //当前应用程序的句柄          
        NULL);                //附加数据一般为NULL        

    if(hwnd == NULL)                    //是否创建成功          
        return 0;

    CreateButton(hwnd);        //创建按钮,需要在父窗口创建成功后调用


    //4.显示窗口
    ShowWindow(hwnd, SW_SHOW);


    //5.消息循环
    MSG msg;
    while(GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return 0;
}


//6.回调函数,由操作系统来调用
/*
窗口消息处理程序 窗口回调函数:

1、窗口回调函数处理过的消息,必须传回0.

2、窗口回调不处理的消息,由DefWindowProc来处理.
*/


LRESULT CALLBACK WindowProc(
                            IN  HWND hwnd,
                            IN  UINT uMsg,
                            IN  WPARAM wParam,
                            IN  LPARAM lParam
                            )
{
    switch(uMsg)
    {
        //窗口消息                            
    case WM_CREATE:
        {
            DbgPrintf("WM_CREATE %d %d\n",wParam,lParam);
            CREATESTRUCT* createst = (CREATESTRUCT*)lParam;
            DbgPrintf("CREATESTRUCT %s\n",createst->lpszClass);

            return 0;
        }
    case WM_MOVE:
        {
            DbgPrintf("WM_MOVE %d %d\n",wParam,lParam);
            POINTS points = MAKEPOINTS(lParam);
            DbgPrintf("X Y %d %d\n",points.x,points.y);

            return 0;
        }
    case WM_SIZE:
        {
            DbgPrintf("WM_SIZE %d %d\n",wParam,lParam);
            int newWidth  = (int)(short) LOWORD(lParam);
            int newHeight  = (int)(short) HIWORD(lParam);
            DbgPrintf("WM_SIZE %d %d\n",newWidth,newHeight);

            return 0;
        }
    case WM_DESTROY:
        {
            DbgPrintf("WM_DESTROY %d %d\n",wParam,lParam);
            PostQuitMessage(0);

            return 0;
        }
        //键盘消息                            
    case WM_KEYUP:
        {
            DbgPrintf("WM_KEYUP %d %d\n",wParam,lParam);

            return 0;
        }
    case WM_KEYDOWN:
        {
            DbgPrintf("WM_KEYDOWN %d %d\n",wParam,lParam);

            return 0;
        }
        //鼠标消息                            
    case WM_LBUTTONDOWN:
        {
            DbgPrintf("WM_LBUTTONDOWN %d %d\n",wParam,lParam);
            POINTS points = MAKEPOINTS(lParam);
            DbgPrintf("WM_LBUTTONDOWN %d %d\n",points.x,points.y);

            return 0;
        }
        //按钮消息
    case WM_COMMAND:
        {
            switch(LOWORD(wParam))
            {
                case 1001:
                    MessageBox(hwnd,"Hello Button 1","Demo",MB_OK);
                    return 0;
                case 1002:
                    MessageBox(hwnd,"Hello Button 2","Demo",MB_OK);
                    return 0;
                case 1003:
                    MessageBox(hwnd,"Hello Button 3","Demo",MB_OK);
                    return 0;
            }
            return DefWindowProc(hwnd,uMsg,wParam,lParam);
        }
    }
    return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
窗口:
 
3.分析按钮的事件处理
对于有多个按钮的窗口,有时需要只分析一个按钮事件处理;
 
例如:自分析上图窗口中的普通按钮,单选按钮和复选按钮不分析;
首先要找到程序入口winmain;
然后找回调函数,在回调函数开始处下断点;
因为回调函数可能需要处理很多消息,需要筛选按钮事件的消息WM_COMMAND;
也就是给断点添加条件;
条件为消息类型为WM_COMMAND,并且wParam所带的窗口id为普通按钮的id;
断点处回调函数的堆栈:
 
然后确定普通按钮的id:
    在od中点W可以查看子窗口信息
 
然后确定断点条件:
 
 
 
 
 
12-13 17:36
查看更多