japan.internet.com The Internet & IT Network


RSSニュース検索
カテゴリ
> トップページ
> Webビジネス
> Eコマース
> Webファイナンス
> Webマーケティング
> パブリック
> Webテクノロジー
> 携帯・ワイヤレス
> Linux Today
> Linux Tutorial
> J.I.C.ブログ
キャリア
> 転職ならen
> 派遣ならen
> アルバイトならen
> IT求人情報
ヘッドライン
> 今日のヘッドライン
> 週間ヘッドライン
Special Link
> フォトコミュニティ
> ストックフォト
> クリップアート
> イラスト
> フェリカ
> Web2.0
> 写真
イベント&セミナー
> イベントカレンダー
> 書評「IT の耳」
> 出張・接待検索
> ニュースガジェット 注目
無料ニュースメール
> 新規登録
> 変更・解除
> オプトインメールの登録・変更・解除
インフォメーション
> パートナーサイト
転職ならエン
就職ならen
求人ならen
履歴書ならen
アルバイトならエン
CRM/SFAならオラクル
> グループ会社
株式会社アエリア
(株)サンゼロミニッツ
株式会社エアネット
> お問い合わせ
> 広告掲載について
> リンクについて
> 著作権について
> その他お問い合わせ
> 利用規約
> 個人情報保護方針
> 会社概要地図
コラム コラム一覧へ戻る

japan.internet.com 編集部 japan.internet.com 編集部
米国 Jupitermedia が運営する、
Microsoft のインターネットソリューションで作業する開発者向けの、
フリーのリソースを提供するサイト。


 メール  著者にメールする
 ホーム  http://www.15seconds.com/

最新コラム

COM相互運用機能の利用 - パート2

著者: Patrick Steele プリンター用 記事を転送
2006年7月4日 10:00 付の記事
■海外internet.com発の記事

はじめに

 この記事は、私が書いた記事『COM相互運用機能の利用』の続編です。今回は、.NETイベントをCOMクライアントに公開する方法について説明します。

 パート1の記事と同様、今回もCOMのイベントについての簡単な復習から始めます。これによって.NETイベントをCOMに公開する方法が理解しやすくなります。残念ながら、.NETイベントを作成して後はCOM呼び出し可能ラッパー(CCW)に任せるというような単純なものではありません。COM/.NETのイベント統合を適切に行うには、いくつかの属性を適用する必要があります。

COMイベントの背景

 パート1の記事と同じように、COMイベントの背景についてもインターフェイスを中心に説明します。なぜなら、COMには.NETのような真のイベントのネイティブサポートがないからです。COMはインターフェイスを使ってイベントをシミュレートしています。

 イベントメカニズムには、以下の主要機能が必要です。

  1. 何か(つまりイベント)が発生したことを他のオブジェクトに通知するオブジェクト。通常は、これをソース(送信元)オブジェクトと呼ぶ。
  2. イベントをサブスクライブ(通知待機)するオブジェクト。ソースオブジェクトのイベントを飲み込む(sink)ので、通常は、これをシンクオブジェクトと呼ぶ。
  3. シンクオブジェクトは、ソースオブジェクトと通信してイベントをサブスクライブする。
  4. 1つのソースオブジェクトに対し、複数のシンクによるイベント待機がサポートされる。イベントが発生すると、サブスクライブ中のすべてのシンクにそのイベントが通知される。
  5. シンクオブジェクトは、それ以降のイベント通知が不要になった場合、サブスクライブを解除できる。

 COMでは、「コネクションポイントプロトコル」というプロセスでインターフェイスを介してこれらのメカニズムを実現しています。

ソースオブジェクト

 COMのコネクションポイントプロトコルでは、あるオブジェクトをイベントのソースにするには、そのイベントを表現するメソッドを含んだインターフェイスをソースオブジェクト内に定義します。この点は重要なので、確実に覚えておいてください。

 例えばClickイベントとMouseOverイベントを持つ単純なVB6オブジェクトがあるとします。このオブジェクトの名前を「CButton」としましょう。コンパイルすると、このオブジェクトには既に1つのインターフェイスが実装されています。パート1の記事でも説明したデフォルトインターフェイスです。このデフォルトインターフェイスはVB6によって自動的に作成され、この場合は「_CButton」という名前になります。ここではもう1つ「__CButton」という名前のインターフェイスも作成されます。CButtonが公開するイベントはこのインターフェイスで定義されます。

 VB6コードは次のようになります。

Public Event Click()
Public Event MouseOver(ByVal x As Integer, ByVal y As Integer)

 これをコンパイルすると、COMタイプライブラリで示されるCButtonコクラスは次のようになります。

coclass CButton {
    [default] interface _CButton;
    [default, source] dispinterface __CButton;
};

 1番目のインターフェイス_CButtonは、VB6で作成されたデフォルトインターフェイスです。2番目のインターフェイス__CButtonは、デフォルトのソースインターフェイス、つまりイベントです。この2番目のインターフェイスも、VB6によって自動的に作成され、タイプライブラリで次のように定義されます。

dispinterface __CButton {
    properties:
    methods:
        [id(0x00000001)]
        void Click();
        [id(0x00000002)]
        void MouseOver(
            [in] short x,
            [in] short y);
};

 VB6で定義したイベントが、ソースインターフェイスのメソッドになっている点に注目してください。この点を理解しておくことが、COMに公開する.NETイベントを設定する上で重要です。

 このソースオブジェクト(CButton)では、IConnectionPointという特別なインターフェイスも実装されます(タイプライブラリでは見えません)。このCOMインターフェイスがあることですべてのソースインターフェイスのリストを取得できるので、他のオブジェクトはイベントのサブスクライブやサブスクライブ解除、およびイベントリストの取得を行えます。VB6 IDEも、IConnectionPointインターフェイスを使うことですべてのソースインターフェイスを列挙でき、これによって各オブジェクトがどのようなイベントをサポートしているかを認識できます。

シンクオブジェクト

 イベント発生の通知を受ける、つまりイベントを「サブスクライブ」する側のオブジェクトは、ソースインターフェイスを実装することでこれが可能になります。上記の例の場合は、シンクオブジェクトに__CButtonインターフェイスを実装し、ClickメソッドとMouseOverメソッドにイベント処理コードを追加します。シンクオブジェクトは、IConnectionPointインターフェイスを介して自身のオブジェクトインスタンスをソースオブジェクトに渡します。ソースオブジェクトは、このインスタンスの参照を、イベントを待機するシンクオブジェクト(サブスクライバ)のリストに追加します。

 ソースオブジェクトがイベントを発生させるときには、サブスクライバリストを順に見ていき、個々のサブスクライバをインターフェイスにキャストして、各シンクオブジェクトの当該イベントを表すインターフェイスメソッドを呼び出します(先ほど述べたように、シンクオブジェクトにはイベントを表すインターフェイスが実装されています)。例えばClickイベントの場合は、このイベントのサブスクライバリストに含まれるすべてのシンクオブジェクトのClickメソッドが呼び出されます。シンクオブジェクトには__CButtonインターフェイスが実装されているので、Clickメソッドのコードを実行できます。

 このように、COMのコネクションポイントプロトコルは複雑なコールバックの仕組みになっています。コールバック(イベント)はインターフェイスで定義されます。COMに組み込まれているさまざまなインターフェイスが、サブスクライブ、サブスクライブ解除、シンクオブジェクトへのコールバックの機能を受け持ち、イベントメカニズム全体を構成しています。

.NETイベントをCOMに公開する

 .NETでは、イベントはデリゲート(委譲)によって実現されますが、ここではデリゲートの説明は省略します。デリゲートの詳細については、MSDNマガジン2003年2月号の記事『A Primer on Creating Type-Safe References to Methods in Visual Basic .NET』を参照してください。

 まず、開発者が特に何もしなかった場合に、.NETイベントがどのようにCOMに公開されるかを見てみましょう。単純なBug(虫)クラスがあり、このクラスがHungryイベント(虫が空腹になると発生するイベント)とFoundイベント(虫が餌、木などを見つけたときに発生するイベント)を公開するとします。

VB.NETの場合
Option Strict On
Option Explicit On

Namespace BugVB

    Public Class Bug
        Public Delegate Sub HungryEventHandler()
        Public Delegate Sub FoundEventHandler(ByVal item As String)

        Public Event Hungry As HungryEventHandler
        Public Event Found As FoundEventHandler

    End Class

End Namespace
C#の場合
using System;

namespace BugCS
{
    public class Bug
    {
        public delegate void HungryEventHandler();
        public delegate void FoundEventHandler(string item);

        public event HungryEventHandler Hungry;
        public event FoundEventHandler Found;

    }
}

 VB.NETのコード例で、イベントのデリゲートを明示的に定義している点に注目してください。この定義はVB.NETで自動的に行われるものなので必須ではありませんが、同じ処理のコードをC#とVB.NETの両方で表す場合、私はコードができる限り同じになるようにし、便利なコンパイラ機能に依存しないようにしています。

 TLBEXP.EXEを使用してこのオブジェクトのCOMタイプライブラリを生成すると、タイプライブラリには各デリゲートのコクラスができますが、イベントを検出するメカニズムは含まれていません。前述のVB6の例のようなソースインターフェイスがないのです。ソースインターフェイスがないと、VB6でこのタイプライブラリへの参照を追加してオブジェクトブラウザで確認しても、イベントは表示されません。

 COMオブジェクトがCOMのコネクションポイントプロトコルを使ってこれらのイベントをサブスクライブできるようにするには、HungryメソッドとFoundメソッドを定義するインターフェイスが必要です。このインターフェイスによって、シンクオブジェクトへのコールバックが行えるようになります。スムーズな.NET/COM統合を行うために、この手順に進みましょう。

イベントを表すインターフェイスの定義

 まず、通常の.NETインターフェイスを定義することから始めましょう。それからそのインターフェイスをCOMソースインターフェイス(イベント用インターフェイス)として公開します。ここで以下の点が重要になります。

  • 他のクラスやインターフェイスをCOMに公開する場合と同様、固有のGUIDが必要です(COM相互運用機能とGUIDの詳細についてはパート1の記事を参照)。
  • これらのイベントがVB6で動作するようにするには、イベントをCOMのDispInterface型にします。これを行うにはインターフェイスにInterfaceType属性を適用します。
  • ソースインターフェイスの各メソッドのメソッド名およびシグネチャを、各イベント(デリゲートではない)の名前およびシグネチャと完全に同一にします。
  • ソースインターフェイスの各メソッドには、0より大きい一意の値のSystem.Runtime.InteropServices.DispId属性を適用します。一意のDispIdを指定すると、COMは遅延バインディングを行わずに直接メソッドを呼び出せます。

 Bugオブジェクトのソースインターフェイスとして使用する.NETインターフェイスを以下に示します。

VB.NETの場合
Imports System.Runtime.InteropServices

<Guid("A66356CF-7408-4bf5-B02E-17158FE30DA3"), _
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface IBugEvents
    <DispId(1)> _
    Sub Hungry()

    <DispId(2)> _
    Sub Found(ByVal item As String)
End Interface
C#の場合
using System.Runtime.InteropServices;

[Guid("A66356CF-7408-4bf5-B02E-17158FE30DA3")]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IBugEvents
{
    [DispId(1)]
    void Hungry();

    [DispId(2)]
    void Found(string item);
}

 インターフェイスを定義したので、これをイベントのソースインターフェイスにすることをTLBEXP.EXEに指定します。Bugクラスでこのインターフェイスを実装する必要はありません。このインターフェイスはCOM相互運用機能がプレースホルダとして使うだけです。インターフェイスメソッドのシグネチャを.NETイベントのシグネチャと同じにすることが重要なのはそのためです。TLBEXP.EXEにとってはプレースホルダにすぎないので、これらが同じでなくても.NETではその不一致が検出されないからです。BugクラスではComSourceInterfaces属性を使用して、どのインターフェイスをイベントシンクが受信できるソースインターフェイスとするかをCOMに通知します。Bugクラスの例では、IBugEventsインターフェイスを指定します。

VB.NETの場合
<ComSourceInterfaces(GetType(IBugEvents))> _
Public Class Bug
    Public Delegate Sub HungryEventHandler()
    Public Delegate Sub FoundEventHandler(ByVal item As String)

    Public Event Hungry As HungryEventHandler
    Public Event Found As FoundEventHandler

End Class
C#
[ComSourceInterfaces(typeof(IBugEvents))]
public class Bug
{
    public delegate void HungryEventHandler();
    public delegate void FoundEventHandler(string item);

    public event HungryEventHandler Hungry;
    public event FoundEventHandler Found;

}

 この.NETクラスをCOMにエクスポートすると次のようになります。

coclass Bug {
    [default] interface _Bug;
    interface _Object;
    [default, source] dispinterface IBugEvents;
};

 パーフェクトです。これでIBugEventsインターフェイスがソースインターフェイスとして指定されました。「COM相互運用機能の利用」パート1の記事と同様、ここでも周辺の具体的な処理は省略しました。このサンプルでは、COMのイベントメカニズムを示す部分だけを記載してます。VB6のオブジェクトブラウザでこのタイプライブラリを確認すると、意図したとおりにイベントが表示されます。

まとめ

 COMに公開するイベントの定義はとても簡単ですが、次のような手順が必要です。

  1. 必要なイベントを.NETコンポーネント内で定義する。
  2. .NETコンポーネントのイベントと同じメソッド名とシグネチャを含むインターフェイスを作成する。
  3. InterfaceType属性を使用してこのインターフェイスをIDispatchインターフェイスにする。
  4. .NETオブジェクトでComSourceInterfaces属性を使用して、このインターフェイスをCOMイベントのソースインターフェイスにする。

著者紹介

Patrick Steele(Patrick Steele)
ミシガン南東部で独立コンサルタントとして活動。ASP.NET、WinForms、COM+、COM相互運用機能など.NETの広範な経験を持つ。ミシガン五大湖地域の.NETユーザーグループ(GANG - http://www.migang.org)の主事でもあり、過去5年にわたりMicrosoft社の.NET MVPを受賞。連絡したい場合は、ブログhttp://weblogs.asp.net/psteeleから、またはpatrick@mvps.orgまで。

過去コラム集
COM相互運用機能の利用
Visual Studio 2005のフォームにおけるBindingNavigatorの拡張
NAntによるバックアップの自動化
サービスコンポーネントのさまざまな配置方法
コードアクセスセキュリティの仕組みと設定方法
.NET Remotingの追跡サービスと独自の追跡ハンドラの実装
クエリアナライザによるストアドプロシージャのデバッグテクニック
不快感を与えるテキストデータの入力を排除する
Visual Studio .NETのウィザードを使ってN階層アプリケーションを作成する:パート3
Visual Studio .NETのウィザードを使ってN階層アプリケーションを作成する:パート2
海外のインターネットコム アメリカ韓国ドイツトルコ
関連企業のサイト:ストックフォト イラスト ネットストリート ホテル予約サイト タウン情報 出張 事業継承 シミュレーション トランクルーム 優待映画チケット 田舎暮らしガイド オリジナルデザインTシャツ ニタコエ
Copyright 2008 Jupitermedia Corporation All Rights Reserved. http://www.internet.com/
space.gif space.gif