1.按钮是什么
前面我们创建了第一个窗口
首先定义了WinMain函数,还有四个参数
第一个参数是程序的imgebase
第二个参数一般都为NULL,所以不用管
第三个参数是命令行参数
第四个参数是窗口显示的模式
其次我们创建了一个WNDCLASS结构体的对象wndclass对其进行初始化
然后对成员进行赋值
hbrBackground 背景板的颜色
lpfnWndProc 消息处理函数的名字
lpszClassName 窗口类的名字
hInstance 定义窗口类的程序的实例句柄(imagebase)
然后调用RegisterWindow函数进行注册
参数为wndclass对象的地址
再然定义了一个窗口句柄的指针hwnd,用来接收CreateWindow函数的返回值
//创建窗口
HWND hwnd=CreateWindow(
className, //类名(这里使用定义类名是为了告诉系统这里我需要使用自己定义的窗口属性)
TEXT("我的第一个窗口"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口外观样式
10, //相对于父窗口的x坐标
10, //相对于父窗口的y坐标
600, //窗口的宽度
300, //窗口的高度
NULL, //父窗口句柄,为NULL
NULL, //菜单句柄,为NULL
hInstance, //当前应用程序的句柄
NULL); //附加数据一般为NULL
if(hwnd==NULL) //是否创建成功
return 0;
然后显示窗口——————使用ShowWindow函数
参数:
hwnd 创建窗口的句柄
nCmdShow 前面WinMain函数的第四个参数(表示显示的模式)
下面我们在项目中添加如下代码:
// WinMain.cpp : Defines the entry point for the application.
//
#include "stdAfx.h"
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam);
HINSTANCE hAppInstance;
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);
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//OutputDebugStringF("------------ %s\n",lpCmdLine);
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam);
//窗口的类名
TCHAR className[]="My First Window";
//创建窗口类的对象
WNDCLASS wndclass={0}; //初始化
wndclass.hbrBackground=(HBRUSH)COLOR_MENU; //背景板颜色(HBRUSH)是背景画刷句柄
wndclass.lpfnWndProc=WindowProc; //消息处理函数名称
wndclass.lpszClassName=className; //窗口类的名字
wndclass.hInstance=hInstance; //定义窗口类应用程序的实例句柄
RegisterClass(&wndclass); //注册窗口类
//创建窗口
HWND hwnd=CreateWindow(
className, //类名(这里使用定义类名是为了告诉系统这里我需要使用自己定义的窗口属性)
TEXT("我的第一个窗口"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口外观样式
10, //相对于父窗口的x坐标
10, //相对于父窗口的y坐标
600, //窗口的宽度
300, //窗口的高度
NULL, //父窗口句柄,为NULL
NULL, //菜单句柄,为NULL
hInstance, //当前应用程序的句柄
NULL); //附加数据一般为NULL
if(hwnd==NULL) //是否创建成功
return 0;
hAppInstance=hInstance;
CreateButton(hwnd); //创建BUTTON
//显示窗口
ShowWindow(hwnd,nCmdShow);
//消息循环
MSG msg;
while(GetMessage(&msg,NULL,0,0)) //
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
switch(uMsg)
{
//窗口消息
case WM_CREATE:
{
//OutputDebugStringF("WM_CREATE %d %d\n",wParam,lParam);
OutputDebugStringF("WM_CREATE %d\n",uMsg);
CREATESTRUCT* createst = (CREATESTRUCT*)lParam;
OutputDebugStringF("CREATESTRUCT %s\n",createst->lpszClass);
return 0;
}
case WM_DESTROY:
{
OutputDebugStringF("WM_DESTROY %d %d\n",wParam,lParam);
OutputDebugStringF("WM_CREATE %d\n",uMsg);
PostQuitMessage(0);
return 0;
}
case WM_MOVE:
{
DbgPrintf("WM_MOVE %d %d\n",wParam,lParam);
DWORD xPos = (int)(short) LOWORD(lParam); // horizontal position
DWORD yPos = (int)(short) HIWORD(lParam); // vertical position
//POINTS points = MAKEPOINTS(lParam);
OutputDebugStringF("X Y %d %d\n",xPos,yPos);
return 0;
}
case WM_KEYUP:
{
OutputDebugStringF("WM_KEYUP %d %d\n",wParam,lParam);
return 0;
}
case WM_KEYDOWN:
{
OutputDebugStringF("WM_KEYDOWN %d %d\n",wParam,lParam);
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);
OutputDebugStringF("WM_SIZE %d %d\n",newWidth,newHeight);
return 0;
}
//键盘消息
//鼠标消息
case WM_LBUTTONDOWN:
{
OutputDebugStringF("WM_LBUTTONDOWN %d %d\n",wParam,lParam);
POINTS points = MAKEPOINTS(lParam);
OutputDebugStringF("WM_LBUTTONDOWN %d %d\n",points.x,points.y);
return 0;
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
注意:在创建Button的函数中有一个参数是hInstance,因此我们需要将hInstance创建为全局变量来方便使用,并且一定要将CreateButton函数的调用放在父窗口被创建之前(没有父何来子)
在这里我们进行点击测试一下,发现父类窗口的回调函数依旧能够正常运行
但是点击按钮窗口之后就不做反应,我们可以推测是按钮窗口类的回调函数并没有运行
两个疑问:
第一个问题:CreateWindow函数第一个参数之前我们传参的是我们自己定义注册的一个类名,这里直接使用了系统定义注册好了的类名,因此我们可以 直接使用系统给我们设置好的窗口类
第二个问题:在之前我们创建窗口时传参的是NULL,因为我们不需要定义菜单,但是该位置的参数代表了菜单的句柄,在这里,不再代表菜单句柄,代表的是该被创建窗口的编号
总结:按钮的实质就是窗口
2.按钮事件的处理
前面我们点击窗口测试了回调函数是否运行可知:父窗口的回调函数正常,按钮窗口的会点番薯没有反应
按钮的WNDCLASS不是我们定义的,是系统预定义好的。如果我们想知道,系统预定义的WNDCLASS都包含什么样的信息,怎么做?
TCHAR szBuffer[0x20];
GetClassName(hwndPushButton,szBuffer,0x20);
WNDCLASS wc;
GetClassInfo(hAppInstance,szBuffer,&wc);
OutputDebugStringF("-->%s\n",wc.lpszClassName);
OutputDebugStringF("-->%x\n",wc.lpfnWndProc);
GetClassName函数:获取指定窗口所属的类的名称。
第一个参数:窗口的句柄
第二个参数:(是一个输出参数)将类名输出值该空间
第三个参数:(第二个输出参数的大小)类名参数空间的大小
GetClassInfo函数:检索窗口类的信息。(即将类窗口的信息输出到参数中)
第一个参数:应用程序实例句柄(不是窗口类句柄)
第二个参数:类名
第三个参数:(输出参数)存储类信息的缓冲的地址
我们可以理解为,按钮类窗口是系统定义的窗口类,因此消息处理函数也是系统给的
但是系统给的消息处理函数实现的功能是将按钮的消息转换为WM_COMMAND后
再发给父窗口的消息处理函数,由父窗口的消息处理函数来进行功能的实现
我们在父窗口的消息处理函数中添加一个case 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);
}
此时的case选项应该用前面按钮窗口属性中的编号来进行判断
// WinMain.cpp : Defines the entry point for the application.
//
#include "stdAfx.h"
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam);
HINSTANCE hAppInstance;
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);
/* TCHAR szBuffer[0x20]={0};
GetClassName(hwndPushButton,szBuffer,0x20);
WNDCLASS mc;
GetClassInfo(hAppInstance,szBuffer,&mc);
OutputDebugStringF("-->%s\n",mc.lpszClassName);
OutputDebugStringF("-->%x\n",mc.lpfnWndProc);*/
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
//OutputDebugStringF("------------ %s\n",lpCmdLine);
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam);
//窗口的类名
TCHAR className[]="My First Window";
//创建窗口类的对象
WNDCLASS wndclass={0}; //初始化
wndclass.hbrBackground=(HBRUSH)COLOR_MENU; //背景板颜色(HBRUSH)是背景画刷句柄
wndclass.lpfnWndProc=WindowProc; //消息处理函数名称
wndclass.lpszClassName=className; //窗口类的名字
wndclass.hInstance=hInstance; //定义窗口类应用程序的实例句柄
RegisterClass(&wndclass); //注册窗口类
//创建窗口
HWND hwnd=CreateWindow(
className, //类名(这里使用定义类名是为了告诉系统这里我需要使用自己定义的窗口属性)
TEXT("我的第一个窗口"), //窗口标题
WS_OVERLAPPEDWINDOW, //窗口外观样式
10, //相对于父窗口的x坐标
10, //相对于父窗口的y坐标
600, //窗口的宽度
300, //窗口的高度
NULL, //父窗口句柄,为NULL
NULL, //菜单句柄,为NULL
hInstance, //当前应用程序的句柄
NULL); //附加数据一般为NULL
if(hwnd==NULL) //是否创建成功
return 0;
hAppInstance=hInstance;
CreateButton(hwnd); //创建BUTTON
//显示窗口
ShowWindow(hwnd,nCmdShow);
//消息循环
MSG msg;
while(GetMessage(&msg,NULL,0,0)) //
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return 0;
}
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam)
{
switch(uMsg)
{
//窗口消息
case WM_CREATE:
{
//OutputDebugStringF("WM_CREATE %d %d\n",wParam,lParam);
OutputDebugStringF("WM_CREATE %d\n",uMsg);
CREATESTRUCT* createst = (CREATESTRUCT*)lParam;
OutputDebugStringF("CREATESTRUCT %s\n",createst->lpszClass);
return 0;
}
case WM_DESTROY:
{
OutputDebugStringF("WM_DESTROY %d %d\n",wParam,lParam);
OutputDebugStringF("WM_CREATE %d\n",uMsg);
PostQuitMessage(0);
return 0;
}
case WM_MOVE:
{
DbgPrintf("WM_MOVE %d %d\n",wParam,lParam);
DWORD xPos = (int)(short) LOWORD(lParam); // horizontal position
DWORD yPos = (int)(short) HIWORD(lParam); // vertical position
//POINTS points = MAKEPOINTS(lParam);
OutputDebugStringF("X Y %d %d\n",xPos,yPos);
return 0;
}
case WM_KEYUP:
{
OutputDebugStringF("WM_KEYUP %d %d\n",wParam,lParam);
return 0;
}
case WM_KEYDOWN:
{
OutputDebugStringF("WM_KEYDOWN %d %d\n",wParam,lParam);
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);
OutputDebugStringF("WM_SIZE %d %d\n",newWidth,newHeight);
return 0;
}
//键盘消息
//鼠标消息
case WM_LBUTTONDOWN:
{
OutputDebugStringF("WM_LBUTTONDOWN %d %d\n",wParam,lParam);
POINTS points = MAKEPOINTS(lParam);
OutputDebugStringF("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.作业_按钮事件处理逻辑定位
现在我们找到了按钮窗口的消息处理入口,观察反汇编我们可以发现3个case
我们在函数调用最开始位置下断点,然后设置条件判断
[esp+8]==WM_COMMAND && [esp+c]==0x3e9
来定位到我们想要的消息处理的位置
在这里我们发现,在callMessageBox函数后,case又跳到了函数最开始的位置
因此我们可以判断,这里的case选项中没有return 0条件表达式,因此在跳到函数调用
来到switch位置时并不能进入case中,只有来到default中