.NET

Get Unicode SSID of WiFi networks in C#

.NET Framework does not have API that provides detailed information on Wi-Fi networks such as SSIDs. Instead, Native WiFi is available for C++ and it can be used in C# by P/Invoke. There is a managed implementation of this API, Managed Wifi API. However, the development had already ended and its sample code does not expect SSIDs in Unicode string. So I wrote my own implementation focusing on getting SSIDs.

First, the method to get SSIDs of available Wi-Fi networks:

public static IEnumerable<string> GetAvailableNetworkSsids()
{
  var clientHandle = IntPtr.Zero;
  var interfaceList = IntPtr.Zero;
  var availableNetworkList = IntPtr.Zero;

  try
  {
    uint negotiatedVersion;
    if (WlanOpenHandle(
      2, // Client version for Windows Vista and Windows Server 2008
      IntPtr.Zero,
      out negotiatedVersion,
      out clientHandle) != ERROR_SUCCESS)
      yield break;

    if (WlanEnumInterfaces(
      clientHandle,
      IntPtr.Zero,
      out interfaceList) != ERROR_SUCCESS)
      yield break;

    var interfaceInfoList = new WLAN_INTERFACE_INFO_LIST(interfaceList);

    Debug.WriteLine("Interface count: {0}", interfaceInfoList.dwNumberOfItems);

    for (int i = 0; i < interfaceInfoList.dwNumberOfItems; i++)
    {
      var interfaceGuid = interfaceInfoList.InterfaceInfo[i].InterfaceGuid;

      if (WlanGetAvailableNetworkList(
        clientHandle,
        interfaceGuid,
        WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_MANUAL_HIDDEN_PROFILES,
        IntPtr.Zero,
        out availableNetworkList) != ERROR_SUCCESS)
        continue;

      var networkList = new WLAN_AVAILABLE_NETWORK_LIST(availableNetworkList);

      for (int j = 0; j < networkList.dwNumberOfItems; j++)
      {
        var network = networkList.Network[j];

        Debug.WriteLine("Interface: {0}, SSID: {1}, Quality: {2}",
          interfaceInfoList.InterfaceInfo[i].strInterfaceDescription,
          network.dot11Ssid.ToSsidString(),
          network.wlanSignalQuality);

        yield return network.dot11Ssid.ToSsidString();
      }
    }
  }
  finally
  {
    if (availableNetworkList != IntPtr.Zero)
      WlanFreeMemory(availableNetworkList);

    if (interfaceList != IntPtr.Zero)
      WlanFreeMemory(interfaceList);

    if (clientHandle != IntPtr.Zero)
      WlanCloseHandle(clientHandle, IntPtr.Zero);
  }
}

Second, the method to get SSIDs of connected Wi-Fi networks:

public static IEnumerable<string> GetConnectedNetworkSsids()
{
  var clientHandle = IntPtr.Zero;
  var interfaceList = IntPtr.Zero;
  var queryData = IntPtr.Zero;

  try
  {
    uint negotiatedVersion;
    if (WlanOpenHandle(
      2, // Client version for Windows Vista and Windows Server 2008
      IntPtr.Zero,
      out negotiatedVersion,
      out clientHandle) != ERROR_SUCCESS)
      yield break;

    if (WlanEnumInterfaces(
      clientHandle,
      IntPtr.Zero,
      out interfaceList) != ERROR_SUCCESS)
      yield break;

    var interfaceInfoList = new WLAN_INTERFACE_INFO_LIST(interfaceList);

    Debug.WriteLine("Interface count: {0}", interfaceInfoList.dwNumberOfItems);

    for (int i = 0; i < interfaceInfoList.dwNumberOfItems; i++)
    {
      var interfaceGuid = interfaceInfoList.InterfaceInfo[i].InterfaceGuid;

      uint dataSize;
      if (WlanQueryInterface(
        clientHandle,
        interfaceGuid,
        WLAN_INTF_OPCODE.wlan_intf_opcode_current_connection,
        IntPtr.Zero,
        out dataSize,
        ref queryData,
        IntPtr.Zero) != ERROR_SUCCESS) // If not connected to a network, ERROR_INVALID_STATE will be returned.
        continue;

      var connection = (WLAN_CONNECTION_ATTRIBUTES)Marshal.PtrToStructure(queryData, typeof(WLAN_CONNECTION_ATTRIBUTES));
      if (connection.isState != WLAN_INTERFACE_STATE.wlan_interface_state_connected)
        continue;

      var association = connection.wlanAssociationAttributes;

      Debug.WriteLine("Interface: {0}, SSID: {1}, Quality: {2}",
        interfaceInfoList.InterfaceInfo[i].strInterfaceDescription,
        association.dot11Ssid.ToSsidString(),
        association.wlanSignalQuality);

      yield return association.dot11Ssid.ToSsidString();
    }
  }
  finally
  {
    if (queryData != IntPtr.Zero)
      WlanFreeMemory(queryData);

    if (interfaceList != IntPtr.Zero)
      WlanFreeMemory(interfaceList);

    if (clientHandle != IntPtr.Zero)
      WlanCloseHandle(clientHandle, IntPtr.Zero);
  }
}

See repository for complete source code.

The information on SSID will be stored in DOT11_SSID structure. Its equivalent in C# for P/Invoke looks like below:

[StructLayout(LayoutKind.Sequential)]
private struct DOT11_SSID
{
  public uint uSSIDLength;

  [MarshalAs(UnmanagedType.ByValArray, SizeConst = 32)]
  public byte[] ucSSID;

  public byte[] ToSsidBytes()
  {
    return (ucSSID != null)
      ? ucSSID.Take((int)uSSIDLength).ToArray()
      : null;
  }

  public string ToSsidString()
  {
    return (ucSSID != null)
      ? Encoding.UTF8.GetString(ToSsidBytes())
      : null;
  }
}

According to IEEE 802.11 specifications, a SSID is Octet string (byte array) of 0-32 length and it can be UTF-8 string. So, in this structure, ToSsidBytes method returns SSID in byte array and ToSsidString method returns SSID in UTF-8 string.

Advertisements