本記事は、COM Advent Calendar 2014 – Qiitaの14日目の記事です。


前回(STAのメソッド呼び出しを見てみる)はSTAだったので、今度はMTAです。

MTAは再入が無い代わりに、スレッドプール(MTA所属)で実行されることがあります。前回同様にコールスタックを見ても良いのですが、こちらはそこまで念入りに確認することもないと思いました。というわけで、今回は簡単にスレッドIDの出力で見てみることにします。

前回のプログラムを少し改造しただけです。

#define _ATL_NO_AUTOMATIC_NAMESPACE
 
#include <iostream>
#include <thread>
#include <tchar.h>
#include <windows.h>
#include <atlbase.h>
#include <atlcom.h>
#include <atlutil.h>
 
class Module : public ATL::CAtlExeModuleT<Module> {};
Module module;
 
ATL::CComGITPtr<ISequentialStream> g1;
 
class ATL_NO_VTABLE Hoge
  : public ATL::CComObjectRootEx<ATL::CComSingleThreadModel>
  , public ISequentialStream
{
public:
  BEGIN_COM_MAP(Hoge)
    COM_INTERFACE_ENTRY(ISequentialStream)
  END_COM_MAP()
 
  IFACEMETHOD(Read)(
    _Out_writes_bytes_to_(cb, *pcbRead) void* pv,
    _In_ ULONG cb,
    _Out_opt_ ULONG *pcbRead) override
  {
    std::cout << "Read:\t" << std::this_thread::get_id() << std::endl;
 
    return E_NOTIMPL;
  }
 
  IFACEMETHOD(Write)(
    _In_reads_bytes_(cb) const void* pv,
    _In_ ULONG cb,
    _Out_opt_ ULONG *pcbWritten) override
  {
    return E_NOTIMPL;
  }
};
 
void worker()
{
  if (FAILED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE)))
    return;
 
  std::cout << "worker:\t" << std::this_thread::get_id() << std::endl;
  try
  {
 
    ATL::CComPtr<ISequentialStream> s;
    ATLENSURE_SUCCEEDED(g1.CopyTo(&s));
 
    char buf;
    s->Read(&buf, sizeof buf, nullptr);
  }
  catch (const ATL::CAtlException& e)
  {
    std::clog << ATL::AtlGetErrorDescription(e) << std::endl;
  }
  CoUninitialize();
}
 
int main()
{
  if (FAILED(CoInitializeEx(nullptr, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE)))
    return 1;
 
  std::cout << "Main:\t" << std::this_thread::get_id() << std::endl;
  {
    ATL::CComObjectStackEx<Hoge> obj;
    g1 = &obj;
    std::thread t(worker);
 
    t.join();
    g1.Revoke();
  }
  CoUninitialize();
}

main関数をMTAにし、worker関数のスレッドをSTAにしています。関数workerからs->ReadでMTAへのメソッド呼び出しを行っています。main、worker、Hoge::Readの3ヶ所でスレッドIDを出力させており、実行結果はこんな感じです。

Main:   38252
worker: 37672
Read:   25552

このように、3つとも別々のスレッドです。このように、MTAに対する他アパートメントからの呼び出しはAPIが抱えているスレッドで実行されます。


MSDNライブラリ上では、このスレッドはWindows VistaまではRPC APIのスレッドプール、Windows 7以降はデフォルトのスレッドプールを使うと書かれています。アプリケーションマニフェストのSupportedOS要素での指定で動作が切り替わる項目です。アプリケーション マニフェストの「RPC の既定のスレッド プール」欄をご覧ください。

なお、アプリケーションマニフェストのほか、IGlobalOptionsのCOMGLB_RPC_THREADPOOL_SETTINGでも設定できるようです。


スポンサード リンク

この記事のカテゴリ

  • ⇒ MTAのメソッド呼び出しを見てみる