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:  https://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 TStdHandleTraits {
  using THandleType = HANDLE;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != CloseHandle(handle);
  }
};
using TStdHandle = THandle<TStdHandleTraits>;

// INVALID_HANDLE_VALUE handle type
struct TIHVHandleTraits {
  using THandleType = HANDLE;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != CloseHandle(handle);
  }
};
using TIHVHandle = THandle<TIHVHandleTraits>;
using TFileHandle = THandle<TIHVHandleTraits>;

// Change notification handle type
struct TChangeNotHandleTraits {
  using THandleType = HANDLE;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != FindCloseChangeNotification(handle);
  }
};
using TChangeNotHandle = THandle<TChangeNotHandleTraits>;

// FindFirst handle type
struct TFindFirstHandleTraits {
  using THandleType = HANDLE;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != FindClose(handle);
  }
};
using TFindFirstHandle = THandle<TFindFirstHandleTraits>;

// Icon handle type
struct TIconHandleTraits {
  using THandleType = HICON;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != DestroyIcon(handle);
  }
};
using TIconHandle = THandle<TIconHandleTraits>;

// Cursor handle type
struct TCursorHandleTraits {
  using THandleType = HCURSOR;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != DestroyCursor(handle);
  }
};
using TCursorHandle = THandle<TCursorHandleTraits>;

// GDI object handle type
struct TGDIHandleTraits {
  using THandleType = HGDIOBJ;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != DeleteObject(handle);
  }
};
using TGDIHandle = THandle<TGDIHandleTraits>;

// Brush object handle type
struct TBrushHandleTraits {
  using THandleType = HBRUSH;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != DeleteObject(handle);
  }
};
using TBrushHandle = THandle<TBrushHandleTraits>;

// RGN handle type
struct TRegionHandleTraits {
  using THandleType = HRGN;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != DeleteObject(handle);
  }
};
using TRegionHandle = THandle<TRegionHandleTraits>;

// Icon handle type
struct TMenuHandleTraits {
  using THandleType = HMENU;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return FALSE != DestroyMenu(handle);
  }
};
using TMenuHandle = THandle<TMenuHandleTraits>;

// Window handle type
struct TWinHandleTraits {
  using THandleType = HWND;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return true;
  }
};
using TWinHandle = THandle<TWinHandleTraits>;

// Mutex handle type
struct TMutexHandleTraits {
  using THandleType = HANDLE;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    bool allOK = FALSE != ReleaseMutex(handle);
    allOK &= FALSE != CloseHandle(handle);
    return allOK;
  }
};
using TMutexHandle = THandle<TMutexHandleTraits>;

// Image list handle type
struct TImgListHandleTraits {
  using THandleType = HIMAGELIST;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return 0 != ImageList_Destroy(handle);
  }
};
using TImgListHandle = THandle<TImgListHandleTraits>;

// Resource handle type
struct TResourceHandleTraits {
  using THandleType = HGLOBAL;
  static const THandleType m_invalidHandle;
  static bool KillHandle(THandleType handle) {
    return 0 != FreeResource(handle);
  }
};
using TResourceHandle = THandle<TResourceHandleTraits>;

#endif // INCLUDE_TWOLOGS_COMMON_HANDLE_H

Handle.cpp

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

  Version: 5
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://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 TStdHandleTraits::THandleType
      TStdHandleTraits::m_invalidHandle = NULL;
const TIHVHandleTraits::THandleType
      TIHVHandleTraits::m_invalidHandle = INVALID_HANDLE_VALUE;
const TChangeNotHandleTraits::THandleType
      TChangeNotHandleTraits::m_invalidHandle = INVALID_HANDLE_VALUE;
const TFindFirstHandleTraits::THandleType
      TFindFirstHandleTraits::m_invalidHandle = INVALID_HANDLE_VALUE;
const TIconHandleTraits::THandleType
      TIconHandleTraits::m_invalidHandle = NULL;
const TCursorHandleTraits::THandleType
      TCursorHandleTraits::m_invalidHandle = NULL;
const TGDIHandleTraits::THandleType
      TGDIHandleTraits::m_invalidHandle = NULL;
const TBrushHandleTraits::THandleType
      TBrushHandleTraits::m_invalidHandle = NULL;
const TRegionHandleTraits::THandleType
      TRegionHandleTraits::m_invalidHandle = NULL;
const TMenuHandleTraits::THandleType
      TMenuHandleTraits::m_invalidHandle = NULL;
const TWinHandleTraits::THandleType
      TWinHandleTraits::m_invalidHandle = NULL;
const TMutexHandleTraits::THandleType
      TMutexHandleTraits::m_invalidHandle = NULL;
const TImgListHandleTraits::THandleType
      TImgListHandleTraits::m_invalidHandle = NULL;
const TResourceHandleTraits::THandleType
      TResourceHandleTraits::m_invalidHandle = NULL;

HandleBase.h

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

  Version: 5
  Author:  Carl Colijn, TwoLogs
  Contact: c.colijn@twologs.com
  Source:  https://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 THandleTraitsTPL>
class THandle {
public:
  // Defines
  using TOurType = THandle<THandleTraitsTPL>;
  using THandleType = typename THandleTraitsTPL::THandleType;

  // Con- & destructors
  THandle();
  THandle(THandleType handle);
  THandle(const TOurType& handle);
  virtual ~THandle();

  // Takes over the handle
  void Assign(THandleType handle);
  TOurType& operator = (THandleType otherHandle);
  void Assign(const TOurType& handle);
  TOurType& operator = (const TOurType& otherHandle);

  // Gets the handle
  THandleType Get() const;
  operator THandleType() 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
  THandleType 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
  THandleType Release();

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

protected:
  // The handle
  struct THandleInfo {
    THandleType handle;  // The handle
    DWORD refCount;      // Ref count on the handle
  };
  THandleInfo* m_handleInfoPtr;

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

  // Generates a new handle with the given value
  void CreateNewHandleRef(THandleType newHandle);

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

// Constructor
template <typename THandleTraitsTPL>
THandle<THandleTraitsTPL>::THandle() {
  CreateNewHandleRef(THandleTraitsTPL::m_invalidHandle);
}

// Constructor
template <typename THandleTraitsTPL>
THandle<THandleTraitsTPL>::THandle(THandleType handle) {
  CreateNewHandleRef(handle);
}

// Constructor
template <typename THandleTraitsTPL>
THandle<THandleTraitsTPL>::THandle(const TOurType& handle) {
  m_handleInfoPtr = handle.m_handleInfoPtr;
  ++m_handleInfoPtr->refCount;
}

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

// Takes over the handle
template <typename THandleTraitsTPL>
void THandle<THandleTraitsTPL>::Assign(THandleType handle) {
  ReleaseRef(true, true);
  m_handleInfoPtr->handle = handle;
}

// Takes over the handle
template <typename THandleTraitsTPL>
THandle<THandleTraitsTPL>&
THandle<THandleTraitsTPL>::operator = (THandleType otherHandle) {
  Assign(otherHandle);
  return *this;
}

// Takes over the handle
template <typename THandleTraitsTPL>
void THandle<THandleTraitsTPL>::Assign(const TOurType& handle) {
  if (this != &handle) {
    ReleaseRef(false, true);
    m_handleInfoPtr = handle.m_handleInfoPtr;
    ++m_handleInfoPtr->refCount;
  }
}

// Takes over the handle
template <typename THandleTraitsTPL>
THandle<THandleTraitsTPL>&
THandle<THandleTraitsTPL>::operator = (const TOurType& otherHandle) {
  Assign(otherHandle);
  return *this;
}

// Gets the handle
template <typename THandleTraitsTPL>
typename THandleTraitsTPL::THandleType THandle<THandleTraitsTPL>::Get() const {
  return m_handleInfoPtr->handle;
}

// Gets the handle
template <typename THandleTraitsTPL>
THandle<THandleTraitsTPL>::operator THandleType() const {
  return m_handleInfoPtr->handle;
}

// Gets whether the handle is defined
template <typename THandleTraitsTPL>
bool THandle<THandleTraitsTPL>::IsSet() const {
  return m_handleInfoPtr->handle != THandleTraitsTPL::m_invalidHandle;
}

// Gets whether the handle is defined
template <typename THandleTraitsTPL>
bool THandle<THandleTraitsTPL>::IsClear() const {
  return m_handleInfoPtr->handle == THandleTraitsTPL::m_invalidHandle;
}

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

// Makes a new unique copy of the handle
template <typename THandleTraitsTPL>
typename THandleTraitsTPL::THandleType THandle<THandleTraitsTPL>::Copy() const {
  THandleType copiedHandle = THandleTraitsTPL::m_invalidHandle;
  if (m_handleInfoPtr->handle != THandleTraitsTPL::m_invalidHandle) {
    HANDLE thisProcess = GetCurrentProcess();
    BOOL handleDuplicated = DuplicateHandle(
      thisProcess,
      m_handleInfoPtr->handle,
      thisProcess,
      &copiedHandle,
      0,
      TRUE,
      DUPLICATE_SAME_ACCESS
    );
    if (!handleDuplicated) {
      copiedHandle = THandleTraitsTPL::m_invalidHandle;
    }
  }
  return copiedHandle;
}

// 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 THandleTraitsTPL>
typename THandleTraitsTPL::THandleType THandle<THandleTraitsTPL>::Release() {
  THandleType oldHandle = m_handleInfoPtr->refCount == 1?
    m_handleInfoPtr->handle:
    THandleTraitsTPL::m_invalidHandle;
  ReleaseRef(true, false);
  return oldHandle;
}

// Gets the number of THandle's referencing this handle
template <typename THandleTraitsTPL>
DWORD THandle<THandleTraitsTPL>::RefCount() const {
  return m_handleInfoPtr->refCount;
}

// Releases control over the (shared) handle and returns to a non-shared
// invalid handle
template <typename THandleTraitsTPL>
bool THandle<THandleTraitsTPL>::ReleaseRef(bool createNewHandleRef,
 bool releaseHandle) {
  // Decrease the refcount
  --m_handleInfoPtr->refCount;

  // Look if anyone else is still reffing this handle
  bool allOK = true;
  if (m_handleInfoPtr->refCount > 0) {
    // Yes -> look if to generate a new handle
    if (createNewHandleRef) {
      // Yes -> do so
      CreateNewHandleRef(THandleTraitsTPL::m_invalidHandle);
    }
  } else {
    // No -> kill the handle, if needed
    if (releaseHandle && m_handleInfoPtr->handle != THandleTraitsTPL::m_invalidHandle) {
      allOK = THandleTraitsTPL::KillHandle(m_handleInfoPtr->handle);
    }

    // And look if to generate a new handle
    if (createNewHandleRef) {
      // Yes -> make sure we have an invalid handle
      m_handleInfoPtr->handle = THandleTraitsTPL::m_invalidHandle;
      m_handleInfoPtr->refCount = 1;
    } else {
      // No -> kill the handle info
      delete m_handleInfoPtr;
    }
  }

  // And return if successful
  return allOK;
}

// Generates a new handle with the given value
template <typename THandleTraitsTPL>
void THandle<THandleTraitsTPL>::CreateNewHandleRef(THandleType newHandle) {
  m_handleInfoPtr = new THandleInfo;
  m_handleInfoPtr->refCount = 1;
  m_handleInfoPtr->handle = newHandle;
}

// Makes sure the handle is unique
template <typename THandleTraitsTPL>
void THandle<THandleTraitsTPL>::MakeUnique() {
  // Look if we're already unique
  if (m_handleInfoPtr->refCount > 1) {
    // No -> create a new handle with the old value
    CreateNewHandleRef(m_handleInfoPtr->handle);
  }
}

#endif // INCLUDE_TWOLOGS_COMMON_HANDLEBASE_H