Windows学习笔记-07(资源的添加、载入和使用)

一、资源的添加

右键"资源文件"->添加->资源:

此时自动创建了resource.h头文件和资源脚本(.rc),同时自动在resource.h头文件中进行了宏定义:

在Visual Studio中添加资源

  • 方法1:通过解决方案资源管理器

    1. 右键点击项目 → 添加 → 资源

    2. 选择资源类型(Icon、Bitmap、Dialog等)

    3. 编辑资源并自动生成.rc文件

  • 方法2:手动添加

    1. 将.rc文件添加到项目

    2. 将resource.h添加到项目

MAKEINTRESOURCE宏

cpp 复制代码
// 作用:将整数ID转换为资源标识符字符串
// 资源ID <= 65535 时使用
#define MAKEINTRESOURCE(i) ((LPWSTR)((ULONG_PTR)((WORD)(i))))

// 使用示例
LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));

// 注意:资源ID > 65535 时使用字符串名称
LoadIcon(hInstance, L"MyLargeIcon");

二、各阶段的资源载入时机

1. 窗口类注册时(WinMain中)

cpp 复制代码
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nCmdShow)
{
    // 时机1:注册窗口类时
    WNDCLASSEX wc = {0};
    wc.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APP_ICON));      // 主图标
    wc.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_APP_SMALL));   // 小图标
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);                           // 光标
    
    // 时机2:检查DPI缩放,可能需要不同尺寸的图标
    if (GetDpiForSystem() > 96)  // 高DPI
    {
        wc.hIcon = (HICON)LoadImage(hInstance, 
                                   MAKEINTRESOURCE(IDI_APP_ICON_LARGE),
                                   IMAGE_ICON, 48, 48, 0);
    }
    
    RegisterClassEx(&wc);
    
    // 时机3:创建主窗口前预载入常用资源
    PreloadFrequentlyUsedResources(hInstance);
    
    // 主消息循环...
}

2. 窗口创建时(WM_CREATE消息)

cpp 复制代码
case WM_CREATE:
{
    // 时机:窗口刚创建,适合初始化窗口特定资源
    
    // 1. 载入并设置菜单
    HMENU hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU));
    SetMenu(hWnd, hMenu);
    
    // 2. 载入加速键表
    g_hAccel = LoadAccelerators(hInstance, 
                               MAKEINTRESOURCE(IDR_ACCELERATOR));
    
    // 3. 创建工具栏并载入位图
    CreateToolbar(hWnd, hInstance);
    
    // 4. 载入字符串资源用于设置窗口标题
    TCHAR szTitle[256];
    LoadString(hInstance, IDS_APP_TITLE, szTitle, 256);
    SetWindowText(hWnd, szTitle);
    
    // 5. 创建状态栏
    CreateStatusBar(hWnd);
    
    // 6. 初始化图像列表(用于列表视图、树视图)
    g_hImageList = ImageList_Create(16, 16, ILC_COLOR32 | ILC_MASK, 5, 5);
    HICON hFolderIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_FOLDER));
    ImageList_AddIcon(g_hImageList, hFolderIcon);
    
    return 0;
}

3. 窗口显示时(WM_SHOWWINDOW消息)

cpp 复制代码
case WM_SHOWWINDOW:
    if (wParam)  // 窗口正在显示
    {
        // 时机:窗口首次显示或重新显示
        if (!g_bResourcesLoaded)
        {
            // 延迟载入大资源(如图片、音频)
            LoadBackgroundImage(hWnd);
            LoadAudioResources();
            g_bResourcesLoaded = TRUE;
        }
    }
    break;

4. 绘制阶段(WM_PAINT消息)

cpp 复制代码
case WM_PAINT:
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hWnd, &ps);
    
    // 时机:绘制时按需载入资源
    if (!g_hBackgroundBitmap)  // 懒加载
    {
        g_hBackgroundBitmap = LoadBitmap(hInstance, 
                                        MAKEINTRESOURCE(IDB_BACKGROUND));
    }
    
    // 绘制位图
    HDC hdcMem = CreateCompatibleDC(hdc);
    HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, g_hBackgroundBitmap);
    BitBlt(hdc, 0, 0, 800, 600, hdcMem, 0, 0, SRCCOPY);
    SelectObject(hdcMem, hOldBitmap);
    DeleteDC(hdcMem);
    
    EndPaint(hWnd, &ps);
    return 0;
}

5. 窗口大小改变时(WM_SIZE消息)

cpp 复制代码
case WM_SIZE:
{
    int width = LOWORD(lParam);
    int height = HIWORD(lParam);
    
    // 时机:窗口大小改变,可能需要不同尺寸的资源
    if (width > 1024 && !g_hLargeBackground)  // 大窗口
    {
        // 载入高清背景
        g_hLargeBackground = (HBITMAP)LoadImage(
            hInstance,
            MAKEINTRESOURCE(IDB_BACKGROUND_LARGE),
            IMAGE_BITMAP,
            width, height,
            LR_DEFAULTCOLOR
        );
    }
    
    // 调整控件位置
    MoveWindow(g_hStatusBar, 0, height - 20, width, 20, TRUE);
    
    // 强制重绘
    InvalidateRect(hWnd, NULL, TRUE);
    
    return 0;
}

6. 主题/外观改变时(WM_THEMECHANGED消息)

cpp 复制代码
case WM_THEMECHANGED:
    // 时机:系统主题改变,可能需要更换资源
    if (IsThemeActive())
    {
        // 使用主题化资源
        LoadThemeAwareResources();
    }
    else
    {
        // 使用经典资源
        LoadClassicResources();
    }
    
    // 重绘窗口
    InvalidateRect(hWnd, NULL, TRUE);
    return 0;

7. 用户交互时(WM_COMMAND等)

cpp 复制代码
case WM_COMMAND:
    switch (LOWORD(wParam))
    {
    case ID_FILE_OPEN:
        // 时机:用户打开文件时
        LoadDocumentResources();
        break;
        
    case ID_VIEW_LARGE_ICONS:
        // 时机:切换视图模式
        ImageList_Destroy(g_hImageList);
        g_hImageList = ImageList_Create(32, 32, ILC_COLOR32, 0, 10);
        // 载入大图标
        LoadLargeIcons();
        break;
        
    case ID_HELP_ABOUT:
        // 时机:显示关于对话框
        DialogBox(hInstance, MAKEINTRESOURCE(IDD_ABOUT), 
                 hWnd, AboutDialogProc);
        break;
    }
    break;

8. 鼠标/键盘交互时

cpp 复制代码
case WM_MOUSEMOVE:
    // 时机:鼠标移动时动态改变光标
    POINT pt = {GET_X_LPARAM(lParam), GET_Y_LPARAM(lParam)};
    RECT rcButton;
    GetClientRect(g_hButton, &rcButton);
    
    if (PtInRect(&rcButton, pt))
    {
        // 鼠标在按钮上,显示手型光标
        SetCursor(LoadCursor(NULL, IDC_HAND));
    }
    else
    {
        // 恢复默认光标
        SetCursor(LoadCursor(NULL, IDC_ARROW));
    }
    break;

case WM_SETCURSOR:
    // 时机:系统设置光标前
    if (LOWORD(lParam) == HTCLIENT)
    {
        // 自定义客户端区域光标
        SetCursor(g_hCustomCursor);
        return TRUE;
    }
    break;

三、资源载入函数详解

1. Load系列函数(同步载入)

函数 用途 何时使用 内存管理
LoadIcon 载入图标 窗口创建、设置图标时 系统管理,无需销毁
LoadCursor 载入光标 鼠标消息、设置光标时 系统管理,无需销毁
LoadBitmap 载入位图 绘制前、控件图像 必须DeleteObject销毁
LoadMenu 载入菜单 窗口创建、改变菜单时 附加到窗口则自动销毁,否则需DestroyMenu
LoadString 载入字符串 显示文本前 无需销毁
LoadAccelerators 载入加速键表 消息循环前 必须DestroyAcceleratorTable销毁
cpp 复制代码
// ANSI版本
int LoadStringA(
    HINSTANCE hInstance,  // 包含字符串资源的模块句柄
    UINT      uID,        // 字符串资源ID
    LPSTR     lpBuffer,   // 接收字符串的缓冲区
    int       cchBufferMax // 缓冲区大小(字符数)
);

// Unicode版本(推荐)
int LoadStringW(
    HINSTANCE hInstance,
    UINT      uID,
    LPWSTR    lpBuffer,
    int       cchBufferMax
);

// 通用宏定义
int LoadString(
    HINSTANCE hInstance,
    UINT      uID,
    LPTSTR    lpBuffer,
    int       cchBufferMax
);

2. LoadImage函数(通用载入)

cpp 复制代码
// 可载入多种类型资源,更灵活
HANDLE LoadImage(
    HINSTANCE hInst,      // 实例句柄
    LPCSTR name,          // 资源名/ID
    UINT type,            // 类型:IMAGE_BITMAP/ICON/CURSOR
    int cx, int cy,       // 期望尺寸(0=原始)
    UINT load             // 载入标志
);

// 常用标志:
// LR_DEFAULTCOLOR    - 默认颜色(不转换)
// LR_LOADFROMFILE    - 从文件载入
// LR_CREATEDIBSECTION- 创建DIB段(位图)
// LR_SHARED          - 共享资源(多线程)
// LR_LOADTRANSPARENT - 透明色

3. FindResource/LoadResource(原始资源访问)

cpp 复制代码
// 用于访问自定义二进制资源
HRSRC FindResource(HINSTANCE hModule, LPCSTR name, LPCSTR type);
DWORD SizeofResource(HINSTANCE hModule, HRSRC hResInfo);
HGLOBAL LoadResource(HINSTANCE hModule, HRSRC hResInfo);
LPVOID LockResource(HGLOBAL hResData);

// 使用模式:
HRSRC hRes = FindResource(hInst, MAKEINTRESOURCE(IDR_DATA), "BINARY");
if (hRes)
{
    DWORD dwSize = SizeofResource(hInst, hRes);
    HGLOBAL hGlobal = LoadResource(hInst, hRes);
    BYTE* pData = (BYTE*)LockResource(hGlobal);
    // 使用数据...
    // 注意:不需要调用UnlockResource或FreeResource
}

示例:

cpp 复制代码
#include <windows.h>
#include "resource.h"  // 包含IDR_MAIN_MENU定义

HINSTANCE g_hInstance;
HWND g_hWndMain;

// 菜单命令处理函数
void HandleMenuCommand(WPARAM wParam)
{
    switch (LOWORD(wParam))
    {
    case ID_FILE_NEW:
        MessageBox(g_hWndMain, "新建文件", "菜单", MB_OK);
        break;
    case ID_FILE_OPEN:
        MessageBox(g_hWndMain, "打开文件", "菜单", MB_OK);
        break;
    case ID_FILE_EXIT:
        PostQuitMessage(0);
        break;
    }
}

// 窗口过程
LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
    switch (msg)
    {
    case WM_CREATE:
        {
            // 载入并设置菜单
            HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAIN_MENU));
            if (hMenu)
            {
                SetMenu(hWnd, hMenu);
                
                // 可选:启用/禁用特定菜单项
                EnableMenuItem(hMenu, ID_FILE_SAVE, MF_GRAYED);
                
                // 可选:检查菜单项
                CheckMenuItem(hMenu, ID_VIEW_TOOLBAR, MF_CHECKED);
            }
        }
        break;
        
    case WM_COMMAND:
        HandleMenuCommand(wParam);
        break;
        
    case WM_DESTROY:
        PostQuitMessage(0);
        break;
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, 
                   LPSTR lpCmdLine, int nCmdShow)
{
    g_hInstance = hInstance;
    
    // 注册窗口类
    WNDCLASSEX wc = {0};
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    wc.lpszClassName = TEXT("MyWindowClass");
    
    if (!RegisterClassEx(&wc))
        return 0;
    
    // 创建窗口
    g_hWndMain = CreateWindow(
        TEXT("MyWindowClass"), TEXT("菜单示例"),
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );
    
    if (!g_hWndMain)
        return 0;
    
    ShowWindow(g_hWndMain, nCmdShow);
    UpdateWindow(g_hWndMain);
    
    // 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    
    return (int)msg.wParam;
}

四、资源设置/改变函数

1. 图标相关

cpp 复制代码
// 设置窗口图标(大/小)
SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIconSmall);

// 直接设置窗口类图标(影响后续窗口)
SetClassLongPtr(hWnd, GCLP_HICON, (LONG_PTR)hIcon);
SetClassLongPtr(hWnd, GCLP_HICONSM, (LONG_PTR)hIconSmall);

// 获取当前图标
HICON hCurrentIcon = (HICON)GetClassLongPtr(hWnd, GCLP_HICON);

2. 光标相关

cpp 复制代码
// 立即设置光标(仅当前线程)
SetCursor(hCursor);

// 设置窗口类光标(影响后续光标)
SetClassLongPtr(hWnd, GCLP_HCURSOR, (LONG_PTR)hCursor);

// 在WM_SETCURSOR中处理
case WM_SETCURSOR:
    if (LOWORD(lParam) == HTCLIENT)  // 在客户端区域
    {
        SetCursor(hCustomCursor);
        return TRUE;  // 阻止默认处理
    }
    break;

3. 位图相关

cpp 复制代码
// 设置静态控件位图
SendMessage(hStatic, STM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);

// 设置按钮位图
SendMessage(hButton, BM_SETIMAGE, IMAGE_BITMAP, (LPARAM)hBitmap);

// 设置列表框项目图像
SendMessage(hList, LB_SETITEMDATA, index, (LPARAM)hBitmap);

// 更换图像列表中的位图
ImageList_Replace(g_hImageList, index, hNewBitmap, NULL);

4. 菜单相关

cpp 复制代码
// 设置窗口菜单
SetMenu(hWnd, hNewMenu);

// 修改菜单项
ModifyMenu(hMenu, ID_ITEM, MF_BYCOMMAND | MF_STRING, 
          ID_ITEM, "新文本");

// 启用/禁用菜单项
EnableMenuItem(hMenu, ID_ITEM, MF_GRAYED | MF_DISABLED);
EnableMenuItem(hMenu, ID_ITEM, MF_ENABLED);

// 标记菜单项
CheckMenuItem(hMenu, ID_ITEM, MF_CHECKED);
CheckMenuItem(hMenu, ID_ITEM, MF_UNCHECKED);

5. 字符串相关

cpp 复制代码
// 设置窗口标题
SetWindowText(hWnd, "新标题");

// 设置控件文本
SetDlgItemText(hDlg, IDC_EDIT, "新文本");

// 设置状态栏文本
SendMessage(hStatusBar, SB_SETTEXT, part, (LPARAM)"状态");

// 动态创建字符串资源
TCHAR szBuffer[256];
wsprintf(szBuffer, "格式化的: %s %d", szName, nValue);
SetWindowText(hWnd, szBuffer);

五、常见问题

1. LoadBitmap载入的位图需要销毁吗?为什么?

  • 必须用DeleteObject销毁
  • 原因:GDI对象由应用程序管理,系统不会自动释放
  • 不销毁会导致资源泄漏(GDI泄漏)

2. LoadMenu和LoadString的内存管理有什么区别?

复制代码
- LoadMenu:
  - 附加到窗口时:窗口销毁自动销毁
  - 不附加时:必须用DestroyMenu手动销毁
  - 动态创建的菜单:必须用DestroyMenu销毁
  
- LoadString:
  - 无需特殊销毁
  - 字符串缓冲由调用者管理
  - 资源段中的字符串只读,系统管理
相关推荐
2501_926978332 小时前
“LLM的智能本质--AGI的可能路径--人类的意识本质”三者的统一基底(5.0理论解读)
人工智能·经验分享·笔记·深度学习·机器学习·ai写作·agi
لا معنى له2 小时前
WAM与AC-WM:具身智能时代的世界动作模型与动作条件世界模型
人工智能·笔记·学习
薛先生_0993 小时前
js学习语法第一天
开发语言·javascript·学习
Wilber的技术分享4 小时前
【LeetCode高频手撕题 2】面试中常见的手撕算法题(小红书)
笔记·算法·leetcode·面试
愚昧之山绝望之谷开悟之坡4 小时前
合格境外投资者
笔记
寒秋花开曾相惜6 小时前
(学习笔记)3.8 指针运算(3.8.3 嵌套的数组& 3.8.4 定长数组)
java·开发语言·笔记·学习·算法
攻城狮在此6 小时前
SecureCRT与MobaXterm详细对比:哪个更强、谁更适合你?
windows
是翔仔呐6 小时前
第11章 显示外设驱动:I2C协议OLED屏、SPI协议LCD屏字符/图片/中文显示
c语言·开发语言·stm32·单片机·嵌入式硬件·学习·gitee
_李小白6 小时前
【AI大模型学习笔记之平台篇】第五篇:Trae常用模型介绍与性能对比
人工智能·笔记·学习
承渊政道7 小时前
【优选算法】(实战体会位运算的逻辑思维)
数据结构·c++·笔记·学习·算法·leetcode·visual studio