适当的方式接近WinAPI的手柄(重复闭合的避免)(Proper way close WinAPI

2019-07-02 09:10发布

我有一些把手,我需要关闭它。 有一个在代码,其中手柄可关闭一些地方。 所以,这是关闭手柄正确的方式?

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();

有一些变通方法对于这种情况。 我们需要定义barduck这样的功能:

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

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

Answer 1:

这并不是说使用所有功能HANDLE使用CloseHandle()一些使用其他功能收盘代替。 此外,并非所有HANDLE值使用INVALID_HANDLE_VALUE ,无论是。 一些使用NULL来代替。

HBITMAP从不使用INVALID_HANDLE_VALUE ,它总是使用NULL 。 你应该调用DeleteObject()HBITMAP你没有自己。

所以,简单的答案是 - 如果你想创造一些通用手柄的管理,不要打扰。 你可能弄错了。 如果分配/打开一些把手,你必须知道正确的方法来关闭它,你不能在其猜测。

如果你想把手来管理自己,那么RAII是最好的选择。 我更喜欢使用专门性状的模板类,以减少differrent类型的手柄,如重复的代码:

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(...));

等等。



Answer 2:

使用RAII模式 。

包装一个手柄成分配在构造函数中的手柄并破坏它的析构函数的类。 你可以找到在MFC中一些例子,如CGdiObject类像GDI对象HBITMAP

另见本SO问题: RAII和智能指针在C ++



Answer 3:

是。

  • CloseHandle()关闭windows 内核对象句柄。
  • DeleteObject()删除GDI对象。

我觉得你的困惑来自于他们两个被称为“手柄”,但它们是不同的对象的“类”。 在术语手柄HBITMAP用在这里更是“不透明标识符”。 此外,还有大量其假设的“把手” ==“Windows内核句柄”文档。

一般来说,如果你想知道如何删除的东西,你应该看看构造函数的文档。



Answer 4:

下面的代码也许是你追求的:

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;
}


Answer 5:

这是没有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);
};


文章来源: Proper way close WinAPI HANDLEs (avoiding of repeated closing)