本記事は、COM Advent Calendar 2014 – Qiitaの25日目の記事です。今日で最後です。


COMには、CoInitializeやCoUninitializeが呼び出されたことの通知を受け取る手段が用意されています。IInitializeSpyおよびCoRegisterInitializeSpyそしてCoRevokeInitializeSpyです。

#include <iostream>
#include <windows.h>
#include <shlwapi.h>
#include <roapi.h>
#include <comdef.h>
 
class InitializeSpy : public IInitializeSpy
{
public:
  InitializeSpy() = default;
  InitializeSpy(const InitializeSpy&) = delete;
  InitializeSpy& operator=(const InitializeSpy&) = delete;
  ~InitializeSpy() = default;
 
  IFACEMETHOD(QueryInterface)(
    _In_ REFIID riid, _COM_Outptr_ void** ppv) override
  {
    static const QITAB qit[] = {
      {&__uuidof(IInitializeSpy), OFFSETOFCLASS(IInitializeSpy, InitializeSpy)},
      {},
    };
    return QISearch(this, qit, riid, ppv);
  }
  IFACEMETHOD_(ULONG, AddRef)() override { return 0; }
  IFACEMETHOD_(ULONG, Release)() override { return 0; }
 
  IFACEMETHOD(PreInitialize)(
    _In_ DWORD dwCoInit,
    _In_ DWORD dwCurThreadAptRefs) override
  {
    std::cout << "PreInitialize:    "
      << std::dec << dwCurThreadAptRefs
      << ", dwCoInit: " << std::hex << dwCoInit << std::endl;
    return S_OK;
  }
 
  IFACEMETHOD(PostInitialize)(
    _In_ HRESULT hrCoInit,
    _In_ DWORD dwCoInit,
    _In_ DWORD dwNewThreadAptRefs) override
  {
    std::cout << "PostInitialize:   "
      << std::dec << dwNewThreadAptRefs
      << ", dwCoInit: " << dwCoInit
      << ", hrCoInit: " << std::hex << hrCoInit << std::endl;
    return S_OK;
  }
 
  IFACEMETHOD(PreUninitialize)(
    _In_ DWORD dwCurThreadAptRefs) override
  {
    std::cout << "PreUninitialize:  "
      << std::dec << dwCurThreadAptRefs << std::endl;
    return S_OK;
  }
 
  IFACEMETHOD(PostUninitialize)(
    _In_ DWORD dwNewThreadAptRefs) override
  {
    std::cout << "PostUninitialize: "
      << std::dec << dwNewThreadAptRefs << std::endl;
    return S_OK;
  }
};
 
int main()
{
  try
  {
    InitializeSpy spy;
    ULARGE_INTEGER cookie;
    _com_util::CheckError(CoRegisterInitializeSpy(&spy, &cookie));
 
    std::cout << std::showbase;
 
    std::cout << "---- CoInitialize" << std::endl;
    _com_util::CheckError(CoInitializeEx(
      nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
    _com_util::CheckError(CoInitializeEx(
      nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
    CoUninitialize();
    CoUninitialize();
 
    std::cout << "---- CoInitialize (failed)" << std::endl;
    _com_util::CheckError(CoInitializeEx(
      nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE));
    _com_util::CheckError(CoInitializeEx(
      nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE));
    CoUninitialize();
 
    std::cout << "---- OleInitialize" << std::endl;
    OleInitialize(nullptr);
    OleUninitialize();
 
    std::cout << "---- RoInitialize (MTA)" << std::endl;
    RoInitialize(RO_INIT_MULTITHREADED);
    RoUninitialize();
 
    std::cout << "---- RoInitialize (STA)" << std::endl;
    RoInitialize(RO_INIT_SINGLETHREADED);
    RoUninitialize();
 
    _com_util::CheckError(CoRevokeInitializeSpy(cookie));
  }
  catch(const _com_error& e)
  {
    std::cout << std::hex << std::showbase << e.Error() << std::endl;
  }
}

実行結果は以下のようになりました。Windows 8.1です。

---- CoInitialize
PreInitialize:    0, dwCoInit: 0x6
PostInitialize:   1, dwCoInit: 6, hrCoInit: 0
PreInitialize:    1, dwCoInit: 0x6
PostInitialize:   2, dwCoInit: 6, hrCoInit: 0x1
PreUninitialize:  2
PostUninitialize: 1
PreUninitialize:  1
PostUninitialize: 0
---- CoInitialize (failed)
PreInitialize:    0, dwCoInit: 0x6
PostInitialize:   1, dwCoInit: 6, hrCoInit: 0
PreInitialize:    1, dwCoInit: 0x4
PostInitialize:   1, dwCoInit: 4, hrCoInit: 0x80010106
PreUninitialize:  1
PostUninitialize: 0
---- OleInitialize
PreInitialize:    0, dwCoInit: 0x2
PostInitialize:   1, dwCoInit: 2, hrCoInit: 0
PreUninitialize:  1
PostUninitialize: 0
---- RoInitialize (MTA)
PreInitialize:    0, dwCoInit: 0
PostInitialize:   1, dwCoInit: 0, hrCoInit: 0
PreUninitialize:  1
PostUninitialize: 0
---- RoInitialize (STA)
PreInitialize:    0, dwCoInit: 0x6
PostInitialize:   1, dwCoInit: 6, hrCoInit: 0
PreUninitialize:  1
PostUninitialize: 0

変更前または変更後の参照カウントや、初期化関数に渡されたCOINIT値、初期化関数の結果 (HRESULT)などが渡されます。

dwCoInitの0x2はCOINIT_APARTMENTTHREADED、0x6はCOINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDEになります。RoInitialize(RO_INIT_SINGLETHREADED)がCOINIT_DISABLE_OLE1DDEを組み合わせて指定していることが分かりますね。

また、hrCoInitの0x80010106はRPC_E_CHANGED_MODEで、MSDNライブラリの記載どおりの挙動です。

なお、CoRegisterInitializeSpyはMSDNライブラリに記載があるとおり、呼び出したスレッドのみに有効です。


なんとか25日すべてが終わりました。候補に挙げたものの、結局取り上げなかったものがまだまだあるのが心残りですが、いったんこれでおしまいです。26日目はありません。もし12月26日にブログを書いたとしても、おそらくCOMに関係ない話にすると思います。

ここまですべて読んでいただいた皆さま、お疲れ様でした。また、「読んでいます」と声を掛けてくださった方、大変励みになりました。これにてCOM Advent Calendar 2014終わります。


スポンサード リンク

この記事のカテゴリ

  • ⇒ CoInitializeとCoUninitializeが呼び出されたことを知る