属性
属性になじみがない人のために、簡単に説明しましょう。属性とは、「記述的な宣言」です。型、フィールド、メソッド、クラスなどのプログラミング要素に「注釈」を付ける役割を果たします。属性には関連する値を指定でき、その値と属性情報は、他のすべての.NETメタデータと一緒に保存されます。属性を使うと、CLR(共通言語ランタイム)に対してコードを説明したり、実行時のアプリケーション動作を変更できます。
属性を使用して、TLBEXP.EXEによるタイプライブラリの作成方法を制御し、こうしたデフォルトインターフェイスが作成されないように指定できます。これを行うには、ClassInterfaceAttribute
にClassInterfaceType.None
列挙メンバを指定してクラスに適用します。これをアセンブリレベルで指定して、アセンブリ内のすべてのパブリッククラスに適用することもできます。上記の例の場合、Bee
クラスを少し変更すればこの属性を適用できます。
Option Strict On Option Explicit On Imports System.Runtime.InteropServices Namespace QuickNET <ClassInterface(ClassInterfaceType.None)> _ Public Class Bee Implements IBee Public Sub FindFlower() Implements IBee.FindFlower End Sub Public Sub MakeHoney() Implements IBee.MakeHoney End Sub End Class End Namespace
using System; using System.Runtime.InteropServices; namespace QuickNET { [ClassInterface(ClassInterfaceType.None)] public class Bee : IBee { public void FindFlower() { } public void MakeHoney() { } } }
これをコンパイルしてからタイプライブラリを作成し直すと、OLE Viewには次のように整った出力が表示されます。
interface IBee : IDispatch { ... HRESULT FindFlower(); ... HRESULT FindHoney(); }; coclass Bee { interface _Object; [default] interface IBee; };
_Bee
インターフェイスがなくなっただけでなく、IBee
インターフェイスがデフォルトインターフェイスとして指定されています。これはなぜでしょうか。ClassInterfaceAttribute
でインターフェイス自動生成を行わないように指定した場合、TLBEXP.EXEは、このクラスで1番目に実装されているインターフェイスをデフォルトインターフェイスにします。従って、.NET
クラスをCOMに公開する場合は、ソースコード内でどのインターフェイスを1番目に実装するか(VB.NETのImplements
キーワードで指定、またはC#
クラス定義の:
の後の1番目のインターフェイスで指定)が重要です。
GUIDの制御
最後に制御するのはGUID(グローバル一意識別子)です。これまでもGUIDは見たことがあるでしょう。例えば、次のようなGUIDがあります。
82CC3E6A-148E-4b77-866E-598DBEDC5C74
COM内のすべてのインターフェイスとすべてのコクラス(作成可能クラスオブジェクト)は、それぞれ固有のGUIDで識別されます。VB6では、開発者の代わりにVB6がGUIDの作成を制御します。VB6で常に同じGUIDがコクラスやインターフェイスに使われるようにするには、VB6プロジェクトをコンパイルするときに「バイナリ互換性」モードを使います。.NETのTLBEXP.EXEユーティリティでも、COMにエクスポートする各インターフェイスとクラスのGUIDが自動生成されますが、属性を使うと開発者がGUIDを定義することができます。
なぜそのような制御が必要なのでしょうか。.NETオブジェクトをCOMコンポーネントとして登録する場合、レジストリエントリが作成されます。これらのレジストリエントリには、COMでクラスとインターフェイスを識別するためのGUIDもあります。自分で定義したGUIDを指定しなかった場合、TLBEXP.EXEはCOMタイプライブラリを作成し直すたびに新しいGUIDを生成します。作成済みのCOMクライアントで古いGUIDへの参照を使用している場合には、GUIDが変更されると動作しなくなります。クラスとインターフェイスのGUIDを制御して特定のGUIDを定義すれば、GUIDは変わらず、COMクライアントはコンパイルし直さなくても新しい.NETコンポーネントを使用できます。
作成したクラスとインターフェイスのGUIDは、属性を使って定義できます。独自のGUIDを「考え出す」代わりに、VS .NET(および以前のバージョンのVisual Studio)には、GUIDを生成する「guidgen.exe」というツールが付属しています。このツールは「\Program Files\Microsoft Visual Studio .NET 2003\Common7\Tools」ディレクトリにあります。このファイルをダブルクリックすると、次のような画面が表示されます。
GUIDはさまざまな場所で使われるので、guidgenでは4種類の形式のGUIDを作成できるようになっています。ここでは4番目の、レジストリ形式のGUIDを作成します。GUIDの前後の中かっこ({})は不要なので削除する必要があります。[New GUID]をクリックすると新しいGUIDが生成され、[Copy]をクリックすると生成されたGUIDがクリップボードにコピーされます。
では、IBee
インターフェイスのGUIDを生成してみましょう(コードにSystem.Runtime.InteropServices名前空間を追加してあることを確認してください)。クリップボードを通してGuid
属性に貼り付け、前後の中かっこを削除します。
<Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")> _ Public Interface IBee Sub FindFlower() Sub MakeHoney() End Interface
[Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")] public interface IBee { void FindFlower(); void MakeHoney(); }
次に、クラスにもGUIDが必要です。guidgenに戻り、[New GUID]をクリックし、[Copy]をクリックします。クラスのGuid
属性に新しいGUID値を指定します(ここでも中かっこは削除します)。
<ClassInterface(ClassInterfaceType.None), _ Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")> _ Public Class Bee Implements IBee Public Sub FindFlower() Implements IBee.FindFlower End Sub Public Sub MakeHoney() Implements IBee.MakeHoney End Sub End Class
[ClassInterface(ClassInterfaceType.None)] [Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")] public class Bee : IBee { public void FindFlower() { } public void MakeHoney() { } }
これで、tblexpが常に同じGUIDを使用するようになります。