前回(ATLでCOMインタフェースを実装するコード例)の続きです。ATL::CComObject<>::CreateInstanceに注目していきます。
ATL::CComObject<>::CreateInstanceのラッパー関数を作る
少し複雑なので、オブジェクトの作成部分を関数に切り出したいと思います。戻り値はオブジェクトへのポインタとし、失敗したら前回同様ATLENSURE_SUCCEEDEDで例外を投げることにします。
単純に作るとこうなりますが、実は少し問題があります。
template<typename T> ATL::CComPtr<T> CreateComObject() { ATL::CComObject<T>* obj = nullptr; auto hr = ATL::CComObject<T>::CreateInstance(&obj); ATLENSURE_SUCCEEDED(hr); return ATL::CComPtr<T>(obj); } |
ATL::CComObject<>::CreateInstanceの問題
その問題は、CComObjectのCreateInstanceの実装を見ると分かります。
// Visual C++ 2013のatlcom.h template <class Base> HRESULT WINAPI CComObject<Base>::CreateInstance( _COM_Outptr_ CComObject<Base>** pp) throw() { ATLASSERT(pp != NULL); if (pp == NULL) return E_POINTER; *pp = NULL; HRESULT hRes = E_OUTOFMEMORY; CComObject<Base>* p = NULL; ATLTRY(p = _ATL_NEW CComObject<Base>()) if (p != NULL) { p->SetVoid(NULL); p->InternalFinalConstructAddRef(); hRes = p->_AtlInitialConstruct(); if (SUCCEEDED(hRes)) hRes = p->FinalConstruct(); if (SUCCEEDED(hRes)) hRes = p->_AtlFinalConstruct(); p->InternalFinalConstructRelease(); if (hRes != S_OK) { delete p; p = NULL; } } *pp = p; return hRes; } |
お分かりでしょうか。
ATLTRY(p = _ATL_NEW CComObject<Base>()) |
ATLTRYは、try{…;} catch(…) {}に展開されます。つまり、コンストラクタで何か例外を投げてもすべてE_OUTOFMEMORYにされてしまうのです。HRESULTを返すインタフェースメソッドの実装中などであればそれでもよいのですが、例外を活用する前提でC++のコードを書いているときには少し困ります。
// atldef.h #ifndef _ATL_DISABLE_NOTHROW_NEW #include <new.h> #define _ATL_NEW new(std::nothrow) #else #define _ATL_NEW new #endif // …… #define ATLTRYALLOC(x) __pragma(warning(push)) __pragma(warning(disable: 4571)) try{x;} catch(...) {} __pragma(warning(pop)) // …… #define ATLTRY(x) ATLTRYALLOC(x) |
解決方法
結論として自前で関数を作りました。CComObject<>::CreateInstanceとCComCreator<>::CreateInstanceの2つを参考にしています。
template<typename T> ATL::CComPtr<T> CreateComObject() { std::unique_ptr<ATL::CComObject<T>> p(new ATL::CComObject<T>()); p->SetVoid(nullptr); p->InternalFinalConstructAddRef(); HRESULT hRes = p->_AtlInitialConstruct(); if (SUCCEEDED(hRes)) hRes = p->FinalConstruct(); if (SUCCEEDED(hRes)) hRes = p->_AtlFinalConstruct(); p->InternalFinalConstructRelease(); return hRes == S_OK ? p.release() : nullptr; } |
もちろん、自分で作らずに済む方法がないか、他のATLクラスのCreateInstance関数も当たってみました。しかし、やはりどれも同じようにダメだったので、自分で作る道を選びました。
もちろん、例外を使わないコードを書いている場合など、CComObject<>::CreateInstanceが最適な場合はそのまま使うべきです。
2014年2月16日追記:CraeteComObjectからCreateComObjectに書き間違いを直しました。
スポンサード リンク |