HBitmap在C语言中如何正确使用与内存管理?

99ANYc3cd6
预计阅读时长 26 分钟
位置: 首页 C语言 正文

HBITMAP 是 Windows API(应用程序编程接口)中的一个核心数据类型,它不是一个普通的 C 语言类型(如 intchar*),而是一个句柄

c语言 hbitmap
(图片来源网络,侵删)

什么是 HBITMAP?

HBITMAPHandle to a BITMAP 的缩写,中文意思是“位图句柄”。

  • 句柄:在 Windows 中,句柄是一个唯一的标识符(通常是一个整数或指针),由操作系统内核管理,应用程序通过这个句柄来请求操作系统管理一个资源(如窗口、图标、画笔、位图等),应用程序本身不应该直接解析或修改句柄的值,而应该将其视为一个“黑盒”,只通过 API 函数来操作它。
  • 位图:位图是 Windows 中一种非常重要的图形对象,它由像素点阵组成,可以表示图像、图标等,你可以把它想象成一个由彩色小方块(像素)组成的网格。

HBITMAP 是你的程序告诉 Windows:“你好,我这里有一个位图图像,请你帮我管理它,这是它的身份证号(句柄)”。


如何使用 HBITMAP?(完整生命周期)

使用 HBITMAP 通常遵循一个固定的生命周期:创建 -> 加载/创建 -> 使用 -> 销毁

步骤 1: 包含头文件

要使用 Windows API,你必须包含相应的头文件。

c语言 hbitmap
(图片来源网络,侵删)
#include <windows.h> // 包含了所有核心 Windows API 定义

步骤 2: 创建或加载位图

你有两种主要方式来获取一个 HBITMAP

方式 A: 从文件加载(最常用)

使用 LoadImage 函数可以从磁盘上的 .bmp 文件加载位图。

// 函数原型
HANDLE LoadImage(
  HINSTANCE hinst,       // 实例句柄,对于资源文件,通常为 NULL
  LPCSTR    lpszName,    // 文件路径或资源名称
  UINT      uType,       // 图像类型: IMAGE_BITMAP, IMAGE_ICON, etc.
  int       cxDesired,   // 期望的宽度 (0 表示使用原始宽度)
  int       cyDesired,   // 期望的高度 (0 表示使用原始高度)
  UINT      fuLoad       // 加载标志: LR_LOADFROMFILE, LR_DEFAULTSIZE, etc.
);
// 示例
HBITMAP hBitmap = (HBITMAP)LoadImage(
    NULL,                      // 不从资源加载,从文件
    L"C:\\path\\to\\your\\image.bmp", // 文件路径 (注意是宽字符 L"...")
    IMAGE_BITMAP,              // 加载位图
    0, 0,                      // 使用原始尺寸
    LR_LOADFROMFILE | LR_CREATEDIBSECTION // 标志:从文件加载,并创建 DIB Section
);
if (hBitmap == NULL) {
    // 加载失败,检查错误
    DWORD error = GetLastError();
    // ... 错误处理 ...
}

方式 B: 在内存中创建

你可以创建一个空的位图,然后逐个像素绘制,或者从其他来源(如剪贴板)获取数据来填充它。

// 函数原型
HBITMAP CreateBitmap(
  int       nWidth,  // 位图宽度(像素)
  int       nHeight, // 位图高度(像素)
  UINT      nPlanes, // 颜色平面数 (必须为 1)
  UINT      nBitsPerPel, // 每像素的颜色数 (通常为 1, 4, 8, 16, 24, 32)
  const VOID *lpvBits // 指向位图像素数据的指针 (可为 NULL)
);
// 示例:创建一个 100x100 像素的 24位色位图
HBITMAP hBitmap = CreateBitmap(100, 100, 1, 24, NULL);
if (hBitmap == NULL) {
    // 创建失败
}

步骤 3: 使用位图(在设备上下文中绘制)

你不能直接在屏幕上绘制 HBITMAP,你需要一个设备上下文作为“画布”,然后将位图选入这个画布中,再进行绘制。

c语言 hbitmap
(图片来源网络,侵删)

最常用的函数是 BitBlt (Bit Block Transfer)。

// 1. 获取目标设备上下文 (窗口的设备上下文)
HWND hWnd = GetConsoleWindow(); // 假设我们要画在控制台窗口上 (仅作示例)
HDC hdc = GetDC(hWnd);         // 获取窗口的 DC
// 2. 创建一个与目标 DC 兼容的内存 DC
HDC memDC = CreateCompatibleDC(hdc);
if (memDC == NULL) { /* 错误处理 */ }
// 3. 将位图选入内存 DC
HBITMAP hOldBitmap = (HBITMAP)SelectObject(memDC, hBitmap);
if (hOldBitmap == NULL) { /* 错误处理 */ }
// 4. 使用 BitBlt 将位图从内存 DC 复制到目标 DC
// 函数原型
BOOL BitBlt(
  HDC   hdcDest,      // 目标设备上下文
  int   xDest,        // 目标区域的左上角 X 坐标
  int   yDest,        // 目标区域的左上角 Y 坐标
  int   nWidth,       // 要复制的宽度
  int   nHeight,      // 要复制的高度
  HDC   hdcSrc,       // 源设备上下文 (我们的 memDC)
  int   xSrc,         // 源区域的左上角 X 坐标
  int   ySrc,         // 源区域的左上角 Y 坐标
  DWORD dwRop         // 光栅操作码 (SRCCOPY 表示直接复制)
);
BitBlt(hdc, 10, 10, 100, 100, memDC, 0, 0, SRCCOPY);
// 5. 清理:恢复并释放资源
SelectObject(memDC, hOldBitmap); // 恢复旧的位图
DeleteDC(memDC);                // 删除内存 DC
ReleaseDC(hWnd, hdc);            // 释放窗口 DC

步骤 4: 销毁位图

当你不再需要位图时,必须调用 DeleteObject 来释放它占用的系统资源(内存)。

if (hBitmap != NULL) {
    DeleteObject(hBitmap);
}

重要提示:你只能删除那些没有被选入任何设备上下文的 GDI 对象,在删除之前,最好确保已经用 SelectObject 将其替换掉了。


代码示例:在窗口中显示一个 BMP 图片

下面是一个完整的、简单的 Win32 程序,它在窗口客户区的左上角绘制一个位图。

#include <windows.h>
// 窗口过程函数的声明
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
    // 1. 注册窗口类
    WNDCLASSEX wc = { 0 };
    wc.cbSize = sizeof(WNDCLASSEX);
    wc.lpfnWndProc = WndProc;
    wc.hInstance = hInstance;
    wc.lpszClassName = L"MyBitmapWindow";
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
    if (!RegisterClassEx(&wc)) {
        MessageBox(NULL, L"窗口类注册失败!", L"错误", MB_ICONERROR);
        return 1;
    }
    // 2. 创建窗口
    HWND hWnd = CreateWindowEx(
        0, L"MyBitmapWindow", L"HBITMAP 示例",
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, CW_USEDEFAULT, 800, 600,
        NULL, NULL, hInstance, NULL
    );
    if (hWnd == NULL) {
        MessageBox(NULL, L"窗口创建失败!", L"错误", MB_ICONERROR);
        return 1;
    }
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);
    // 3. 消息循环
    MSG msg;
    while (GetMessage(&msg, NULL, 0, 0)) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }
    return (int)msg.wParam;
}
// 窗口过程函数
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) {
    switch (message) {
    case WM_PAINT: {
        // 在 WM_PAINT 消息中绘制图形
        PAINTSTRUCT ps;
        HDC hdc = BeginPaint(hWnd, &ps);
        // --- HBITMAP 的核心使用部分 ---
        // 1. 加载位图 (请确保你的 C 盘根目录下有 test.bmp 文件)
        HBITMAP hBitmap = (HBITMAP)LoadImage(
            NULL,
            L"C:\\test.bmp",
            IMAGE_BITMAP,
            0, 0,
            LR_LOADFROMFILE | LR_CREATEDIBSECTION
        );
        if (hBitmap) {
            // 2. 创建兼容的内存 DC
            HDC memDC = CreateCompatibleDC(hdc);
            if (memDC) {
                // 3. 将位图选入内存 DC
                HBITMAP hOldBitmap = (HBITMAP)SelectObject(memDC, hBitmap);
                // 4. 获取位图信息,以确定其尺寸
                BITMAP bm;
                GetObject(hBitmap, sizeof(BITMAP), &bm);
                // 5. 将位图从内存 DC 复制到窗口 DC
                BitBlt(hdc, 10, 10, bm.bmWidth, bm.bmHeight, memDC, 0, 0, SRCCOPY);
                // 6. 清理 GDI 对象
                SelectObject(memDC, hOldBitmap);
                DeleteDC(memDC);
            }
            // 7. 删除位图对象
            DeleteObject(hBitmap);
        }
        EndPaint(hWnd, &ps);
    }
        return 0;
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    return DefWindowProc(hWnd, message, wParam, lParam);
}

HBITMAP 与 DIB/DDB 的关系

  • DDB (Device-Dependent Bitmap, 设备相关位图)

    • 这是最传统的位图类型,就是我们上面用 CreateBitmapLoadImage (不带 LR_CREATEDIBSECTION) 创建的。
    • 它的格式和颜色信息依赖于它被创建时所基于的设备上下文(即显示器的设置)。
    • 优点:绘制速度快,因为格式已经适配了设备。
    • 缺点:不能直接访问像素数据,灵活性差。
  • DIB (Device-Independent Bitmap, 设备无关位图)

    • DIB 有自己的颜色表和像素数据格式,不依赖于任何设备。
    • 你可以自由地读写它的像素数据。
    • HBITMAP 可以同时代表 DDB 和 DIB,当你使用 LoadImage 并带上 LR_CREATEDIBSECTION 标志时,你得到的是一个 DIB Section,它既有 DIB 的灵活性(可以访问像素),又可以像 DDB 一样被 SelectObjectBitBlt 高效使用。

现代 C++ 开发建议: 在现代 Windows 开发中,尤其是在 C++ 中,推荐使用更高级的 GDI+ 或 Direct2D/Direct3D API,它们提供了更强大、更易用的图形功能,并且能更好地利用现代 GPU 的加速能力。HBITMAP 是 GDI(图形设备接口)的一部分,是一个相对底层的 API。

特性 描述
本质 一个句柄,不是图像数据本身。
来源 通过 LoadImage (从文件)、CreateBitmap (在内存中创建) 等函数获取。
使用 必须先选入一个内存设备上下文,然后通过 BitBlt 等函数绘制到目标设备上下文(如窗口)上。
销毁 使用完毕后,必须调用 DeleteObject 释放系统资源,否则会造成内存泄漏
类型 可以是设备相关位图或设备无关位图。
适用场景 传统的 Win32 程序、简单的图像显示和操作。
-- 展开阅读全文 --
头像
织梦为何无法删除数据库?
« 上一篇 04-17
织梦如何快速搭建门户网站?
下一篇 » 04-17

相关文章

取消
微信二维码
支付宝二维码

目录[+]