次の方法で共有


エクスポート時の型の変換

更新 : 2007 年 11 月

このトピックでは、次に示す型がエクスポート プロセスでどのように変換されるかを示します。

  • クラス

  • インターフェイス

  • 値型

  • 列挙体

一般に、エクスポートされた型は、アセンブリ内で持っていた名前と同じ名前を保持します。ただし、マネージ名に関連付けられている名前空間は保持されません。たとえば、次のコード例の A.B.IList 型は、エクスポートされたタイプ ライブラリでは、IList に変換されます。COM のクライアントは、この型を A.B.IList の代わりに IList として参照できます。

Namespace A
    Namespace B
        Interface IList
               …       
        End Interface
    End Namespace
End Namespace
namespace A {
    namespace B {
        interface IList {
               …
        }
    }
}

異なる名前空間の型は同じ名前を持つことができるため、この手法では、アセンブリ内の型名が衝突する可能性があります。エクスポート プロセスは衝突を検出すると、名前空間を保持して、名前のあいまいさを解決します。同じ型名が含まれている 2 つの名前空間のコード例を次に示します。

Namespace A
    Namespace B
        Public Class LinkedList
            Implements IList
        End Class
      
        Public Interface IList
        End Interface 
    End Namespace
End Namespace

Namespace C
    Public Interface IList
    End Interface
End Namespace
namespace A {
    namespace B {
        public class LinkedList : IList {…}
        public interface IList {…}
    }
}
   namespace C {
       public interface IList {…}
}

それぞれの型名の解決を示すタイプ ライブラリ表現を次に示します。ピリオド (.) はタイプ ライブラリ名では無効なので、エクスポート プロセスは、それぞれのピリオドをアンダースコア (_) に置き換えます。

タイプ ライブラリ表現

library Widgets 
{
    […]
    coclass LinkedList 
    {
        interface A_B_IList
    };

    […]
    interface A_B_IList {…};
    […]
    interface C_IList {…};
};

また、エクスポート プロセスは、名前空間と型名を結合してプログラム ID (ProgId) を自動的に生成します。たとえば、前の例で示したマネージ クラス LinkedList に対して生成される ProgId は、A.B.LinkedList です。

名前空間と型名を結合すると、無効な ProgId が生成される場合があります。ProgId は 39 文字までに制限されており、ピリオド以外の区切り記号を含めることはできません。この制限を回避するには、エクスポート プロセスで ID を自動生成するのではなく、ProgIdAttribute を適用することにより、ソース コード内に ProgId を指定します。

クラス

エクスポートのプロセスでは、アセンブリの (ComVisible (false) 属性を省略する) 各パブリック クラスをタイプ ライブラリのコクラスに変換します。エクスポートされたコクラスは、メソッドもプロパティも持っていません。しかし、マネージ クラスの名前を保持し、マネージ クラスで明示的に実装されているすべてのインターフェイスを実装します。

IShape インターフェイスと Circle クラスを定義して IShape を実装するコード例を次に示します。コード例の後に、変換後のタイプ ライブラリ表現を示します。

Public Interface IShape
    Sub Draw()
    Sub Move(x As Integer, y As Integer)
End Interface

Class Circle
    Implements IShape
    Sub Draw Implements IShape.Draw
    …    
    Sub Move(x As Integer, y As Integer) Implements IShape.Move
    …
    Sub Enlarge(x As Integer)
    …
End Class
public interface IShape {
    void Draw();
    void Move(int x, int y);
}
class Circle : IShape  {
    void Draw();
    void Move(int x, int y);
    void Enlarge(int x);
}

タイプ ライブラリ表現

[ uuid(…), dual, odl, oleautomation ]
interface IShape : IDispatch {
    HRESULT Draw();
    HRESULT Move(int x, int y);
}

[ uuid(…) ]
coclass Circle {
    interface IShape;
}

それぞれのコクラスは、クラス インターフェイスと呼ばれるもう 1 つのインターフェイスを実装できます。このインターフェイスは、エクスポート プロセスによって自動的に生成されます。クラス インターフェイスは、元のマネージ クラスで利用可能なすべてのメソッドおよびプロパティを公開します。COM のクライアントは、クラス インターフェイスを使って呼び出すことによって、メソッドやプロパティにアクセスできます。

マネージ クラス定義のすぐ上で GuidAttribute を適用することにより、そのクラスに固有の汎用一意識別子 (UUID) を割り当てることができます。変換中、エクスポート プロセスは、GuidAttribute に提供された値をタイプ ライブラリ内の UUID に転送します。それ以外の場合は、エクスポート プロセスは、クラスの完全名 (名前空間を含む) が含まれているハッシュから UUID を取得します。完全名を使用することにより、特定の名前空間の特定の名前のクラスからは常に同じ UUID が生成され、名前の異なる 2 つのクラスからは同じ UUID が生成されないことが保証されます。

抽象クラスおよびパブリックの既定のコンストラクタを持たないクラスには、noncreatable タイプ ライブラリ属性が設定されます。licensedhiddenrestrictedcontrol など、コクラスに適用されるその他のタイプ ライブラリ属性は設定されません。

インターフェイス

エクスポート プロセスは、マネージ インターフェイスをマネージ インターフェイスと同じメソッドとプロパティを持つ COM のインターフェイスに変換しますが、メソッド シグネチャは大きく異なります。

インターフェイス ID

COM のインターフェイスには、それぞれのインターフェイスを区別するためのインターフェイス ID (IID) が含まれています。GuidAttribute 属性を適用することにより、マネージ インターフェイスに固定 IID を割り当てることができます。この属性が適用されておらず、固定 IID が割り当てられていない場合は、変換中、エクスポート プロセスが自動的に固定 IID を割り当てます。ランタイムによって割り当てられる IID は、インターフェイス名 (名前空間を含む) とそのインターフェイス内に定義されているすべてのメソッドの完全シグネチャで構成されます。マネージ インターフェイスのメソッドの順序を変更するか、またはメソッドの引数や戻り値の型を変更することにより、インターフェイスに割り当てられた IID を変更します。メソッド名を変更しても、IID には影響しません。

COM のクライアントは、ランタイムによって実装された QueryInterface メソッドを使用して、固定 IID またはランタイムによって割り当てられた IID を持つインターフェイスを取得できます。ランタイムによって生成された IID は、その型のメタデータ内では永続化されません。

インターフェイスの型

エクスポート プロセスは、他の指定がない場合、タイプ ライブラリ内ですべてのマネージ インターフェイスをデュアル インターフェイスに変換します。デュアル インターフェイスにより、COM のクライアントは事前バインディングと遅延バインディングのどちらかを選択できます。

インターフェイスに InterfaceTypeAttribute 属性を適用することにより、インターフェイスをデュアル インターフェイスとしてエクスポートするか、IUnknown から派生したインターフェイスとしてエクスポートするか、またはディスパッチ専用インターフェイス (Dispinterface) としてエクスポートするかを選択できます。エクスポートされたすべてのインターフェイスは、マネージ コードでのインターフェイスの継承階層に関係なく、すべて IUnknown または IDispatch から直接拡張されます。

インターフェイスの型を制御する省略可能な値を示すコード例を次に示します。タイプ ライブラリにエクスポートされた後、これらのオプションは、コードの後のタイプ ライブラリ表現で示す結果を生成します。

' Creates a Dual interface by default.
Public Interface InterfaceWithNoInterfaceType
    Sub test()
End Interface

' Creates a Dual interface explicitly.
<InterfaceType(ComInterfaceType.InterfaceIsDual)> _
Public Interface InterfaceWithInterfaceIsDual
    Sub test()
End Interface

' Creates an IUnknown interface (not dispatch).
<InterfaceType(ComInterfaceType.InterfaceIsIUnknown)> _
Public Interface InterfaceWithInterfaceIsIUnknown
    Sub test()
End Interface

' Creates a Dispatch-only interface (dispinterface).
<InterfaceType(ComInterfaceType.InterfaceIsIDispatch)> _
Public Interface InterfaceWithInterfaceIsIDispatch
    Sub test()
End Interface
// Creates a Dual interface by default.
public interface InterfaceWithNoInterfaceType {
    void test();
}
// Creates a Dual interface explicitly.
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface InterfaceWithInterfaceIsDual {
    void test();
}
// Creates an IUnknown interface (not dispatch).
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface InterfaceWithInterfaceIsIUnknown {
    void test();
}
// Creates a Dispatch-only interface(dispinterface).
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface InterfaceWithInterfaceIsIDispatch {
    void test();
}

タイプ ライブラリ表現

[ odl, uuid(…), dual, oleautomation ]
interface InterfaceWithNoInterfaceType : IDispatch {
    HRESULT test();
};
[ odl, uuid(…), dual, oleautomation ]
interface InterfaceWithInterfaceIsDual : IDispatch {
     HRESULT test();
};
[ odl, uuid(…), oleautomation ]
interface InterfaceWithInterfaceIsIUnknown : IUnknown {
     HRESULT test();
};
[ uuid(…) ]
dispinterface InterfaceWithInterfaceIsIDispatch {
     properties:
     methods:
         void test();
};

ほとんどのインターフェイスには、エクスポート プロセス中に odl タイプ ライブラリ属性と oleautomation タイプ ライブラリ属性が設定されています。ディスパッチ インターフェイスだけは例外です。デュアル インターフェイスには、dual タイプ ライブラリ属性が設定されています。デュアル インターフェイスは、IDispatch インターフェイスから派生しますが、メソッドの vtable スロットも公開します。

クラス インターフェイス

クラス インターフェイスの詳細と、使用に関する推奨事項については、「クラス インターフェイスの概要」を参照してください。エクスポート プロセスは、マネージ コードに明示的に定義されているインターフェイスを使用せずに、マネージ クラスの代わりとして自動的にこのインターフェイスを生成できます。COM のクライアントは、クラス メソッドに直接にはアクセスできません。

基本クラスと派生クラスのコード例を次に示します。基本クラスも派生クラスも明示的なインターフェイスを実装しません。エクスポート プロセスは、両方のマネージ クラスに対してクラス インターフェイスを提供します。

Public Class BaseClassWithClassInterface
    Private Shared StaticPrivateField As Integer
    Private PrivateFld As Integer
   
    Private Property PrivateProp() As Integer
        Get
            Return 0
        End Get
        Set
        End Set
    End Property
   
    Private Sub PrivateMeth()
        Return
    End Sub 
    Friend Shared StaticInternalField As Integer
    Friend InternalFld As Integer
   
    Friend Property InternalProp() As Integer
        Get
            Return 0
        End Get
        Set
        End Set
    End Property
   
    Friend Sub InternalMeth()
        Return
    End Sub 
    Public Shared StaticPublicField As Integer
    Public PublicFld As Integer
   
    Public Property PublicProp() As Integer
        Get
            Return 0
        End Get
        Set
        End Set
    End Property
   
    Public Sub PublicMeth()
        Return
    End Sub
End Class
 
Public Class DerivedClassWithClassInterface
    Inherits BaseClassWithClassInterface
   
    Public Sub Test()
        Return
    End Sub
End Class
public class BaseClassWithClassInterface {
    private  static int  StaticPrivateField;
    private  int  PrivateFld;
    private  int  PrivateProp{get{return 0;} set{;}}
    private  void PrivateMeth() {return;}

    internal static int  StaticInternalField;
    internal int  InternalFld;
    internal int  InternalProp{get{return 0;} set{;}}
    internal void InternalMeth() {return;}

    public   static int  StaticPublicField;
    public   int  PublicFld;
    public   int  PublicProp{get{return 0;} set{;}}
    public   void PublicMeth() {return;}
}

public class DerivedClassWithClassInterface : BaseClassWithClassInterface {
    public void Test() {return;}
}

タイプ ライブラリ表現

[odl,uuid(…), hidden, dual, nonextensible, oleautomation]
interface _BaseClassWithClassInterface : IDispatch {
     [id(00000000),propget]   HRESULT ToString([out, retval] BSTR* p);
     [id(0x60020001)]         HRESULT Equals([in] VARIANT obj, 
                                [out, retval] VARIANT_BOOL* p);
     [id(0x60020002)]         HRESULT GetHashCode([out,retval] long* p);
     [id(0x60020003)]         HRESULT GetType([out, retval] _Type** p);
     [id(0x60020004),propget] HRESULT PublicProp([out,retval] long* p);
     [id(0x60020004),propput] HRESULT PublicProp([in] long p);
     [id(0x60020006)]         HRESULT PublicMeth();
     [id(0x60020007),propget] HRESULT PublicFld([out, retval]long* p);
     [id(0x60020007),propput] HRESULT PublicFld([in] long p);
};
[odl,uuid(…), hidden, dual, nonextensible, oleautomation]
interface _DerivedClassWithClassInterface : IDispatch {
     [id(00000000),propget]   HRESULT ToString([out, retval] BSTR* p);
     [id(0x60020001)]         HRESULT Equals([in] VARIANT obj, 
                                [out, retval] VARIANT_BOOL* p);
     [id(0x60020002)]         HRESULT GetHashCode([out,retval] long* p);
     [id(0x60020003)]         HRESULT GetType([out, retval] _Type** p);
     [id(0x60020004),propget] HRESULT PublicProp([out,retval] long* p);
     [id(0x60020004),propput] HRESULT PublicProp([in] long p);
     [id(0x60020006)]         HRESULT PublicMeth();
     [id(0x60020007),propget] HRESULT PublicFld([out, retval]long* p);
     [id(0x60020007),propput] HRESULT PublicFld([in] long p);
     [id(0x60020008)]         HRESULT Test();
}

エクスポートされたクラス インターフェイスは、次の特性があります。

  • 各クラス インターフェイスは、マネージ クラスの名前を保持していますが、名前の前にアンダースコア (_) は付いていません。インターフェイス名が、既に定義されているインターフェイス名と競合する場合は、新しい名前にアンダースコア (_) と連続して増加する数値が付加されます。たとえば、_ClassWithClassInterface の代わりとして次に使用できる名前は、_ClassWithClassInterface_2 です。

  • エクスポート プロセスは、常に新しいインターフェイス ID (IID) を生成します。クラス インターフェイスの IID を明示的に設定することはできません。

  • 既定では、どちらのインターフェイスも、IDispatch インターフェイスから派生します。

  • インターフェイスは、ODL、dual、hidden、nonextensible、oleautomation の各属性があります。

  • どちらのインターフェイスも、その基本クラス (System Object) のすべてのパブリック メンバを持ちます。

  • 基本クラスのプライベート メンバまたは内部メンバは、どちらのインターフェイスにも含まれていません。

  • 各メンバには、自動的に一意の DispId が割り当てられています。DispId は、クラスのメンバに DispIdAttribute を割り当てることによって明示的に設定できます。

  • メソッド シグネチャは、HRESULT を返すように変換され、[out, retval] パラメータを含みます。

  • プロパティおよびフィールドは、[propget]、[propput]、および [propputref] に変換されます。

既定インターフェイス

COM には既定インターフェイスの概念があります。既定インターフェイスのメンバは、Visual Basic のような遅延バインディング言語では、クラスのメンバとして扱われます。.NET Framework では、クラス自体がメンバを持つことができるため、既定インターフェイスは不要です。ただし、COM にクラスを公開するときは、クラスが既定インターフェイスを持っていると、クラスははるかに使いやすくなります。

マネージ クラスをタイプ ライブラリにコクラスとしてエクスポートすると、通常、1 つのインターフェイスがそのクラスの既定インターフェイスとして識別されます。タイプ ライブラリでどのインターフェイスも既定インターフェイスとして識別されない場合、ほとんどの COM アプリケーションは、最初に実装されたインターフェイスをそのコクラスの既定インターフェイスと見なします。

次のコード例に示すように、エクスポート プロセスは、クラス インターフェイスを持たないマネージ クラスを変換し、最初に実装されたインターフェイスをエクスポート タイプ ライブラリ内の既定インターフェイスと見なします。コード例の後に、変換後のクラスのタイプ ライブラリ表現を示します。

<ClassInterface(ClassInterfaceType.None)> _
Public Class ClassWithNoClassInterface
    Implements IExplicit
    Implements IAnother
    Sub M()
    …
End Class
[ClassInterface(ClassInterfaceType.None)]
public class ClassWithNoClassInterface : IExplicit, IAnother {
    void M();   
}

タイプ ライブラリ表現

coclass ClassWithNoClassInterface {
    [default] IExplicit; 
    IAnother;
}

エクスポート プロセスは、そのクラスが明示的に実装しているそれ以外のインターフェイスとは関係なく、常にクラス インターフェイスをそのクラスの既定インターフェイスと見なします。2 つのクラスを次の例に示します。

<ClassInterface(ClassInterfaceType.AutoDispatch)> _
Public Class ClassWithAutoDispatch
    Implements IAnother
    Sub M()
    …
End Class 
 
<ClassInterface(ClassInterfaceType.AutoDual)> _
Public Class ClassWithAutoDual
    Implements IAnother
    Sub M()
    …
End Class
[ClassInterface(ClassInterfaceType.AutoDispatch)]
public class ClassWithAutoDispatch : IExplicit, IAnother {
    void M();   
}

[ClassInterface(ClassInterfaceType.AutoDual)]
public class ClassWithAutoDual : IExplicit, IAnother {
    void M();   
}

タイプ ライブラリ表現

// ClassWithAutoDispatch: IDispatch
coclass ClassWithAutoDispatch {
    [default] _ClassWithAutoDispatch;
    interface _Object;
    IExplicit;
    IAnother;
}
interface _ClassWithAutoDual {…}

coclass ClassWithAutoDual {
    [default] _ClassWithAutoDual; 
    IExplicit;
    IAnother;
}

値型

値型 (System.Value を拡張する型) は、型定義を持つ C スタイルの構造体としてタイプ ライブラリにエクスポートされます。構造体のメンバのレイアウトは、その型に適用されている StructLayoutAttribute 属性によって制御されます。値型のフィールドだけがエクスポートされます。値型がメソッドを持っている場合は、COM からはアクセスできません。

次に例を示します。

[StructLayout(LayoutKind.Sequential)]
public struct Point {
    int x;
    int y;
    public void SetXY(int x, int y){ 
        this.x = x;
        this.y = y;
    }
};

Point 値型は、次の例に示すように、ポイント typedef として COM にエクスポートされます。

typedef 
[uuid(…)]
struct tagPoint {
    short x;
    short y;
} Point;

変換プロセスによって、typedef から SetXY メソッドが削除されることに注意してください。

列挙体

エクスポート プロセスは、メンバ名の一意性を保証するために変更されたメンバ名を持つ列挙型として、マネージ列挙体をタイプ ライブラリに追加します。各メンバ名が一意であることを保証するために、Tlbexp.exe は、エクスポート プロセス中、各メンバの前に列挙型の名前とアンダースコア (_) を付けます。たとえば、次の簡単な列挙体は、一連のタイプ ライブラリ表現を生成します。

Enum DaysOfWeek {
    Sunday = 0;
    Monday;
    Tuesday;
    …
};

タイプ ライブラリ表現

enum DaysOfWeek {
    DaysOfWeek_Sunday = 0;
    DaysOfWeek_Monday;
    DaysOfWeek_Tuesday;
    …
};

ランタイムは、マネージ列挙型のメンバをそれらのメンバが属する列挙型にスコープを設定します。たとえば、前の例で示した DaysOfWeek 列挙型の Sunday へのすべての参照は、DaysOfWeek 列挙型で修飾される必要があります。DaysOfWeek.Sunday の代わりに Sunday を参照することはできません。メンバ名が一意であることは、COM 列挙体の必須条件です。

参照

概念

エクスポート時のアセンブリの変換

エクスポート時のモジュールの変換

エクスポート時のメンバの変換

エクスポート時のパラメータの変換

その他の技術情報

アセンブリからタイプ ライブラリへの変換の要約