Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++11

The Power of C++11 in Creating Windows Smart Pointers

4.83/5 (15 votes)
16 Jul 2015CPOL2 min read 31.3K  
Smart pointers for some Windows handles

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, HANDLEs which are invalid when INVALID_HANDLE_VALUE and the rest, which is invalid when 0:

C++
// Invalid handle classes
// -----------------------------

// Everything except HANDLE
template<typename T>
class invalidation_policy
    {
    public:
        static T inv()
            {
            return 0;
            }
    };

// HANDLE
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:

C++
/// GDI Objects
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; };

// Destruction policy
// -----------------------------
// Everything Else
template<typename T,typename J = T>
class destruction_policy
    {
    public:
        static void destruct(T h)
            {
            CloseHandle(h);
            }
    };

// GDI Objects
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:

C++
// Duplicate handle classes
// -----------------------------
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;
            }
    };

// HBITMAP
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:

C++
#include <windows.h>
#include <memory>
#include <type_traits>

namespace WINPTR
    {
    // Specializations for specific types

    /// GDI Objects
    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; };

    // Invalid handle classes
    // -----------------------------
    // Everything except HANDLE
    template<typename T>
    class invalidation_policy
        {
        public:
            static T inv()
                {
                return 0;
                }
        };

    // HANDLE
    template<>
    class invalidation_policy<HANDLE>
        {
        public:
            static HANDLE inv()
                {
                return INVALID_HANDLE_VALUE;
                }
        };

    // Destruction policy
    // -----------------------------
    // Everything Else
    template<typename T,typename J = T>
    class destruction_policy
        {
        public:
            static void destruct(T h)
                {
                CloseHandle(h);
                }
        };

    // GDI Objects
    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);
                }
        };

    // Duplicate Policy
    // -----------------------------
    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;
                }
        };

    // HBITMAP
    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:

            // Closing items
            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

C++
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; // Auto destroy for all handles 
    }

History

  • 16/07/2015: First ideas

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)