Click here to Skip to main content
Click here to Skip to main content

Handle Handling with CHandleX

By , 18 Jun 2003
 

Introduction

You known the problem of orphaned handles? CHandleX is a generic handle class which helps you handle handles :).

Background

ATL already contains a handle class (CHandle). But this class always uses CloseHandle() for closing the handles. CHandleX allows the caller to specify which handle close function should be used.

Using the code

Using the code is very easy. The constructor takes 2 arguments:

class CHandleX
{
public:
    inline CHandleX(void* pCloseHandleFunction = CloseHandle, 
                    UINT iCallType = CHandleX::CALL_TYPE_WINAPI);
};

The first argument is a pointer to a CloseHandle function, the second is the call type. Default arguments are CloseHandle and CHandleX::CALL_TYPE_WINAPI.

iCallType depends on the close handle function. Valid arguments are:

  • CHandleX::CALL_TYPE_WINAPI // (__stdcall)

  • CHandleX::CALL_TYPE_CDECL // (__cdecl)

  • CHandleX::CALL_TYPE_FASTCALL // (__fastcall)

Most of the WINAPI calls use __stdcall. In this case, you do not need to specify CHandleX::CALL_TYPE_WINAPI because it is the default value.

IMPORTANT:

If you declare the wrong call type for "iCallType", the standard debugger in Visual Studio will not detect this. You need 3rd party tools to detect this. E.g. DevPartner.

It is assumed that the handle close function always looks like this:

int CloseHandleFunction(void *pHandle).

Samples

The following samples demonstrate how to use the CHandleX class.

//
// Normal Handle Sample
//
void Sample1()
{
    //normal handle -> uses CloseHandle()
    CHandleX hDrive;

    hDrive = CreateFile(L"\\\\.\\PhysicalDrive0",
                        0,
                        FILE_SHARE_READ | FILE_SHARE_WRITE,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL
                );
    // .
    // .
    // .
    // here, the destructor of hDrive (CHandleX) will use 
    // CloseHandle() to free up the handle
}

// Service Handle Sample
//
bool Sample2()
{
    // hScm will use CloseServiceHandle() to close the handle
    CHandleX hSCM(CloseServiceHandle, 0);

    hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);

    if (!hSCM.IsValid())
        return false;

    // .
    // .
    // .
    return true;
}
// here, the destructor of hSCM (CHandleX) will use CloseServiceHandle() 
// to free up the handle

//
// Bitmap Handle Sample
//
void Sample3()
{
    // hScm will use DeleteObject() to close the handle
    CHandleX hBitmap(DeleteObject);

    hBitmap = ::CreateBitmap(100, 100, 1,8, NULL);
    // .
    // .
    // .
}

// here, the destructor of hBitmap (CHandleX) will use DeleteObject() 
// to free up the handle

int main()
{
    Sample1();    //normal file handle
    Sample2();    //service handle
    Sample3();    //bitmap handle

    return 0;
}

There are many more possibilities. A HANDLE is defined as void*.

In addition, memory blocks can be managed using functions: free(), LocalFree() and so on.

// Memory Buffer Sample using malloc/free (__cdecl)
//
void Sample4()
{
    CHandleX hBuffer(free , CHandleX::CALL_TYPE_CDECL); //free uses __cdecl

    hBuffer = malloc(1024);
}

// Memory Buffer Sample using LocalAlloc/LocalFree
//
void Sample5()
{
    CHandleX hBuffer(LocalFree);

    hBuffer = LocalAlloc(LMEM_ZEROINIT, 1024);
}

//
// Main
//
int main()
{
    Sample4();	//memory buffer with __cdecl
    Sample5();	//memory buffer
    return 0;
}          

Code of the CHandleX class

The entire code of CHandleX class is very simple:
#pragma once

#include <stdexcept>

#ifdef __NEVER_DEFINED_00FFAAD3_C204_4c95_8607_4E7D9CD05C0F__
    #ifndef START_HIDE
        #define START_HIDE {
    #endif
    #ifndef END_HIDE
        #define END_HIDE }
    #endif
#else
    #ifndef START_HIDE
        #define START_HIDE
    #endif
    #ifndef END_HIDE
        #define END_HIDE
    #endif
#endif

START_HIDE
typedef BOOL (__stdcall *LPFN_WINAPI_HANDLE_CLOSE_FUNCTION)(void*);
typedef BOOL (__cdecl *LPFN_CDELC_HANDLE_CLOSE_FUNCTION)(void*);
typedef BOOL (__fastcall *LPFN_FASTCALL_HANDLE_CLOSE_FUNCTION)(void*);
END_HIDE

class CHandleX
{
public: //static CALL type constants
    static const UINT CALL_TYPE_WINAPI   = 1;
    static const UINT CALL_TYPE_CDECL    = 2;
    static const UINT CALL_TYPE_FASTCALL = 3;

public:
    inline CHandleX(void* pCloseHandleFunction = CloseHandle, 
                    UINT iCallType = CHandleX::CALL_TYPE_WINAPI)
    {
        m_bAttached    = false;
        m_hHandle = NULL;
        this->SetCloseHandleFunction(pCloseHandleFunction, iCallType);
    }

    inline ~CHandleX()
    {
        if (this->IsValid())
        {
            switch (m_iCallType)
            {
            case CALL_TYPE_WINAPI:
                ((LPFN_WINAPI_HANDLE_CLOSE_FUNCTION) m_pCloseHandleFunction)
                    (m_hHandle);
                break;

            case CALL_TYPE_CDECL:
                ((LPFN_CDELC_HANDLE_CLOSE_FUNCTION) m_pCloseHandleFunction)
                    (m_hHandle);
                break;

            case CALL_TYPE_FASTCALL:
                ((LPFN_FASTCALL_HANDLE_CLOSE_FUNCTION)m_pCloseHandleFunction)
                    (m_hHandle);
                break;

            default:
                break;
            }
        }
    }

    inline void operator=(HANDLE hHandle)
    {
        m_bAttached = true;
        m_hHandle = hHandle;
    }

    inline void Attach(HANDLE hHandle)
    {
        if (hHandle==NULL || hHandle==INVALID_HANDLE_VALUE)
        {
            m_bAttached = false;
            m_hHandle = NULL;
            return;
        }

        m_bAttached = true;
        m_hHandle = hHandle;
    }

    inline void Detach()
    {
        m_bAttached = false;
        m_hHandle = NULL;
    }

    inline operator HANDLE() const
    {
        return m_hHandle;
    }

    inline HANDLE GetHandle()
    {
        return m_hHandle;
    }

    inline bool IsValid()
    {
        if (m_bAttached == false)
            return false;

        if (m_hHandle == NULL || m_hHandle == INVALID_HANDLE_VALUE)
            return false;

        return true;
    }

    inline void SetCloseHandleFunction(
                    void* pCloseHandleFunction=CloseHandle,
                    UINT iCallType = CHandleX::CALL_TYPE_WINAPI)
    {
        m_iCallType    = iCallType;
        m_pCloseHandleFunction = pCloseHandleFunction;

        if (pCloseHandleFunction == NULL)
        {
            RaiseException(ERROR_INVALID_DATA, EXCEPTION_NONCONTINUABLE, 0, 
                           NULL);
            return;
        }

        switch (m_iCallType)
        {
        case CALL_TYPE_WINAPI:
        case CALL_TYPE_CDECL:
        case CALL_TYPE_FASTCALL:
            break;

        default:
            RaiseException(ERROR_INVALID_DATA, EXCEPTION_NONCONTINUABLE, 0, 
                           NULL);
            break;
        }
    }

    START_HIDE
private:
    void *m_pCloseHandleFunction;
    HANDLE m_hHandle;
    bool m_bAttached;
    UINT m_iCallType;
    END_HIDE
};

Important Notes

CHandleX is very generic, but NEVER use CHandleX for
  • wrapping new/delete
  • wrapping classes/structures

Other ways of handling handles

The disadvantage of CHandleX is that it is not type-safe. Another way would be an auto generated the class using macros. This would be type-safe, but it makes it more complicated.

History

  • June, 10. 2002, Initial Version 1.0
  • June, 13. 2002, Updated Version 1.0 to 1.1 ( Documentation - Added samples for LocalAlloc/malloc )

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

TeeBee303
Web Developer
Switzerland Switzerland
Member
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
Generalideas for improvmentsmembercider15 Jun '03 - 11:30 
why not make the CHandleX a template which takes a "dispose" policy as an
template argument ?
 
class template
class CHandleX : protected DisposePolicy
{
H mHandle;
~CHandleX () { DisposePolicy::dispose(mHandle) };
}
 
struct FileDisposer
{
void dispose(int fd) { fclose(fd); }
}
 
typedef CHandleX SmartFileHandle;

 
this would make it typesafe
GeneralRe: ideas for improvments PinmemberPatje15 Jun '03 - 20:18 
You're right Cider.
This is somewhat similar to the idea I describe in my AutoRunner article[^]. A simple template class that is given some methods (or with your suggestion another simple class) that performs the cleanup.
I still think that most developers under-estimate the power of templates. When used correctly, templates can perform wonderful things.
 
Enjoy life, this is not a rehearsal !!!
 
My Articles:
- Implementing a Subject/Observer pattern with templates
- Different ways of writing class factories
- AutoRunner: a template class to automatically run start- and cleanup-code in code blocks
GeneralRe: ideas for improvments PinmemberTeeBee30315 Jun '03 - 20:50 
i saw this CSmartHandle class which is doing something similar.
 
i also thought about using templates, but my goal was to create something "very easy" to use. (without extending it and so on.)
 
i know the power of templates. but as i said, it should be like "no need to extend". Smile | :)
 


GeneralRe: ideas for improvments Pinmembercider15 Jun '03 - 22:05 
i didn't want to offend you...
 
> something "very easy" to use
I'm not sure of passing a function pointer and setting _cdecl stuff
correctly is easier then just using a typedef Wink | ;)
 
but of your goal was something which doesn't need to be extended, than your
solution is of cousrse the most esieast AND most flexible one !
GeneralRe: ideas for improvments Pinmembercider15 Jun '03 - 22:01 
> I still think that most developers under-estimate the power of templates.
 
or just don't understand them Wink | ;) ever read "modern c++ desings" from Alexandrescu ? that really openend my eyes Wink | ;)
 
> When used correctly, templates can perform wonderful things.
fully agree. templates are to most powerfull things i have seen (except
"block" from smalltalk maybe Wink | ;)
GeneralRe: ideas for improvments PinmemberPatje15 Jun '03 - 22:51 
I've got the Alexandrescu lying around here, but I never got the courage to start reading it. I only started using C++ about 2 years ago (after having 15 years of experience in plain C). Even my colleague (with much more C++ experience) tells me that it's a very complex book.

 
Enjoy life, this is not a rehearsal !!!
 
My Articles:
- Implementing a Subject/Observer pattern with templates
- Different ways of writing class factories
- AutoRunner: a template class to automatically run start- and cleanup-code in code blocks
GeneralRe: ideas for improvments Pinmembercider16 Jun '03 - 0:37 
I'm having about 12years experience in c++, but sometimes i feel like a beginner Wink | ;) befor reading that book my point of view of templates was
mostly limited to containers and the like, even tough i implemented
a command pattern with templates, quite similar to the one
he describes, a few years ago Wink | ;)
 
> but I never got the courage to start reading it
hehe, indeed it's not something a read befor going to bed Wink | ;)
but on the other hand it's very fascinating. i mean i was totaly astonished
what can be done and even more astonished about how one comes to ideas like that !
 
the "policies" idiom was something which quite impressed me. i now use it very
often. and it's astonishing what flexibility can be achieved and how clean
the code can get with it, i.e recently i implemented a class which implements a c-interface and loads the lib either by LoadModel/GetProcAddress or is
linked via the import library at compile time. now i just need to modify a typedef and the lib is bound during link-time or loaded during runtime whithout modifiyng the project setup Wink | ;) without templates , this wouldnt be possible...
 
you see, i'm realy in love with them Wink | ;)
 


GeneralRe: ideas for improvments PinmemberPatje16 Jun '03 - 20:23 
Thanks for your views, cider.
This gives me hope and the courage to start reading the book.
Thanks.
 
Enjoy life, this is not a rehearsal !!!
 
My Articles:
- Implementing a Subject/Observer pattern with templates
- Different ways of writing class factories
- AutoRunner: a template class to automatically run start- and cleanup-code in code blocks

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Permalink | Advertise | Privacy | Mobile
Web01 | 2.6.130516.1 | Last Updated 19 Jun 2003
Article Copyright 2003 by TeeBee303
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid