打開/創建一個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(...));
妙哉!