WinRT ci permette di creare componenti da utilizzare con qualsiasi linguaggio, senza le complicazioni derivanti dal marshalling dei dati. Questi componenti possono essere utilizzati in qualunque app Windows Store, anche di tipo HTML/JavaScript, ma non possiamo sfruttare JavaScript per crearli. Si tratta in effetti di librerie Windows Runtime da scrivere solo in C#, VB o C++ (niente JavaScript).
Nell'articolo dedicato ai background task, abbiamo già avuto modo di lavorare con un componente Windows Runtime. In questo articolo vedremo rapidamente quali sono i concetti e l'architettura che stanno dietro a un Windows Runtime Component per poi rimandare alla programmazione in C# per la creazione di un oggetto di questo tipo.
WinRT e WinMD
Per capire come funziona un componente WinMD, è utile partire dall'architettura di WinRT. Sin dalle sue prime versioni, infatti, Microsoft Windows ha messo a disposizione degli sviluppatori librerie e API per interagire con il sistema operativo. Tuttavia, prima di Windows 8 queste API e librerie erano spesso complesse e non facili da usare. Inoltre, anche lavorando con il framework .NET non era raro dover interagire con Component Object Model (COM) Interop e Win32 Interoperability via P/Invoke (Platform Invoke) per poter sfruttare le funzionalità del sistema operativo.
Vediamo uno snippet in C# che mostra un esempio dei passaggi necessari, prima di WinRT, per importare due DLL native Win32 all'interno del mondo .NET allo scopo di catturare un'immagine dalla webcam del proprio PC. La sintassi non brilla certo per chiarezza ed è facile commettere errori.
[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindow")]
static extern int capCreateCaptureWindow(
string lpszWindowName,
int dwStyle,
int X,
int Y,
int nWidth,
int nHeight,
int hwndParent,
int nID);
[DllImport("avicap32.dll")]
static extern bool capGetDriverDescription(
int wDriverIndex,
[MarshalAs(UnmanagedType.LPTStr)] ref string lpszName,
int cbName,
[MarshalAs(UnmanagedType.LPTStr)] ref string lpszVer,
int cbVer);
Per rendere le cose più semplici in WinRT è stata modificata l'interazione con il sistema operativo e abbiamo a disposizione un insieme di API completamente ridisegnato, che permette di evitare le complicazioni proprie di P/Invoke e Interop.
Vediamo adesso come, grazie a WinRT, la sintassi utilizzata per raggiungere lo stesso obiettivo visto prima (scattare una foto dalla webcam) risulti estremamente più semplice e pulita, e conseguentemente più facile da manutenere. E si può fare anche in JavaScript:
var camera = new capture.CameraCaptureUI();
camera.captureFileAsync(capture.CameraCaptureUIMode.photo)
.then(function (file) {
if (file != null) {
media.shareFile = file;
}
});
WinRT si pone proprio l'obiettivo di unificare l'esperienza di sviluppo di un'applicazione Windows Store, indipendentemente dal linguaggio utilizzato. Dal punto di vista architetturale, WinRT poggia sul Windows Runtime Core, ossia su un insieme di librerie C++ che fungono da ponte tra WinRT e il sottostante sistema operativo. La prossima immagine, presa dalla documentazione ufficiale di MSDN, mostra l'architettura complessiva di WinRT.
Il Windows Runtime Core sfrutta internamente un insieme proprietario di tipi di dati. Ad esempio, HSTRING
è il tipo che rappresenta un valore testuale in WinRT, ma esistono anche tipi numerici come INT32
e UINT64
, collezioni come IVector<T>
, enum, strutture, classi del runtime, ecc.
Subito sopra il Windows Runtime Core troviamo una serie di librerie e tipi specifici a disposizione di qualunque applicazione Windows 8. Ad esempio, esistono librerie per effettuare richieste in rete e per accedere allo storage (locale o remoto); picker per selezionare file, immagini, date, ecc.; nonché un insieme di classi in grado di sfruttare i media service, e così via.
Tutti questi tipi e librerie sono definiti in un insieme strutturato di namespace e sono descritti da metadati che prendono il nome di Windows Metadata (WinMD) e utilizzano lo stesso standard utilizzato dal framework .NET per la definizione dei metadati, ossia il Common Language Interface (ECMA-335).
Per poter consumare questi tipi da qualunque linguaggio di programmazione supportato, WinRT sfrutta un apposito projection layer che, grazie ai Windows Metadata, provvede a "tradurre" i tipi interni di WinRT in quelli del linguaggio di destinazione. Ad esempio, il tipo HSTRING
sopra menzionato viene tradotto nel System.String
di .NET, nel caso di un'applicazione basata sul Common Language Runtime (CLR, VB/C#), ovvero in un Platform::String
nel caso di un'applicazione sviluppata in C++.
In questo modo, il projection layer permette di esporre i tipi di WinRT tramite classi e oggetti propri del linguaggio prescelto (per una mappatura completa dei diversi tipi WinRT in .NET si rinvia alla documentazione ufficiale MSDN).
Possiamo facilmente ispezionare i file contenenti i metadati che definiscono i tipi di WinRT. Per default, questi file sono memorizzati sotto C:\Windows\System32\WinMetadata
. Il contenuto di default della directory contenente i file WinMD è il seguente:
Windows.ApplicationModel.winmd
Windows.Data.winmd
Windows.Devices.winmd
Windows.Foundation.winmd
Windows.Globalization.winmd
Windows.Graphics.winmd
Windows.Management.winmd
Windows.Media.winmd
Windows.Networking.winmd
Windows.Security.winmd
Windows.Storage.winmd
Windows.System.winmd
Windows.UI.winmd
Windows.UI.Xaml.winmd
Windows.Web.winmd
Come si può vedere, nella directory compare un file denominato Windows.Media.winmd
, il quale contiene la definizione della classe CameraCaptureUI
usata in apertura di questo articolo. È possibile ispezionare i vari file WinMD tramite l'Intermediate Language Disassembler (ILDASM), un semplice tool disponibile nel Microsoft .NET SDK sin dal 2002, a sua volta distribuito assieme a Microsoft Visual Studio o scaricabile come parte del Microsoft .NET Framework SDK.
La prossima immagine mostra la finestra dell'ILDASM con la struttura del file Windows.Media.winmd con evidenziata la definizione del tipo CameraCaptureUI
già menzionato.
Con un doppio click sul nome del metodo è possibile vedere la sua definizione, che non è rappresentata dal codice sorgente del metodo stesso (il componente è infatti scritto in linguaggio nativo), quando piuttosto dai metadati che consentono di "mapparlo" con la libreria nativa utilizzata dietro le quinte. Nel seguente estratto è possibile osservare la definizione dei metadati del metodo CaptureFileAsync
esposto dalla classe CameraCaptureUI
.
.method public hidebysig newslot virtual final
instance class [Windows.Foundation]Windows.Foundation.IAsyncOperation`1
CaptureFileAsync([in] valuetype Windows.Media.Capture.CameraCaptureUIMode mode) runtime managed
{
.override Windows.Media.Capture.ICameraCaptureUI::CaptureFileAsync
} // end of method CameraCaptureUI::CaptureFileAsync
.method public hidebysig newslot virtual final
Spetterà poi all'infrastruttura del language projection il compito di tradurre questa definizione neutrale nel tipo equivalente nel linguaggio prescelto.
Creare una libreria WinMD
Come abbiamo detto, per la creazione di un componente Windows Runtime dobbiamo servirci di linguaggi come C# e C++, per questo rimandiamo alla creazione di una libreria nella lezione omologa a questa per la piattaforma di sviluppo C#/XAML.
È comunque importate avere chiaro il ruolo giocato dai componenti Windows Runtime all'interno della complessiva architettura di WinRT, anche semplicemente per sfruttarle, come vedremo nella prossima lezione.