使用shared_ptr/unique_ptr自动关闭Win32 HANDLE的代码有效性咨询
Win32 HANDLE智能指针管理问题解析
我不清楚如何自行验证这一点,因此在此提问。我刚接触智能指针,想咨询:在以下代码示例中,所有Win32 HANDLE是否都会被自动关闭?还有其他需要处理的事项吗?另外,我无法使用CHandle类。
你的原始代码如下:
#include <stdio.h> #include <windows.h> #include <fileapi.h> #include <stdlib.h> #include <string.h> #include <string> #include <memory> struct Deleter { void operator()(HANDLE* h) { CloseHandle(*h); } }; void Close(HANDLE* handle) { CloseHandle(*handle); } void helper() { std::wstring str = L"Project2.exe 30 30"; STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi; CreateProcess(NULL, str.data(), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &si, &pi); STARTUPINFO li = { sizeof(si) }; PROCESS_INFORMATION ki; CreateProcess(NULL, str.data(), nullptr, nullptr, false, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &li, &ki); std::unique_ptr<HANDLE, Deleter> xx = std::unique_ptr<HANDLE, Deleter>(&pi.hProcess); std::unique_ptr<HANDLE, Deleter> yy(&pi.hThread); std::shared_ptr<HANDLE> zz(&ki.hProcess, Close); std::shared_ptr<HANDLE> ww(&ki.hThread, Close); } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { helper(); }
首先明确:你的代码不会正确管理HANDLE,反而有未定义行为风险
核心问题出在智能指针的使用方式错误:
- 你让
unique_ptr/shared_ptr管理的是栈上HANDLE变量的地址,而不是HANDLE资源本身。智能指针的设计意图是持有资源的所有权,这里传递&pi.hProcess(栈变量地址),虽然deleter会调用CloseHandle(*h)关闭句柄,但存在隐患:如果栈变量被覆盖、后续代码误操作指针地址,都会导致错误。 - 没有检查
CreateProcess的返回值:如果进程创建失败,pi/ki里的HANDLE是NULL,虽然CloseHandle(NULL)不会报错,但你无法感知错误,也会做无意义的操作。 - 存在重复释放风险:如果后续代码手动调用
CloseHandle,加上智能指针的自动调用,会导致重复关闭句柄,触发未定义行为。
正确的智能指针管理HANDLE方式
我们让智能指针直接持有HANDLE资源,自定义deleter直接操作HANDLE,而不是HANDLE的指针。
方案1:unique_ptr + 自定义仿函数deleter
#include <stdio.h> #include <windows.h> #include <fileapi.h> #include <stdlib.h> #include <string.h> #include <string> #include <memory> // 自定义deleter,直接处理HANDLE,判断无效句柄 struct HandleDeleter { void operator()(HANDLE h) const { if (h != INVALID_HANDLE_VALUE && h != NULL) { CloseHandle(h); } } }; // 定义别名简化使用 using UniqueHandle = std::unique_ptr<void, HandleDeleter>; void helper() { std::wstring str = L"Project2.exe 30 30"; STARTUPINFO si = { sizeof(si) }; PROCESS_INFORMATION pi{}; // 必须检查CreateProcess是否成功 if (CreateProcessW(NULL, str.data(), nullptr, nullptr, FALSE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &si, &pi)) { // 直接把HANDLE交给智能指针,转移所有权 UniqueHandle processHandle(pi.hProcess); UniqueHandle threadHandle(pi.hThread); // 将原结构体中的HANDLE置空,避免后续误操作 pi.hProcess = NULL; pi.hThread = NULL; } else { // 处理错误,打印错误码 printf("CreateProcess失败,错误码:%lu\n", GetLastError()); } STARTUPINFO li = { sizeof(li) }; PROCESS_INFORMATION ki{}; if (CreateProcessW(NULL, str.data(), nullptr, nullptr, FALSE, NORMAL_PRIORITY_CLASS, nullptr, nullptr, &li, &ki)) { // shared_ptr同样直接管理HANDLE std::shared_ptr<void> sharedProcessHandle(ki.hProcess, HandleDeleter()); std::shared_ptr<void> sharedThreadHandle(ki.hThread, HandleDeleter()); ki.hProcess = NULL; ki.hThread = NULL; } else { printf("CreateProcess失败,错误码:%lu\n", GetLastError()); } } int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine, int nCmdShow) { helper(); return 0; }
方案2:shared_ptr + 单独的清理函数
如果你偏好单独的清理函数,也可以这么写:
// 单独的清理函数 void CloseWin32Handle(HANDLE h) { if (h != INVALID_HANDLE_VALUE && h != NULL) { CloseHandle(h); } } // 使用时: std::shared_ptr<void> sharedProcessHandle(ki.hProcess, CloseWin32Handle);
其他需要注意的事项
- 一定要检查Win32 API的返回值:
CreateProcess失败时返回FALSE,这时候句柄是无效的,及时处理错误能避免无效操作。 - 避免重复释放:把句柄交给智能指针后,将原结构体中的句柄置为NULL,防止后续手动调用
CloseHandle导致重复释放。 - 区分无效句柄类型:有些Win32函数返回
INVALID_HANDLE_VALUE(比如文件操作),有些返回NULL(比如进程创建),deleter里同时判断这两种情况更安全。 - 用void作为智能指针模板参数:HANDLE本质是
void*,用void作为模板参数可以避免不必要的类型转换,deleter直接处理HANDLE即可。
内容的提问来源于stack exchange,提问作者RandomUser890
相关产品推荐
相关产品推荐

