1.第一个图形界面
步骤一:创建Windows应用程序(win32 Application)
步骤二:在新建窗口中选择C++代码文件,创建一个新的cpp文件,在这里我们选择让编译器替我们创建入口函数
步骤三:在新的文件中添加:#include<Windows.h>
步骤四:设计窗口
//窗口的类名
TCHAR className[] = "My First Window";
// 创建窗口类的对象
WNDCLASS wndclass = {0}; //一定要先将所有值赋值
wndclass.hbrBackground = (HBRUSH)COLOR_MENU; //窗口的背景色
wndclass.lpfnWndProc = WindowProc; //窗口过程函数
wndclass.lpszClassName = className; //窗口类的名字
wndclass.hInstance = hInstance; //定义窗口类的应用程序的实例句柄
注意:
这里的wndclass对象必须进行初始化,因为在赋值时,我们并没有将所有的成员都进行赋值,因此在我们为进行初始化的时候,其他成员的值就是堆栈的原始状态,并不符合系统对于窗口属性的格式!!
这里WNDCLASS是一个结构体类型
WNDCLASS结构包含由RegisterClass函数注册的窗口类属性。
这个结构已经被与RegisterClassEx函数一起使用的WNDCLASSEX结构所取代。
如果不需要设置与窗口类相关联的小图标,仍然可以使用WNDCLASS和RegisterClass。
步骤五:注册窗口类
这里使用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;
在这里我们设置窗口的属性(一直疑惑为什么前面创建WNDCLASS结构体来设置窗口属性了,这里创建窗口的时候又有其他属性要设置,为什么不能放在一个结构体中直接定义一个自己的窗口呢)
步骤七:显示窗口
ShowWindow(hwnd, SW_SHOW);
注意到现在出现了两个句柄
WNDCLASS结构体中的->hInstance中存储的是定义窗口类的应用程序的实例句柄
CreateWindow函数返回的是窗口类的实例句柄(HWND)
步骤八:消息循环
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
个人理解:在消息循环阶段,我们定义一个消息类结构体,在消息循环时,GetMessage函数会对消息进行加工处理
可以看到,这里函数中的参数都是输出参数
GetMessage函数返回值:
如果函数检索到的消息不是WM_QUIT,返回值是非零的。
如果函数获取WM_QUIT消息,返回值为0。
如果出现错误,则返回值为-1。例如,如果hWnd是无效的窗口句柄或lpMsg是无效的指针,则函数失败。要获取扩展的错误信息,请调用GetLastError。
步骤九:回调函数
/*
窗口消息处理程序 窗口回调函数:
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;
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
注意:这里的回调函数并不能随意定义,必须按照操作系统定义的格式进行定义
2.什么是事件,什么是消息,什么是消息处理函数
事件 消息
Windows中的事件是一个“动作”,这个动作可能是用户操作应用程序产生的,也可能是Windows自己产生的.
而消息,就是用来描述这些“动作”的,比如:
这个动作是什么时候产生的?
哪个应用程序产生的?
在什么位置产生的?
等等。。。
Windows为了能够准确的描述这些信息,提供了一个结构体:MSG,该结构体里面记录的事件的详细信息.
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
} MSG, *PMSG;
说明:
1、hwnd:
个人理解为,hwnd是窗口的句柄
表示消息所属的窗口
一个消息一般都是与某个窗口相关联的
在Windows中 HWND类型的变量通常用来标识窗口。
2、message
在Windows中,消息是由一个数值来表示的
但是由于数值不便于记忆,所以Windows将消息对应的数值定义为WM_XXX宏(WM == Window Message)
鼠标左键按下 WM_LBUTTONDOWN 键盘按下 WM_KEYDOWN
3、wParam 和 lParam
32位消息的特定附加信息,具体表示什么处决于message
4、time
消息创建时的时间
5、消息创建时的鼠标位置
消息处理函数就是回调函数
为什么叫回调函数?因为该函数并不是由我们来进行调用,而是系统将消息进行加工处理后,将消息分发给操作系统,系统再调用我们的消息处理函数进行处理操作
用户产生的事件通过MSG进行描述后,首先在系统队列中进行排队,通过分类后来到了属于各自的应用程序消息队列中再进行排队