打开/创建一个HANDLE而忘记close的情况时有发生。利用RAII的思想,将HANDLE封装为一个类,在其析构函数中进行close,是一个不错
的方法。
ATL提供了一个CHandle类,但是提出了以下使用注意事项:
Some API functions will use NULL as an empty or invalid handle, while others use INVALID_HANDLE_VALUE. CHandle only uses NULL and will treat INVALID_HANDLE_VALUE as a real handle. If you call an API which can return INVALID_HANDLE_VALUE, you should check for this value before calling CHandle::Attach or passing it to the CHandle constructor, and instead pass NULL.
即:有些API将NULL作为无效的HANLDE,但有些则将INVALID_HANDLE_VALUE作为无效值。CHandle只使用NULL作为无效HANDLE,
而将INVALID_HANLDE_VALUE视为一个真正的HANDLE.
看看相关定义:
HANDLE定义为:typedef void *HANDLE;(在WinNt.h中定义的更详细一些)
NULL定义为:#define NULL 0 (在stddef.h中定义的更详细一些)
INVALID_HANDLE_VALUE定义为:#define INVALID_HANDLE_VALUE ((HANDLE)(LONG_PTR)-1) . 其实就是补码表示的-1解释为
无符号数,就是0xFFFFFFFF.
比如,CreateThread返回的无效HANDLE是NULL,而CreateFile则以INVALID_HANDLE_VALUE表示无效HANDLE.因此使用返回
HANDLE的API需查看MSDN以保证安全.
下面这篇文章分析了HANDLE无效值不统一表示的历史原因以及相关注意:
Why are HANDLE return values so inconsistent?
那么,对HANDLE的封装怎么处理为好?
基本类模板:
template< class traits >class HandleWrapper
{private:
traits::HandleType FHandle;public:
HandleWrapper():FHandle(traits::InvalidValue){}HandleWrapper(const traits::HandleType value):
FHandle(value){}~HandleWrapper(){Close();}void Close()
{if (FHandle != traits::InvalidValue)
{traits::Close(FHandle);FHandle = traits::InvalidValue;}}bool operator !() const {return (FHandle == traits:::InvalidValue);
}operator bool() const {return (FHandle != traits:::InvalidValue);
}operator traits::HandleType() {
return FHandle;
}};
针对不同的Windows对象,提供不同的特化traits:
struct KernelHandleTraits
{typedef HANDLE HandleType;
static const HANDLE InvalidValue = INVALID_HANDLE_VALUE;static void Close(HANDLE value){CloseHandle(value);}};HandleWrapper<KernelHandleTraits> hFile(CreateFile(...));
struct NullKernelHandleTraits
{typedef HANDLE HandleType;
static const HANDLE InvalidValue = NULL;static void Close(HANDLE value){CloseHandle(value);}};HandleWrapper<NullKernelHandleTraits> hMapping(CreateFileMapping(...));
struct FileMapViewTraits
{typedef void* HandleType;static const void* InvalidValue = NULL;static void Close(void *value){UnmapViewOfFile(value);}};HandleWrapper<FileMapViewTraits> hView(MapViewOfFile(...));
struct GDIBitmapHandleTraits
{typedef HBITMAP HandleType;
static const HBITMAP InvalidValue = NULL;static void Close(HBITMAP value){DeleteObject(value);}};HandleWrapper<GDIBitmapTraits> hBmp(CreateBitmap(...));
妙哉!