本記事は、COM Advent Calendar 2014 – Qiitaの6日目の記事です。時空が歪んでいます。


MSXMLのDOMDocumentクラスのオブジェクトは、QueryInterfaceでIStreamを要求すると面白い挙動を示します。

#include <iostream>
#include <windows.h>
#include <comdef.h>
#include <atlbase.h>
#include <atlutil.h>
 
#import <msxml6.dll>
 
template<typename I>
void OutputInterfacePtr(const char* s, const _com_ptr_t<I>& p)
{
  IUnknownPtr u = p;
  std::cout << s << ": " << static_cast<void*>(p) << ", IUnknown: " << static_cast<void*>(u) << std::endl;
}
 
int main()
{
  CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
  try
  {
    MSXML2::IXMLDOMDocumentPtr doc(__uuidof(MSXML2::DOMDocument60));
    if (!doc->loadXML(L"<hoge><piyo/></hoge>"))
    {
      std::clog << "Bad XML!" << std::endl;
      return 1;
    }
 
    IStreamPtr s1 = doc;
    IStreamPtr s2 = doc;
    OutputInterfacePtr("doc", doc);
    OutputInterfacePtr(" s1", s1);
    OutputInterfacePtr(" s2", s2);
  }
  catch (const _com_error& e)
  {
    std::clog << e.ErrorMessage() << std::endl;
  }
  catch (const ATL::CAtlException& e)
  {
    std::clog << ATL::AtlGetErrorDescription(e) << std::endl;
  }
  CoUninitialize();
}

実行結果はこのようになります。

doc: 024D4A40, IUnknown: 00C7D850
 s1: 024D19D0, IUnknown: 00C7D850
 s2: 024D1AB0, IUnknown: 00C7D850

IStreamをQueryInterfaceするたびに新しい内部オブジェクトを作っているらしく、毎回異なるアドレスへのポインタを返してくるのです。

その一方、そこからさらにIUnknownをQueryInterfaceすると同じポインタを返します。このため、これでQueryInterfaceの規則は守っているということなのでしょう。

試してはいませんが、ATLだとCOM_INTERFACE_ENTRY_TEAR_OFFでこのようなQueryIntarfaceのたびに新しい内部オブジェクトを作る実装が可能そうな気がします。


次回(QueryInterfaceで増えるオブジェクト (2))に続きます。

スポンサード リンク

この記事のカテゴリ

  • ⇒ QueryInterfaceで増えるオブジェクト