Condividi tramite


Creazione di componenti Windows Runtime in C# e Visual Basic

Con .NET Framework 4.5 puoi utilizzare il codice gestito per creare tipi di Windows Runtime personalizzati, inclusi in un pacchetto con un componente di Windows Runtime. Puoi utilizzare il componente nelle app di Windows Store con C++, JavaScript, Visual Basic o C#. In questo articolo vengono descritte le regole per la creazione di un componente e vengono illustrati alcuni aspetti del supporto di .NET Framework per Windows Runtime. Questo supporto è in genere progettato per essere trasparente al programmatore di .NET Framework. Tuttavia, quando crei un componente da utilizzare con JavaScript o C++, devi tenere presenti le differenze nella modalità di supporto di Windows Runtime in questi linguaggi.

Nota

Se crei un componente da utilizzare solo nelle app di Windows Store con Visual Basic o C# e il componente non contiene controlli Windows Store, valuta di utilizzare il modello Libreria di classi (app di Windows Store) anziché il modello Componente Windows Runtime. Una libreria di classi semplice prevede meno limitazioni.

In questo articolo sono incluse le sezioni seguenti:

  • Dichiarazioni di tipi nei componenti Windows Runtime

  • Debug del componente

  • Passaggio di tipi di Windows Runtime al codice gestito

  • Passaggio di tipi gestiti a Windows Runtime

  • Passaggio di matrici

  • Metodi di overload

  • Operazioni asincrone

  • Generazione di eccezioni

  • Dichiarazione e generazione di eventi

Dichiarazione di tipi nei componenti di Windows Runtime

Internamente, i tipi di Windows Runtime nel componente possono utilizzare qualsiasi funzionalità di .NET Framework consentita in un'app in Windows Store Windows. Per ulteriori informazioni, vedi Panoramica di .NET per le applicazioni Windows Store. Esternamente, i membri dei tipi possono esporre solo tipi di Windows Runtime per i relativi parametri e valori restituiti. Nell'elenco seguente vengono descritte le limitazioni dei tipi di .NET Framework esposti dai componenti di Windows Runtime.

  • Campi, parametri e valori restituiti di tutti i tipi e membri pubblici nel componente devono essere tipi di Windows Runtime.

    Questa restrizione include i tipi di Windows Runtime creati nonché tipi forniti dallo stesso Windows Runtime. Include inoltre una serie di tipi .NET Framework. L'inclusione di questi tipi fa parte del supporto fornito da .NET Framework per abilitare l'utilizzo naturale di Windows Runtime nel codice gestito. Il codice sembra utilizzare tipi .NET Framework comuni anziché i tipi di Windows Runtime sottostanti. Puoi ad esempio utilizzare i tipi primitivi di .NET Framework come Int32 e Double, alcuni tipi di base come DateTimeOffset e Uri e alcuni tipi di interfaccia generici utilizzati comunemente come IEnumerable<T> (IEnumerable(Of T) in Visual Basic) e IDictionary<TKey,TValue>. Nota che gli argomenti di questi tipi generici devono essere tipi di Windows Runtime. Questo aspetto viene illustrato nelle sezioni Passaggio di tipi di Windows Runtime al codice gestito e Passaggio di tipi gestiti a Windows Runtime, più avanti in questo articolo.

  • Le classi e le interfacce pubbliche possono contenere metodi, proprietà ed eventi. Puoi dichiarare i delegati per gli eventi o utilizzare il delegato EventHandler<T>. Una classe pubblica o un'interfaccia non può:

    • Essere generica.

    • Implementare un'interfaccia che non è un'interfaccia di Windows Runtime. Puoi tuttavia creare interfacce di Windows Runtime personalizzate e distribuirle.

    • Derivare da tipi che non sono in Windows Runtime, ad esempio System.Exception e System.EventArgs.

  • Tutti i tipi pubblici devono avere uno spazio dei nomi radice corrispondente al nome dell'assembly e il nome di assembly non deve iniziare con "Windows".

    Nota

    Per impostazione predefinita, i progetti di Visual Studio hanno nomi dello spazio dei nomi corrispondenti al nome dell'assembly. In Visual Basic l'istruzione Namespace per questo spazio dei nomi predefinito non è visualizzata nel codice.

  • Le strutture pubbliche non possono contenere membri diversi dai campi pubblici e questi campi devono essere stringhe o tipi di valore.

  • Le classi pubbliche devono essere sealed (NotInheritable in Visual Basic). Se il modello di programmazione in uso richiede il polimorfismo, puoi creare un'interfaccia pubblica e implementarla nelle classi che devono essere polimorfiche.

Debug del componente

Se sia l'app in Windows Store sia il componente sono compilati con codice gestito, puoi eseguirne il debug contemporaneamente.

Quando testi il componente come parte di un'app in Windows Store utilizzando C++, puoi eseguire il debug del codice gestito e nativo contemporaneamente. Per impostazione predefinita viene eseguito il debug del solo codice nativo.

Per eseguire il debug sia del codice C++ nativo sia del codice gestito

  1. Apri il menu di scelta rapida per il progetto di Visual C++ e scegli Proprietà.

  2. Nelle pagine delle proprietà scegli Debug in Proprietà di configurazione.

  3. Scegli Tipo di debugger e imposta Misto (gestito e nativo) anziché Solo nativo nella casella di riepilogo a discesa. Scegli OK.

  4. Imposta i punti di interruzione nel codice nativo e in quello gestito.

Quando testi il componente come parte di un'app in Windows Store utilizzando JavaScript, per impostazione predefinita la soluzione è in modalità di debug JavaScript. In Visual Studio 2012 e Visual Studio Express 2012 per Windows 8 non puoi eseguire contemporaneamente il debug di JavaScript e del codice gestito.

Per eseguire il debug del codice gestito anziché JavaScript

  1. Apri il menu di scelta rapida per il progetto JavaScript e scegli Proprietà.

  2. Nelle pagine delle proprietà scegli Debug in Proprietà di configurazione.

  3. Scegli Tipo di debugger e imposta Solo gestito anziché Solo script nella casella di riepilogo a discesa. Scegli OK.

  4. Imposta i punti di interruzione nel codice gestito ed esegui il debug nel modo consueto.

Passaggio di tipi di Windows Runtime al codice gestito

Come riportato in precedenza nella sezione Dichiarazione di tipi nei componenti Windows Runtime, alcuni tipi .NET Framework possono essere visualizzati nelle firme di membri delle classi pubbliche. Questo aspetto è parte del supporto fornito da .NET Framework per abilitare l'utilizzo naturale di Windows Runtime nel codice gestito. Include tipi primitivi e alcune classi e interfacce. Quando il componente viene utilizzato da JavaScript o da codice C++, è importante conoscere i tipi .NET Framework visualizzati al chiamante. Per esempi con JavaScript, vedi Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript. In questa sezione vengono descritti tipi di uso comune.

In .NET Framework, i tipi primitivi quali la struttura Int32 presentano molti metodi e proprietà utili, ad esempio il metodo TryParse. Al contrario, i tipi primitivi e le strutture in Windows Runtime hanno solo campi. Quando passi questi tipi al codice gestito, risultano essere tipi .NET Framework e puoi utilizzare le proprietà e i metodi di tali tipi nel modo consueto. Nell'elenco seguente vengono riepilogate le sostituzioni effettuate automaticamente nell'IDE:

  • Per le primitive di Windows Runtime Int32, Int64, Single, Double, Boolean, String (una raccolta non modificabile di caratteri Unicode), Enum, UInt32, UInt64 e Guid, utilizza il tipo con lo stesso nome nello spazio dei nomi System.

  • Per UInt8, utilizza System.Byte.

  • Per Char16, utilizza System.Char.

  • Per l'interfaccia IInspectable, utilizza System.Object.

Se C# o Visual Basic fornisce una parola chiave del linguaggio per uno di questi tipi, puoi invece utilizzare la parola chiave del linguaggio.

Oltre ai tipi primitivi, alcuni tipi di Windows Runtime di base utilizzati comunemente vengono visualizzati nel codice gestito come gli equivalenti di .NET Framework. Supponi ad esempio che nel codice JavaScript venga utilizzata la classe Windows.Foundation.Uri e di volerla passare a un metodo C# o Visual Basic. Il tipo equivalente nel codice gestito è la classe System.Uri di .NET Framework, ovvero il tipo da utilizzare per il parametro del metodo. Puoi determinare quando un tipo di Windows Runtime risulta come un tipo di .NET Framework, perché quando scrivi codice gestito IntelliSense in Visual Studio nasconde il tipo di Windows Runtime e presenta il tipo di .NET Framework equivalente. In genere i due tipi hanno lo stesso nome. Nota tuttavia che la struttura Windows.Foundation.DateTime è presente nel codice gestito come System.DateTimeOffset, non come System.DateTime.

Per alcuni tipi di raccolte comunemente utilizzati, il mapping è tra le interfacce implementate da un tipo di Windows Runtime e le interfacce implementate dal tipo .NET Framework corrispondente. Come per i tipi indicati sopra, per dichiarare i tipi di parametro utilizzi il tipo .NET Framework. In questo modo alcune differenze tra i tipi vengono nascoste e la scrittura del codice .NET Framework è più naturale. Nella tabella seguente sono elencati i più comuni tra questi tipi di interfaccia generici, con altri mapping di interfacce e classi comuni. Per un elenco completo dei tipi di Windows Runtime mappati da .NET Framework, vedi Mapping di .NET Framework dei tipi di Windows Runtime.

Windows Runtime

.NET Framework

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

    

Quando un tipo implementa più interfacce, puoi utilizzare qualsiasi interfaccia implementata come tipo di parametro o tipo restituito di un membro. Ad esempio, puoi passare o restituire Dictionary<int, string> (Dictionary(Of Integer, String) in Visual Basic) come IDictionary<int, string>, IReadOnlyDictionary<int, string> o IEnumerable<System.Collections.Generic.KeyValuePair<TKey, TValue>>.

Importante

JavaScript utilizza l'interfaccia visualizzata per prima nell'elenco di interfacce implementate da un tipo gestito. Se ad esempio restituisci Dictionary<int, string> al codice JavaScript, viene visualizzato come IDictionary<int, string> indipendentemente dall'interfaccia specificata come tipo restituito. Ciò significa che se la prima interfaccia non include un membro visualizzato nelle interfacce successive, tale membro non è visibile a JavaScript.

In Windows Runtime IMap<K, V> e IMapView<K, V> vengono iterati utilizzando IKeyValuePair. Quando li passi al codice gestito vengono visualizzati come IDictionary<TKey, TValue> e IReadOnlyDictionary<TKey, TValue>, perciò utilizzi naturalmente System.Collections.Generic.KeyValuePair<TKey, TValue> per enumerarli.

L'aspetto delle interfacce nel codice gestito influisce sull'aspetto dei tipi che implementano le interfacce. Ad esempio, la classe PropertySet implementa IMap<K, V>, che compare nel codice gestito come IDictionary<TKey, TValue>. PropertySet compare come se avesse implementato IDictionary<TKey, TValue> anziché IMap<K, V>, pertanto nel codice gestito risulta avere un metodo Add che si comporta come il metodo Add nei dizionari di .NET Framework. Non risulta avere un metodo Insert. Puoi vedere questo esempio nell'articolo Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript.

Passaggio di tipi gestiti a Windows Runtime

Come illustrato nella sezione precedente, alcuni tipi di Windows Runtime possono risultare come tipi .NET Framework nelle firme dei membri del componente o nelle firme dei membri di Windows Runtime quando vengono utilizzati nell'IDE. Quando passi i tipi di .NET Framework a questi membri o li utilizzi come valori restituiti dei membri del componente, sono interpretati dal codice dall'altro lato come il tipo di Windows Runtime corrispondente. Per esempi degli effetti di questo aspetto quando il componente viene chiamato da JavaScript, vedi la sezione "Restituzione di tipi gestiti dal componente" in Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript.

Passaggio di matrici

In Windows Runtime tutti i parametri sono per input o output e non sono presenti parametri ref (ByRef in Visual Basic). Il contenuto delle matrici passate al componente di Windows Runtime deve essere destinato all'input o all'output. Ciò significa che le matrici non devono essere considerate modificabili. Se una matrice viene passata per valore (ByVal in Visual Basic), devi applicare l'attributo ReadOnlyArrayAttribute o l'attributo WriteOnlyArrayAttribute per stabilire il motivo. Vedi Passaggio di matrici a un componente Windows Runtime.

Metodi di overload

In Windows Runtime è possibile eseguire l'overload dei metodi. Tuttavia, se dichiari più overload con lo stesso numero di parametri, devi applicare l'attributo Windows.Foundation.Metadata.DefaultOverloadAttribute a uno solo degli overload. Tale overload è l'unico che puoi chiamare da JavaScript. Nel codice seguente, ad esempio, l'overload che accetta int (Integer in Visual Basic) è l'overload predefinito.

        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

Avviso

JavaScript ti consente di passare qualsiasi valore a OverloadExamplee assegna il valore al tipo richiesto dal parametro. Puoi chiamare OverloadExample con "forty-two", "42" o 42.3, ma tutti i valori vengono passati all'overload predefinito. L'overload predefinito dell'esempio precedente restituisce rispettivamente 0, 42 e 42.

Non puoi applicare l'attributo DefaultOverloadAttribute ai costruttori. Tutti i costruttori di una classe devono avere numeri di parametri diversi.

Operazioni asincrone

Per implementare un metodo asincrono nel componente, aggiungi "Async" alla fine del nome del metodo e restituisci una delle interfacce di Windows Runtime che rappresentano azioni o operazioni asincrone: IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult> o IAsyncOperationWithProgress<TResult, TProgress>.

Puoi utilizzare le attività di .NET Framework (la classe Task e la classe generica Task<TResult>) per implementare il metodo asincrono. Devi restituire un'attività che rappresenta un'operazione in corso, ad esempio un'attività restituita da un metodo asincrono scritto in C# o in Visual Basic o un'attività restituita dal metodo Task.Run. Se utilizzi un costruttore per creare l'attività, devi chiamarne il metodo Task.Start prima di restituirlo.

Un metodo che utilizza await (Await in Visual Basic) richiede la parola chiave async (Async in Visual Basic). Se esponi tale metodo da un componente di Windows Runtime, applica la parola chiave async al delegato passato al metodo Run.

Per le azioni e le operazioni asincrone che non supportano la creazione di report sull'annullamento o sullo stato, puoi utilizzare il metodo di estensione AsAsyncOperation<TResult> o WindowsRuntimeSystemExtensions.AsAsyncAction per eseguire il wrapping dell'attività nell'interfaccia appropriata. Ad esempio, il codice seguente implementa un metodo asincrono tramite il metodo Task.Run per avviare un'attività. Il metodo di estensione AsAsyncOperation<TResult> restituisce l'attività come operazione asincrona di Windows Runtime.

        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


Il codice JavaScript seguente mostra in che modo è possibile chiamare il metodo utilizzando un oggetto WinJS.Promise. La funzione passata al metodo then viene eseguita al completamento della chiamata asincrona. Il parametro stringList contiene l'elenco di stringhe restituito dal metodo DownloadAsStringAsync e la funzione esegue l'elaborazione richiesta.

function asyncExample(id) {

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

Per le azioni e le operazioni asincrone che supportano la creazione di report sull'annullamento o sullo stato, utilizza la classe AsyncInfo per generare un'attività avviata e collegare le funzionalità di report sullo stato e sull'annullamento dell'attività con le funzionalità di report sullo stato e sull'annullamento dell'interfaccia di Windows Runtime appropriata. Per un esempio che supporti la creazione di report sia sull'annullamento sia sullo stato, vedi Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript.

Nota che puoi utilizzare i metodi della classe AsyncInfo anche se il metodo asincrono non supporta la creazione di report sull'annullamento o sullo stato. Se utilizzi una funzione lambda di Visual Basic o un metodo anonimo C#, non fornire i parametri per il token e l'interfaccia IProgress<T>. Se utilizzi una funzione lambda in C#, fornisci un parametro token ma ignoralo. L'esempio precedente, in cui si utilizzava il metodo AsAsyncOperation<TResult>, è simile al seguente se utilizzi invece l'overload del metodo 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 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

Se crei un metodo asincrono che supporta facoltativamente la creazione di report sull'annullamento o sullo stato, valuta di aggiungere gli overload che non dispongono di parametri per un token di annullamento o l'interfaccia IProgress<T>.

Generazione di eccezioni

Puoi generare qualsiasi tipo di eccezione incluso in .NET per le applicazioni Windows Store: API supportate. Non puoi dichiarare tipi di eccezione pubblica personalizzati in un componente di Windows Runtime, ma puoi dichiarare e generare tipi pubblici.

Se il componente non gestisce l'eccezione, un'eccezione corrispondente viene generata dal codice che ha chiamato il componente. La modalità di visualizzazione dell'eccezione al chiamante dipende dalla modalità di supporto di Windows Runtime da parte del linguaggio chiamante.

  • In JavaScript l'eccezione appare come un oggetto in cui il messaggio di eccezione è sostituito da una traccia stack. Quando esegui il debug dell'app in Visual Studio, puoi vedere il testo del messaggio originale visualizzato nella finestra di dialogo dell'eccezione del debugger, identificato come "Informazioni WinRT". Non puoi accedere al testo del messaggio originale dal codice JavaScript.

    Nota

    Attualmente, la traccia stack contiene il tipo di eccezione gestita, ma non ti consigliamo di analizzare la traccia per identificare il tipo di eccezione. Utilizza invece un valore HRESULT come descritto più avanti in questa sezione.

  • In C++ l'eccezione ha l'aspetto di un'eccezione della piattaforma. Se la proprietà HResult dell'eccezione gestita può essere mappata a un oggetto HRESULT di un'eccezione della piattaforma specifica, viene usata l'eccezione specifica; in caso contrario, viene generata un'eccezione Platform::COMException. Il testo del messaggio dell'eccezione gestita non è disponibile nel codice C++. Se è stata generata un'eccezione della piattaforma specifica, viene visualizzato il testo del messaggio predefinito per tale eccezione. In caso contrario, non viene visualizzato alcun testo del messaggio. Vedi Eccezioni (C++/CX).

  • In C# o in Visual Basic si tratta di una normale eccezione gestita.

Quando generi un'eccezione dal componente, puoi semplificare la gestione dell'eccezione da parte di un chiamante JavaScript o C++ generando un tipo di eccezione non pubblica il cui valore della proprietà HResult sia specifico per il componente. HRESULT è disponibile a un chiamante JavaScript tramite la proprietà number dell'oggetto dell'eccezione e a un chiamante C++ tramite la proprietà COMException::HResult.

Nota

Utilizza un valore negativo per HRESULT. Un valore positivo viene interpretato come successo e non viene generata alcuna eccezione nel chiamante JavaScript o C++.

Dichiarazione e generazione di eventi

Quando dichiari un tipo per contenere i dati dell'evento, deriva da Object anziché da EventArgs poiché EventArgs non è un tipo di Windows Runtime. Utilizza EventHandler<TEventArgs> come tipo dell'evento e utilizza il tipo di argomento dell'evento come argomento di tipo generico. Genera l'evento come procederesti in un'applicazione .NET Framework.

Quando il componente di Windows Runtime viene utilizzato da JavaScript o C++, l'evento segue lo schema di eventi di Windows Runtime previsto da questi linguaggi. Quando utilizzi il componente da C# o Visual Basic, l'evento viene visualizzato come normale evento di .NET Framework. In Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript viene fornito un esempio.

Se implementi funzioni di accesso a eventi personalizzati (dichiari un evento con la parola chiave Custom in Visual Basic), devi seguire lo schema di eventi di Windows Runtime nell'implementazione. Vedi Eventi personalizzati e funzioni di accesso agli eventi nei componenti Windows Runtime. Nota che quando gestisci l'evento dal codice C# o Visual Basic, risulta comunque essere un normale evento di .NET Framework.

Vedere anche

Concetti

Panoramica di .NET per le applicazioni Windows Store

.NET per le applicazioni Windows Store: API supportate

Procedura dettagliata: creazione di un componente semplice in C# o Visual Basic e chiamata da JavaScript

Creazione di componenti Windows Runtime

Eventi personalizzati e funzioni di accesso agli eventi nei componenti Windows Runtime

Passaggio di matrici a un componente Windows Runtime

Mapping di .NET Framework dei tipi di Windows Runtime

Diagnosi delle condizioni di errore di componenti Windows Runtime