Finalmente, durante queste vacanze di Natale, ho trovato del tempo da dedicare ad un progetto che mi rimbalzava in testa già da un po'... Ovvero una liberia che fornisse funzionalità UPnP alle applicazioni .NET. In giro avevo già trovato qualcosa del genere, ma niente che rispondesse in pieno alle mie esigenze. In particolare, quello di cui avevo bisogno erano dei metodi per realizzare sistemi di NAT traversal (che dovrebbero essere alla base di un'altra applicazione che sto progettando... Ma questa è un'altra storia
).
E' così nato il progetto SharpUPnP, che nei giorni scorsi ho pubblicato su CodePlex. Al momento è ancora allo stato iniziale: ho definito la procedura per identificare le periferiche UPnP compatibili col NAT presenti nella rete e il metodo per creare un port mapping tra una porta "pubblica" del router e quella di un client all'interno della rete. Nelle mie intenzioni, a breve vorrei completare almeno tutta la parte relativa al NAT traversal, quindi aggiungere anche le funzioni per recuperare l'indirizzo IP pubblico del router, rimuovere un port mapping e ottenere la lista dei mapping attivi.
Come ormai è tradizione, concludo il mio post invitando chiunque sia interessato al progetto a contattarmi
.
Il .NET Framework 2.0 offre due classi che consentono di sapere se una connessione di rete è disponibile e di ricevere notifiche quando lo stato della connessione cambia: si tratta rispettivamente di NetworkInterface e NetworkChange, entrambe contenute nel namespace System.Net.NetworkInformation. In particolare, NetworkChange è una classe statica che espone un metoodo di nome NetworkAvailabilityChanged, che viene generato quando lo stato della connessione cambia da non disponibile a disponibile, e viceversa.
Il problema di questa classe (se così si può chiamare) è che l'evento NetworkAvailabilityChanged viene generato in un thread secondario, quindi se si cerca di aggiornare l'interfaccia utente nell'handler di tale evento si ottiene un'eccezione causata dal tentativo di operazione cross-thread. Come è risaputo, in una situazione del genere è necessario utilizzare il metodo Invoke, così da garantire un accesso thread-safe ai controlli.
Come ho già scritto in questo post, è possibile evitare tale approccio utilizzando gli oggetti AsyncOperation e SendOrPostCallback. Non ci vuole molto per adattare il codice che avevo presentato in quell'occasione al nuovo contesto:
1 using System;
2 using System.ComponentModel;
3 using System.Threading;
4
5 namespace System.Net.NetworkInformation
6 {
7 public static class NetworkChange2
8 {
9 private static AsyncOperation operation;
10 private static SendOrPostCallback callback;
11
12 public static event NetworkAvailabilityChangedEventHandler
13 NetworkAvailabilityChanged;
14
15 static NetworkChange2()
16 {
17 callback = new SendOrPostCallback(Handler);
18 operation = AsyncOperationManager.CreateOperation(null);
19 NetworkChange.NetworkAvailabilityChanged +=
20 new NetworkAvailabilityChangedEventHandler
21 (NetworkChange_NetworkAvailabilityChanged);
22 }
23
24 public static bool IsNetworkAvailable()
25 {
26 return NetworkInterface.GetIsNetworkAvailable();
27 }
28
29 private static void NetworkChange_NetworkAvailabilityChanged
30 (object sender, NetworkAvailabilityEventArgs e)
31 {
32 operation.Post(callback, e);
33 }
34
35 private static void Handler(object stateInfo)
36 {
37 if (NetworkAvailabilityChanged != null)
38 NetworkAvailabilityChanged(null,
39 (NetworkAvailabilityEventArgs)stateInfo);
40 }
41 }
42 }
La classe NetworkChange2 non è altro che un wrapper per l'oggetto NetworkChange: quando quest'ultimo genera l'evento NetworkAvailabilityChanged, il suo gestore (righe 26-29) fa in modo di rilanciarlo all'interno del thread principale, attraverso il metodo Handler (righe 31-35).
L'utilizzo di questa nuova classe è immediato; è sufficiente cambiare le dichiarazioni come la seguente:
NetworkChange.NetworkAvailabilityChanged +=
new NetworkAvailabilityChangedEventHandler
(NetworkChange_NetworkAvailabilityChanged);
Utilizzando invece NetworkChange2:
NetworkChange2.NetworkAvailabilityChanged +=
new NetworkAvailabilityChangedEventHandler
(NetworkChange_NetworkAvailabilityChanged);
Fatto questo, all'interno del metodo NetworkChange_NetworkAvailabilityChanged sarà possibile accedere direttamente agli oggetti dell'interfaccia senza incorrere nell'errore InvalidOperationException.
Technorati tags:
.NET,
Programming,
CS