Proper way close WinAPI HANDLEs (avoiding of repeated closing)

拥有回忆 提交于 2019-11-30 21:35:55

Not all functions that use HANDLE use CloseHandle(), some use other closing functions instead. Also, not all HANDLE values use INVALID_HANDLE_VALUE, either. Some use NULL instead.

HBITMAP never uses INVALID_HANDLE_VALUE, it always uses NULL. And you should never call DeleteObject() for an HBITMAP you do not own.

So the short answer is - if you are trying to create some general purpose handle management, don't bother. You are likely to get it wrong. If you allocate/open some handle, you have to know the correct way to close it, you can't guess at it.

If you want the handles to manage themselves, then RAII is the best choice. I prefer to use a templated class with specialized traits to reduce code duplication for differrent types of handles, eg:

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

Etc.

Andrey

Use RAII pattern.

Wrap a handle into a class which allocates the handle in the constructor and destroys it in the destructor. You can find some examples in MFC, e.g. CGdiObject class for GDI objects like HBITMAP.

See also this SO question: RAII and smart pointers in C++

cdleonard

Yes.

I think your confusion comes from them both being called "handles", but they are different "classes" of objects. The term handle in HBITMAP is used here more as "opaque identifier". There is also plenty of documentation which assumes "handle" == "windows kernel handle".

In general if you're wondering how to delete something you should look at the constructor's documentation.

The following code is maybe what you're after:

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

It's no RAII but it helps to delete/close handler.

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);
};
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!