.NETまでもう少し
ここまでくれば、.NETコードに近づいてきました。COMに公開可能な.NETコンポーネントの作成に進むには、まず、こうした概念をよく理解しておくことが重要です。家を建てる前に必要な設計図のようなものと考えてください。設計図なしでも進めることは可能ですが、正しく始めた方が良い家になります。
VB6でCOMオブジェクトを作成する際に内部でどのような処理が行われているか、簡単に復習します。
- COMはインターフェイスに基づいています。COM内のすべてがインターフェイスを介して呼び出されます。
- VB6では、COMクラスを作成するときに開発者がインターフェイスを直接実装することはできません。VB6が開発者の代わりにインターフェイスを作成してくれます。作成されるインターフェイスは、クラス名の前にアンダースコア(_)を付けた名前になります。
- VB6で作成されたインターフェイスは、常にデフォルトCOMインターフェイスとして指定されるので、スクリプティングクライアントにはこのインターフェイスのメソッドのみが公開されます(上記の例の場合、
Robot
オブジェクトのIMaid
のメソッドはVBScriptからは使えません)。
メソッドはどこに消えた!?
多くの人は、初めて.NETオブジェクトをCOMに公開してそのオブジェクトをVB6から使おうとしたときに、メソッドが1つもないと驚きます。COMに公開する簡単な.NETクラスライブラリを作成して、なぜそうなるのか見ていきましょう。
Option Strict On Option Explicit On Namespace QuickNET Public Class Bee Public Sub FindFlowers() End Sub Public Sub MakeHoney() End Sub End Class End Namespace
using System; namespace QuickNET { public class Bee { public void FindFlowers() { } public void MakeHoney() { } } }
この.NETコンポーネントからCOMタイプライブラリを作成し、どのようになるか見てみます(この段階では、COMタイプライブラリを作成するだけでCOM用の登録は行いません)。Visual Studio .NETコマンドプロンプト([スタート]→[すべてのプログラム]→[Microsoft Visual Studio .NET]→[Visual Studio .NET Tools]→[Microsoft Visual Studio.NET Command Prompt])を開き、上記をコンパイルした.NETコンポーネントのディレクトリに移動し、以下のコマンドを入力します。
TLBEXP.EXE QuickNET.dll /out:Com.QuickNET.tlb
「TLBEXP.EXE」ユーティリティは、.NETアセンブリからCOMタイプライブラリを生成します。生成されるタイプライブラリには任意の名前を付けることができますが、通常は慣例的ルールに従って.tlb拡張子を付けます。また、私はCOMに公開するタイプライブラリの名前の先頭に「Com.」と付けることにしています。次に、OLE Viewで「Com.QuickNET.tlb」というタイプライブラリを開きます。以下に重要な部分を抜粋します。
coclass Bee { [default] interface _Bee; interface _Object; }; interface _Bee : IDispatch { };
これは、VB6のCOMタイプライブラリとよく似ています。Bee
クラスに_Bee
というインターフェイスが作成されており、これがデフォルトインターフェイスになっています。ただし、このインターフェイスにはメソッドが1つもありません。VB6を起動してプロジェクトにこのタイプライブラリへの参照を追加し、オブジェクトブラウザで確認しても、Bee
クラスにメソッドはありません(VB6は常に、デフォルトインターフェイスからそのクラスのメソッドを探します)。
_Object
インターフェイスは重要ではありません。あらゆるクラスの継承元となる.NETのObject
クラスは_Object
インターフェイスを公開しているので、エクスポートされた型すべてに_Object
インターフェイスが追加されます。 TLBEXP.EXEユーティリティで_Bee
インターフェイスにすべてのメソッドが追加されなかったのはなぜでしょうか。COMインターフェイスのレイアウトはバインディングコントラクトなので、.NET
クラスに新しいメソッドを追加してCOMタイプライブラリを生成し直すとレイアウトが変わる場合があり、前のレイアウトに基づいてコンパイルした既存のCOMクライアントが動作しなくなることがあります。空のインターフェイスを定義することによって、すべてのクライアントが遅延バインディング呼び出しを行うことになり、新しいバージョンの.NETコンポーネント(およびそのコンポーネント用のCOMラッパー )も、COMクライアントをコンパイルし直さなくても、正しく動作します。
各種の制御
それでは、VB6で.NETコンポーネントのこれらのメソッドを表示するにはどうすればよいのでしょうか。VB6とCOMについて言えることを、.NETにも適用できます。基本的に、必要な操作は以下の2つです。
- 作成したメソッドを定義するインターフェイスを作成する。
- そのインターフェイスをデフォルトインターフェイスにする。
1番目は簡単です。COMに公開したいメソッドを含む.NETインターフェイスを追加します。
Public Interface IBee Sub FindFlower() Sub MakeHoney() End Interface
public interface IBee { void FindFlower(); void MakeHoney(); }
次にこのインターフェイスをBee
クラスに実装します。
Public Class Bee Implements IBee Public Sub FindFlower() Implements IBee.FindFlower End Sub Public Sub MakeHoney() Implements IBee.MakeHoney End Sub End Class
public class Bee : IBee { public void FindFlower() { } public void MakeHoney() { } }
このコンポーネントをコンパイルし、TLBEXP.EXEユーティリティを実行し、OLE Viewで確認します。結果は意外かもしれません。
interface IBee : IDispatch { ... HRESULT FindFlower(); ... HRESULT MakeHoney(); }; coclass Bee { [default] interface _Bee; interface _Object; interface IBee; }; interface _Bee : IDispatch { };
標準COMインターフェイスとして、IBee
インターフェイスが現れました。しかも、そこにメソッドが含まれています。さらに、Bee
コクラスにこのクラスが実装されています(これは予想どおり)。しかし、まだ(空の)_Bee
インターフェイスがデフォルトインターフェイスに指定されたままになっています。VB6がクラスのメソッドを探すのは、やはりこのデフォルトインターフェイスです。
それでも、IBee
インターフェイスは標準COMインターフェイスとして公開されているので、このインターフェイスを介して必要な操作を問題なく実行できます。例えば、上記で作成したタイプライブラリを使う以下のVB6コードは正しく実行できます。
Dim bee As IBee Set bee = New Bee bee.FindFlower bee.MakeHoney
しかしながら、もっときちんと統合するには、IBee
インターフェイスをデフォルトインターフェイスにする必要があります。これは、属性を使って行います。