WINDOWS消息
Unit01消息队列
01消息队列概念
- 消息队列是用于存放消息的队列
- 消息在队列中先进先出
- 所有窗口程序都有消息队列
- 程序(GetMessage)可以从队列中获消息
02消息队列分类
- 系统消息队列:由系统维护的消息队列(这个队列非常庞大),存放系统产生的所有消息,例如鼠标、键盘等
- 程序消息队列:属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护
03消息和队列关系
- 消息和消息队列的关系
- 当鼠标、键盘产生消息时,会将消息放到系统消息队列
- 系统会根据存放的消息,找到对应程序的消息队列
- 将消息投递到程序的消息队列中
- 根据消息和消息队列之间使用关系,将消息分成两类:
- 队列消息:消息的发送和获取,都是通过消息队列完成
- 非队列消息:消息的发送和获取,是直接调用消息的窗口处理完成(就是消息不进队列直接调用消息处理函数对消息进行处理)
- 队列消息:消息发送后,首先放入队列,然后通过消息循环,从队列当中完成
- GetMessage:从消息队列中获取消息
- PostMessage:将消息投递到消息队列
- 常见队列消息:WM_PAINT、键盘、鼠标、定时器
- 非队列消息:消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息
- SendMessage:直接将消息发送给窗口的处理函数,并等待处理结果
- 常见非队列消息:WM_CREATE、WM_SIZE等
04深谈GetMessage原理
- 在程序(线程)消息队列超找消息,如果队列中有消息,检查消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,否则从队列中取出消息返回
- 如果程序(线程)消息队列中没有消息,向系统消息队列获取属于本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中。
- 如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINT消息,取得消息返回处理
- 如果没有重新绘制的区域,检查定时器如果有到时得定时器,产生WM_TIMER,返回处理执行
- 如果没有到时的定时器,整理程序的资源、内存等
- GetMessage会继续等待下一条消息。PeekMessage会返回FALSE,交出程序的控制权
- 注意:GetMessage如果获取到时WM_QUIT,函数或返回FALSE
05WM_PAINT消息
- 产生时间:当窗口需要绘制的时候
- 附带信息(对于此消息,附带消息没用)
- wParam:0
- lParam:0
- 专职用法:用于绘图
#include <windows.h>
HANDLE g_hOutput = 0;
void OnPaint(HWND hWnd){
char* pszText = "WM_PAINT\n";
WriteConsole(g_hOutput,pszText,strlen(pszText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_PAINT:
OnPaint(hWnd);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
- 窗口无效区域:需要重新绘制的区域
BOOL InvalidateRect(
HWND hWnd, // handle to window 窗口句柄
CONST RECT* lpRect, // rectangle coordinates 区域的矩形坐标(如果为NULL指的是整个窗口)
BOOL bErase // erase state 重绘前是否先擦除
);
示例代码:
#include <windows.h>
HANDLE g_hOutput = 0;
void OnPaint(HWND hWnd){
char* pszText = "WM_PAINT\n";
WriteConsole(g_hOutput,pszText,strlen(pszText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_PAINT:
OnPaint(hWnd);
break;
case WM_LBUTTONDOWN://点击鼠标的左键消息
InvalidateRect(hWnd,NULL,TRUE);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
- 消息处理步骤
- 开始绘图
HDC BeginPaint( HWND hwnd, // handle to window 绘制窗口句柄 LPPAINTSTRUCT lpPaint // paint information 绘图参数的BUFF );//返回绘图设备句柄HDC
- 正式绘图
- 结束绘图
BOOL EndPaint( HWND hWnd, // handle to window 绘制窗口句柄 CONST PAINTSTRUCT *lpPaint // paint data 绘图参数的指针BeginPaint返回 );
示例代码:
#include <windows.h>
HANDLE g_hOutput = 0;
void OnPaint(HWND hWnd){
char* pszText = "WM_PAINT\n";
WriteConsole(g_hOutput,pszText,strlen(pszText),NULL,NULL);
//这个结构体内部的内容不用管
PAINTSTRUCT ps = {0};
//开始绘图
HDC hdc = BeginPaint(hWnd, &ps);
//正式绘图
TextOut(hdc, 100, 100, "hello", 5);
//结束绘图
EndPaint(hWnd, &ps);
//以上绘图的代码,必须放在处理WM_PAINT消息时调用
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_PAINT:
OnPaint(hWnd);
break;
case WM_LBUTTONDOWN://点击鼠标的左键消息
InvalidateRect(hWnd,NULL,TRUE);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
Unit02键盘消息
01键盘消息分类
WM_KEYDOWN
按键被按下时产生WM_KEYUP
按键被放开时产生WM_SYSKEYDOWN
系统按键下时产生,比如:ALT、F10WM_SYSKEYUP
系统按键放开时产生- 这些消息附带信息:
- WPARAM:按键的Virtual Key(键码值世界通用)
- LPARAM:按键的参数,例如按下次数
#include <windows.h>
#include <stdio.h>
HANDLE g_hOutput = 0;
void OnKeyDown(HWND hWnd,WPARAM wParam){
char szText[256] = {0};
sprintf(szText,"WM_KEYDOWN:键码值=%d\n",wParam);
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnKeyUp(HWND hWnd,WPARAM wParam){
char szText[256] = {0};
sprintf(szText,"WM_KEYUP:键码值=%d\n",wParam);
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_KEYDOWN:
OnKeyDown(hWnd,wParam);
break;
case WM_KEYUP:
OnKeyUp(hWnd,wParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
图中笔误TransMessage应该是TranslateMessage
02字符消息(WM_CHAR)
TreanslateMessage
在转换WM_KEYDOWN
消息时,对于可见字符可以产生WM_CHAR
,不可见字符没有此消息- 附带信息:
- WPARAM:输入的字符的ASCII字符编码值
- LPARAM:按键相关参数(这个参数在此没什么用)
TranslateMessage
函数执行过程的伪代码:
TranslateMessage(&Msg){
if(nMsg.message != WM_KEYDOWN){
return;
}
//根据nMsg.wParam(键码值)可以获取哪个按键被按下
if(不可见字符的按键){
return;
}
//查看CapsLock(大写锁定键)是否处于打开状态
if(打开)
//65是A的键码值(示例按下的是A键)
PostMessage(nMsg.hwnd,WM_CHAR,65,...);
else
//97是a的键码值
PostMessage(nMsg.hwnd,WM_CHAR,97,...);
}
示例代码
#include <windows.h>
#include <stdio.h>
HANDLE g_hOutput = 0;
void OnKeyDown(HWND hWnd,WPARAM wParam){
char szText[256] = {0};
sprintf(szText,"WM_KEYDOWN:键码值=%d\n",wParam);
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnKeyUp(HWND hWnd,WPARAM wParam){
char szText[256] = {0};
sprintf(szText,"WM_KEYUP:键码值=%d\n",wParam);
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnChar(HWND hWnd,WPARAM wParam){
char szText[256] = {0};
sprintf(szText,"WM_KEYUP:键码值=%d\n",wParam);
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_CHAR:
OnChar(hWnd,wParam);
break;
case WM_KEYDOWN:
OnKeyDown(hWnd,wParam);
break;
case WM_KEYUP:
OnKeyUp(hWnd,wParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
Unit03鼠标消息
01鼠标消息分类
- 基本鼠标消息
WM_LBUTTONDOWN
鼠标左键按下WM_LBUTTONUP
鼠标左键抬起WM_RBUTTONDOWN
鼠标右键按下WM_RBUTTONUP
鼠标右键抬起WM_MOUSEMOVE
鼠标移动消息
- 双击消息
WM_LBUTTONDBLCLK
鼠标左键双击WM_RBUTTONDBLCLK
鼠标右键双击
- 滚轮消息
WM_MOUSEWHEEL
鼠标滚轮消息
02鼠标基本消息
- 附带信息
- WPARAM:其他按键的状态,例如Ctrl/shift等
- LPARAM:
- 鼠标的位置,窗口客户坐标系
LOWORD
x坐标的位置HIWORD
y坐标的位置
- 鼠标的位置,窗口客户坐标系
- 一般情况鼠标按下或抬起成对出现,在鼠标移动过程中,会根据移动速度产生一系列的
WM_MOUSEMOVE
消息
#include <windows.h>
#include <stdio.h>
HANDLE g_hOutput = 0;
void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_LBUTTONDOWN:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_LBUTTONUP:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_MOUSEMOVE:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_MOUSEMOVE:
OnMouseMove(hWnd,wParam,lParam);
break;
case WM_LBUTTONDOWN:
OnLButtonDown(hWnd,wParam,lParam);
break;
case WM_LBUTTONUP:
OnLButtonUp(hWnd,wParam,lParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
03鼠标双击消息
- 附带信息
- WPARAM:其他按键的状态,例如Ctrl/shift等
- LPARAM:
- 鼠标的位置,窗口客户坐标系
LOWORD
x坐标的位置HIWORD
y坐标的位置
- 鼠标的位置,窗口客户坐标系
- 消息产生顺序
- 以左键双击为例
WM_LBUTTINDOWN
WM_LBUTTONUP
WM_LBUTTONBLACK
WM_LBUTTONUP
- 以左键双击为例
注意
:使用时需要在注册窗口类的时候加上CS_DBLCLKS
风格
#include <windows.h>
#include <stdio.h>
HANDLE g_hOutput = 0;
void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_LBUTTONDOWN:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_LBUTTONUP:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_MOUSEMOVE:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
//WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnButtonDoubleClick(HWND hWnd,WPARAM wParam,LPARAM lParam){
char* szText = "WM_LBUTTONDBLCLK\n";
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_LBUTTONDBLCLK:
OnButtonDoubleClick(hWnd,wParam,lParam);
break;
case WM_MOUSEMOVE:
OnMouseMove(hWnd,wParam,lParam);
break;
case WM_LBUTTONDOWN:
OnLButtonDown(hWnd,wParam,lParam);
break;
case WM_LBUTTONUP:
OnLButtonUp(hWnd,wParam,lParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
04鼠标滚动消息
- 附带信息
- WPARAM:
LOWORD
其他按键的状态HIWORD
滚轮的偏移量,通过正负值表示滚动的方向(正:向前滚动,负:向后滚动)
- LPARAM:鼠标当前的位置,屏幕坐标系
LOWORD
x坐标的位置HIWORD
y坐标的位置
- WPARAM:
- 使用:通过偏移量获取滚动的
方向
和距离
#include <windows.h>
#include <stdio.h>
HANDLE g_hOutput = 0;
void OnLButtonDown(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_LBUTTONDOWN:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnLButtonUp(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_LBUTTONUP:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnMouseMove(HWND hWnd,WPARAM wParam,LPARAM lParam){
char szText[256] = {0};
sprintf(szText,"WM_MOUSEMOVE:其他按键状态:%d, X=%d,Y=%d\n",
wParam,LOWORD(lParam),HIWORD(lParam));
//WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnButtonDoubleClick(HWND hWnd,WPARAM wParam,LPARAM lParam){
char* szText = "WM_LBUTTONDBLCLK\n";
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
void OnMouseWheel(HWND hWnd, WPARAM wParam){
short nDelta = HIWORD(wParam);
char szText[256] = {0};
sprintf(szText,"WM_MOUSEWHEEL:nDelta=%d\n", nDelta);
WriteConsole(g_hOutput, szText, strlen(szText), NULL, NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_MOUSEWHEEL:
OnMouseWheel(hWnd, wParam);
break;
case WM_LBUTTONDBLCLK:
OnButtonDoubleClick(hWnd,wParam,lParam);
break;
case WM_MOUSEMOVE:
OnMouseMove(hWnd,wParam,lParam);
break;
case WM_LBUTTONDOWN:
OnLButtonDown(hWnd,wParam,lParam);
break;
case WM_LBUTTONUP:
OnLButtonUp(hWnd,wParam,lParam);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW|CS_DBLCLKS;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
Unit04定时器消息
01定时器的消息介绍
- 产生时间:在程序中创建定时器,当达到时间间隔时,定时器会向程序发送一个
WM_TIMER
消息(实际上是GetMessage发送的消息)
。定时器的精度是毫秒
,但是准确度很低。例如设置时间间隔为1000ms,但是会在非1000毫秒到达消息。 - 附带信息:
wParam
:定时器IDlParam
:定时器处理函数指针
02创建销毁定时器
- 创还能定时器
UINT_PTR SetTimer(
HWND hWnd,//定时器窗口句柄
UINT_PTR nIDEvent,//定时器ID
UINT uElapse,//时间间隔
TIMERPROC lpTimerFunc//定时器处理函数指针(一般不使用,为NULL)
);//创建成功返回非0
- 关闭定时器
BOOL KillTimer(
HWND hWnd,//定时器窗口句柄
UINT_PTR uIDEvent//定时器ID
);
示例代码:
#include <windows.h>
#include <stdio.h>
HANDLE g_hOutput = 0;
void OnTimer(HWND hWnd,WPARAM wParam){
char szText[256] = {0};
sprintf(szText,"WM_TIMER:定时器ID=%d\n",wParam);
WriteConsole(g_hOutput,szText,strlen(szText),NULL,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_TIMER:
OnTimer(hWnd, wParam);
break;
case WM_CREATE:
SetTimer(hWnd, 1, 1000, NULL);
SetTimer(hWnd, 2,2000, NULL);
break;
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
AllocConsole();
g_hOutput = GetStdHandle(STD_OUTPUT_HANDLE);
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
wc.lpszMenuName = NULL;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
Unit05菜单资源
01菜单分类
- 窗口的顶层菜单(窗口title下面的一长条菜单)
- 弹出式菜单(右键菜单)
- 系统菜单(点击窗口title或者logo弹出的菜单)
- 注意:
HMENU
(菜单句柄)类型表示菜单,ID
表示菜单项
02资源相关
- 资源脚本文件:
*.rc
文件 - 编译器:
RC.EXE
03菜单资源使用
- 添加菜单资源
- 加载菜单资源
- 注册窗口类时设置菜单
- 创建窗口传参设置菜单
- 在主窗口
WM_CREATE
消息中利用SetMenu
函数设置菜单
//加载资源
HMENU LoadMenu(
HINSTANCE hInstance,//handle to module
LPCTSTR lpMenuName//menu name or resource identifier
);
示例代码:
#include <windows.h>
#include "resource.h"
//***********************
//保存全局窗口实例,为了第三种方法加载菜单中使用SetMenu()函数中获取实例
HINSTANCE g_hIns = 0;
//第三种方法加载菜单在创建窗口消息中触发加载菜单
void OnCreate(HWND hWnd){
HMENU hMenu = LoadMenu(g_hIns, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
//***********************
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
//*****************
//第三种加载菜单的的方法:在创建窗口消息中加载菜单
case WM_CREATE:
OnCreate(hWnd);
break;
//****************
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
//将当前窗口实例保存到全局变量中
g_hIns = hIns;
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
//第一种加载菜单的方法:(char*)IDR_MENU1
wc.lpszMenuName = NULL;//(char*)IDR_MENU1;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//***********************
//第二种加载菜单的方法:在创建窗口时使用LoadMenu()函数加载菜单
//HMENU hMenu = LoadMenu(hIns,(char*)IDR_MENU1);
//在内存创建窗口,添加菜单
//HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,hMenu,hIns,NULL);
//***********************
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
04 命令消息(WM_CIMMADN)处理
WM_CIMMADN
点击菜单项发出的消息- 附带信息
- WPARAM:
- HIWORD:对于菜单为0(说明没有用)
- LOWORD:菜单项的ID
- LPARAM:对于菜单为0
- WPARAM:
示例代码:
#include <windows.h>
#include "resource.h"
//***********************
//保存全局窗口实例,为了第三种方法加载菜单中使用SetMenu()函数中获取实例
HINSTANCE g_hIns = 0;
//第三种方法加载菜单在创建窗口消息中触发加载菜单
void OnCreate(HWND hWnd){
HMENU hMenu = LoadMenu(g_hIns, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
//***********************
void OnCommand(HWND hWnd,WPARAM wParam){
switch(LOWORD(wParam)){
case ID_NEW:
MessageBox(hWnd, "新建被点击", "Infor", MB_OK);
break;
case ID_EXIT:
MessageBox(hWnd, "退出被点击", "Infor", MB_OK);
break;
case ID_ABOUT:
MessageBox(hWnd, "关于被点击", "Infor", MB_OK);
break;
}
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
case WM_COMMAND:
OnCommand(hWnd,wParam);
break;
//*****************
//第三种加载菜单的的方法:在创建窗口消息中加载菜单
case WM_CREATE:
OnCreate(hWnd);
break;
//****************
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
//将当前窗口实例保存到全局变量中
g_hIns = hIns;
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
//第一种加载菜单的方法:(char*)IDR_MENU1
wc.lpszMenuName = NULL;//(char*)IDR_MENU1;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//***********************
//第二种加载菜单的方法:在创建窗口时使用LoadMenu()函数加载菜单
//HMENU hMenu = LoadMenu(hIns,(char*)IDR_MENU1);
//在内存创建窗口,添加菜单
//HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,hMenu,hIns,NULL);
//***********************
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
05 菜单项状态
06上下文菜单
- 显示上下文菜单(弹出式菜单)
BOOL TrackPopupMenu(
HMENU hMenu, //菜单句柄
UINT uFlags, //显示方式
int x, //鼠标水平位置,屏幕坐标系
int y, //鼠标垂直位置,屏幕坐标系
int nReserved, //保留,必须为0
HWND hWnd, //处理菜单消息的窗口句柄
const RECT* prcRect //NULL,此参数可忽略
); //TrackPopupMenu是阻塞函数
- 处理上下文菜单弹出的两种方式
WM_RBUTTONUP
:鼠标右键抬起消息为窗口坐标系,要使用需将其转成屏幕坐标系坐标,可通过ClientToScreen
函数进行转换WM_CONTEXTMENU
WPARAM
:右键点击的窗口句柄LPARAM
:对于菜单为0HIWORD
:X坐标,屏幕坐标LOWORD
:Y坐标,屏幕坐标
WM_CONTEXTMENU
消息是在WM_RBUTTONUP
消息之后产生
BOOL ClientToScreen(
HWND hWnd, // handle to window
LPPOINT lpPoint // screen coordinates
);
示例代码1:通过WM_RBUTTONUP消息进行弹出,这种方式显示的上下文菜单有一点问题,因为我们所需要的鼠标位置是屏幕坐标系下的坐标,而通过lParam获取的是客户窗口坐标系的坐标,所以导致鼠标右键点击出现的上下文菜单和鼠标的位置有一定的距离,偏差较大
#include <windows.h>
#include "resource.h"
//***********************
//保存全局窗口实例,为了第三种方法加载菜单中使用SetMenu()函数中获取实例
HINSTANCE g_hIns = 0;
//第三种方法加载菜单在创建窗口消息中触发加载菜单
void OnCreate(HWND hWnd){
HMENU hMenu = LoadMenu(g_hIns, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
//***********************
void OnCommand(HWND hWnd,WPARAM wParam){
switch(LOWORD(wParam)){
case ID_NEW:
MessageBox(hWnd, "新建被点击", "Infor", MB_OK);
break;
case ID_EXIT:
MessageBox(hWnd, "退出被点击", "Infor", MB_OK);
break;
case ID_ABOUT:
MessageBox(hWnd, "关于被点击", "Infor", MB_OK);
break;
}
}
void OnRButtonUp(HWND hWnd,LPARAM lParam){
HMENU hMain = LoadMenu(g_hIns, (char*)IDR_MENU1);
//GetSubMenu函数获取hMain大菜单[文件0,帮助1]中的小菜单[文件0]
HMENU hPopup = GetSubMenu(hMain,0);
TrackPopupMenu(hPopup,TPM_CENTERALIGN|TPM_VCENTERALIGN,LOWORD(lParam),HIWORD(lParam),0,hWnd,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
//上下文菜单弹出的时机:在鼠标右键抬起时弹出上下文菜单
case WM_RBUTTONUP:
OnRButtonUp(hWnd,lParam);
break;
case WM_COMMAND:
OnCommand(hWnd,wParam);
break;
//*****************
//第三种加载菜单的的方法:在创建窗口消息中加载菜单
case WM_CREATE:
OnCreate(hWnd);
break;
//****************
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
//将当前窗口实例保存到全局变量中
g_hIns = hIns;
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
//第一种加载菜单的方法:(char*)IDR_MENU1
wc.lpszMenuName = NULL;//(char*)IDR_MENU1;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//***********************
//第二种加载菜单的方法:在创建窗口时使用LoadMenu()函数加载菜单
//HMENU hMenu = LoadMenu(hIns,(char*)IDR_MENU1);
//在内存创建窗口,添加菜单
//HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,hMenu,hIns,NULL);
//***********************
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}
示例代码2:通过WM_CONTEXTMENU方式弹出
#include <windows.h>
#include "resource.h"
//***********************
//保存全局窗口实例,为了第三种方法加载菜单中使用SetMenu()函数中获取实例
HINSTANCE g_hIns = 0;
//第三种方法加载菜单在创建窗口消息中触发加载菜单
void OnCreate(HWND hWnd){
HMENU hMenu = LoadMenu(g_hIns, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
//***********************
void OnCommand(HWND hWnd,WPARAM wParam){
switch(LOWORD(wParam)){
case ID_NEW:
MessageBox(hWnd, "新建被点击", "Infor", MB_OK);
break;
case ID_EXIT:
MessageBox(hWnd, "退出被点击", "Infor", MB_OK);
break;
case ID_ABOUT:
MessageBox(hWnd, "关于被点击", "Infor", MB_OK);
break;
}
}
void OnContextMenu(HWND hWnd,LPARAM lParam){
HMENU hMain = LoadMenu(g_hIns, (char*)IDR_MENU1);
//GetSubMenu函数获取hMain大菜单[文件0,帮助1]中的小菜单[文件0]
HMENU hPopup = GetSubMenu(hMain,0);
TrackPopupMenu(hPopup,TPM_LEFTALIGN|TPM_TOPALIGN,LOWORD(lParam),HIWORD(lParam),0,hWnd,NULL);
}
//窗口处理函数(自定义,处理函数)
LRESULT CALLBACK WndProc(HWND hWnd,UINT msgID,WPARAM wParam,LPARAM lParam)
{
switch(msgID){
//上下文菜单弹出的时机
case WM_CONTEXTMENU:
OnContextMenu(hWnd,lParam);
break;
case WM_COMMAND:
OnCommand(hWnd,wParam);
break;
//*****************
//第三种加载菜单的的方法:在创建窗口消息中加载菜单
case WM_CREATE:
OnCreate(hWnd);
break;
//****************
case WM_DESTROY:
PostQuitMessage(0);//可以使GetMessage函数返回0
break;
}
return DefWindowProc(hWnd, msgID, wParam, lParam);
}
//入口函数
int CALLBACK WinMain(HINSTANCE hIns, HINSTANCE hPreIns,LPSTR lpCmdLine,int nCmdShow)
{
//将当前窗口实例保存到全局变量中
g_hIns = hIns;
//注册窗口类
WNDCLASS wc = {0};
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.hCursor = NULL;
wc.hIcon = NULL;
wc.hInstance = hIns;
wc.lpfnWndProc = WndProc;
wc.lpszClassName = "Main";
//第一种加载菜单的方法:(char*)IDR_MENU1
wc.lpszMenuName = NULL;//(char*)IDR_MENU1;
wc.style = CS_HREDRAW|CS_VREDRAW;
RegisterClass(&wc);//将以上所有赋值全部写入操作系统内核
//***********************
//第二种加载菜单的方法:在创建窗口时使用LoadMenu()函数加载菜单
//HMENU hMenu = LoadMenu(hIns,(char*)IDR_MENU1);
//在内存创建窗口,添加菜单
//HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,hMenu,hIns,NULL);
//***********************
//在内存创建窗口
HWND hWnd = CreateWindowEx(0,"Main","window",WS_OVERLAPPEDWINDOW,100,100,500,500,NULL,NULL,hIns,NULL);
//显示窗口
ShowWindow(hWnd,SW_SHOW);
UpdateWindow(hWnd);
//消息循环
MSG nMsg = {0};
while(GetMessage(&nMsg,NULL,0,0))
{
TranslateMessage(&nMsg);
DispatchMessage(&nMsg);//将消息交给窗口处理函数来处理
}
return 0;
}