アクティビティの利用
概要
CoCreateActivity 関数を用いて、COM+ システムに対し同期または非同期のバッチ作業を登録するためのアクティビティを生成することができます。
- IServiceCall インターフェイスを実装したコンポーネントを作成 - IServiceCall::OnCall がユーザー定義作業
- CServerConfig を用いてスレッドプールのプロパティを設定
- バッチ作業用の Activity を作成
- IServiceActivity::AsynchronousCall または IServiceActivity::SynchronousCall
を用いて処理をポストする。
このとき、パラメータとして IServiceCall インターフェイスポインタを渡す。
コードの概要
本資料で使用するファイル名やクラス名は以下となります。
- IServiceCall インターフェイスを実装したコンポーネントを作成 - IServiceCall::OnCall がユーザー定義作業
IServiceCall インターフェイスを実装したコンポーネントは CActivityTest と名付けています。
ヘッダファイルは ActivityCom.h で、実装は ActivityCom.cpp で行います。 - CServerConfig を用いてスレッドプールのプロパティを設定
- バッチ作業用の Activity を作成
- IServiceActivity::AsynchronousCall または IServiceActivity::SynchronousCall
を用いて処理をポストする。
このとき、パラメータとして IServiceCall インターフェイスポインタを渡す。
IServiceCall::OnCall の実装としては、ここではプロセス ID とスレッド ID をプリントするだけです。
上記コードをビルドしてから、それらを同期呼び出し及び非同期呼び出しの両方で動作を確認します。
コード
1. 以下の内容をそれぞれ ActivityCom.h, ActivityCom.cpp, ActTest.cpp 及び makefile と名前をつけてファイルに保存します。同一のフォルダに配置してください。
ActivityCom.h はこちらです。IUnknown と IServiceCall インターフェイスのメソッドを定義しています。
#pragma once #include <comsvcs.h> class CActivityTest : public IServiceCall { private: LONG m_cRefs; public: // IUnknown メソッド STDMETHODIMP QueryInterface (REFIID iid, void **ppv); STDMETHODIMP_(ULONG) AddRef (); STDMETHODIMP_(ULONG) Release (); // IServiceCall メソッド STDMETHODIMP OnCall (); // コンストラクタ・デストラクタ CActivityTest (); ~CActivityTest (); };
次は ActivityCom.cpp です。
#include "activitycom.h" #include <stdio.h> CActivityTest::CActivityTest () : m_cRefs (1) { printf ("CActivityTest()\n"); } CActivityTest::~CActivityTest () { printf ("~CActivityTest()\n"); } STDMETHODIMP CActivityTest::QueryInterface (REFIID iid, void **ppv) { if (IID_IUnknown == iid || IID_IServiceCall == iid ) { *ppv = this; AddRef (); return S_OK; } return E_NOINTERFACE; } STDMETHODIMP_(ULONG) CActivityTest::AddRef () { LONG lRefs = InterlockedIncrement (&m_cRefs); return lRefs; } STDMETHODIMP_(ULONG) CActivityTest::Release () { LONG lRefs = InterlockedDecrement (&m_cRefs); if (lRefs != 0 ) { return lRefs; } delete this; return 0; } /////////////////////////////////////////////////////////////////////////////// // IServiceCall メソッド STDMETHODIMP CActivityTest::OnCall () { // // バッチジョブ // printf ("[%04x:%04x] Callback CActivityTest::OnCall\n", GetCurrentProcessId (), GetCurrentThreadId ()); return S_OK; }
次はテスト用のクライアントとなる ActTest.cpp です。
#include "activitycom.h" #include <windows.h> #include <objbase.h> #include <stdio.h> #include <assert.h> #define FORMAT1 "%-50s" void ShowUsage(); void main(int argc, char* argv[]) { HRESULT hr; IServiceActivity* pActivity; IServiceCall* pServiceCall; IUnknown* pUnknownCSC; IServiceThreadPoolConfig* pServiceThreadPoolConfig; bool bAsync; // オプションの解析 if (argc != 2) { ShowUsage (); return; } if ('s' == *argv[1]) { bAsync = false; } else if ('a' == *argv[1]) { bAsync = true; } else { printf ("Invalid parameter\n"); return; } // メインスレッドの情報を出力 printf ("[%04x:%04x] main thread\n", GetCurrentProcessId (), GetCurrentThreadId ()); // STA に入る CoInitializeEx (NULL, COINIT_APARTMENTTHREADED); // CServerConfig オブジェクトを生成 hr = CoCreateInstance( CLSID_CServiceConfig, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void**)&pUnknownCSC); assert (SUCCEEDED (hr)); // CServerConfig オブジェクトから IServiceThreadPoolConfig インターフェイスポインタを取得 hr = pUnknownCSC->QueryInterface ( IID_IServiceThreadPoolConfig, (void**) &pServiceThreadPoolConfig); assert (SUCCEEDED(hr)); // バッチ処理のためのスレッドプールを設定 pServiceThreadPoolConfig->SelectThreadPool (CSC_MTAThreadPool); // アクティビティの生成 printf (FORMAT1, "CoCreateActivity"); hr = CoCreateActivity( pUnknownCSC, IID_IServiceActivity, (void**)&pActivity); assert (SUCCEEDED(hr)); printf ("OK\n"); // CActivityTest オブジェクトの生成 CActivityTest* p = new CActivityTest (); // IServiceCall インターフェイスポインタの取得 p->QueryInterface (IID_IServiceCall, (void**) &pServiceCall); p->Release (); for (int i=0; i<10; i++) { if (bAsync) { printf ("[%04x:%04x] %d AsynchronousCall\n", GetCurrentProcessId (), GetCurrentThreadId (), i+1); hr = pActivity->AsynchronousCall (pServiceCall); } else { printf ("[%04x:%04x] %d SynchronousCall\n", GetCurrentProcessId (), GetCurrentThreadId (), i+1); hr = pActivity->SynchronousCall(pServiceCall); } } printf (FORMAT1, "Just waiting for 5 sec"); Sleep(5 * 1000); printf ("OK\n"); pServiceCall->Release (); CoUninitialize(); } void ShowUsage () { printf ("acttest a|s\n"); printf (" a - AsynchronousCall\n"); printf (" s - SynchronousCall\n"); }
makefile は次の通り。
TARGETNAME=acttest OUTDIR=.\chk LINK32=link.exe ALL : "$(OUTDIR)\$(TARGETNAME).exe" CPPFLAGS=\ /nologo\ /MT\ /W4\ /Fo"$(OUTDIR)\\" /Fd"$(OUTDIR)\\"\ /c\ /Zi\ /Gd\ /EHsc\ /DWIN32\ /DUNICODE\ /D_UNICODE\ /D_WIN32_WINNT=0x0600 LINK32_FLAGS=\ ole32.lib\ comsvcs.lib\ user32.lib\ /nologo\ /subsystem:console\ /pdb:"$(OUTDIR)\$(TARGETNAME).pdb"\ /machine:I386\ /out:"$(OUTDIR)\$(TARGETNAME).exe"\ /DEBUG\ /RELEASE LINK32_OBJS= \ "$(OUTDIR)\$(TARGETNAME).obj" "$(OUTDIR)\activitycom.obj" "$(OUTDIR)\$(TARGETNAME).exe" : "$(OUTDIR)" $(LINK32_OBJS) $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS) "$(OUTDIR)" : @if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)" .c{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $< .cpp{$(OUTDIR)}.obj: $(CPP) $(CPPFLAGS) $<
2. プログラムをビルドします。
> nmake
3. 動作を確認します。
同期処理をテストする場合、作成したプログラム acttest.exe を s オプションで実行します。
> acttest s [1368:1394] main thread CoCreateActivity OK CActivityTest() [1368:1394] 1 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 2 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 3 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 4 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 5 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 6 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 7 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 8 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 9 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall [1368:1394] 10 SynchronousCall [1368:14ec] Callback CActivityTest::OnCall Just waiting for 5 sec OK ~CActivityTest() >
出力からわかるように、IServiceActivity::SynchronousCall が呼び出された後、IServiceCall::OnCall (実装は CActivityTest::OnCall) が同期的に呼び出されています。
非同期処理の動作を見る場合は、acttest.exe を a オプションで実行します。
> acttest a [1714:0378] main thread CoCreateActivity OK CActivityTest() [1714:0378] 1 AsynchronousCall [1714:0378] 2 AsynchronousCall [1714:0378] 3 AsynchronousCall [1714:0378] 4 AsynchronousCall [1714:0378] 5 AsynchronousCall [1714:0378] 6 AsynchronousCall [1714:0378] 7 AsynchronousCall [1714:0378] 8 AsynchronousCall [1714:0378] 9 AsynchronousCall [1714:0378] 10 AsynchronousCall Just waiting for 5 sec [1714:0b5c] Callback CActivityTest::OnCall [1714:085c] Callback CActivityTest::OnCall [1714:1258] Callback CActivityTest::OnCall [1714:1768] Callback CActivityTest::OnCall [1714:1278] Callback CActivityTest::OnCall [1714:0cd0] Callback CActivityTest::OnCall [1714:1548] Callback CActivityTest::OnCall [1714:1404] Callback CActivityTest::OnCall [1714:1648] Callback CActivityTest::OnCall [1714:0f28] Callback CActivityTest::OnCall OK ~CActivityTest() >
出力から IServiceActivity::AsynchronousCall と IServiceCall::OnCall のコールバックが非同期に発生していることが分かります。