资源是 Windows 应用程序图形用户界面(GUI)的重要组成部分,简单来说是每一个应用程序或多或少会用到的几类数据,这些数据在编译后,被包含进 EXE 文件。
包括光标、位图、图标、加速键、菜单、字符串和对话框等。
我们无法直接定位到资源,并使用函数操作他们,和窗口一样,一般情况我们都是得到资源的。
#include <Windows.h>#include <CommCtrl.h>#include "resource.h"// 一旦添加了资源文件,就会自动的生成 resource.h 头文件和 rc文件// 其中头文件定义了资源的 ID ,rc 文件提供了资源的布局以及和 ID 的// 对应关系,在 VS 中,无法同时打开这两个文件。不要尝试手动的修改头// 文件,容易导致资源错乱或程序崩溃。LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam){// 定义一个静态局部变量,通过函数 GetModuleHandle 获取实例句柄static HINSTANCE hInstance = GetModuleHandle(NULL);switch (uMsg){// 在调用 CreateWindow 时会发送这个消息,通常用于初始化控件case WM_CREATE:{CreateWindow(WC_BUTTON, L"切换菜单", WS_CHILD | WS_VISIBLE,10, 10, 200, 50, hWnd, (HMENU)0x1001, hInstance, NULL);CreateWindow(WC_BUTTON, L"修改光标", WS_CHILD | WS_VISIBLE,10, 70, 200, 50, hWnd, (HMENU)0x1002, hInstance, NULL);CreateWindow(WC_BUTTON, L"修改图标", WS_CHILD | WS_VISIBLE,10, 130, 200, 50, hWnd, (HMENU)0x1003, hInstance, NULL);break;}// 响应按钮使用的是 WM_COMMAND 消息case WM_COMMAND:{if (LOWORD(wParam) == 0x1001){static bool b = true;HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1 + b));SetMenu(hWnd, hMenu);b = !b;}if (LOWORD(wParam) == 0x1002){// SetClassLong 和 SetWindowLong 区别主要在于,SetClassLong 设置的是窗口类// 会影响使用当前窗口类的所有对象,SetWindowLong 只会影响到被设置的窗口SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_HELP));}if (LOWORD(wParam) == 0x1003){SetClassLong(hWnd, GCL_HICON, (LONG)LoadIcon(NULL, IDI_APPLICATION));}// 菜单也是在 WM_COMMAND 响应的,LOWROD(wParam)保存的是 IDif (LOWORD(wParam) == ID_40001){MessageBox(hWnd, L"菜单点击", L"菜单点击", MB_OK);}break;}// 当右键按下的时候,响应消息,弹出菜单case WM_RBUTTONDOWN:{// WM_RBUTTONDOWN 提供的坐标实际上是一个客户区坐标,但是弹出// 菜单要求提供的坐标是相对于屏幕上左上角的坐标,所以需要进行转换POINT pt = { LOWORD(lParam), HIWORD(lParam) };ClientToScreen(hWnd, &pt);// 先获取到一个菜单资源,一个菜单资源中可能有多个顶级菜单,而弹出// 的菜单只能是其中的一个,所以需要再次获取子菜单项HMENU hParentMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));HMENU hChildMenu = GetSubMenu(hParentMenu, 0);// 必须提供窗口句柄,表示菜单产生的消息由哪一个窗口进行响应TrackPopupMenu(hChildMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);break;}// 当用户点击关闭按钮时会产生 WM_CLOSE 消息case WM_CLOSE:{DestroyWindow(hWnd);break;}// 当调用 DestroyWindow 函数的时候会产生下面的消息case WM_DESTROY:{PostQuitMessage(0);break;}}// 对于任何不关系的消息,我们应该使用操作系统的默认行为做出相应return DefWindowProc(hWnd, uMsg, wParam, lParam);}int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){// 1. 注册窗口类,必须需要提供的成员有回调函数和窗口类名WNDCLASS WndClass{};WndClass.lpfnWndProc = WndProc;WndClass.lpszClassName = L"myclass";// 可以直接在窗口类中指定使用的光标和图标资源WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));WndClass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));// 可以在窗口类中添加菜单,优先级是最低的WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);if (!RegisterClass(&WndClass)){MessageBoxA(NULL, "窗口类注册失败", "错误消息", MB_OK | MB_ICONERROR);return -1;}// 2. 使用注册好的窗口类创建窗口并显示更新窗口,也可以在创建窗口时指定菜单HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU2));HWND hWnd = CreateWindow(L"myclass", L"window", WS_OVERLAPPEDWINDOW,CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);ShowWindow(hWnd, SW_SHOWNORMAL);UpdateWindow(hWnd);// 3. 编写消息泵接收并分发消息,【消息本质上是一个结构体】MSG msg{};while (GetMessage(&msg, NULL, 0, 0)){DispatchMessage(&msg);}return 0;}
