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
CHandleX::CALL_TYPE_CDECL
CHandleX::CALL_TYPE_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.
void Sample1()
{
CHandleX hDrive;
hDrive = CreateFile(L"\\\\.\\PhysicalDrive0",
0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL
);
}
bool Sample2()
{
CHandleX hSCM(CloseServiceHandle, 0);
hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS);
if (!hSCM.IsValid())
return false;
return true;
}
void Sample3()
{
CHandleX hBitmap(DeleteObject);
hBitmap = ::CreateBitmap(100, 100, 1,8, NULL);
}
int main()
{
Sample1();
Sample2();
Sample3();
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.
void Sample4()
{
CHandleX hBuffer(free , CHandleX::CALL_TYPE_CDECL);
hBuffer = malloc(1024);
}
void Sample5()
{
CHandleX hBuffer(LocalFree);
hBuffer = LocalAlloc(LMEM_ZEROINIT, 1024);
}
int main()
{
Sample4();
Sample5();
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 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 )