01 消息队列
1.消息队列概念
- 消息队列是用于存放消息的队列。
- 消息在队列中先入先出。
- 所有窗口程序都具有消息队列。
- 程序可以从队列中获取消息。
2.消息队列分类
- 系统消息队列 -由系统维护的消息队列。存放系统产生的消息,例如鼠标,键盘等。
- 程序消息队列 -属于每一个应用程序(线程)的消息队列。由应用程序(线程)维护。
消息先进系统队列,再分给程序消息队列:
PostMessage到系统队列中:
3.消息和队列关系
-
消息和消息队列的关系
1.当鼠标,键盘产生消息时,会将消息存放到系统消息队列
2.系统会根据存放的消息,找到对应程序的消息队列。
3.将消息投递到程序的消息队列中。 -
根据消息和消息队列之间使用关系,将消息分成两类:
队列消息 -消息的发送和获取,都是通过消息队列完成。
非队列消息 -消息的发送和获取,是直接调用消息的窗口处理完成。 -
队列消息 -消息发送后,首先放入队列,然后通过消息循环,从队列当中获取。
GetMessage -从消息队列中获取消息
PostMessage -将消息投递到消息队列
常见队列消息:WM_PAINT,键盘,鼠标,定时器 -
非队列消息 -消息发送时,首先查找消息接收窗口的窗口处理函数,直接调用处理函数,完成消息。
SendMessage -直接将消息发送给窗口的处理函数,并等候处理结果。
常见消息:WM_CREATE,WM_SIZE等。
4.深谈GetMessage原理
- 在程序(线程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND,ID范围),不满足条件就不会取出消息,否则从队列取出消息返回。
- 如果程序(线程)消息队列没有消息,向系统消息队列获取属于本程序的消息。如果系统队列的当前消息属于本程序,系统会将消息转发到程序消息队列中。
- 如果系统消息队列也没有消息,检查当前进程的所有窗口的需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINT消息,取得消息返回处理。
- 如果没有重新绘制区域,检查定时器如果有到时的定时器,产生WM_TIMER,返回处理执行。
- 如果没有到时的定时器,整理程序的资源,内存等等。
- GetMessage会继续等候下一条消息。PeekMessage会返回FALSE,交出程序的控制权。
- 注意:GetMessage如果获取到是WM_QUIT,函数会返回FALSE.
5.WM_PAINT消息
-
产生时间:当窗口需要绘制的时候。
-
附带消息:wParam:0。
lParam:0。 -
专职用法:用于绘图。
-
窗口无效区域:需要重新绘制的区域。
BOOL InvalidateRect(
HWND hWnd, //窗口句柄
CONST RECT* lpRect, //区域的矩形坐标
BOOL bErase //重绘前是否先擦除
); -
消息处理步骤
1>开始绘图
HDC BeginPaint(
HWND hwnd, //绘制窗口
LPPAINSTRUCT lpPaint //绘图参数的BUFF
);//返回汇入设备句柄HDC
2>正式绘图
3>结束绘图
BOOL EndPaint(
HWND hwnd, //绘制窗口
CONST LPPAINSTRUCT *lpPaint //绘图参数的指针BeginPaint返回
);
#include<Windows.h>
#include<stdio.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);
}
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();//程序中增加DOS窗口
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;
}
02 键盘消息
1.键盘消息分类
- WM_KEYDOWN -按键被按下时产生
- WM_KEYUP -按键被放开时产生
- WM_SYSKEYDOWM -系统按键按下时产生
- WM_SYSKEYUP -系统按键放开时产生
附带信息:
WPARAM -按键的Virtual Key
LPARAM -按键的参数,例如按下次数
2.字符消息(WM_CHAR)
-
TranslateMessage在转换WM_KEYDOWN消息时,对于可见字符可以产生WM_CHAR,不可见字符无此消息。
-
附带消息
WPARAM -输入的字符的ASCII字符编码值
LPARAM -按键的相关参数TranslateMessage内部实现过程:
键盘消息流程:
#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_CHAR:wParam:%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();//程序中增加DOS窗口
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 鼠标消息
1.鼠标消息分类
- 基本鼠标消息
WM_LBUTTONDOWN -鼠标左键按下
WM_LBUTTONUP-鼠标左键抬起
WM_RBUTTONDOWN -鼠标右键按下
WM_RBUTTONUP -鼠标右键抬起
WM_MOUSEMOVE -鼠标移动消息 - 双击消息
WM_LBUTTONDBLCLK -鼠标左键双击
WM_RBUTTONDBLCLK -鼠标右键双击 - 滚轮消息
WM_MOUSEWHEEL -鼠标滚轮消息
2.鼠标消息基本消息
- 附带信息:
wPARAM:其他按键的状态,例如Ctrl/Shift等
lPARAM:鼠标的位置,窗口客户区坐标系。
LOWORD X坐标位置
HIWORD Y坐标位置 - 一般情况鼠标按下/抬起成双出现。在鼠标移动过程中,会根据移动速度产生一系列的WM_MOUSEMOVE。
3.鼠标双击消息
- 附带信息:
wPARAM:其他按键的状态,例如Ctrl/Shift等
lPARAM:鼠标的位置,窗口客户区坐标系。
LOWORD(lParam) // X坐标位置
HIWORD(lParam) // Y坐标位置 - 消息产生顺序
以左键双击为例:
WM_LBUTTONDOWN
WM_LBUTTONUP
WM_LBUTTONDBLCLK
WM_LBUTTONUP
使用时需要在注册窗口类的时候添加CS_DBLCLKS风格。
4.鼠标滚轮消息
- 附带信息:
wPARAM:
LOWORD -其他按键的状态
HIWORD -滚轮的偏移量,通过正负值表示滚轮方向。
正:向前滚动 负:向后滚动
lPARAM:鼠标当前的位置,屏幕坐标系
LOWORD -X坐标
HIWORD -Y坐标 - 使用:通过偏移量,获取滚动的方向和距离
#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 OnLButtonDblClk(HWND hWnd)
{
char szText[256] = "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_MOUSEHWHEEL: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_MOUSEHWHEEL:
OnMouseWheel(hWnd, wParam);
break;
case WM_LBUTTONDBLCLK:
OnLButtonDblClk(hWnd);
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();//程序中增加DOS窗口
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 定时器消息
1.定时器消息介绍
- 产生时间:
在程序中创建定时器,当到达时间间隔时,定时器会向程序发送一个WM_TIMER消息。定时器的精度是毫秒,但是准确度很低。例如设置时间间隔为1000ms,但是会在非1000毫秒到达消息。 - 附带信息:
wPARAM:定时器ID
lPARAM:定时器处理函数的指针
2.创建销毁定时器
-
创建定时器
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)
{
/*switch (wParam) {
case 1:
//...
break;
case 2:
//...
break;
}*/
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();//程序中增加DOS窗口
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;
}
05 菜单资源
1.菜单分类
- 窗口的顶层菜单
- 弹出式菜单
- 系统菜单
HMENU类型表示菜单,ID表示菜单项。
2.资源相关
3.菜单资源使用
-
添加菜单资源
-
加载菜单资源
1>注册窗口类时设置菜单
2>创建窗口传参设置餐单
3>在主窗口WM_CREATE消息中利用SetMenu函数设置菜单加载菜单资源
HMENU LoadMenu(
HINSTANCE hInstance, //handle to module
LPCTSTR lpMenuName //menu name or resource identifier
);
步骤:
右键->添加->资源
添加名字,更改ID
添加分隔符:空格,属性分隔符改为true
最终效果:
加载菜单资源:
1.注册窗帘类创菜单,将菜单注册到窗口:
wc.lpszMenuName = (char )IDR_MENU1;
2.创建窗口传参设置菜单
HMENU hMenu = LoadMenu(hIns, (char)IDR_MENU1);
HWND hWnd = CreateWindowEx(0, “Main”, “window”, WS_OVERLAPPEDWINDOW, 100, 100, 500, 500, NULL, hMenu, hIns, NULL);
3.在主窗口WM_CREATE消息中利用SetMenu函数设置菜单
#include<Windows.h>
#include<stdio.h>
#include"resource.h"
HINSTANCE g_hInstance = 0;
void OnCreate(HWND hWnd)
{
HMENU hMenu = LoadMenu(g_hInstance, (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_hInstance = 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";
//wc.lpszMenuName = (char *)IDR_MENU1;
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;
}
4.命令消息处理
- 附带信息
wPARAM:
HIWORD -对于菜单为0
LOWOED -菜单项的ID
lPARAM -对应菜单为0
#include<Windows.h>
#include<stdio.h>
#include"resource.h"
HINSTANCE g_hInstance = 0;
void OnCreate(HWND hWnd)
{
HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
void OnCommand(HWND hWnd, WPARAM wParam)
{
switch (LOWORD(wParam))
{
case ID_NEW:
MessageBox(hWnd, "新建被点击", "Info", MB_OK);
break;
case ID_EXIT:
MessageBox(hWnd, "退出被点击", "Info", MB_OK);
break;
case ID_ABOUT:
MessageBox(hWnd, "关于被点击", "Info", 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_hInstance = 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";
//wc.lpszMenuName = (char *)IDR_MENU1;
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;
}
5.上下文菜单
- 显示上下文菜单
BOOL TrackPopupMenu(
HMENU hMenu, //菜单句柄
UINT uFlags, //显示方式
int x,//水平位置,屏幕坐标系
int y,//垂直位置,屏幕坐标系
int nReserved, //保留,必须0
HWND hWnd, //处理菜单消息的窗口句柄
CONST RECT *prcRect //NULL,忽略
);TrackPopupMenu是阻塞函数
#include<Windows.h>
#include<stdio.h>
#include"resource.h"
HINSTANCE g_hInstance = 0;
void OnCreate(HWND hWnd)
{
HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
void OnCommand(HWND hWnd, WPARAM wParam)
{
switch (LOWORD(wParam))
{
case ID_NEW:
MessageBox(hWnd, "新建被点击", "Info", MB_OK);
break;
case ID_EXIT:
MessageBox(hWnd, "退出被点击", "Info", MB_OK);
break;
case ID_ABOUT:
MessageBox(hWnd, "关于被点击", "Info", MB_OK);
break;
}
}
void OnRButtonUp(HWND hWnd, LPARAM lParam)
{
HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
HMENU hPopup = GetSubMenu(hMenu,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_hInstance = 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";
//wc.lpszMenuName = (char *)IDR_MENU1;
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;
}
-
WM_RBUTTONUP
鼠标右键弹起消息为窗口坐标系坐标,要使用需将其转换成屏幕坐标系坐标ClientToScreen -
WM_CONTEXTMENU
wParam:右键点击的窗口句柄
lPARAM:LOWORD X坐标,屏幕坐标系
HIWORD Y坐标,屏幕坐标系
WM_CONTEXTMENU消息是在WM_RBUTTONUP消息之后产生。
#include<Windows.h>
#include<stdio.h>
#include"resource.h"
HINSTANCE g_hInstance = 0;
void OnCreate(HWND hWnd)
{
HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
SetMenu(hWnd, hMenu);
}
void OnCommand(HWND hWnd, WPARAM wParam)
{
switch (LOWORD(wParam))
{
case ID_NEW:
MessageBox(hWnd, "新建被点击", "Info", MB_OK);
break;
case ID_EXIT:
MessageBox(hWnd, "退出被点击", "Info", MB_OK);
break;
case ID_ABOUT:
MessageBox(hWnd, "关于被点击", "Info", MB_OK);
break;
}
}
void OnContextMenu(HWND hWnd, LPARAM lParam)
{
HMENU hMenu = LoadMenu(g_hInstance, (char*)IDR_MENU1);
HMENU hPopup = GetSubMenu(hMenu,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_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_hInstance = 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";
//wc.lpszMenuName = (char *)IDR_MENU1;
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;
}