COM+ LCE

1. はじめに

COM+ では「疎結合の」イベントモデル (Loosely Coupled Event Model, LCE) が導入されました。従来のイベントモデルである Event/Sink は「密な」結合を要求するイベントモデルと言われます。密なイベントモデルでは、イベントの発行元と受け取り側が同時に起動している必要がありました。しかし、このようなモデルは分散環境には不向きです。このため疎結合のイベントが必要となる場合があります。

2. 演習の概要

  1. イベントクラスの作成
  2. イベントクラスの登録
  3. Publisher の作成
  4. Subscriber の作成
  5. Subscriber の登録
  6. 動作確認

3. 演習

3.1 イベントクラスの作成

以下のコードを SimpleEventObject.cs として保存します。

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.EnterpriseServices;

[assembly:AssemblyKeyFile("key.snk")]

namespace SimpleEventObject {

    /// Class Definition
    [ Guid("34B2C49A-EAC9-446b-8B74-9DBC705A0D69"), InterfaceType (ComInterfaceType.InterfaceIsDual)]
    public interface IFoo {
        void Bar (string sMsg);
    }

    /// Implementation
    [Guid("3430A322-8E41-4609-9704-4D850CEB269E"), ClassInterface (ClassInterfaceType.None)]
    public class Foo : ServicedComponent, IFoo {
        public Foo () {
        }

        public void Bar (string sMsg) {
        }
    }
}

GUID については、各自 Guidgen 等で生成し独自の GUID を割り当てても良いと思います。

makefile は以下です。

CSC = csc.exe
TARGETNAME=SimpleEventObject
SOURCE_FILE=SimpleEventObject.cs
OUTDIR=.\chk

all: "$(OUTDIR)\$(TARGETNAME).dll"

clean:
    -del $(OUTDIR)\*.xml
    -del $(OUTDIR)\*.pdb

cleanall:clean
    -del $(OUTDIR)\$(TARGETNAME).dll


"$(OUTDIR)" :
    @if not exist "$(OUTDIR)" mkdir "$(OUTDIR)"

CSC_OPT=\
    /nologo\
    /target:library\
    /out:$(OUTDIR)\$(TARGETNAME).dll\
    /doc:$(OUTDIR)\$(TARGETNAME).xml\
    /debug+\
    /debug:full\
    /optimize-\
    /warn:3

REF=

$(OUTDIR)\$(TARGETNAME).dll: "$(OUTDIR)" $(SOURCE_FILE) key.snk
    $(CSC) $(CSC_OPT) $(REF) $(SOURCE_FILE)

key.snk:
    sn -k key.snk

この例では C# でイベントクラスを作成していますから、.NET コンポーネントを COM+ で使う手順として以下が必要となります。

> RegAsm SimpleEventObject.dll /tlb:SimpleEventObject.tlb

また、今回は Publisher を作る際にも タイプライブラリは必要になりますから、タイプライブラリもエクスポートします。

3.2 イベントクラスの登録

コンポーネントサービススナップインから、新しい COM+ アプリケーションを作成してください。名前は SimpleEventApp にしてください。

SimpleEventApp に 3.1 で作成した SimpleEventObject をイベントクラスとして登録します。普通のコンポーネントのようにコンポーネントフォルダにドラッグアンドドロップしても、イベントクラスとしては登録されませんので注意してください。イベントクラスとして登録するためには、コンポーネントフォルダを右クリックして新規作成を選び、下図のようなダイアログが表示されたときに、Install new event class(es) を選択します。

次の画面では 3.1 で作成したイベントクラスのタイプライブラリを指定します。

3.3 Publisher の作成

Publisher は C 言語でダイアログとして作成します。完成図は以下のようになります。OK をクリックすると、イベントが発行されます。

メインのコードは以下です。これを publisher.cpp としてください。

#include "resource.h"

#include <windows.h>
#include <windowsx.h>

#import "SimpleEventObject.tlb" named_guids
using namespace SimpleEventObject;


void OnCommand(HWND hwnd, int nID, HWND hWndCtl, UINT codeNotify) {

    IFoo *pFoo;
    WCHAR szMessage [256];

    switch(nID) {
    case IDC_SHOW:

        CoInitialize (NULL);
        CoCreateInstance (CLSID_Foo, NULL, CLSCTX_ALL, IID_IFoo, (void**) &pFoo);
        Edit_GetText (GetDlgItem(hwnd, IDC_EDIT_MESSAGE), szMessage, sizeof(szMessage));
        pFoo->Bar (szMessage);
        CoUninitialize ();

        break;
    case IDCANCEL:
        EndDialog(hwnd, 0);
        break;
    }
}


BOOL CALLBACK MainWndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
    switch(uMsg) {
        HANDLE_MSG(hWnd, WM_COMMAND, OnCommand);
        return TRUE;
    }
    return FALSE;
}


int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) {

    ::DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAINWND), NULL, MainWndProc);
    return 0;
}

この中で、イベントオブジェクトのタイプライブラリをインポートしているので、Publisher のコードを格納しているフォルダに、3.1 で作成した SimpleEventObject.tlb をコピーしてください。

リソースファイルは以下です。これを publisher.rc としてください。

#include <winuser.h>
#include "resource.h"

IDD_MAINWND DIALOGEX 0, 0, 186, 90
STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "LCE Test - Publisher"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
        DEFPUSHBUTTON "OK",IDC_SHOW,129,7,50,14
        EDITTEXT IDC_EDIT_MESSAGE,7,7,117,14,ES_AUTOHSCROLL
END

ここで使用している resource.h は以下です。

#define IDD_MAINWND           101
#define IDC_EDIT_MESSAGE   1000
#define IDC_SHOW                 1001

ビルドに使う makefile は以下です。

TARGETNAME=publisher
OUTDIR=.\chk
LINK32=link.exe

ALL : "$(OUTDIR)\$(TARGETNAME).exe"

"$(OUTDIR)" :
    if not exist "$(OUTDIR)/$(NULL)" mkdir "$(OUTDIR)"

CPP_PROJ=\
    /MT\
    /W4\
    /Fo"$(OUTDIR)\\"\
    /Fd"$(OUTDIR)\\"\
    /c\
    /Zi\
    /DWIN32\
    /DUNICODE\
    /D_UNICODE\

RSC_PROJ=/l 0x411 /fo"$(OUTDIR)\$(TARGETNAME).res"

LINK32_FLAGS=\
    user32.lib\
    /subsystem:windows\
    /pdb:"$(OUTDIR)\$(TARGETNAME).pdb"\
    /machine:I386\
    /out:"$(OUTDIR)\$(TARGETNAME).exe"\
    /DEBUG\
    /RELEASE

LINK32_OBJS= \
    "$(OUTDIR)\$(TARGETNAME).obj" "$(OUTDIR)\$(TARGETNAME).res"

"$(OUTDIR)\$(TARGETNAME).exe" : "$(OUTDIR)" $(LINK32_OBJS)
    $(LINK32) $(LINK32_FLAGS) $(LINK32_OBJS)

.c{$(OUTDIR)}.obj:
    $(CPP) $(CPP_PROJ) $<

.cpp{$(OUTDIR)}.obj:
    $(CPP) $(CPP_PROJ) $<

.rc{$(OUTDIR)}.res:
    rc.exe $(RSC_PROJ) $<

上記のファイルをすべて同じフォルダに格納し、次のコマンドでビルドします。

> nmake

3.4 Subscriber の作成

Subscriber はさまざまな形式で実装できますが、ここでは購読用 (Subscriber) のコンポーネントを作成します。

以下のコードを SubscriberDll.cs として保存します。

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.EnterpriseServices;
using System.Windows.Forms;
using SimpleEventObject;

[assembly:AssemblyKeyFile("key.snk")]

namespace SimpleSubscriber {

    public class FooSubscriber : ServicedComponent, IFoo {

        public void Bar (string sMsg) {
            MessageBox.Show ( sMsg, "SubscriberDll", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

    }
}

ここで、3.1 で作成したイベントクラスのインターフェイス IFoo を継承していることに注意してください。ビルド時にアセンブリが参照できるように、3.1 で作成した SimpleEventObject.dll を同じフォルダにコピーしておいてください。

makefile は以下です。

CSC = csc.exe

TARGETNAME=subscriberdll
SOURCE_FILE=subscriberdll.cs
KEY_FILE=key.snk
OUTDIR=.\chk

all: "$(OUTDIR)\$(TARGETNAME).dll"

clean:
    -del $(OUTDIR)\*.xml
    -del $(OUTDIR)\*.pdb

cleanall:clean
    -del $(OUTDIR)\$(TARGETNAME).dll

"$(OUTDIR)" :
    @if not exist "$(OUTDIR)" mkdir "$(OUTDIR)"

CSC_OPT=\
    /nologo\
    /target:library\
    /out:$(OUTDIR)\$(TARGETNAME).dll\
    /doc:$(OUTDIR)\$(TARGETNAME).xml\
    /debug+\
    /debug:full\
    /optimize-\
    /warn:3

REF=SimpleEventObject.dll

$(OUTDIR)\$(TARGETNAME).dll: "$(OUTDIR)" $(SOURCE_FILE) $(KEY_FILE)
    $(CSC) $(CSC_OPT) /r:$(REF) $(SOURCE_FILE)

key.snk:
    sn -k $(KEY_FILE)

ビルドします。

> nmake

さらに、COM+ コンポーネントとして登録します。chk フォルダに移動して次のコマンドを実行します。

>RegAsm subscriberdll.dll /tlb:subscriberdll.tlb

3.5 Subscriber の登録

コンポーネントサービススナップインを開き、新しい COM+ アプリケーションを作成します。名前は SimpleSubscriberApp としてください。SimpleSubscriberApp の Components フォルダに 3.4 で作成した SubscriberDll.dll をドラッグアンドドロップします。すると、以下のように SimpleSubscriber.FooSubscriber が登録されます。

SimpleSubscriber.FooSubscriber 以下を展開し、Subscriptions フォルダにて新規作成 (New) を選択して新しい Subscription を作成します。

すると、New Subscription Wizard が開始します。このウィザードを以下のように完了させます。

正常終了すると、上図のように Test Subscription が作成されます。途中、Event Class の選択リストに何も表示されないなど、上の流れと異なる場合はプログラムの作成手順に誤りがありますので、再度確認してください。

3.6 動作確認

publisher.exe を起動します。テキストボックスに "こんにちは" と入力して [OK] をクリックすると以下のようにメッセージボックスが表示されます。

メッセージボックスのタイトルが SubscriberDll であること、"Test Subscription" を削除するとメッセージボックスが表示されなくなることなどからイベントが正しく動作していることがわかります。

ここまでお読みいただき、誠にありがとうございます。SNS 等でこの記事をシェアしていただけますと、大変励みになります。どうぞよろしくお願いします。

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