书籍:《Visual C++ 2017从入门到精通》的2.9 内存管理
环境:visual studio 2022
内容:[例 2.52] 文件API函数简单应用
以下是 CloseHandle() 函数的详细解析,涵盖其作用、参数、使用场景、注意事项及代码示例:
函数原型
BOOL CloseHandle(
HANDLE hObject // 要关闭的句柄
);
参数说明
**hObject**
类型: HANDLE说明: 需要关闭的句柄,可以是以下类型:
文件或设备的句柄(通过 CreateFile 获取)。线程或进程的句柄(通过 CreateThread/CreateProcess 获取)。内存映射文件句柄(通过 CreateFileMapping 获取)。其他内核对象句柄(如事件、互斥量、信号量等)。 示例:
HANDLE hFile = CreateFile(L"test.txt", GENERIC_READ, ...);
CloseHandle(hFile); // 关闭文件句柄
返回值
成功: 返回 TRUE。失败: 返回 FALSE,可通过 GetLastError() 获取错误码。
常见错误码:
ERROR_INVALID_HANDLE (6): 句柄无效(已关闭或从未有效)。ERROR_NOT_FOUND (1168): 句柄未被系统识别(可能已被其他进程关闭)。
核心作用
释放系统资源
每个 CreateFile、CreateThread 等函数创建的句柄均占用系统资源。未关闭的句柄会导致资源泄漏,最终可能耗尽系统资源(如句柄数上限)。 确保数据完整性
对于文件或设备句柄,关闭时会自动刷新缓冲区并释放资源。示例: 文件写入后未关闭句柄,可能导致数据未完全写入磁盘。 安全合规
敏感资源(如密码文件句柄)需及时关闭,避免被恶意利用。
使用场景
1. 文件操作后关闭
HANDLE hFile = CreateFile(L"data.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile != INVALID_HANDLE_VALUE) {
WriteFile(hFile, "Hello World", 11, NULL, NULL);
CloseHandle(hFile); // 必须关闭文件句柄
}
2. 线程或进程句柄管理
HANDLE hThread = CreateThread(NULL, 0, ThreadProc, NULL, 0, NULL);
// 等待线程结束并关闭句柄
WaitForSingleObject(hThread, INFINITE);
CloseHandle(hThread); // 释放线程句柄
3. 内存映射文件释放
HANDLE hMapFile = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1024, L"MyMap");
if (hMapFile) {
CloseHandle(hMapFile); // 关闭映射文件句柄
}
关键注意事项
禁止重复关闭句柄
对已关闭的句柄再次调用 CloseHandle 会导致 ERROR_INVALID_HANDLE。示例:
HANDLE hFile = CreateFile(...);
CloseHandle(hFile);
CloseHandle(hFile); // 错误!返回 FALSE
句柄有效性检查
在关闭前可检查句柄是否有效(但非必需,因 CloseHandle 对无效句柄返回错误)。示例: 内核对象的引用计数
句柄是内核对象的引用计数器。只有当计数为0时,系统才会销毁对象。示例:
若多个句柄指向同一内核对象(如文件被多个进程打开),需逐一关闭所有句柄。 与 DeleteObject 的区别
DeleteObject 用于图形资源(如 GDI 对象),而 CloseHandle 用于内核对象。错误示例:
HBITMAP hBitmap = CreateBitmap(...);
CloseHandle(hBitmap); // 错误!应使用 DeleteObject(hBitmap)
自动关闭机制
子进程句柄: 当父进程关闭子进程句柄后,子进程仍可独立运行。跨进程传递句柄: 使用 DuplicateHandle 复制句柄后,原句柄仍需手动关闭。
常见问题与解决方案
问题1:程序运行后句柄数异常增长
原因: 未正确关闭文件、线程或设备句柄。解决:
使用工具(如 Process Explorer)监控句柄使用情况。确保每个 CreateXXX 调用都有对应的 CloseHandle。
问题2:文件关闭后仍被占用
原因:
文件映射视图(MapViewOfFile)未解除。其他进程持有该文件句柄。 解决:
确保调用 UnmapViewOfFile 解除内存映射。检查其他进程是否未正确关闭句柄。
问题3:关闭句柄后文件被立即删除
场景: 使用 FILE_FLAG_DELETE_ON_CLOSE 标志时,关闭句柄会删除文件。示例代码:
HANDLE hFile = CreateFile(
L"tempfile.tmp",
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_FLAG_DELETE_ON_CLOSE, // 句柄关闭时删除文件
NULL
);
CloseHandle(hFile); // 文件被立即删除
最佳实践
RAII 模式管理句柄
封装句柄在对象中,利用析构函数自动调用 CloseHandle。示例:
class FileHandle {
public:
FileHandle(LPCTSTR filename) {
hFile = CreateFile(filename, GENERIC_READ, ...);
}
~FileHandle() {
if (hFile != INVALID_HANDLE_VALUE) {
CloseHandle(hFile);
}
}
private:
HANDLE hFile;
};
避免全局或静态句柄
全局句柄可能因程序逻辑复杂导致漏关,优先使用局部变量。 调试技巧
使用 Visual Studio 调试器 监控 CloseHandle 调用。启用 Windows 内核调试 检查句柄泄漏(需专业工具)。
与示例代码的关联
在用户提供的代码中,CloseHandle 的调用如下:
::CloseHandle(handle); // 关闭文件句柄,触发 FILE_FLAG_DELETE_ON_CLOSE 自动删除文件
关键行为:
由于 CreateFile 指定了 FILE_FLAG_DELETE_ON_CLOSE,句柄关闭时文件被立即删除。若需保留文件,应移除该标志。
总结
核心功能: 释放内核对象资源,避免泄漏。核心原则:
每个 CreateXXX 必须有对应的 CloseHandle。及时关闭不再使用的句柄。 高级场景:
通过 DuplicateHandle 共享句柄。结合 FILE_FLAG_DELETE_ON_CLOSE 实现临时文件自动清理。