Web/DB プログラミング徹底解説

ホーム > Windows 徹底解説 > アクティビティの利用

アクティビティの利用

概要

 CoCreateActivity 関数を用いて、COM+ システムに対し同期または非同期のバッチ作業を登録するためのアクティビティを生成することができます。

  1. IServiceCall インターフェイスを実装したコンポーネントを作成 - IServiceCall::OnCall がユーザー定義作業
  2. CServerConfig を用いてスレッドプールのプロパティを設定
  3. バッチ作業用の Activity を作成
  4. IServiceActivity::AsynchronousCall または IServiceActivity::SynchronousCall を用いて処理をポストする。
    このとき、パラメータとして IServiceCall インターフェイスポインタを渡す。

コードの概要

本資料で使用するファイル名やクラス名は以下となります。

  1. IServiceCall インターフェイスを実装したコンポーネントを作成 - IServiceCall::OnCall がユーザー定義作業
    IServiceCall インターフェイスを実装したコンポーネントは CActivityTest と名付けています。
    ヘッダファイルは ActivityCom.h で、実装は ActivityCom.cpp で行います。
  2. CServerConfig を用いてスレッドプールのプロパティを設定
  3. バッチ作業用の Activity を作成
  4. 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.exes オプションで実行します。

> 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.exea オプションで実行します。


> 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 のコールバックが非同期に発生していることが分かります。

参考資料

MSDN: COM+ Services without Components