WlanApi.cs 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Runtime.InteropServices;
  4. using Vinno.FIS.Sonopost.Wireless.Win32.Helpers;
  5. using Vinno.FIS.Sonopost.Wireless.Win32.Interop;
  6. namespace Vinno.FIS.Sonopost.Wireless.Win32
  7. {
  8. /// <summary>
  9. /// Represents a client to the Zeroconf (Native Wifi) service.
  10. /// </summary>
  11. /// <remarks>
  12. /// This class is the entrypoint to Native Wifi management. To manage WiFi settings, create an instance
  13. /// of this class.11
  14. /// </remarks>
  15. public class WlanClient
  16. {
  17. private const int NO_WIFI = 1062;
  18. private readonly Dictionary<Guid, WlanInterface> ifaces = new Dictionary<Guid, WlanInterface>();
  19. internal IntPtr clientHandle;
  20. internal uint negotiatedVersion;
  21. internal WlanInterop.WlanNotificationCallbackDelegate wlanNotificationCallback;
  22. public bool NoWifiAvailable = false;
  23. /// <summary>
  24. /// Creates a new instance of a Native Wifi service client.
  25. /// Throws Win32 errors: ERROR_INVALID_PARAMETER, ERROR_NOT_ENOUGH_MEMORY, RPC_STATUS, ERROR_REMOTE_SESSION_LIMIT_EXCEEDED.
  26. /// </summary>
  27. public WlanClient()
  28. {
  29. int errorCode = 0;
  30. OperatingSystem osInfo = Environment.OSVersion;
  31. bool isWinXP =
  32. osInfo.Platform == PlatformID.Win32NT &&
  33. osInfo.Version.Major == 5 &&
  34. osInfo.Version.Minor != 0;
  35. if (isWinXP && osInfo.ServicePack == "Service Pack 1") // wlanapi not supported in sp1 (or sp2 without hotfix)
  36. {
  37. errorCode = NO_WIFI;
  38. }
  39. else
  40. {
  41. // Perform exception safe init
  42. // It can be SP2 without hotfix which would generate exception
  43. try
  44. {
  45. errorCode = WlanInterop.WlanOpenHandle(WlanInterop.WLAN_CLIENT_VERSION_LONGHORN, IntPtr.Zero, out negotiatedVersion, out clientHandle);
  46. }
  47. catch
  48. {
  49. errorCode = NO_WIFI;
  50. }
  51. }
  52. if (errorCode != 0)
  53. {
  54. NoWifiAvailable = true;
  55. return;
  56. }
  57. // 1062 = no wifi
  58. // OK!
  59. // WlanInterop.ThrowIfError(errorCode);
  60. try
  61. {
  62. // Interop callback
  63. wlanNotificationCallback = new WlanInterop.WlanNotificationCallbackDelegate(OnWlanNotification);
  64. WlanNotificationSource prevSrc;
  65. WlanInterop.ThrowIfError(WlanInterop.WlanRegisterNotification(clientHandle, WlanNotificationSource.All, false, wlanNotificationCallback, IntPtr.Zero, IntPtr.Zero, out prevSrc));
  66. }
  67. catch
  68. {
  69. WlanInterop.WlanCloseHandle(clientHandle, IntPtr.Zero);
  70. throw;
  71. }
  72. }
  73. ~WlanClient()
  74. {
  75. // Free the handle when deconstructing the client. There won't be a handle if its xp sp 2 without wlanapi installed
  76. try
  77. {
  78. WlanInterop.WlanCloseHandle(clientHandle, IntPtr.Zero);
  79. }
  80. catch
  81. { }
  82. }
  83. // Called from interop
  84. private void OnWlanNotification(ref WlanNotificationData notifyData, IntPtr context)
  85. {
  86. if (NoWifiAvailable)
  87. return;
  88. WlanInterface wlanIface = ifaces.ContainsKey(notifyData.interfaceGuid) ? ifaces[notifyData.interfaceGuid] : null;
  89. switch (notifyData.notificationSource)
  90. {
  91. case WlanNotificationSource.ACM:
  92. switch ((WlanNotificationCodeAcm)notifyData.notificationCode)
  93. {
  94. case WlanNotificationCodeAcm.ConnectionStart:
  95. case WlanNotificationCodeAcm.ConnectionComplete:
  96. case WlanNotificationCodeAcm.ConnectionAttemptFail:
  97. case WlanNotificationCodeAcm.Disconnecting:
  98. case WlanNotificationCodeAcm.Disconnected:
  99. WlanConnectionNotificationData? connNotifyData = WlanHelpers.ParseWlanConnectionNotification(ref notifyData);
  100. if (connNotifyData.HasValue && wlanIface != null)
  101. wlanIface.OnWlanConnection(notifyData, connNotifyData.Value);
  102. break;
  103. case WlanNotificationCodeAcm.ScanFail:
  104. int expectedSize = Marshal.SizeOf(typeof(int));
  105. if (notifyData.dataSize >= expectedSize)
  106. {
  107. int reasonInt = Marshal.ReadInt32(notifyData.dataPtr);
  108. // Want to make sure this doesn't crash if windows sends a reasoncode not defined in the enum.
  109. if (Enum.IsDefined(typeof(WlanReasonCode), reasonInt))
  110. {
  111. WlanReasonCode reasonCode = (WlanReasonCode)reasonInt;
  112. if (wlanIface != null)
  113. wlanIface.OnWlanReason(notifyData, reasonCode);
  114. }
  115. }
  116. break;
  117. }
  118. break;
  119. case WlanNotificationSource.MSM:
  120. switch ((WlanNotificationCodeMsm)notifyData.notificationCode)
  121. {
  122. case WlanNotificationCodeMsm.Associating:
  123. case WlanNotificationCodeMsm.Associated:
  124. case WlanNotificationCodeMsm.Authenticating:
  125. case WlanNotificationCodeMsm.Connected:
  126. case WlanNotificationCodeMsm.RoamingStart:
  127. case WlanNotificationCodeMsm.RoamingEnd:
  128. case WlanNotificationCodeMsm.Disassociating:
  129. case WlanNotificationCodeMsm.Disconnected:
  130. case WlanNotificationCodeMsm.PeerJoin:
  131. case WlanNotificationCodeMsm.PeerLeave:
  132. case WlanNotificationCodeMsm.AdapterRemoval:
  133. WlanConnectionNotificationData? connNotifyData = WlanHelpers.ParseWlanConnectionNotification(ref notifyData);
  134. if (connNotifyData.HasValue && wlanIface != null)
  135. wlanIface.OnWlanConnection(notifyData, connNotifyData.Value);
  136. break;
  137. }
  138. break;
  139. }
  140. if (wlanIface != null)
  141. wlanIface.OnWlanNotification(notifyData);
  142. }
  143. /// <summary>
  144. /// Gets the WLAN interfaces.
  145. ///
  146. /// Possible Win32 exceptions:
  147. ///
  148. /// ERROR_INVALID_PARAMETER: A parameter is incorrect. This error is returned if the hClientHandle or ppInterfaceList parameter is NULL. This error is returned if the pReserved is not NULL. This error is also returned if the hClientHandle parameter is not valid.
  149. /// ERROR_INVALID_HANDLE: The handle hClientHandle was not found in the handle table.
  150. /// RPC_STATUS: Various error codes.
  151. /// ERROR_NOT_ENOUGH_MEMORY: Not enough memory is available to process this request and allocate memory for the query results.
  152. /// </summary>
  153. /// <value>The WLAN interfaces.</value>
  154. public WlanInterface[] Interfaces
  155. {
  156. get
  157. {
  158. if (NoWifiAvailable)
  159. return null;
  160. IntPtr ifaceList;
  161. WlanInterop.ThrowIfError(WlanInterop.WlanEnumInterfaces(clientHandle, IntPtr.Zero, out ifaceList));
  162. try
  163. {
  164. WlanInterfaceInfoListHeader header = (WlanInterfaceInfoListHeader)Marshal.PtrToStructure(ifaceList, typeof(WlanInterfaceInfoListHeader));
  165. Int64 listIterator = ifaceList.ToInt64() + Marshal.SizeOf(header);
  166. WlanInterface[] interfaces = new WlanInterface[header.numberOfItems];
  167. List<Guid> currentIfaceGuids = new List<Guid>();
  168. for (int i = 0; i < header.numberOfItems; ++i)
  169. {
  170. WlanInterfaceInfo info = (WlanInterfaceInfo)Marshal.PtrToStructure(new IntPtr(listIterator), typeof(WlanInterfaceInfo));
  171. listIterator += Marshal.SizeOf(info);
  172. currentIfaceGuids.Add(info.interfaceGuid);
  173. WlanInterface wlanIface;
  174. if (ifaces.ContainsKey(info.interfaceGuid))
  175. wlanIface = ifaces[info.interfaceGuid];
  176. else
  177. wlanIface = new WlanInterface(this, info);
  178. interfaces[i] = wlanIface;
  179. ifaces[info.interfaceGuid] = wlanIface;
  180. }
  181. // Remove stale interfaces
  182. Queue<Guid> deadIfacesGuids = new Queue<Guid>();
  183. foreach (Guid ifaceGuid in ifaces.Keys)
  184. {
  185. if (!currentIfaceGuids.Contains(ifaceGuid))
  186. deadIfacesGuids.Enqueue(ifaceGuid);
  187. }
  188. while (deadIfacesGuids.Count != 0)
  189. {
  190. Guid deadIfaceGuid = deadIfacesGuids.Dequeue();
  191. ifaces.Remove(deadIfaceGuid);
  192. }
  193. return interfaces;
  194. }
  195. finally
  196. {
  197. WlanInterop.WlanFreeMemory(ifaceList);
  198. }
  199. }
  200. }
  201. }
  202. }