Nessun risultato. Prova con un altro termine.
Guide
Notizie
Software
Tutorial

Enumerare i device

Link copiato negli appunti

Le nostre applicazioni possono aver bisogno di sapere se una particolare periferica è connessa al sistema in un certo istante o se più periferiche dello stesso tipo (come webcam e microfoni) sono contemporaneamente disponibili, oppure, più semplicemente, l'applicazione deve permettere all'utente di scegliere quello più adatto.

Il namespace Windows.Devices.Enumeration, mette a disposizione due strade per recuperare l'elenco dei device connessi al sistema:

  • La prima strada si basa sulla classe DeviceInformation, il cui metodo FindAllAsync permette di recuperare l'elenco delle periferiche disponibili in quel momento sul sistema.
  • La seconda opzione sfrutta la classe DeviceWatcher, che non solo permette di recuperare l'elenco dei dispositivi connessi, ma scatena specifici eventi ogniqualvolta una delle periferiche connesse al sistema viene aggiunta, modificata o rimossa dal sistema.

Vedremo dunque come implementare entrambe queste strategie, che ci possono tornare utili anche per la ricerca di eventuali periferiche Plug and Play (Pnp) presenti sul sistema. Il namespace Windows.Devices.Enumeration.PnP mette a disposizione per questo due classi, PnpObject e PnpDeviceWatcher, che svolgono sostanzialmente le medesime funzioni, rispettivamente, delle classi DeviceInformation e DeviceWatcher: la prima recupera semplicemente l'elenco delle periferiche presenti al momento della chiamata, mentre la seconda notifica al client ogni cambiamento nella collezione iniziale.

Usare la classe DeviceInformation

Il codice che stiamo per esaminare utilizza la funzione FindAllAsync della classe DeviceInformation per recuperare l'elenco delle periferiche disponibili sul sistema. Più precisamente, questo metodo restituisce un oggetto di tipo Windows.Devices.Enumeration.DeviceInformationCollection, che rappresenta una collezione di istanze di tipo DeviceInformation, ciascuna delle quali permette di accedere alle proprietà di un singolo device.

Per ciascuna periferica trovata, il codice mostra a video alcune delle informazioni raccolte, come il nome e l'ID della periferica, il thumbnail che rappresenta il dispositivo, nonché il relativo glifo ("glyph"), ossia il simbolo grafico associato a quel particolare device. Queste ultime due informazioni, ossia thumbnail e glifo, possono essere recuperati in modo asincrono tramite due metodi specifici: rispettivamente, GetThumbnailAsync e GetGlyphThumbnailAsync. La collezione di device è quindi messa in binding con il controllo ListView e mostrato a video.

app.onloaded = function (args) {
	btnEnumerate.addEventListener("click", deviceEnumeration_click);
};
function deviceEnumeration_click() {
	Windows.Devices.Enumeration.DeviceInformation.findAllAsync()
		.done(function (devCollection) {
			var numDevices = devCollection.length;
			for (var i = 0; i < numDevices; i++) {
				displayDeviceInterface(devCollection[i], i);
			}
		},
		function (err) {
			// ...
		});
}
function displayDeviceInterface(deviceInterface, index) {
results.innerHTML +=
	"<h3>" + deviceInterface.name + "</h3>" +
	"<p>Device ID: " + deviceInterface.id + "</p>" +
	"<p>Thumbnail: <img class=\"thumbnail\" id=\"thumbnail" + index + "\" width=\"128\" /></p>" +
	"<p>Glifo: <img class=\"glyph\" id=\"glyph" + index + "\" width=\"64\" /></p>";
	deviceInterface.getThumbnailAsync()
		.done(function (thumbnail) {
			document.getElementById("thumbnail" + index).src = window.URL.createObjectURL(thumbnail, {oneTimeOnly: true });
		});
	deviceInterface.getGlyphThumbnailAsync()
		.done(function (glyph) {
			document.getElementById("glyph" + index).src = window.URL.createObjectURL(glyph, { oneTimeOnly: true });
		});
}

Per testare questo codice puoi usare come riferimento la seguente definizione HTML per la pagina di default della tua applicazione (gli stili CSS non sono inclusi).

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title>Demo.Html.it.EnumeratingDeviceSample.JS</title>
	<!-- WinJS references -->
	<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" /gt;
	<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
	<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
	<!-- Demo.Html.it.EnumeratingDeviceSample.JS references -->
	<link href="/css/default.css" rel="stylesheet" />
	<script src="/js/default.js"></script>
</head>
<body>
	<div id="container">
		<button class="button" id="btnEnumerate">Elenca le periferiche</button>
		<div id="results"></div>
	</div>
</body>
</html>

Lanciando l'app, dovremmo ottenere un risultato simile a quello mostrato nella prossima immagine (ovviamente l'elenco dei dispositivi connessi a ciascun PC sarà differente).

La classe DeviceInformation espone diversi overload del metodo FindAllAsync, in modo da specificare quale tipo di periferiche cercare. Un primo overload, ad esempio, accetta come parametro un oggetto di tipo DeviceClass che indica il tipo di device da ricercare. In particolare, l'enum DeviceClass può assumere uno dei seguenti valori:

Valore Descrizione
All enumera tutte le periferiche connesse al sistema
AudioCapture enumera unicamente i device per la cattura audio
AudioRender enumera solo i dispositive per il rendering audio
PortableStorageDevice enumera i dispositivi di storage portatitili
VideoCapture enumera i device per la cattura video

Il seguente snippet mostra una versione rivista del codice iniziale che enumera unicamente i dispositivi di storage portatili presenti sul sistema, avvalendosi dell'enum DeviceClass:

function deviceEnumeration_click() {
	Windows.Devices.Enumeration.DeviceInformation.findAllAsync(Windows.Devices.Enumeration.DeviceClass.portableStorageDevice)
		.done(function (devCollection) {
			var numDevices = devCollection.length;
			for (var i = 0; i < numDevices; i++) {
				displayDeviceInterface(devCollection[i], i);
			}
		},
		function (err) {
			// ...
		});
}

Lanciamo ancora l'applicazione e vedremo l'elenco con i soli dispositivi di storage rimovibili (se presenti sul nostro sistema).

Gli altri due overload del metodo FindAllAsync consentono di specificare più nel dettaglio il tipo di periferica ricercata. Entrambi questi due metodi accettano infatti come parametro una stringa di tipo Advanced Query Syntax (AQS): si tratta di una particolare tipo di sintassi che Windows utilizza internamente per definire in modo dettagliato i parametri delle query di ricerca (come vedremo fra breve, uno di questi overload accetta anche, come secondo parametro, un array di stringhe che permette di specificare ulteriori proprietà del device da cercare all'interno dell'elenco dei dispositivi presenti sul sistema).

Possiamo usare questa stringa per specificare la classe implementata dalla periferica che stiamo cercando. Infatti, il driver di ogni dispositivo – fisico, logico o virtuale che sia – deve necessariamente fornire un nome che identifichi in modo univoco il dispositivo stesso. A partire da Windows 2000, questi driver utilizzano una classe (denominata device interface class) che permette di esporre le proprie funzionalità verso altri componenti di sistema. Ciascuna device interface class è associata a uno specifico GUID.

Il seguente codice mostra una versione rivista del metodo deviceEnumeration_click già illustrato in precedenza, in modo da sfruttare l'Advanced Query Syntax per elencare le stampanti connesse al sistema:

function deviceEnumeration_click() {
	var selector = "System.Devices.InterfaceClassGuid:=\"{0ECEF634-6EF0-472A-8085-5AD023ECBCCD}\"";
	Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, null)
		.done(function (devCollection) {
			var numDevices = devCollection.length;
			for (var i = 0; i < numDevices; i++) {
				displayDeviceInterface(devCollection[i], i);
			}
		},
		function (err) {
			// ...
		});
}

Eseguendo l'applicazione adesso, il risultato dovrebbe essere simile a quello mostrato nella prossima figura:

Alcune delle API di WinRT mettono a disposizione metodi che permettono di recuperare l'ID (o selector) dei tipi di periferiche più comunemente utilizzate. Qui di seguito sono riportati alcuni esempi (inclusi i nomi delle classi e dei relativi namespace), con i link alla documentazione ufficiale MSDN:

Windows.Media.Devices.MediaDevice.GetAudioCaptureSelector
Windows.Media.Devices.MediaDevice.GetAudioRenderSelector
Windows.Media.Devices.MediaDevice.GetVideoCaptureSelector
Windows.Media.Devices.MediaDevice.GetDefaultAudioRenderId
Windows.Media.Devices.MediaDevice.GetDefaultAudioCaptureId
Windows.Devices.Portable.StorageDevice.GetDeviceSelector
Windows.Devices.Portable.ServiceDevice.GetDeviceSelector
Windows.Devices.Portable.ServiceDevice.GetDeviceSelectorFromServiceId
Windows.Devices.Sms.SmsDevice.GetDeviceSelector
Windows.Networking.Proximity.ProximityDevice.GetDeviceSelector

Ad esempio, il codice seguente utilizza il metodo GetDeviceSelector della classe ProximityDevice per recuperare il selector corrispondente ai sensori di prossimità eventualmente installati sulla macchina (un sensore di prossimità permette di operare "near-field communication" (NFC), nella quale due computer sono connessi a breve distanza senza il bisogno di utilizzare reti Wi-Fi):

function deviceEnumeration_click() {
	var selector = Windows.Networking.Proximity.ProximityDevice.getDeviceSelector();
	Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, null)
		.done(function (devCollection) {
			var numDevices = devCollection.length;
			for (var i = 0; i < numDevices; i++) {
				displayDeviceInterface(devCollection[i], i);
			}
		},
		function (err) {
			// ...
		});
}

Come si è accennato, il secondo overload del metodo FindAllAsync accetta come secondo parametro, oltre alla stringa AQS, un array di stringhe che permettono di specificare quali informazioni recuperare per ciascun device. Per default, infatti, gli oggetti di tipo DeviceInformation recuperati tramite il metodo FindAllAsync (così come quelli recuperati tramite il metodo CreateWatcher discusso nel prossimo paragrafo) incapsulano solo alcune delle informazioni disponibili: l'ID e il nome del device (esposte rispettivamente, dalle proprietà Id e Name della classe DeviceInformation); se la periferica è abilitata (corrispondente alla proprietà IsEnabled); se un certo device rappresenta la periferica di default per determinate operazioni (corrispondente alla proprietà IsDefault), nonché il path per raggiungere il device (mediante la proprietà EnclosureLocation).

Il seguente snippet, ad esempio, cerca di recuperare una serie di informazioni aggiuntive, come il friendly name della periferica, il produttore, il nome e il numero del modello (tieni presente che queste informazioni potrebbero non essere disponibili per tutte le periferiche):

function deviceEnumeration_click() {
	var selector = "System.Devices.InterfaceClassGuid:=\"{0ECEF634-6EF0-472A-8085-5AD023ECBCCD}\"";
	var properties = new Array(
		"System.Devices.FriendlyName",
		"System.Devices.Manufacturer",
		"System.Devices.ModelName",
		"System.Devices.ModelNumber");
	Windows.Devices.Enumeration.DeviceInformation.findAllAsync(selector, properties)
		.done(function (devCollection) {
			var numDevices = devCollection.length;
			for (var i = 0; i < numDevices; i++) {
				displayDeviceInterface(devCollection[i], i);
			}
		},
		function (err) {
			// ...
		});
}

Le informazioni aggiuntive vengono incapsulate all'interno della proprietà Properties della classe DeviceInformation. Per l'elenco completo delle proprietà che possono essere recuperate tramite questo overload del metodo FindAllAsync (ovvero del metodo CreateWatcher) si rinvia alla documentazione ufficiale su MSDN.

Reagire alle modifiche nell'elenco delle periferiche disponibili tramite la classe DeviceWatcher

La classe DeviceWatcher è responsabile per recuperare l'elenco delle periferiche in modo dinamico: dopo l'enumerazione iniziale, infatti, ogni volta che un device viene aggiunto, rimosso o modificato, vengono scatenati specifici eventi per segnalare che qualcosa è cambiato.

Gli eventi esposti dalla classe DeviceWatcher sono i seguenti:

Evento Trigger
Added quando un nuovo device viene aggiunto alla collezione
EnumerationCompleted quando l'enumerazione iniziale delle periferiche è stato completato
Removed quando un device è rimosso dalla collezione
Stopped quando l'enumerazione viene interrotta
Updated quando un device della collezione viene aggiornato

Per enumerare dinamicamente le periferiche disponibili, è necessario per prima cosa ottenere una reference a un oggetto di tipo DeviceWatcher tramite il metodo statico CreateWatcher della classe DeviceInformation.

La seconda cosa da fare è abbonarsi ai vari eventi a cui l'applicazione è interessata per essere notificati su qualunque cambiamento nell'elenco dei dispositivi. A questo punto, per avviare la ricerca iniziale dei device presenti sul sistema è sufficiente invocare il metodo Start della classe DeviceWatcher.

Vale la pena ricordare che il metodo CreateWatcher presenta anche tre overload che permettono di specificare i criteri di ricerca dei device. Questi overload accettano gli stessi parametri già visti quando abbiamo discusso il metodo FindAllAsync. Per l'uso dell'Advanced Query Syntax e per il recupero delle informazioni addizionali si rinvia pertanto al precedente paragrafo.

Il seguente snippet mostra un esempio di utilizzo della classe DeviceWatcher che recupera l'elenco dei dispositivi di storage rimovibili attualmente connessi al sistema e notifica all'utente ogni successiva modifica all'enumerazione iniziale.

var watcher;
var counter = 0;
app.onloaded = function (args) {
	btnStartWatcher.addEventListener("click", startWatcher_click);
	btnStopWatcher.addEventListener("click", stopWatcher_click);
	watcher = new Windows.Devices.Enumeration.DeviceInformation.
		createWatcher(Windows.Devices.Enumeration.DeviceClass.portableStorageDevice);
	watcher.onadded = watcher_added;
	watcher.onupdated = watcher_updated;
	watcher.onremoved = watcher_removed;
	watcher.onenumerationcompleted = watcher_enumerationCompleted;
};
function startWatcher_click(args) {
	if (watcher.status != Windows.Devices.Enumeration.DeviceWatcherStatus.started && watcher.status != Windows.Devices.Enumeration.DeviceWatcherStatus.stopping) {
		try {
			watcher.start();
			watcherStatus.innerHTML += "<p>Device Watcher avviato.</p>";
		} catch (e) {
			// ...
		}
	}
}
function watcher_enumerationCompleted(args) {
	watcherStatus.innerHTML += "<p>Enumerazione completata.</p>";
	deviceCounter.innerHTML = "<p>" + counter + " device presenti sul tuo sistema.</p>";
}
function watcher_added(args) {
	if (args != null) {
		var name = args.properties["System.ItemNameDisplay"];
		counter++;
		watcherStatus.innerHTML += "<p>E' stato aggiunto il seguente device: " + name + "</p>";
		deviceCounter.innerHTML = "<p>" + counter + " device presenti sul tuo sistema.</p>";
	}
}
function watcher_updated(args) {
	watcherStatus.innerHTML += "<p>Un device è stato aggiornato.</p>";
}
function watcher_removed(args) {
	counter--;
	watcherStatus.innerHTML += "<p>Un device è stato aggiunto all'elenco.</p>";
	deviceCounter.innerHTML = "<p>" + counter + " device presenti sul tuo sistema.</p>";
}
function stopWatcher_click(args) {
	if (watcher.status != Windows.Devices.Enumeration.DeviceWatcherStatus.stopped && watcher.status != Windows.Devices.Enumeration.DeviceWatcherStatus.stopping) {
		watcher.stop();
		watcherStatus.innerHTML += "<p>Device Watcher fermato.</p>";
	}
}

Durante l'enumerazione inziale, la classe DeviceWatcher scatena l'evento Added ogni volta che un nuovo device viene trovato sul sistema, fino a quando l'elenco non è completo. A questo punto viene scatenato l'evento EnumerationCompleted. A partire da questo momento, la classe DeviceWatcher continuerà a scatenare gli eventi di Added, Removed e Updated ogni volta che un nuovo device è, rispettivamente, aggiunto, rimosso o modificato.

Una volta che l'app non ha più bisogno di ricevere notifiche di eventuali cambiamenti nell'elenco dei device, è sufficiente invocare il metodo Stop per interrompere il monitoraggio ad opera dell'oggetto DeviceWatcher.

Per testare il codice proposto, utilizziamo il seguente markup HTML come riferimento per la pagina di default dell'applicazione (default.html):

<!DOCTYPE html>
<html>
<head>
	<meta charset="utf-8" />
	<title>Demo.Html.it.EnumeratingDeviceSample.JS</title>
	<!-- WinJS references -->
	<link href="//Microsoft.WinJS.1.0/css/ui-dark.css" rel="stylesheet" />
	<script src="//Microsoft.WinJS.1.0/js/base.js"></script>
	<script src="//Microsoft.WinJS.1.0/js/ui.js"></script>
	<!-- Demo.Html.it.EnumeratingDeviceSample.JS references -->
	<link href="/css/default.css" rel="stylesheet" />
	<script src="/js/default.js"></script>
</head>
<body>
	<div id="content">
		<div id="buttonRow">
			<button id="btnStartWatcher">Start Watcher </button>
			<button id="btnStopWatcher">Stop Watcher</button>
		</div>
		<div id="deviceCounter"></div>
		<div id="watcherStatus"></div>
	</div>
</body>
</html>

La prossima immagine mostra come l'applicazione usata in questo articolo ai cambiamenti nella collezione di device di storage rimovibili presenti sul sistema.

Vale la pena di osservare come il codice sopra riportato utilizzi l'enum DeviceWatcherStatus per controllare lo stato del DeviceWatcher prima di avviare o interrompere le relative operazioni.

L'enum DeviceWatcherStatus può assumere uno dei seguenti valori:

Valore Descrizione
Created È lo stato iniziale dell'oggetto DeviceWatcher non appena instanziato. Durante questo stato, il client può registrare gli handler dei vari eventi.
Started Dopo che il metodo Start è stato invocato, il DeviceWatcher inizia a enumerare le periferiche connesse.
EnumerationCompleted Il DeviceWatcher ha completato l'enumerazione iniziale. Nuovi device possono essere aggiunti, modificati o rimossi dall'elenco.
Stopping Il metodo Stop è stato invocato dal client e il DeviceWatcher è in procinto di fermarsi. Nuovi eventi possono essere scatenati durante questo stato.
Stopped Il DeviceWatcher ha completato le sue operazioni. Nessun nuovo evento verrà sollevato al cambiare dell'elenco.
Aborted Il DeviceWatcher è stato interrotto dal sistema. Anche in questo caso non verranno scatenati nuovi eventi.

Il metodo Start non può essere invocato quando il DeviceWatcher è in stato di Started o Stopping. Il metodo Stop, dall'altro lato, scatena l'evento Stopped e il DeviceWatcher passa nell'evento di Stopping. Lo stato di Stopped verrà raggiunto solo dopo che tutti gli eventi già sollevati durante il processo sono stati completati.

La seguente immagine, presa dalla documentazione ufficiale su MSDN, mostra le transizioni tra i differenti stati del DeviceWatcher:

Enumerare i device Plug and Play (Pnp)

Il namespace Windows.Devices.Enumeration.PnP consente di enumerare non soltanto i device, ma anche le device interface, le device interface class, e i device container. Un device interface rappresenta un link simbolico verso un device Pnp che l'applicazione può usare per accedere al device stesso. Una device interface class è invece un'interfaccia che espone le funzionalità interne di ogni tipologia di device. Un device container, infine, rappresenta il device fisico vero e proprio e permette di accedere a informazioni interne del device (anziché alle sole funzionalità esposte tramite interfaccia), come il produttore o il nome del modello.

In particolare, il namespace Windows.Devices.Enumeration.PnP mette a disposizione la classe PnpObject per esporre una serie di informazioni relative alle varie periferiche presenti sul sistema, in modo simile a quanto già visto quando abbiamo discusso la classe DeviceInformation. Ad esempio, anche la classe PnpObject espone due metodi, FindAllAsync e CreateWatcher, già visti nei paragrafi precedenti. In questo caso, tuttavia, i due metodi accettano (oltre alla stringa AQS e l'indicazione delle informazioni da recuperare) un oggetto di tipo PnpObjectType, un enum che indica quale tipo di "oggetto" stiamo cercando.

Questo enum può assumere uno dei seguenti valori:

Valore Descrizione
Unknown Indica un oggetto di tipo sconosciuto. Questo valore non è solitamente usato.
DeviceInterface Indica di cercare tra le device interface.
DeviceContainer Indica di cercare tra i device container.
DeviceInterfaceClass Indica di cercare tra i device interface class.

Il prossimo estratto mostra un esempio di utilizzo della classe PnpObject che sfrutta il metodo FindAllAsync per recuperare (sotto forma di collezione di oggetti di tipo PnpObject) tutti i device container presenti sul sistema, assieme ad una serie di informazioni aggiuntive, come il friendly name, il numero del modello, il produttore e l'indicazione se la periferica è connessa o meno.

function deviceEnumeration_click() {
	var properties = new Array(
		"System.Devices.FriendlyName",
		"System.Devices.ModelName",
		"System.Devices.Manufacturer",
		"System.Devices.Connected");
	Windows.Devices.Enumeration.Pnp.PnpObject.findAllAsync(Windows.Devices.Enumeration.Pnp.PnpObjectType.deviceContainer, properties)
		.done(doCompleted, doError);
}

Come abbiamo detto, la classe PnpObject mette a disposizione anche la possibilità di utilizzare un oggetto di tipo PnpDeviceWatcher (sostanzialmente identico a quello rappresentato dalla classe DeviceWatcher vista in precedenza) per tenere sotto controllo ogni modifica all'enumerazione iniziale. Il prossimo snippet mostra il tipico pattern già incontrato con la classe DeviceWatcher.

var watcher;
app.onloaded = function (args) {
	btnStartWatcher.addEventListener("click", startWatcher_click);
	btnStopWatcher.addEventListener("click", stopWatcher_click);
	var properties = new Array(
		"System.ItemNameDisplay",
		"System.Devices.ModelName",
		"System.Devices.Connected",
		"System.Devices.FriendlyName",
		"System.Devices.Manufacturer",
		"System.Devices.ModelNumber");
	watcher = new Windows.Devices.Enumeration.Pnp.PnpObject.createWatcher( Windows.Devices.Enumeration.DeviceClass.portableStorageDevice, properties);
	watcher.onadded = watcher_added;
	watcher.onupdated = watcher_updated;
	watcher.onremoved = watcher_removed;
	watcher.onenumerationcompleted = watcher_enumerationCompleted;
}

Ti consigliamo anche