资源是 Windows 应用程序图形用户界面(GUI)的重要组成部分,简单来说是每一个应用程序或多或少会用到的几类数据,这些数据在编译后,被包含进 EXE 文件。
    包括光标、位图、图标、加速键、菜单、字符串和对话框等。
    我们无法直接定位到资源,并使用函数操作他们,和窗口一样,一般情况我们都是得到资源的。

    1. #include <Windows.h>
    2. #include <CommCtrl.h>
    3. #include "resource.h"
    4. // 一旦添加了资源文件,就会自动的生成 resource.h 头文件和 rc文件
    5. // 其中头文件定义了资源的 ID ,rc 文件提供了资源的布局以及和 ID 的
    6. // 对应关系,在 VS 中,无法同时打开这两个文件。不要尝试手动的修改头
    7. // 文件,容易导致资源错乱或程序崩溃。
    8. LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
    9. {
    10. // 定义一个静态局部变量,通过函数 GetModuleHandle 获取实例句柄
    11. static HINSTANCE hInstance = GetModuleHandle(NULL);
    12. switch (uMsg)
    13. {
    14. // 在调用 CreateWindow 时会发送这个消息,通常用于初始化控件
    15. case WM_CREATE:
    16. {
    17. CreateWindow(WC_BUTTON, L"切换菜单", WS_CHILD | WS_VISIBLE,
    18. 10, 10, 200, 50, hWnd, (HMENU)0x1001, hInstance, NULL);
    19. CreateWindow(WC_BUTTON, L"修改光标", WS_CHILD | WS_VISIBLE,
    20. 10, 70, 200, 50, hWnd, (HMENU)0x1002, hInstance, NULL);
    21. CreateWindow(WC_BUTTON, L"修改图标", WS_CHILD | WS_VISIBLE,
    22. 10, 130, 200, 50, hWnd, (HMENU)0x1003, hInstance, NULL);
    23. break;
    24. }
    25. // 响应按钮使用的是 WM_COMMAND 消息
    26. case WM_COMMAND:
    27. {
    28. if (LOWORD(wParam) == 0x1001)
    29. {
    30. static bool b = true;
    31. HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1 + b));
    32. SetMenu(hWnd, hMenu);
    33. b = !b;
    34. }
    35. if (LOWORD(wParam) == 0x1002)
    36. {
    37. // SetClassLong 和 SetWindowLong 区别主要在于,SetClassLong 设置的是窗口类
    38. // 会影响使用当前窗口类的所有对象,SetWindowLong 只会影响到被设置的窗口
    39. SetClassLong(hWnd, GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_HELP));
    40. }
    41. if (LOWORD(wParam) == 0x1003)
    42. {
    43. SetClassLong(hWnd, GCL_HICON, (LONG)LoadIcon(NULL, IDI_APPLICATION));
    44. }
    45. // 菜单也是在 WM_COMMAND 响应的,LOWROD(wParam)保存的是 ID
    46. if (LOWORD(wParam) == ID_40001)
    47. {
    48. MessageBox(hWnd, L"菜单点击", L"菜单点击", MB_OK);
    49. }
    50. break;
    51. }
    52. // 当右键按下的时候,响应消息,弹出菜单
    53. case WM_RBUTTONDOWN:
    54. {
    55. // WM_RBUTTONDOWN 提供的坐标实际上是一个客户区坐标,但是弹出
    56. // 菜单要求提供的坐标是相对于屏幕上左上角的坐标,所以需要进行转换
    57. POINT pt = { LOWORD(lParam), HIWORD(lParam) };
    58. ClientToScreen(hWnd, &pt);
    59. // 先获取到一个菜单资源,一个菜单资源中可能有多个顶级菜单,而弹出
    60. // 的菜单只能是其中的一个,所以需要再次获取子菜单项
    61. HMENU hParentMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU1));
    62. HMENU hChildMenu = GetSubMenu(hParentMenu, 0);
    63. // 必须提供窗口句柄,表示菜单产生的消息由哪一个窗口进行响应
    64. TrackPopupMenu(hChildMenu, TPM_LEFTALIGN, pt.x, pt.y, 0, hWnd, NULL);
    65. break;
    66. }
    67. // 当用户点击关闭按钮时会产生 WM_CLOSE 消息
    68. case WM_CLOSE:
    69. {
    70. DestroyWindow(hWnd);
    71. break;
    72. }
    73. // 当调用 DestroyWindow 函数的时候会产生下面的消息
    74. case WM_DESTROY:
    75. {
    76. PostQuitMessage(0);
    77. break;
    78. }
    79. }
    80. // 对于任何不关系的消息,我们应该使用操作系统的默认行为做出相应
    81. return DefWindowProc(hWnd, uMsg, wParam, lParam);
    82. }
    83. int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    84. {
    85. // 1. 注册窗口类,必须需要提供的成员有回调函数和窗口类名
    86. WNDCLASS WndClass{};
    87. WndClass.lpfnWndProc = WndProc;
    88. WndClass.lpszClassName = L"myclass";
    89. // 可以直接在窗口类中指定使用的光标和图标资源
    90. WndClass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
    91. WndClass.hCursor = LoadCursor(hInstance, MAKEINTRESOURCE(IDC_CURSOR1));
    92. // 可以在窗口类中添加菜单,优先级是最低的
    93. WndClass.lpszMenuName = MAKEINTRESOURCE(IDR_MENU1);
    94. if (!RegisterClass(&WndClass))
    95. {
    96. MessageBoxA(NULL, "窗口类注册失败", "错误消息", MB_OK | MB_ICONERROR);
    97. return -1;
    98. }
    99. // 2. 使用注册好的窗口类创建窗口并显示更新窗口,也可以在创建窗口时指定菜单
    100. HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MENU2));
    101. HWND hWnd = CreateWindow(L"myclass", L"window", WS_OVERLAPPEDWINDOW,
    102. CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, hMenu, hInstance, NULL);
    103. ShowWindow(hWnd, SW_SHOWNORMAL);
    104. UpdateWindow(hWnd);
    105. // 3. 编写消息泵接收并分发消息,【消息本质上是一个结构体】
    106. MSG msg{};
    107. while (GetMessage(&msg, NULL, 0, 0))
    108. {
    109. DispatchMessage(&msg);
    110. }
    111. return 0;
    112. }