使用带有自定义删除器的shared_ptr使HANDLE RAII兼容

Eta*_*tan 11 c++ winapi raii

我最近在SO上发布了关于RAII的一般性问题.但是,我的HANDLE示例仍然存在一些实现问题.

A HANDLE是typedeffed void *in windows.h.因此,shared_ptr需要正确的定义

std::tr1::shared_ptr<void> myHandle (INVALID_HANDLE_VALUE, CloseHandle);
Run Code Online (Sandbox Code Playgroud)

示例1 CreateToolhelp32Snapshot:返回HANDLE并工作.

const std::tr1::shared_ptr<void> h
    (CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL), CloseHandle);
Run Code Online (Sandbox Code Playgroud)

当我void在定义中使用时(正确的方法是什么?)问题继续存在,当我尝试用这个指针调用更多的winapi命令时.它们在功能上有效,但很难看,我相信必须有更好的解决方案.

在以下示例中,h是一个通过顶部定义创建的指针.

例2 OpenProcessToken:最后一个参数是a PHANDLE.与演员一样丑陋.

OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
    (PHANDLE)&h);
Run Code Online (Sandbox Code Playgroud)

例3 Process32First:第一个参数是a HANDLE.非常丑陋.

Process32First(*((PHANDLE)&h), &pEntry);
Run Code Online (Sandbox Code Playgroud)

例4简单比较常数HANDLE.非常丑陋.

if (*((PHANDLE)&h) == INVALID_HANDLE) { /* do something */ }
Run Code Online (Sandbox Code Playgroud)

为HANDLE创建正确的shared_ptr的正确方法是什么?

Éri*_*ant 9

例1没问题

例2是错误的.通过盲目地转换为PHANDLE,绕过shared_ptr逻辑.它应该是这样的:

HANDLE h;
OpenProcessToken(...., &h);
shared_ptr<void> safe_h(h, &::CloseHandle);
Run Code Online (Sandbox Code Playgroud)

或者,分配给预先存在的shared_ptr:

shared_ptr<void> safe_h = ....
{
  HANDLE h;
  OpenProcessToken(...., &h);
  safe_h.reset(h, &::CloseHandle);
}//For extra safety, limit visibility of the naked handle
Run Code Online (Sandbox Code Playgroud)

或者,创建自己的,安全的OpenProcessToken版本,它返回一个共享句柄而不是一个PHANDLE:

// Using SharedHandle defined at the end of this post
SharedHandle OpenProcess(....)
{
    HANDLE h = INVALID_HANDLE_VALUE;
    ::OpenProcessToken(...., &h);
    return SharedHandle(h);
}
Run Code Online (Sandbox Code Playgroud)

例3:不需要走这些弯路.这应该没问题:

Process32First(h.get(), ...);
Run Code Online (Sandbox Code Playgroud)

例4:再次,没有绕道:

if (h.get() == INVALID_HANDLE){...}
Run Code Online (Sandbox Code Playgroud)

为了使事情变得更好,你可以输入以下内容:

typedef shared_ptr<void> SharedHandle;
Run Code Online (Sandbox Code Playgroud)

或者更好的是,如果要使用CloseHandle()关闭所有句柄,请创建一个包装shared_ptr的SharedHandle类并自动提供正确的删除器:

// Warning: Not tested. For illustration purposes only
class SharedHandle
{
public:
  explicit SharedHandle(HANDLE h) : m_Handle(h, &::CloseHandle){};
  HANDLE get()const{return m_Handle.get();}

  //Expose other shared_ptr-like methods as needed
  //...

private:
  shared_ptr<void> m_Handle;
};
Run Code Online (Sandbox Code Playgroud)