次の方法で共有


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 ステートメントはコードに表示されません。

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

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

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

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

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

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

  1. Visual C++ プロジェクトのショートカット メニューを開き、 Properties を選択します。
  2. プロパティ ページの Configuration Properties で、 Debugging を選択します。
  3. Debugger の種類を選択し、ドロップダウン リスト ボックスで Native OnlyMixed (マネージドおよびネイティブ) に変更します。 OK を選択します。
  4. ネイティブ コードとマネージド コードでブレークポイントを設定します。

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

JavaScript の代わりにマネージド コードをデバッグするには

  1. JavaScript プロジェクトのショートカット メニューを開き、 Properties を選択します。
  2. プロパティ ページの Configuration Properties で、 Debugging を選択します。
  3. [ Debugger の種類を選択し、ドロップダウン リスト ボックスで Script のみManaged Only に変更します。 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.DateTime としてではなくSystem.DateTimeOffset としてマネージド コードに表示されることに注意してください。

よく使用されるコレクション型の一部では、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 の実装を隠すことができるのは、厳密に型指定されたクラス インスタンスで呼び出す場合だけです。

Note

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> インターフェイスのパラメーターを持たないオーバーロードを追加することを検討してください。

例外のスロー

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

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

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

    ヒント。 現時点では、スタック トレースにはマネージド例外の種類が含まれていますが、例外の種類を識別するためにトレースを解析することはお勧めしません。 代わりに、このセクションで後述するように HRESULT 値を使用してください。

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

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

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

Note

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" を置き換えます。