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:
  - 无需特殊销毁
  - 字符串缓冲由调用者管理
  - 资源段中的字符串只读,系统管理
相关推荐
时代的凡人9 小时前
0208晨间笔记
笔记
今天只学一颗糖9 小时前
1、《深入理解计算机系统》--计算机系统介绍
linux·笔记·学习·系统架构
testpassportcn10 小时前
AWS DOP-C02 認證完整解析|AWS DevOps Engineer Professional 考試
网络·学习·改行学it
游乐码13 小时前
c#变长关键字和参数默认值
学习·c#
x***r15113 小时前
SuperScan4单文件扫描安装步骤详解(附端口扫描与主机存活检测教程)
windows
饭碗、碗碗香14 小时前
【Python学习笔记】:Python的hashlib算法简明指南:选型、场景与示例
笔记·python·学习
Wils0nEdwards14 小时前
初中化学1
笔记
不爱学习的老登14 小时前
Windows客户端与Linux服务器配置ssh无密码登录
linux·服务器·windows
魔力军14 小时前
Rust学习Day4: 所有权、引用和切片介绍
开发语言·学习·rust
wubba lubba dub dub75014 小时前
第三十六周 学习周报
学习