C# および Visual Basic を使用した Windows ランタイム コンポーネント

マネージド コードを使用して独自の Windows ランタイム型を作成し、それらを Windows ランタイム コンポーネントにパッケージ化することができます。 コンポーネントは、C++、JavaScript、Visual Basic、C# で記述されたユニバーサル Windows プラットフォーム (UWP) アプリで使用することができます。 このトピックでは、コンポーネントを作成するための規則を示し、Windows ランタイム向けの .NET のサポートをいくつか説明します。 このサポートは、通常、.NET のプログラマが意識しなくても利用できるように設計されています。 ただし、JavaScript や C++ で使うコンポーネントを作成する場合は、これらの言語が Windows ランタイムをサポートする方法の違いに注意する必要があります。

Visual Basic または C# で記述された UWP アプリでのみ使用するコンポーネントを作成し、そのコンポーネントに UWP コントロールが含まれない場合は、Microsoft Visual Studio で Windows ランタイム コンポーネント プロジェクト テンプレートではなく、クラス ライブラリ テンプレートを使用することを検討してください。 単純なクラス ライブラリでは、制限は少なくなります。

Note

.NET 6 以降でデスクトップ アプリを作成する C# 開発者の場合、C#/WinRT を使用して Windows ランタイム コンポーネントを作成してください。 「C#/WinRT を使用して Windows ランタイム コンポーネントを作成する」を参照してください。

Windows ランタイム コンポーネントでの型の宣言

内部的には、コンポーネントの Windows ランタイム型で、UWP アプリで許可されている .NET の機能をすべて使用することができます。 詳しくは、「UWP アプリの .NET」をご覧ください。

外部的には、型のメンバーによってパラメーターと戻り値の Windows ランタイム型のみを公開できます。 Windows ランタイム コンポーネントから公開される .NET 型の制限事項を次に示します。

  • コンポーネント内にあるすべてのパブリック型とメンバーのフィールド、パラメーター、戻り値は、Windows ランタイム型である必要があります。 この制限は、作成した Windows ランタイム型、および Windows ランタイム自体で提供される型を対象としています。 また、さまざまな .NET 型も対象となります。 これらの型の追加は、マネージド コードで Windows ランタイムを通常どおりに使用することができるようにするために、.NET で提供するサポートの一部です。コードは、基になる Windows ランタイム型ではなく、よく利用する .NET 型が使用されているように表示されます。 たとえば、Int32Double などの .NET のプリミティブ型、DateTimeOffsetUri などの特定の基本型、および IEnumerable<T> (Visual Basic では IEnumerable(Of T)) や IDictionary<TKey,TValue> などの一般的に利用されるジェネリック インターフェイス型を使用することができます。 これらのジェネリック型の型引数は Windows ランタイム型にする必要があることに注意してください。 これは、このトピックの後述のセクション「Windows ランタイム型のマネージド コードへの引き渡し」と「マネージド型の Windows ランタイムへの引き渡し」で説明しています。

  • パブリック クラスとインターフェイスには、メソッド、プロパティ、イベントを含めることができます。 イベントのデリゲートを宣言したり、EventHandler<T> デリゲートを使用したりすることができます。 パブリック クラスやインターフェイスでは、次のことが許可されていません。

    • ジェネリックにする。
    • Windows ランタイム インターフェイス以外のインターフェイスを実装する (ただし、独自の Windows ランタイム インターフェイスを作成して実装することはできます)。
    • Windows ランタイムにない型 (System.ExceptionSystem.EventArgs など) から派生させる。
  • すべてのパブリック型にはアセンブリ名に一致するルート名前空間が必要になります。ただし、アセンブリ名の先頭には "Windows" を付けることはできません。

    ヒント。 既定では、Visual Studio プロジェクトにはアセンブリ名に一致する名前空間名があります。 Visual Basic では、この既定の名前空間の Namespace ステートメントはコードに表示されません。

  • パブリック構造体はパブリック フィールド以外のメンバーを持つことができません。また、それらのフィールドは値型または文字列であることが必要です。

  • パブリック クラスは sealed (Visual Basic では NotInheritable) であることが必要です。 プログラミング モデルでポリモーフィズムが必要となる場合は、パブリック インターフェイスを作成し、ポリモーフィックにする必要があるクラスにそのインターフェイスを実装できます。

コンポーネントのデバッグ

UWP アプリとコンポーネントの両方がマネージド コードで作成されている場合、それら両方を同時にデバッグできます。

C++ を使用して UWP アプリの一部としてコンポーネントをテストしている場合は、マネージド コードとネイティブ コードを同時にデバッグできます。 既定では、ネイティブ コードのみになります。

ネイティブ C++ コードとマネージ コードの両方をデバッグするには

  1. Visual C++ プロジェクトのショートカット メニューを開き、[プロパティ] をクリックします。
  2. プロパティ ページの [構成プロパティ] で、[デバッグ] を選びます。
  3. [デバッガーの種類] を選び、ドロップダウン リスト ボックスで、[ネイティブのみ][混合 (マネージとネイティブ)] に変更します。 [OK] をクリックします。
  4. ネイティブ コードとマネージ コードのブレークポイントを設定します。

JavaScript を使用して UWP アプリの一部としてコンポーネントをテストしている場合、ソリューションは既定で JavaScript デバッグ モードになります。 Visual Studio では、JavaScript とマネージ コードを同時にデバッグすることはできません。

JavaScript ではなくマネージ コードをデバッグするには

  1. JavaScript プロジェクトのショートカット メニューを開き、[プロパティ] を選びます。
  2. プロパティ ページの [構成プロパティ] で、[デバッグ] を選びます。
  3. [デバッガーの種類] を選び、ドロップダウン リスト ボックスで、[スクリプトのみ][マネージのみ] に変更します。 [OK] をクリックします。
  4. マネージ コードのブレークポイントを設定し、通常どおりにデバッグします。

マネージ コードへの Windows ランタイム型の引き渡し

Windows ランタイム コンポーネントでの型の宣言」セクションで既に説明したように、特定の .NET 型はパブリック クラスのメンバーのシグネチャに示される場合があります。 これは、マネージド コードで Windows ランタイムを通常どおりに使用できるようにするために、.NET によって提供されるサポートの一部です。 これには、プリミティブ型と一部のクラスやインターフェイスが含まれます。 コンポーネントが JavaScript または C++ コードから使用される場合は、.NET 型が呼び出し元に対してどのように表示されるかを理解することが重要です。 JavaScript を使用した例については、「C# または Visual Basic Windows ランタイム コンポーネントの作成と JavaScript からの呼び出しに関するチュートリアル」をご覧ください。 このセクションでは、よく使われる型について説明します。

.NET では、Int32 構造体などのプリミティブ型に、TryParse メソッドなどの便利なプロパティやメソッドが多数存在します。 これに対して、Windows ランタイムのプリミティブ型と構造体は、フィールドしか保持していません。 これらの型をマネージド コードに渡すと、.NET 型のように表示され、通常どおりに .NET 型のプロパティとメソッドを使用できます。 IDE で自動的に行われる置き換えの概要を次に示します。

  • Windows ランタイムのプリミティブ型 Int32Int64SingleDoubleBooleanString (Unicode 文字の変更できないコレクション)、EnumUInt32UInt64、および Guid に対しては、System 名前空間に含まれる同じ名前の型が使用されます。
  • UInt8 に対しては、System.Byte が使用されます。
  • Char16 に対しては、System.Char が使用されます。
  • IInspectable インターフェイスに対しては、System.Object が使用されます。

C# や Visual Basic で、これらの型に対して言語キーワードが指定されている場合は、代わりに言語キーワードを使用できます。

プリミティブ型に加えて、よく使用される基本的な Windows ランタイム型が、同等の .NET 型としてマネージド コードに表示されます。 たとえば、JavaScript コードで Windows.Foundation.Uri クラスを使用しており、それを C# または Visual Basic のメソッドに渡すとします。 マネージド コードの同等の型は .NET の System.Uri クラスであり、その型がメソッド パラメーター用に使用されます。 マネージド コードを記述するとき、Visual Studio の IntelliSense によって Windows ランタイム型が表示されなくなり、同等の .NET 型が示されるため、Windows ランタイム型が .NET 型として表示されていることがわかります。 (通常、2 つの型の名前は同じです。ただし、 Windows.Foundation.DateTime 構造体は、マネージド コードでは System.DateTimeOffset として表示され、 System.DateTime として表示されないことに注意してください)。

よく使用されるコレクション型の一部では、Windows ランタイム型によって実装されるインターフェイスと、対応する .NET 型によって実装されるインターフェイスと間で対応付けを行います。 上で説明した型と同じように、.NET 型を使用してパラメーターの型を宣言する必要があります。 これにより、型の間にある相違点を意識せずに、.NET コードを通常どおりに記述することができます。

次の表は、最も一般的なジェネリック インターフェイスの型、および他の一般的なクラスやインターフェイスに関する対応付けを示しています。 .NET で対応する Windows ランタイム型の詳しい一覧については、「.NET Framework での Windows ランタイム型の対応付け」をご覧ください。

Windows ランタイム .NET
IIterable<T> IEnumerable<T>
IVector<T> IList<T>
IVectorView<T> IReadOnlyList<T>
IMap<K, V> IDictionary<TKey, TValue>
IMapView<K, V> IReadOnlyDictionary<TKey, TValue>
IKeyValuePair<K, V> KeyValuePair<TKey, TValue>
IBindableIterable IEnumerable
IBindableVector IList
Windows.UI.Xaml.Data.INotifyPropertyChanged System.ComponentModel.INotifyPropertyChanged
Windows.UI.Xaml.Data.PropertyChangedEventHandler System.ComponentModel.PropertyChangedEventHandler
Windows.UI.Xaml.Data.PropertyChangedEventArgs System.ComponentModel.PropertyChangedEventArgs

型によって複数のインターフェイスが実装される場合、メンバーのパラメーターの型または戻り値の型として実装されるインターフェイスをすべて使うことができます。 たとえば、Dictionary<int, string> (Visual Basic では Dictionary(Of Integer, String)) を、IDictionary<int, string>IReadOnlyDictionary<int, string>、または IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>> として渡すか返すことができます。

重要

JavaScript は、マネージド型が実装するインターフェイスのリストに最初に示されるインターフェイスを使用します。 たとえば、Dictionary<int, string> を JavaScript コードに返す場合、戻り値の型としてどのインターフェイスを指定しても、IDictionary<int, string> として表示されます。 つまり、後のインターフェイスにメンバーが最初のインターフェイスに含まれていない場合、そのメンバーは JavaScript では認識されません。

Windows ランタイムでは、IMap<K, V>IMapView<K, V> は IKeyValuePair を使用して反復処理されます。 これらをマネージド コードに渡すと、IDictionary<TKey, TValue> および IReadOnlyDictionary<TKey, TValue> として表示されるため、これらを列挙するには必然的に System.Collections.Generic.KeyValuePair<TKey, TValue> を使用します。

インターフェイスがマネージド コード内に表示される方法によって、これらのインターフェイスを実装する型の表示方法が決まります。 たとえば、PropertySet クラスでは IMap<K, V> を実装しますが、これはマネージド コードでは IDictionary<TKey, TValue> として表示されます。 PropertySet は、IMap<K, V> ではなく IDictionary<TKey, TValue> が実装されているように表示されるため、マネージド コードではそれが .NET ディクショナリの Add メソッドと同じように動作をする Add メソッドがあるように表示されます。 Insert メソッドがないように表示されます。 この例については、「C# または Visual Basic Windows ランタイム コンポーネントの作成と JavaScript からの呼び出しに関するチュートリアル」のトピックをご覧ください。

Windows ランタイムへのマネージ型の引き渡し

前のセクションで説明したように、一部の Windows ランタイム型は、コンポーネントのメンバーのシグネチャ内、または IDE で使用する場合は Windows ランタイム メンバーのシグネチャ内で、.NET 型として表示される場合があります。 .NET 型をこれらのメンバーに渡すか、コンポーネントのメンバーの戻り値として使用すると、対応する Windows ランタイム型として Windows ランタイム側のコードに表示されます。 コンポーネントが JavaScript から呼び出されたときにこれが及ぼす影響に関する例については、「C# または Visual Basic Windows ランタイム コンポーネントの作成と JavaScript からの呼び出しに関するチュートリアル」の「コンポーネントからマネージ型を返す」のセクションをご覧ください。

オーバー ロードされたメソッド

Windows ランタイムでは、メソッドはオーバーロードできます。 ただし、同じ数のパラメーターを持つ複数のオーバーロードを宣言した場合、これらのオーバーロードのうち 1 つのみに Windows.Foundation.Metadata.DefaultOverloadAttribute 属性を適用する必要があります。 この属性が適用されるオーバーロードが、JavaScript から呼び出すことができる唯一のオーバーロードになります。 たとえば、次のコードでは、int (Visual Basic では Integer) を受け取るオーバーロードが既定のオーバーロードです。

public string OverloadExample(string s)
{
    return s;
}

[Windows.Foundation.Metadata.DefaultOverload()]
public int OverloadExample(int x)
{
    return x;
}
Public Function OverloadExample(ByVal s As String) As String
    Return s
End Function

<Windows.Foundation.Metadata.DefaultOverload> _
Public Function OverloadExample(ByVal x As Integer) As Integer
    Return x
End Function

[重要] JavaScript では任意の値を OverloadExample に渡し、パラメーターに必要な値を型に強制することができます。 OverloadExample を "forty-two"、"42"、または 42.3 で呼び出すことができますが、それらの値はすべて既定のオーバーロードに渡されます。 前の例の既定のオーバーロードでは、0、42、および 42 をそれぞれ返します。

コンストラクターに DefaultOverloadAttribute 属性を適用することはできません。 クラスのすべてのコンストラクターは、異なる数のパラメーターを持つ必要があります。

IStringable の実装

Windows 8.1 以降、Windows ランタイムに IStringable インターフェイスが用意されています。そのメソッドは IStringable.ToString の 1 つだけで、Object.ToString で提供されるサポートに相当する基本的な書式設定のサポートを提供します。 Windows ランタイム コンポーネントでエクスポートしたパブリック マネージ型に IStringable を実装する場合は、次の制限が適用されます。

  • IStringable インターフェイスは、次のコードのように、"クラスが実装する" 関係でのみ定義することができます。C# では、次のようになります。

    public class NewClass : IStringable
    

    Visual Basic では、次のようになります。

    Public Class NewClass : Implements IStringable
    
  • インターフェイスで IStringable を実装することはできません。

  • パラメーターの型を IStringable として宣言することはできません。

  • IStringable をメソッド、プロパティ、フィールドの戻り値の型にすることはできません。

  • 次のようなメソッド定義を使用して IStringable の実装を基底クラスから隠すことはできません。

    public class NewClass : IStringable
    {
       public new string ToString()
       {
          return "New ToString in NewClass";
       }
    }
    

    代わりに、IStringable.ToString の実装で基底クラスの実装を常にオーバーライドする必要があります。 ToString の実装を隠すことができるのは、厳密に型指定されたクラス インスタンスで呼び出す場合だけです。

注意

IStringable を実装するマネージ型やその ToString の実装を隠すマネージ型をネイティブ コードから呼び出すと、さまざまな状況で予期しない動作を引き起こす可能性があります。

非同期操作

コンポーネントで非同期メソッドを実装するには、メソッド名の最後に "Async" を追加し、非同期アクションまたは非同期操作を表す、IAsyncActionIAsyncActionWithProgress<TProgress>IAsyncOperation<TResult>IAsyncOperationWithProgress<TResult, TProgress> のいずれかの Windows ランタイム インターフェイスを返します。

.NET タスク (Task クラスとジェネリック Task<TResult> クラス) を使用して、非同期メソッドを実装できます。 C# または Visual Basic で記述された非同期メソッドから返されるタスクや、Task.Run メソッドから返されたタスクなど、進行中の操作を表すタスクを返す必要があります。 コンストラクターを使ってタスクを作成する場合、その Task.Start メソッドを呼び出してから戻す必要があります。

await (Visual Basic では Await) を使用するメソッドには、async キーワード (Visual Basic では Async) が必要です。 Windows ランタイム コンポーネントからそのようなメソッドを公開する場合、Run メソッドに渡すデリゲートに async キーワードを適用します。

取り消しや進行状況の報告をサポートしない非同期アクションと非同期操作では、タスクを適切なインターフェイスにラップするために、WindowsRuntimeSystemExtensions.AsAsyncAction または AsAsyncOperation<TResult> の拡張メソッドを使うことができます。 たとえば、次のコードでは、タスクを開始するために Task.Run<TResult> メソッドを使用して、非同期メソッドを実装します。 AsAsyncOperation<TResult> 拡張メソッドでは、タスクを Windows ランタイムの非同期操作として返します。

public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
{
    return Task.Run<IList<string>>(async () =>
    {
        var data = await DownloadDataAsync(id);
        return ExtractStrings(data);
    }).AsAsyncOperation();
}
Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
     As IAsyncOperation(Of IList(Of String))

    Return Task.Run(Of IList(Of String))(
        Async Function()
            Dim data = Await DownloadDataAsync(id)
            Return ExtractStrings(data)
        End Function).AsAsyncOperation()
End Function

次の JavaScript コードでは、WinJS.Promise オブジェクトを使用してメソッドを呼び出す方法を示します。 then メソッドに渡される関数は、非同期呼び出しが完了したときに実行されます。 stringList パラメーターには DownloadAsStringAsync メソッドによって返される文字列のリストが含まれ、関数で必要な処理を実行します。

function asyncExample(id) {

    var result = SampleComponent.Example.downloadAsStringAsync(id).then(
        function (stringList) {
            // Place code that uses the returned list of strings here.
        });
}

取り消しや進行状況の報告をサポートする非同期アクションと非同期操作では、開始されたタスクを生成して、適切な Windows ランタイム インターフェイスの取り消し機能と進行状況の報告機能を持つタスクの取り消し機能と進行状況の報告機能をフックするために、AsyncInfo クラスを使用します。 取り消しおよび進行状況の報告の両方をサポートする例については、「C# または Visual Basic Windows ランタイム コンポーネントの作成と JavaScript からの呼び出しに関するチュートリアル」をご覧ください。

非同期メソッドで取り消しや進行状況の報告に対応していない場合でも、AsyncInfo クラスのメソッドを使用できます。 Visual Basic のラムダ関数または C# の匿名メソッドを使用する場合は、トークンと IProgress<T> インターフェイスのパラメーターを指定しないでください。 C# のラムダ関数を使う場合は、トークンのパラメーターを指定しますが、無視されます。 AsAsyncOperation<TResult> メソッドを使用した前の例で、代わりに AsyncInfo.Run<TResult>(Func<CancellationToken, Task<TResult>>) メソッド オーバーロードを使用すると、次のようになります。

public static IAsyncOperation<IList<string>> DownloadAsStringsAsync(string id)
{
    return AsyncInfo.Run<IList<string>>(async (token) =>
    {
        var data = await DownloadDataAsync(id);
        return ExtractStrings(data);
    });
}
Public Shared Function DownloadAsStringsAsync(ByVal id As String) _
    As IAsyncOperation(Of IList(Of String))

    Return AsyncInfo.Run(Of IList(Of String))(
        Async Function()
            Dim data = Await DownloadDataAsync(id)
            Return ExtractStrings(data)
        End Function)
End Function

取り消しや進行状況の報告にオプションで対応する非同期メソッドを作成する場合は、キャンセル トークンや IProgress<T> インターフェイスのパラメーターを持たないオーバーロードを追加することを検討してください。

例外のスロー

Windows アプリ用 .NET に含まれている例外の型は、どれでもスローすることができます。 Windows ランタイム コンポーネントで独自のパブリック型の例外を宣言することはできませんが、非パブリック型を宣言し、スローすることはできます。

コンポーネントが例外を処理しない場合は、コンポーネントを呼び出したコードで対応する例外が発生します。 例外が呼び出し元に表示される方法は、呼び出し元の言語が Windows ランタイムをサポートする方法によって異なります。

  • JavaScript では、例外はオブジェクトとして表示され、例外メッセージがスタック トレースで置き換えられています。 Visual Studio でアプリをデバッグするとき、デバッガーの例外ダイアログ ボックスに、"WinRT 情報" として元のメッセージ テキストが表示されます。 JavaScript コードから元のメッセージ テキストにアクセスすることはできません。

    ヒント。 現在、スタック トレースにはマネージド型の例外が含まれますが、トレースを解析して例外の型を確認することはお勧めしません。 このセクションの後半で説明するように、代わりに HRESULT 値を使ってください。

  • C++ では、例外はプラットフォーム例外として表示されます。 マネージ例外の HResult プロパティを特定のプラットフォーム例外の HRESULT にマップできる場合は、そのプラットフォーム例外が使用されます。それ以外の場合は、Platform::COMException 例外がスローされます。 マネージ例外のメッセージ テキストは、C++ コードでは利用できません。 特定のプラットフォーム例外がスローされた場合、その例外の型に関する既定のメッセージ テキストが表示されます。それ以外の場合は、メッセージ テキストは表示されません。 「例外 (C++/CX)」をご覧ください。

  • C# または Visual Basic では、例外は通常のマネージ例外です。

コンポーネントから例外をスローする場合、コンポーネントに固有の HResult プロパティ値を持つ非パブリック型の例外をスローすることにより、JavaScript や C++ の呼び出し元で例外を簡単に処理できるようになります。 HRESULT は、JavaScript の呼び出し元では例外オブジェクトの number プロパティから、C++ の呼び出し元では COMException::HResult プロパティから利用できます。

注意

HRESULT には負の値を使用してください。 正の値は成功と解釈されるので、JavaScript や C++ の呼び出し元で例外がスローされなくなります。

イベントの宣言と発生

イベントのデータを保持する型を宣言する場合、EventArgs は Windows ランタイム型ではないので、EventArgs の代わりに Object から派生させます。 EventHandler<TEventArgs> をイベントの型として使用し、イベント引数の型をジェネリック型引数として使用します。 .NET アプリケーションの場合と同様にイベントを発生させます。

Windows ランタイム コンポーネントが JavaScript や C++ で使われる場合、イベントはそれらの言語で想定されている Windows ランタイムのイベント パターンに従います。 C# や Visual Basic でそのコンポーネントを使用すると、イベントは通常の .NET のイベントとして表示されます。 例については、「C# または Visual Basic Windows ランタイム コンポーネントの作成と JavaScript からの呼び出しに関するチュートリアル」に用意しています。

カスタム イベント アクセサーを実装する場合 (Visual Basic では Custom キーワードでイベントを宣言する場合) は、実装で Windows ランタイムのイベント パターンに従う必要があります。 「Windows ランタイム コンポーネントのカスタム イベントおよびイベント アクセサー」をご覧ください。 C# や Visual Basic コードでイベントを処理する場合でも、通常の .NET のイベントとして表示されます。

次の手順

ユーザーが独自に使用する Windows ランタイム コンポーネントを作成した後に、そのコンポーネントにカプセル化されている機能が他の開発者の役に立つことに気づく場合があります。 他の開発者に配布するためにコンポーネントをパッケージ化する方法は 2 つあります。 「マネージ Windows ランタイム コンポーネントの配布」をご覧ください。

Visual Basic と C# の言語の機能、および Windows ランタイムに関する .NET のサポートについて詳しくは、Visual BasicC# のドキュメントをご覧ください。

トラブルシューティング

症状 解決方法
C++/WinRT アプリで、XAML を使用する C# Windows ランタイム コンポーネントを使用すると、"'MyNamespace_XamlTypeInfo' は 'winrt::MyNamespace' のメンバーではありません" という形式のエラーがコンパイラによって生成されます。"この MyNamespace" は、Windows ランタイム コンポーネントの名前空間の名前です。 使用する側の C++/WinRT アプリの pch.h で、#include <winrt/MyNamespace.MyNamespace_XamlTypeInfo.h> を追加します。必要に応じて "MyNamespace" を置き換えます。