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

什么是 HBITMAP?
HBITMAP 是 Handle to a BITMAP 的缩写,中文意思是“位图句柄”。
- 句柄:在 Windows 中,句柄是一个唯一的标识符(通常是一个整数或指针),由操作系统内核管理,应用程序通过这个句柄来请求操作系统管理一个资源(如窗口、图标、画笔、位图等),应用程序本身不应该直接解析或修改句柄的值,而应该将其视为一个“黑盒”,只通过 API 函数来操作它。
- 位图:位图是 Windows 中一种非常重要的图形对象,它由像素点阵组成,可以表示图像、图标等,你可以把它想象成一个由彩色小方块(像素)组成的网格。
HBITMAP 是你的程序告诉 Windows:“你好,我这里有一个位图图像,请你帮我管理它,这是它的身份证号(句柄)”。
如何使用 HBITMAP?(完整生命周期)
使用 HBITMAP 通常遵循一个固定的生命周期:创建 -> 加载/创建 -> 使用 -> 销毁。
步骤 1: 包含头文件
要使用 Windows API,你必须包含相应的头文件。

#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,你需要一个设备上下文作为“画布”,然后将位图选入这个画布中,再进行绘制。

最常用的函数是 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, 设备相关位图):
- 这是最传统的位图类型,就是我们上面用
CreateBitmap和LoadImage(不带LR_CREATEDIBSECTION) 创建的。 - 它的格式和颜色信息依赖于它被创建时所基于的设备上下文(即显示器的设置)。
- 优点:绘制速度快,因为格式已经适配了设备。
- 缺点:不能直接访问像素数据,灵活性差。
- 这是最传统的位图类型,就是我们上面用
-
DIB (Device-Independent Bitmap, 设备无关位图):
- DIB 有自己的颜色表和像素数据格式,不依赖于任何设备。
- 你可以自由地读写它的像素数据。
HBITMAP可以同时代表 DDB 和 DIB,当你使用LoadImage并带上LR_CREATEDIBSECTION标志时,你得到的是一个 DIB Section,它既有 DIB 的灵活性(可以访问像素),又可以像 DDB 一样被SelectObject和BitBlt高效使用。
现代 C++ 开发建议:
在现代 Windows 开发中,尤其是在 C++ 中,推荐使用更高级的 GDI+ 或 Direct2D/Direct3D API,它们提供了更强大、更易用的图形功能,并且能更好地利用现代 GPU 的加速能力。HBITMAP 是 GDI(图形设备接口)的一部分,是一个相对底层的 API。
| 特性 | 描述 |
|---|---|
| 本质 | 一个句柄,不是图像数据本身。 |
| 来源 | 通过 LoadImage (从文件)、CreateBitmap (在内存中创建) 等函数获取。 |
| 使用 | 必须先选入一个内存设备上下文,然后通过 BitBlt 等函数绘制到目标设备上下文(如窗口)上。 |
| 销毁 | 使用完毕后,必须调用 DeleteObject 释放系统资源,否则会造成内存泄漏。 |
| 类型 | 可以是设备相关位图或设备无关位图。 |
| 适用场景 | 传统的 Win32 程序、简单的图像显示和操作。 |
