Introduction
Yes, you would like shared_ptr<HANDLE>
. But this is not too easy. Let's try to create two classes, shared_handle
and dup_handle
that can help us manage our Windows handles more easily.
The Problems
We have some problems in using smart pointers in Windows:
HANDLE
is a typedef
to void*
. Therefore, shared_ptr<HANDLE>
won't create a shared HANDLE
, it will create a shared pointer to a HANDLE
. - Different
HANDLE
types need different policies of duplication and destruction. For example, HICON
and HCURSOR
are two equal types (typedef HICON HCURSOR
) but different functions are to be used to destroy an HICON
or a HCURSOR
(DestroyIcon
, DestroyCursor
). Also, FindFirstFile()
also returns a HANDLE
but this cannot be destroyed with CloseHandle()
. Therefore, our classes will work only for a subset of windows handles.
Therefore, we are limited to what we can do, but I provide two solutions here:
- A
dup_handle
that will duplicate either a HBITMAP
or a HANDLE
that can be duplicated with DuplicateHandle()
. - A
shared_handle
that will simply copy handles until the last shared_handle
is destroyed, in which case the handle is also destroyed.
Invalidation Policy
There are two cases, HANDLE
s which are invalid when INVALID_HANDLE_VALUE
and the rest, which is invalid when 0
:
template<typename T>
class invalidation_policy
{
public:
static T inv()
{
return 0;
}
};
template<>
class invalidation_policy<HANDLE>
{
public:
static HANDLE inv()
{
return INVALID_HANDLE_VALUE;
}
};
Destruction Policy
There are two cases: One for a HGDIOBJ
, which is destroyed by DeleteObject()
, and one for HANDLE
which is destroyed by CloseHandle()
. Unfortunately, HGDIOBJ
is same typedef
as HANDLE
(grrrr) so we have to use some type traits:
template <typename T>
struct is_gdiobj
{
static const bool value = false;
};
template<> struct is_gdiobj<HBITMAP> { static const bool value = true; };
template<> struct is_gdiobj<HPEN> { static const bool value = true; };
template<> struct is_gdiobj<HBRUSH> { static const bool value = true; };
template<> struct is_gdiobj<HFONT> { static const bool value = true; };
template<> struct is_gdiobj<HRGN> { static const bool value = true; };
template<> struct is_gdiobj<HPALETTE> { static const bool value = true; };
template<typename T,typename J = T>
class destruction_policy
{
public:
static void destruct(T h)
{
CloseHandle(h);
}
};
template <typename T>
class destruction_policy<T,typename std::enable_if<is_gdiobj<T>::value,T>::type>
{
public:
static void destruct(T h)
{
DeleteObject(h);
}
};
Duplication Policy
Our shared_handle
also duplicates, so we have a case when some handle can be duplicated with DuplicateHandle()
, and I've also provided a duplication for HBITMAP
:
template<typename T>
class dup_policy
{
public:
static T dup(T h)
{
T hY = 0;
DuplicateHandle(GetCurrentProcess(),h,GetCurrentProcess(),
&hY,0,true,DUPLICATE_SAME_ACCESS);
return hY;
}
};
template<>
class dup_policy<HBITMAP>
{
public:
static HBITMAP dup(HBITMAP h)
{
HBITMAP hY = (HBITMAP)CopyImage(h,IMAGE_BITMAP,0,0,0);
return hY;
}
};
dup_handle
The dup_handle
must:
- Construct from a Windows handle.
- Copy Semantics: Close itself, duplicate the handle.
- Move Semantics: Close itself, move the handle, invalidate target.
- Destruction: Destroy the handle
shared_handle
The shared_handle
must:
- Construct from a Windows handle.
- Copy Semantics: Close itself if unique, copy the raw handle.
- Move Semantics: Close itself if unique, copy the raw handle.
- Destruction: Close itself if unique
shared_handle
uses a std::shared_ptr
internally to test if the handle is unique.
Entire Code
Therefore, the entire code of shared_handle
and dup_handle
would look like that:
#include <windows.h>
#include <memory>
#include <type_traits>
namespace WINPTR
{
template <typename T>
struct is_gdiobj
{
static const bool value = false;
};
template<> struct is_gdiobj<HBITMAP> { static const bool value = true; };
template<> struct is_gdiobj<HPEN> { static const bool value = true; };
template<> struct is_gdiobj<HBRUSH> { static const bool value = true; };
template<> struct is_gdiobj<HFONT> { static const bool value = true; };
template<> struct is_gdiobj<HRGN> { static const bool value = true; };
template<> struct is_gdiobj<HPALETTE> { static const bool value = true; };
template<typename T>
class invalidation_policy
{
public:
static T inv()
{
return 0;
}
};
template<>
class invalidation_policy<HANDLE>
{
public:
static HANDLE inv()
{
return INVALID_HANDLE_VALUE;
}
};
template<typename T,typename J = T>
class destruction_policy
{
public:
static void destruct(T h)
{
CloseHandle(h);
}
};
template <typename T>
class destruction_policy<T,typename std::enable_if<is_gdiobj<T>::value,T>::type>
{
public:
static void destruct(T h)
{
DeleteObject(h);
}
};
template<typename T>
class dup_policy
{
public:
static T dup(T h)
{
T hY = 0;
DuplicateHandle(GetCurrentProcess(),h,GetCurrentProcess(),
&hY,0,true,DUPLICATE_SAME_ACCESS);
return hY;
}
};
template<>
class dup_policy<HBITMAP>
{
public:
static HBITMAP dup(HBITMAP h)
{
HBITMAP hY = (HBITMAP)CopyImage(h,IMAGE_BITMAP,0,0,0);
return hY;
}
};
template <typename T = HANDLE,typename Destruction = destruction_policy<T>,
typename Invalidation = invalidation_policy<T>,typename Duplication = dup_policy<T>>
class dup_handle
{
private:
T hX = Invalidation::inv();
public:
dup_handle()
{
hX = Invalidation::inv();
}
~dup_handle()
{
Close();
}
dup_handle(const dup_handle& h)
{
hX = Duplication::dup(h.hX);
}
dup_handle(dup_handle&& h)
{
Move(std::forward<dup_handle>(h));
}
dup_handle(T hY)
{
hX = hY;
}
dup_handle& operator =(const dup_handle& h)
{
Close();
hX = Duplication::dup(h.hX);
return *this;
}
dup_handle& operator =(dup_handle&& h)
{
Move(std::forward<dup_handle>(h));
return *this;
}
void Close()
{
if (hX != Invalidation::inv())
Destruction::destruct(hX);
hX = Invalidation::inv();
}
void Move(dup_handle&& h)
{
Close();
hX = h.hX;
h.hX = Invalidation::inv();
}
operator T() const
{
return hX;
}
};
template <typename T = HANDLE,typename Destruction = destruction_policy<T>,
typename Invalidation = invalidation_policy<T>>
class shared_handle
{
private:
T hX = Invalidation::inv();
std::shared_ptr<size_t> ptr = std::make_shared<size_t>();
public:
void Close()
{
if (!ptr || !ptr.unique())
{
ptr.reset();
return;
}
ptr.reset();
if (hX != Invalidation::inv())
Destruction::destruct(hX);
hX = Invalidation::inv();
}
shared_handle()
{
hX = Inv();
}
~shared_handle()
{
Close();
}
shared_handle(const shared_handle& h)
{
Dup(h);
}
shared_handle(shared_handle&& h)
{
Move(std::forward<shared_handle>(h));
}
shared_handle(T hY)
{
hX = hY;
}
shared_handle& operator =(const shared_handle& h)
{
Dup(h);
return *this;
}
shared_handle& operator =(shared_handle&& h)
{
Move(std::forward<shared_handle>(h));
return *this;
}
void Dup(const shared_handle& h)
{
Close();
hX = h.hX;
ptr = h.ptr;
}
void Move(shared_handle&& h)
{
Close();
hX = h.hX;
ptr = h.ptr;
h.ptr.reset();
h.hX = Inv();
}
operator T() const
{
return hX;
}
};
}
Testing
int main()
{
using namespace WINPTR;
dup_handle<> hX = OpenProcess(...);
shared_handle<> hY = GetCurrentThread();
dup_handle<> z1 = hX;
shared_handle<HBITMAP> b2 = LoadBitmap(...);
auto cc = hY;
return 0; }
History