前回(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に書き間違いを直しました。


スポンサード リンク

この記事のカテゴリ

  • ⇒ ATLでCOMオブジェクトを作る関数を作る
  • ⇒ ATLでCOMオブジェクトを作る関数を作る
  • ⇒ ATLでCOMオブジェクトを作る関数を作る