Windows Vista a apporté pas mal de choses au niveau de la gestion des réseaux, notamment grâce au “Centre Réseau et Partage”. Windows reconnait automatiquement le réseau sur lequel vous le connecter et configure la sécurité en fonction du type d’environnement que vous lui précisez.
Cependant, il est parfois intéressant de pouvoir exploiter ces informations au sein d’une application .net pour automatiser à notre tour des tâches : configurer l’imprimante par défaut, les montages réseaux…
Voici donc comment je m’y suis pris pour récupérer ces informations.
Existe-t-il une API ?
Ma première recherche s'est tournée vers les APIs : après tout si Microsoft utilise cette fonctionnalité dans son OS, c'est que derrière, ils ont développés une API ! Et bien, soit j'ai mal cherché... soit elle n'existe pas !
Si vous avez des infos là dessus, contactez moi !
La base de registre ?
Là par contre, ça devient intéressant ; dans la base de registre se trouve une branche intéressante : HKLM\Software\Microsoft\Windows NT\CurrentVersion\NetworkList. Elle contient la liste complète des réseaux auxquels vous avez connecté votre machine ! Et elle peut être longue, pour moi, on y trouve pas loin de 40 références ! Entre les hotspots gratuits, les réseaux d'entreprises, la maison..!
Maintenant que l'on possède la liste des réseaux, il faut être capable de l'exploiter et surtout de faire le lien entre le réseau courant et l'un de ceux enregistrer... en gros, comprenez : retrouver les informations sur le réseau auquel votre machine est connecté. Pour cela, la signature des réseaux contient l'adresse MAC de la passerelle... Un bon moyen d'identifier le réseau !
Maintenant que nous avons tous les éléments, voyons comment nous allons procéder :
- Récupération de la liste des réseaux depuis la base de registre
- Identification du réseau sur lequel on se trouve
- Matching entre le réseau identifié et la liste des réseaux
Récupération de la liste des réseaux : lecture de la base de registre
ATTENTION : Cette branche n'est accessible que par les profiles administrateurs.
Commençons par créer une classe qui contiendra les informations de nos réseaux :
1: public class NetworkInformation {
2: private Guid guid;
3: public string Description {get; internal set;}
4: public string FirstNetwork {get; internal set;}
5: public Guid ProfileGuid {get; internal set;}
6: public byte[] DefaultGateway {get; internal set;}
7: public int Source {get; internal set;}
8: public int CategoryType {get; internal set;}
9: public DateTime DateCreated {get; internal set;}
10: public DateTime DateLastConnected {get; internal set;}
11: public ProfileDescription {get; internal set;}
12: public int IconType {get; internal set;}
13: public bool Managed {get; internal set;}
14: public int NameType {get; internal set;}
15: public string ProfileName {get; internal set;}
16: }
Puis, recherchons les informations dans la base de registre :
1: private static string baseKey = @"SOFTWARE\Microsoft\Windows NT\CurrentVersion\NetworkList";
2:
3: public static List<NetworkInformation> GetNetworkList() {
4: List<NetworkInformation> ret = new List<NetworkInformation>();
5: RegistryKey sKey;
6:
7: RegistryKey key = Registry.LocalMachine.OpenSubKey(baseKey + @"\Signatures\Managed");
8: foreach (string name in key.GetSubKeyNames()) {
9: sKey = key.OpenSubKey(name);
10: // Récupération de la signature des réseaux Managés
11: ret.Add(GetNetworkSignature(sKey));
12: }
13:
14: key = Registry.LocalMachine.OpenSubKey(baseKey + @"\Signatures\Unmanaged");
15: foreach (string name in key.GetSubKeyNames())
16: {
17: sKey = key.OpenSubKey(name);
18: // Récupération de la signature des réseaux non Managés
19: ret.Add(GetNetworkSignature(sKey));
20: }
21:
22: foreach(NetworkInformation ni in ret) {
23: sKey = Registry.LocalMachine.OpenSubKey(string.Concat(baseKey, @"\Profiles\", ni.guid.ToString("B")));
24: if (sKey != null) {
25: ni.Category = (int)sKey.GetValue("Category", 0);
26: ni.CategoryType = (int)sKey.GetValue("CategoryType", 0);
27: // ni.DateCreated = DateTime.FromBinary(BitConverter.ToInt64((byte[])key.GetValue(name), 0));
28: // ni.DateLastConnected = DateTime.FromBinary(BitConverter.ToInt64((byte[])key.GetValue(name), 0));
29: ni.ProfileDescription = (string)sKey.GetValue("Description", string.Empty);
30: ni.IconType = (int)sKey.GetValue("IconType", 0);
31: ni.Managed = (int)sKey.GetValue("Managed", 0) == 1;
32: ni.NameType = (int)sKey.GetValue("NameType", 0);
33: ni.ProfileName = (string)sKey.GetValue("ProfileName", string.Empty);
34: }
35: }
36: return ret;
37: }
38:
39: private static NetworkInformation GetNetworkSignature(RegistryKey sKey)
40: {
41: NetworkInformation ni = new NetworkInformation();
42: ni.FirstNetwork = (string)sKey.GetValue("FirstNetwork");
43: ni.DefaultGetawayMac = (byte[])sKey.GetValue("DefaultGatewayMac");
44: ni.Description = (string)sKey.GetValue("Description", string.Empty);
45: ni.DnsSuffix = (string)sKey.GetValue("DnsSuffix", string.Empty);
46: ni.Source = (int)sKey.GetValue("Source", 0);
47: ni.guid = new Guid((string)sKey.GetValue("ProfileGuid"));
48: return ni;
49: }
Nous possèdons maintenant l'ensemble des informations sur les réseaux auxquels nous avons accédés. Nous allons maintenant voir comment récupérer les informations sur le réseau courant.
Identification du réseau courant
Comme je l'ai écrit ci-dessus, pour identifier le réseau courant dans la liste que nous venons de récupérer, il va falloir que nous récupérions l'adresse MAC de la passerelle. Nous allons nous y prendre en 2 temps :
- Récupération de l'adresse IP
- Récupération de l'identifiant unique
Récupération de l'adresse IP
Le framework étant bien fait dans son ensemble, on y trouve une foultitude de classes très sympa... et NetworkInterface de l'espace de noms System.Net.NetworkInformation fait partie de celles-là. Grace à elle, il est possible de savoir si votre machine est raccordée à un réseau et de connaître (en autres choses) la liste des cartes réseaux. Nous allons donc récupérer la première interface connectée à un réseau et qui possède une passerelle.
1: public static IPAddress GetDefaultGatewayIP() {
2: if (!NetworkInterface.GetIsNetworkAvailable()) {
3: return IPAddress.None;
4: }
5:
6: List<NetworkInterface> nics = NetworkInterface.GetAllNetworkInterfaces().Where(nic =>
7: nic.NetworkInterfaceType != NetworkInterfaceType.Loopback &&
8: nic.NetworkInterfaceType != NetworkInterfaceType.Unknown &&
9: nic.OperationalStatus == OperationalStatus.Up).ToList();
10:
11: if (nics.Count > 0) {
12: // on récupère la première interface trouvée...
13: // on verra après si on trouve mieux
14: GatewayIPAddressInformationCollection gateways = nics[0].GetIPProperties().GatewayAddresses;
15: if (gateways.Count > 0) {
16: return gateways[0].Address;
17: }
18: }
19: return IPAddress.None;
20: }
Récupération de l'identifiant unique
Cette étape est plus délicate que la précédente... en effet, le framework ne possède aucune méthode pour trouver l'adresse MAC. Il faut donc en passer par une API : SendARP.
1: internal class NetworkNativeMethods
2: {
3: [DllImport("iphlpapi.dll", ExactSpelling = true)]
4: public static extern int SendARP(int DestIP, int SrcIP, byte[] pMacAddr, ref uint PhyAddrLen);
5: }
6:
7: public static byte[] GetPhysicalAddressFromIP(IPAddress ip) {
8: byte[] macAddr = new byte[ 6 ];
9: uint macAddrLen = (uint)macAddr.Length;
10:
11: if (NetworkNativeMethods.SendARP(BitConverter.ToInt32(ip.GetAddressBytes(),0), 0, macAddr, ref macAddrLen) != 0) {
12: throw new InvalidOperationException("SendARP failed.");
13: }
14:
15: return macAddr;
16: }
Conclusion
Voila... nous avons la liste des réseaux et l'adresse MAC de notre passerelle, il ne reste plus qu'à afficher les informations sur le réseau courant :
1: List<NetworkInformation> networksInformations = NetworkHelp.GetNetworkList();
2:
3: networksInformations.Sort((ni1, ni2) => ni1.ProfileName.CompareTo(ni2.ProfileName));
4: comboBox1.DataSource = networksInformations;
5: comboBox1.DisplayMember = "ProfileName";
6:
7: if (SystemInformation.Network)
8: {
9: IPAddress gatewayAddress = NetworkHelper.GetDefaultGatewayIP();
10: if (gatewayAddress != IPAddress.None)
11: {
12: byte[] gatewayMac = NetworkHelper.GetPhysicalAddressFromIP(gatewayAddress);
13:
14: NetworkInformation ni = this.networksInformations.Find(n => Convert.ToBase64String(n.DefaultGetawayMac).Equals(Convert.ToBase64String(gatewayMac)));
15: if (ni != null)
16: {
17: comboBox1.SelectedItem = ni;
18: }
19: }
20:
21: }
Si vous avez une autre solution pour arriver au même résultat, profitez-en pour la partager :)
Cette solution sera mise en ligne sur CSharpFR avant la fin de la semaine, le temps de finaliser la version de test.
Edit : Merci à Olivier pour l'api "Network List Manager" : http://blogs.codes-sources.com/findufin/archive/2008/06/26/vista-api-pour-r-cup-rer-les-informations-sur-le-r-seau-courant.aspx