正确关闭WinAPI HANDLEs(避免重复关闭)

时间:2012-10-23 10:53:30

标签: c++ winapi handle hbitmap

我有一些手柄,我需要关闭它。代码中有一些地方可以关闭句柄。那么,这是一种正确的关闭方式吗?

HANDLE h;
....
if ( h != INVALID_HANDLE_VALUE ) {
  ::CloseHandle(h);
  h = INVALID_HANDLE_VALUE;
}

关于位图句柄存在同样的问题:

HBITMAP hb;
....
if ( hb != INVALID_HANDLE_VALUE ) {
  ::DeleteObject(hb);
  hb = INVALID_HANDLE_VALUE;
}

编辑: 我认为,存在一些误解。我知道CloseHandle用于关闭句柄。我想知道关闭手柄的正确方法。删除指针时会出现类似的情况。

Foo *foo = new Foo();

// for example there is 2 functions that can delete foo
void bar() {
  ....
  delete foo;
}
void duck() {
  ....
  delete foo;
}

因此,以下代码意味着问题:

bar();
duck();

这种情况有一些解决方法。我们需要定义bar& duck函数,如下所示:

void bar() {
  ....
  if (foo) {
    delete foo;
    foo = NULL;
  }
}
void duck() {
  ....
  if (foo) {
    delete foo;
    foo = NULL;
  }
}

因此我们避免重复删除foo。问题是关闭句柄的正确方法是什么?我的意思是,如何避免重复关闭句柄问题?

5 个答案:

答案 0 :(得分:8)

并非所有使用HANDLE的函数都使用CloseHandle(),而有些函数则使用其他结束函数。此外,并非所有HANDLE值都使用INVALID_HANDLE_VALUE。有些人使用NULL代替。

HBITMAP从不使用INVALID_HANDLE_VALUE,它始终使用NULL。而且,您永远不应该致电DeleteObject()来获取您不拥有的HBITMAP

所以简短的回答是 - 如果您正在尝试创建一些通用句柄管理,请不要打扰。你很可能弄错了。如果你分配/打开一些句柄,你必须知道关闭它的正确方法,你无法猜测它。

如果您希望句柄自行管理,那么RAII是最佳选择。我更喜欢使用具有专门特征的模板化类来减少不同类型句柄的代码重复,例如:

template< class traits >
class HandleWrapper
{
private:
    traits::HandleType FHandle;

public:
    HandleWrapper()
        FHandle(traits::InvalidValue)
    {
    }

    HandleWrapper(const traits::HandleType value)
        FHandle(value)
    {
    }

    ~HandleWrapper()
    {
        Close();
    }

    void Close()
    {
        if (FHandle != traits::InvalidValue)
        {
            traits::Close(FHandle);
            FHandle = traits::InvalidValue;
        }
    }

    bool operator !() const {
        return (FHandle == traits:::InvalidValue);
    }

    operator bool() const {
        return (FHandle != traits:::InvalidValue);
    }

    operator traits::HandleType() {
        return FHandle;
    }
};

struct KernelHandleTraits
{
    typedef HANDLE HandleType;
    static const HANDLE InvalidValue = INVALID_HANDLE_VALUE;

    static void Close(HANDLE value)
    {
        CloseHandle(value);
    }
};

HandleWrapper<KernelHandleTraits> hFile(CreateFile(...));

struct NullKernelHandleTraits
{
    typedef HANDLE HandleType;
    static const HANDLE InvalidValue = NULL;

    static void Close(HANDLE value)
    {
        CloseHandle(value);
    }
};

HandleWrapper<NullKernelHandleTraits> hMapping(CreateFileMapping(...));

struct FileMapViewTraits
{    
    typedef void* HandleType;
    static const void* InvalidValue = NULL;

    static void Close(void *value)
    {
        UnmapViewOfFile(value);
    }
};

HandleWrapper<FileMapViewTraits> hView(MapViewOfFile(...));

struct GDIBitmapHandleTraits
{    
    typedef HBITMAP HandleType;
    static const HBITMAP InvalidValue = NULL;

    static void Close(HBITMAP value)
    {
        DeleteObject(value);
    }
};

HandleWrapper<GDIBitmapTraits> hBmp(CreateBitmap(...));

答案 1 :(得分:3)

使用RAII pattern

将句柄包装到一个类中,该类在构造函数中分配句柄并在析构函数中销毁它。你可以在MFC中找到一些例子,例如: CGdiObject class用于HBITMAP等GDI对象。

另见这个问题:RAII and smart pointers in C++

答案 2 :(得分:1)

我认为你的困惑来自他们被称为&#34;句柄&#34;,但他们是不同的&#34;类&#34;对象HBITMAP中的术语句柄更多地用作&#34;不透明标识符&#34;。还有大量的文档假定&#34;处理&#34; ==&#34; windows内核句柄&#34;。

一般情况下,如果您想知道如何删除某些内容,则应该查看构造函数的文档。

答案 3 :(得分:1)

以下代码可能是您所追求的:

BOOL CloseValidHandle(HANDLE& handle)
{
  if (handle != INVALID_HANDLE_VALUE && handle != 0)
  {
    if (CloseHandle(handle))
    {
      handle = INVALID_HANDLE_VALUE;
      return TRUE;
    }
    else
    {
      return FALSE;
    }
  }

  return TRUE;
}

答案 4 :(得分:0)

它不是RAII,但它有助于删除/关闭处理程序。

class HandleDel : boost::notcopyable{
public:
    HandleDel(HANDLE h, HANDLE invalid, BOOL(WINAPI *del)(HANDLE)):
        h(h), invalid(invalid), del(del){
    }
    ~HandleDel(){
        if ( h != invalid ) del(h);
    }
private:
    HANDLE h;
    HANDLE invalid;
    BOOL(WINAPI *del)(HANDLE);
};