Skip navigation links
IT services and product development
Menu
TwoLogs
IT services and product development

Source code for C_Handle

Download

The source files for this module are listed below.  You can also download the module C_Handle as a zip archive; this archive contains all the source files and documentation.

Description

CHandle is a class for reference counted safe auto-destruct Windows handles, which can be used identically to regular handles.

Information

The CHandle template class uses a traits class that defines the properties of the handle type.  See the file 'Handle.h' for some useable worked out examples of commonly used Windows handle types.

When you want to define a handle type yourself, create a struct containing:

  • a type named CHandleType that defines the exact type of the handle,
  • a static const member of the above type named m_chInvalid that holds the value of an 'invalid' or empty handle (as e.g. the API would return it in case of failure), and
  • a static bool function that can kill a given handle.

An example for standard Windows Icon handles would be:

  // For in a .h file:
  struct CIconHandleTraits {
    using CHandleType = HICON;
    static const CHandleType m_chInvalid;
    static bool KillHandle(CHandleType hHandle) {
      return FALSE != DestroyIcon(hHandle);
    }
  };

  // For in a .cpp file
  const CIconHandleTraits::CHandleType CIconHandleTraits::m_chInvalid = NULL;

Use the above created struct as the template argument for the CHandle class; you can define a type shortcut for it to make it even easier to use in your code, like e.g.

  using CIconHandle = CHandle<CIconHandleTraits>;

Once you have a handle type defined, you can use a variable of this type just like you would use a variable of the real handle type.  The CHandle class will add a reference count to the handle value, so that when the last variable using the handle goes out of scope, the handle will be released automatically.  This way even exceptions will not result in leaked handles.

To assign a value to the handle, either use the Assign method or just assign the value with the = operator.  If you assign from a plain handle value, the handle will be taken over by the CHandle variable; you do not have to destroy the handle yourself.  When you assign from another CHandle variable, the reference count on the handle will be increased.  When the last variable referencing the handle value is destroyed, the handle is automatically released.  Retrieve the handle value by either calling the Get method or just using it where you would normally use a plain handle variable - CHandle can cast itself to the handle type.

You can also query the handle whether it IsSet or IsClear.  It is then compared to the invalid handle value.

When you want to stop using the handle value, you can call Clear.  If the handle is also referenced by other CHandle variables, they will take over the handle.  If you want to release control over the handle value without destroying it (e.g. to pass on ownership of the handle), you can call Release.  Release returns the plain handle value without destroying it.  Note that if the handle is references by more than one CHandle variable, Release will not return the plain handle value to protect the other CHandle variables from losing there handle.  Use the method RefCount to get the current number of references on the handle value.

Finally, if you want to make a new copy of the handle, you can call the Copy method.  This will generate a new handle that can be used in-place for the original handle.

Files

Each file belonging to this source code module is listed below.

Handle.h

/*******************************************************************************

  Version: 5
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  http://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#ifndef INCLUDE_TWOLOGS_COMMON_HANDLE_H
#define INCLUDE_TWOLOGS_COMMON_HANDLE_H

#include "HandleBase.h"
#include <commctrl.h>

// Standard handle type
struct CStdHandleTraits {
  using CHandleType = HANDLE;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != CloseHandle(hHandle);
  }
};
using CStdHandle = CHandle<CStdHandleTraits>;

// INVALID_HANDLE_VALUE handle type
struct CIHVHandleTraits {
  using CHandleType = HANDLE;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != CloseHandle(hHandle);
  }
};
using CIHVHandle = CHandle<CIHVHandleTraits>;
using CFileHandle = CHandle<CIHVHandleTraits>;

// Change notification handle type
struct CChangeNotHandleTraits {
  using CHandleType = HANDLE;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != FindCloseChangeNotification(hHandle);
  }
};
using CChangeNotHandle = CHandle<CChangeNotHandleTraits>;

// FindFirst handle type
struct CFFHandleTraits {
  using CHandleType = HANDLE;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != FindClose(hHandle);
  }
};
using CFFHandle = CHandle<CFFHandleTraits>;

// Icon handle type
struct CIconHandleTraits {
  using CHandleType = HICON;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != DestroyIcon(hHandle);
  }
};
using CIconHandle = CHandle<CIconHandleTraits>;

// Cursor handle type
struct CCursorHandleTraits {
  using CHandleType = HCURSOR;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != DestroyCursor(hHandle);
  }
};
using CCursorHandle = CHandle<CCursorHandleTraits>;

// GDI object handle type
struct CGDIHandleTraits {
  using CHandleType = HGDIOBJ;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != DeleteObject(hHandle);
  }
};
using CGDIHandle = CHandle<CGDIHandleTraits>;

// Brush object handle type
struct CBrushHandleTraits {
  using CHandleType = HBRUSH;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != DeleteObject(hHandle);
  }
};
using CBrushHandle = CHandle<CBrushHandleTraits>;

// RGN handle type
struct CRGNHandleTraits {
  using CHandleType = HRGN;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != DeleteObject(hHandle);
  }
};
using CRGNHandle = CHandle<CRGNHandleTraits>;

// Icon handle type
struct CMenuHandleTraits {
  using CHandleType = HMENU;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return FALSE != DestroyMenu(hHandle);
  }
};
using CMenuHandle = CHandle<CMenuHandleTraits>;

// Window handle type
struct CWinHandleTraits {
  using CHandleType = HWND;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return true;
  }
};
using CWinHandle = CHandle<CWinHandleTraits>;

// Mutex handle type
struct CMutexHandleTraits {
  using CHandleType = HANDLE;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    bool bSuccess = FALSE != ReleaseMutex(hHandle);
    bSuccess &= FALSE != CloseHandle(hHandle);
    return bSuccess;
  }
};
using CMutexHandle = CHandle<CMutexHandleTraits>;

// Image list handle type
struct CImgLHandleTraits {
  using CHandleType = HIMAGELIST;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return 0 != ImageList_Destroy(hHandle);
  }
};
using CImgLHandle = CHandle<CImgLHandleTraits>;

// Resource handle type
struct CResHandleTraits {
  using CHandleType = HGLOBAL;
  static const CHandleType m_chInvalid;
  static bool KillHandle(CHandleType hHandle) {
    return 0 != FreeResource(hHandle);
  }
};
using CResHandle = CHandle<CResHandleTraits>;

#endif // INCLUDE_TWOLOGS_COMMON_HANDLE_H

Handle.cpp

/*******************************************************************************

  Version: 5
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  http://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#include "Handle.h"

const CStdHandleTraits::CHandleType
      CStdHandleTraits::m_chInvalid = NULL;
const CIHVHandleTraits::CHandleType
      CIHVHandleTraits::m_chInvalid = INVALID_HANDLE_VALUE;
const CChangeNotHandleTraits::CHandleType
      CChangeNotHandleTraits::m_chInvalid = INVALID_HANDLE_VALUE;
const CFFHandleTraits::CHandleType
      CFFHandleTraits::m_chInvalid = INVALID_HANDLE_VALUE;
const CIconHandleTraits::CHandleType
      CIconHandleTraits::m_chInvalid = NULL;
const CCursorHandleTraits::CHandleType
      CCursorHandleTraits::m_chInvalid = NULL;
const CGDIHandleTraits::CHandleType
      CGDIHandleTraits::m_chInvalid = NULL;
const CBrushHandleTraits::CHandleType
      CBrushHandleTraits::m_chInvalid = NULL;
const CRGNHandleTraits::CHandleType
      CRGNHandleTraits::m_chInvalid = NULL;
const CMenuHandleTraits::CHandleType
      CMenuHandleTraits::m_chInvalid = NULL;
const CWinHandleTraits::CHandleType
      CWinHandleTraits::m_chInvalid = NULL;
const CMutexHandleTraits::CHandleType
      CMutexHandleTraits::m_chInvalid = NULL;
const CImgLHandleTraits::CHandleType
      CImgLHandleTraits::m_chInvalid = NULL;
const CResHandleTraits::CHandleType
      CResHandleTraits::m_chInvalid = NULL;

HandleBase.h

/*******************************************************************************

  Version: 5
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  http://www.twologs.com/sourcecode

  This code is freely distributable, as long as this comment remains intact.
  If you find this source useful, you may use this code in your own projects
  free of charge, but some acknowledgement to the author of this code is always
  appreciated :)
  The source is however distributed 'as is' without waranty and/or support, and
  may not be fit for each and every application.  Use it at your own discretion
  and at your own risk.
  The source already has undergone testing.  This doesn't mean however that all
  bugs are removed from this piece of code.  If you find one of them, please
  contact me about it.  I can however not guarantee when and if the bug will be
  fixed.

  More information about this module can be found in the accompanying HTML file.

*******************************************************************************/

#ifndef INCLUDE_TWOLOGS_COMMON_HANDLEBASE_H
#define INCLUDE_TWOLOGS_COMMON_HANDLEBASE_H

#include <Windows.h>

template <typename CHandleTraitsTPL>
class CHandle {
public:
  // Defines
  using COurType = CHandle<CHandleTraitsTPL>;
  using CHandleType = typename CHandleTraitsTPL::CHandleType;

  // Con- & destructors
  CHandle();
  CHandle(CHandleType hHandle);
  CHandle(const COurType& hHandle);
  virtual ~CHandle();

  // Takes over the handle
  void Assign(CHandleType hHandle);
  COurType& operator = (CHandleType hOtherHandle);
  void Assign(const COurType& hHandle);
  COurType& operator = (const COurType& hOtherHandle);

  // Gets the handle
  CHandleType Get() const;
  operator CHandleType() const;

  // Gets whether the handle is defined
  bool IsSet() const;
  bool IsClear() const;

  // Clears the handle
  bool Clear();

  // Makes a new unique copy of the handle
  CHandleType Copy() const;

  // Releases control of the handle
  // Note: if other handle controllers still reference the handle,
  // it is not released, and an 'invalid' handle value is returned
  CHandleType Release();

  // Gets the number of CHandle's referencing this handle
  DWORD RefCount() const;

protected:
  // The handle
  struct CHandleInfo {
    CHandleType hHandle;  // The handle
    DWORD nRefCount;      // Ref count on the handle
  };
  CHandleInfo* m_poHandle;

  // Releases control over the (shared) handle and returns to a non-shared
  // invalid handle
  bool ReleaseRef(bool bCreateNewHandleRef, bool bReleaseHandle);

  // Generates a new handle with the given value
  void CreateNewHandleRef(CHandleType hNewValue);

  // Makes sure the handle is unique
  void MakeUnique();
};

// Constructor
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>::CHandle() {
  CreateNewHandleRef(CHandleTraitsTPL::m_chInvalid);
}

// Constructor
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>::CHandle(CHandleType hHandle) {
  CreateNewHandleRef(hHandle);
}

// Constructor
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>::CHandle(const COurType& hHandle) {
  m_poHandle = hHandle.m_poHandle;
  ++m_poHandle->nRefCount;
}

// Destructor
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>::~CHandle() {
  ReleaseRef(false, true);
}

// Takes over the handle
template <typename CHandleTraitsTPL>
void CHandle<CHandleTraitsTPL>::Assign(CHandleType hHandle) {
  ReleaseRef(true, true);
  m_poHandle->hHandle = hHandle;
}

// Takes over the handle
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>&
CHandle<CHandleTraitsTPL>::operator = (CHandleType hOtherHandle) {
  Assign(hOtherHandle);
  return *this;
}

// Takes over the handle
template <typename CHandleTraitsTPL>
void CHandle<CHandleTraitsTPL>::Assign(const COurType& hHandle) {
  if (this != &hHandle) {
    ReleaseRef(false, true);
    m_poHandle = hHandle.m_poHandle;
    ++m_poHandle->nRefCount;
  }
}

// Takes over the handle
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>&
CHandle<CHandleTraitsTPL>::operator = (const COurType& hOtherHandle) {
  Assign(hOtherHandle);
  return *this;
}

// Gets the handle
template <typename CHandleTraitsTPL>
typename CHandleTraitsTPL::CHandleType CHandle<CHandleTraitsTPL>::Get() const {
  return m_poHandle->hHandle;
}

// Gets the handle
template <typename CHandleTraitsTPL>
CHandle<CHandleTraitsTPL>::operator CHandleType() const {
  return m_poHandle->hHandle;
}

// Gets whether the handle is defined
template <typename CHandleTraitsTPL>
bool CHandle<CHandleTraitsTPL>::IsSet() const {
  return m_poHandle->hHandle != CHandleTraitsTPL::m_chInvalid;
}

// Gets whether the handle is defined
template <typename CHandleTraitsTPL>
bool CHandle<CHandleTraitsTPL>::IsClear() const {
  return m_poHandle->hHandle == CHandleTraitsTPL::m_chInvalid;
}

// Clears the handle
template <typename CHandleTraitsTPL>
bool CHandle<CHandleTraitsTPL>::Clear() {
  return ReleaseRef(true, true);
}

// Makes a new unique copy of the handle
template <typename CHandleTraitsTPL>
typename CHandleTraitsTPL::CHandleType CHandle<CHandleTraitsTPL>::Copy() const {
  CHandleType hCopy = CHandleTraitsTPL::m_chInvalid;
  if (m_poHandle->hHandle != CHandleTraitsTPL::m_chInvalid) {
    HANDLE hThisProcess = GetCurrentProcess();
    BOOL bHandleDuplicated = DuplicateHandle(
      hThisProcess,
      m_poHandle->hHandle,
      hThisProcess,
      &hCopy,
      0,
      TRUE,
      DUPLICATE_SAME_ACCESS
    );
    if (!bHandleDuplicated) {
      hCopy = CHandleTraitsTPL::m_chInvalid;
    }
  }
  return hCopy;
}

// Releases control of the handle
// Note: if other handle controllers still reference the handle,
// it is not released, and an 'invalid' handle value is returned
template <typename CHandleTraitsTPL>
typename CHandleTraitsTPL::CHandleType CHandle<CHandleTraitsTPL>::Release() {
  CHandleType hOldHandle = m_poHandle->nRefCount == 1?
    m_poHandle->hHandle:
    CHandleTraitsTPL::m_chInvalid;
  ReleaseRef(true, false);
  return hOldHandle;
}

// Gets the number of CHandle's referencing this handle
template <typename CHandleTraitsTPL>
DWORD CHandle<CHandleTraitsTPL>::RefCount() const {
  return m_poHandle->nRefCount;
}

// Releases control over the (shared) handle and returns to a non-shared
// invalid handle
template <typename CHandleTraitsTPL>
bool CHandle<CHandleTraitsTPL>::ReleaseRef(bool bCreateNewHandleRef,
 bool bReleaseHandle) {
  // Decrease the refcount
  --m_poHandle->nRefCount;

  // Look if anyone else is still reffing this handle
  bool bSuccess = true;
  if (m_poHandle->nRefCount > 0) {
    // Yes -> look if to generate a new handle
    if (bCreateNewHandleRef) {
      // Yes -> do so
      CreateNewHandleRef(CHandleTraitsTPL::m_chInvalid);
    }
  } else {
    // No -> kill the handle, if needed
    if (bReleaseHandle && m_poHandle->hHandle != CHandleTraitsTPL::m_chInvalid) {
      bSuccess = CHandleTraitsTPL::KillHandle(m_poHandle->hHandle);
    }

    // And look if to generate a new handle
    if (bCreateNewHandleRef) {
      // Yes -> make sure we have an invalid handle
      m_poHandle->hHandle = CHandleTraitsTPL::m_chInvalid;
      m_poHandle->nRefCount = 1;
    } else {
      // No -> kill the handle info
      delete m_poHandle;
    }
  }

  // And return if successfull
  return bSuccess;
}

// Generates a new handle with the given value
template <typename CHandleTraitsTPL>
void CHandle<CHandleTraitsTPL>::CreateNewHandleRef(CHandleType hNewValue) {
  m_poHandle = new CHandleInfo;
  m_poHandle->nRefCount = 1;
  m_poHandle->hHandle = hNewValue;
}

// Makes sure the handle is unique
template <typename CHandleTraitsTPL>
void CHandle<CHandleTraitsTPL>::MakeUnique() {
  // Look if we're already unique
  if (m_poHandle->nRefCount > 1) {
    // No -> create a new handle with the old value
    CreateNewHandleRef(m_poHandle->hHandle);
  }
}

#endif // INCLUDE_TWOLOGS_COMMON_HANDLEBASE_H