在Windows中編程,跟HANDLE打交道是家常便飯。為了防止忘記CloseHandle,我都是使用do-while-false手法:
void f() { HANDLE h = NULL; do { } while (false); if (h) { CloseHandle(h); h = NULL; } }
HANDLE一多,就得寫好幾段長得一樣的清理代碼,比較麻煩。仔細一想,這個其實很容易寫一個關閉器——在出作用域時自動關閉:
class closer : public boost::noncopyable { public: closer(HANDLE h) : h_(h) { } ~closer() { if (h_) { CloseHandle(h_); h_ = NULL; } } private: HANDLE h_; };
創建或打開一個句柄後,將其關閉動作委托給closer。
這是我在寫這篇文章的時候想到的,寫代碼的時候我用的是另一個辦法:scoped_handle。這是從boost裡邊那些運用RAII手法的類上學到得,比如scoped_ptr,scoped_array,名字也是學過來的:
template<HANDLE invalid_value = NULL> class scoped_handle : public boost::noncopyable { public: scoped_handle() : h_(invalid_value) { } scoped_handle(const HANDLE &h) : h_(h) { } ~scoped_handle() { destory(); } //you should ensure not self-assignment void reset(const HANDLE &h) { destory(); h_ = h; } void destory() { if (h_ != invalid_value) { //CloseHandle will set last error code //so we should recover it //someone may use reset(CreateFile(...)) last_error_recover r; CloseHandle(h_); h_ = invalid_value; } } bool valid() const { return h_ != invalid_value; } HANDLE& get_ref() { return h_; } HANDLE* get_ptr() { return &h_; } private: HANDLE h_; };
有一個模板參數invalid_value,這是句柄的無效值,我現在見到的有兩種:NULL,INVALID_HANDLE_VALUE。
用法舉例:
scoped_handle<> hProcess(OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid)); if (!hProcess.valid()) { ErrorLogLastErr("OpenProcess[%lu] fail", pid); } else { s = query(hProcess.get_ref(), native_name); }
scoped_handle<> hToken; if(!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, hToken.get_ptr())) { //... }
scoped_handle<> m_exit_event; m_exit_event.reset(CreateEvent(NULL, TRUE, FALSE, NULL)); if (!m_exit_event.valid()) { //... }
我現在為自己找了一個不使用closer的理由:我不喜歡讓別人幫助我做力所能及的事。closer就像是某人new了一個對象,然後把指針給了另一個人,讓他去“擦屁股”,然而自己“擦屁股”不過是舉手之勞。不過,這個理由不強勁,closer的便利性遠遠大於對“擦屁股”的厭惡。
這裡有一個todo:我希望能定制Close動作,就是Close動作作為一個模板參數,這樣一來,我就可以把HANDLE也提到模板參數的位置上,這個東西的適用范圍就更廣了,但是我不知道如何把Close動作提到模板參數的位置上。
想必眼尖的同學看到了上面的代碼裡有個last_error_recover,這是一個很簡單的類,注釋已經說明了它的用途,下面是實現:
class last_error_recover : public boost::noncopyable { public: last_error_recover() : code_(GetLastError()) { } last_error_recover(const DWORD code) : code_(code) { } ~last_error_recover() { SetLastError(code_); } private: const DWORD code_; };
源碼:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。
2015年10月31日星期六