Windows资源是一种二进制数据,由链接器链接进程序成为程序的一部分,通过资源的方式可以很方便的对应用程序进行扩展。在Windows中资源可以是系统自定义的,也可以是用户自定义的。在本篇文章中为大家讲解菜单资源,上下文菜单,图标资源,光标资源,加速键资源的创建方法,这里我使用的编译器为visual studio2022版本。
文章目录
- 一.菜单资源
- 1.菜单分类
- 2.如何创建菜单资源
- 3.实操展示
- 二.上下文菜单
- 1.显示上下文菜单(右键菜单)
- 2.上下文菜单中的WM_RBUTTONUP消息
- 3.WM_CONTEXTMENU消息
- 三.图标资源,光标资源
- 1.添加图标资源
- 2.加载图标资源
- 3.添加光标资源
- 4.加载光标资源
- 5.设置资源
- 5.WM_SETCURSOR消息
- 三.字符串资源
- 四.加速键资源
- 1.添加加速键资源
- 2.加速键资源的使用
一.菜单资源
1.菜单分类
- 窗口顶层菜单
- 弹出式菜单
- 系统菜单
- HMENU类型表示菜单,ID表示菜单项
2.如何创建菜单资源
对于目前的编译器,我们自己添加资源还是非常容易的,我们只需要使用可视化图形界面就可以。
- 1.添加菜单资源
- <1>.右键单击资源文件,选择添加–>资源
- <2>.选择添加Menu资源
- <3>.添加想要的菜单资源,并且设置消息ID
- <1>.右键单击资源文件,选择添加–>资源
- 2.获取本进程菜单句柄
HMENU LoadMenu(
HINSTANCE hInstance; //包含要加载的菜单资源模块的句柄
LPCSTR lpMenuName //菜单资源的名称
);
- 3.加载菜单资源
- <1>.注册窗口类时设置菜单(lpszMenuName)
- <2>.创建窗口时,传参设置菜单
- <3>.在主窗口WM_CREATE消息中使用SetMenu函数设置菜单
SetMenu(
HWND hWnd, //窗口句柄
HMENU hMenu, //菜单句柄
);设置成功,返回非零
在这里对SetMenu函数不做过多的解释了,大家可以到MSDN官方文档解释SetMenu函数中查看该函数的详细介绍。
- 4.处理菜单消息
在学习处理菜单消息之前,我们来学习一种常见的消息类型:WM_COMMAND- 发出时间:当用户从菜单中选择命令项,空间将通知消息发送到其父窗口或翻译快捷键击时发送。
- 附加信息
- wParam:
- HIWORD:对于菜单为0
- LOWORD:菜单项的ID
- lParam:对于菜单为0
- wParam:
3.实操展示
我们来使用WM_COMMAND消息来处理菜单按钮:
设置菜单:
回调函数处理:
LRESULT CALLBACK WindowProc(
IN HWND hwnd,
IN UINT uMsg,
IN WPARAM wParam,
IN LPARAM lParam
)
{
char output[256] = { 0 };
switch (uMsg)
{
//常见消息
case WM_DESTROY: {
PostQuitMessage(0);
break;
}
case WM_SYSCOMMAND: {
sprintf(output, TEXT("检测到WM_COMMAND消息\n"));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_CREATE: {
sprintf(output, "检测到WM_CREATE消息,将创建窗口。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_SIZE: {
sprintf(output, "lParam:窗口宽变化为:%d,窗口高变化为:%d \n", HIWORD(lParam), LOWORD(lParam));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
//菜单命令消息
case WM_COMMAND: {
switch(LOWORD(wParam)) {
case MY_OPEN: {
sprintf(output, "打开按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case MY_QUIT: {
sprintf(output, "退出按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case IDM_ABOUT: {
sprintf(output, "帮助按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
}
case MY_NEWFILE: {
sprintf(output, "打开新文件按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case MY_LASTTIMEFILE: {
sprintf(output, "上次打开文件按钮被点击,请到回调函数中做具体处理。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
}
break;
}
//键盘消息
case WM_KEYDOWN: {
sprintf(output, "检测到WM_KEYDOWN消息,键码值:%d.\n", wParam);
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_KEYUP: {
sprintf(output, "检测到WM_KEYUP消息,键码值:%d.该按键被放开\n", wParam);
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
//鼠标消息
case WM_LBUTTONDOWN: {
sprintf(output, "检测到WM_LBUTTONDOWN消息,鼠标左键被按下。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_LBUTTONUP: {
sprintf(output, "检测到WM_LBUTTONUP消息,鼠标左键被放开。\n");
WriteConsole(g_hOUTPUT, output, strlen(output),0,0);
break;
}
case WM_RBUTTONDOWN: {
sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被按下。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_RBUTTONUP: {
sprintf(output, "检测到WM_RBUTTON消息,鼠标右键被放开。\n");
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
}
case WM_MOUSEMOVE: {
sprintf(output, "检测到WM_MOUSEMOVE消息,鼠标移动中,鼠标位置(%d,%d).\n", LOWORD(lParam), HIWORD(lParam));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
case WM_MOUSEWHEEL: {
sprintf(output, "鼠标滚轮滚动中,偏移量:%d,鼠标当前位置(%d,%d)\n",HIWORD(wParam), LOWORD(lParam), HIWORD(lParam));
WriteConsole(g_hOUTPUT, output, strlen(output), 0, 0);
break;
}
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
我们来看看处理效果:
二.上下文菜单
1.显示上下文菜单(右键菜单)
MSDN官方文档解释TrankPopupMensu函数
BOOL TrankPopupMenu(
HMENU hMenu, //要显示的快捷菜单的句柄。
UINT nFlags, //指定显示方式
int x,
int y, //x,y分别为快捷菜单位置,x为水平位置,y为垂直位置(屏幕坐标系)
int nReserved, //保留,必须为0
HWND hWnd, //拥有快捷菜单的窗口句柄
const RECT* prcRext //已忽略
);
注意: 这里的第一个参数hMenu,可以通过CreatePopopMenu函数来创建一个新的快捷菜单,也可以通过GetSubMenu函数来检索与现有菜单项关联的子菜单句柄来获取句柄。
这个函数是阻塞函数
2.上下文菜单中的WM_RBUTTONUP消息
鼠标右键被放开为窗口坐标系坐标,如果要转化为屏幕坐标系的话,请使用ClientToScreen
函数。
3.WM_CONTEXTMENU消息
MSDN官方文档结束WM_CONTEXTMENU消息
- 产生时间:鼠标右键被放开时
- 附加信息
- wParam:右键点击的窗口句柄
- lParam:
- LOWORD:x
- HIWORD:y (注意是屏幕坐标系)
我们在讲解鼠标消息的时候,鼠标右键被放开时会产生WM_RBUTTONUP消息,这里的WM_CONTEXTMENU消息是在WM_RBUTTONUP消息之后产生的。
三.图标资源,光标资源
1.添加图标资源
像我们添加菜单资源一样,添加Icon资源
添加好之后,我们可以选择自己画一个图标或者从外部添加图标,注意图标大小,一个图形文件中,可以有多个大小不同的图标。
2.加载图标资源
将图标资源设置好之后,我们需要拿到图标资源句柄
我们可以使用LoadIcon
函数来获取图标句柄:
MSDN官方文档解释LoadIcon函数
HICON LoadIcon(
HINSTANCE hInstance, //图标资源的实例句柄
LPCTSTR lpIconName //要加载的图标资源的名称
);
获取到图标资源句柄之后,我们只需要在注册窗口时将图标资源句柄传给hIcon参数即可。
3.添加光标资源
像前文讲到的添加菜单资源,图标资源一样,我们只需要选择Coursor
添加好光标资源后,每个光标还有FotSpot,是当前鼠标的热点,只有在这一点上,光标才起作用。
4.加载光标资源
添加好光标资源后,我们还是需要拿到光标资源句柄,我们可以使用HCURSOR
函数来获取光标资源句柄。
MSDN官方文档解释LoadCursor函数
HCURSOR LoadCursor(
HINSTANCE hInstance, //光标资源句柄可以为0
LPCTSTR lpCursorName //光标资源名称
);
当hInstance参数为0时,获取系统默认的光标资源
5.设置资源
- 我们拿到光标资源句柄后,在注册窗口时,设置hCursor参数为我们拿到的光标资源句柄即可。
- 我们也可以在创建窗口时,显示窗口之前,使用
SetCursor
函数来设置光标资源。
5.WM_SETCURSOR消息
- 产生时间:如果鼠标导致光标在窗口移动,并且未捕获鼠标输入,则发送到窗口
- 附加消息:
- wParam:包含光标的窗口句柄
- lParam:
- LOWORD:当前区域代码(指定触发此消息的窗口,当进入菜单模式时,此字段为0)
- HIWORD:当前鼠标消息ID
三.字符串资源
在这里我们讲一下,字符串资源到底有什么作用呢?
我们在做开发的时候,客户可能有时候让我们做中文版界面,有时候又想让我们改为英文版界面,这时候字符串资源就显得特别重要了,不让我们要改很多源码。
同样,我们到可视化图形界面来添加字符串资源
- 获取字符串资源句柄
int LoadString(
HINSTANCE hInstance, //包含字符串资源的可执行文件的实例句柄
UINT uID, //要加载的字符串标识符
LPTSTR lpBuffer, //存放字符串的Buff
int nBufferMax //字符串Buff长度
);
四.加速键资源
加速键资源这里定义的有点抽象了,其实就是快捷键。
1.添加加速键资源
我们还是添加资源–>添加Accelerator资源
2.加速键资源的使用
- 加载加速键表
MSDN官方文档解释LoadAccelerstors函数
加载指定的加速器表。
HACCEL LoadAccelerators(
HINSTANCE hInstance, //模块的实例句柄
LPCTSTR lpTableName //要加载的加速表的名称
);
这里注意,我们加载完加速表后,不能直接使用加速键表,我们还需要翻译加速键,
MSDN官方文档解释TranslateAccelerator函数,函数作用:处理菜单命令的快捷键。
int TranslateAccelerator(
HWND hWnd, //要翻译其消息的窗口句柄
HACCEL hAccTable, //加速器表的句柄
LPMSG lpMsg //指向Msg结构的指针,其中包括检索得到的消息
);
那么加速器到底是怎么工作的?我们来写一段伪代码为大家讲解:
TranslateAcceleator(hWnd,hAccTable,&Msg){
if(Msg.message != WM_KEYDOWN) return 0;
根据Msg.wParam获知按键到底是哪个
查表,到hAccelTable中比对是否存在该加速键
if(没找到) return 0;
else{
SendMesage(hWnd,WM_COMMAND,ID_NEW....);
return 0;
}
今天的分享就到这里,如果文章中有错误之处,还请大家指出来,我会非常虚心得学习,希望我们共同进步!!!