The Road to Delphi

Delphi – Free Pascal – Oxygene

Detecting Wifi Networks Using Delphi and Native Wifi API

25 Comments

Today we will use the Native Wifi API and Delphi to enumerate all Wifi Networks availables. In this link you can find a translation of the headers.
I wrote this code using these headers. Tested in Delphi 2007 and Windows Vista.

Try this link for a fixed and updated version of the Native Wifi Delphi headers.

{$APPTYPE CONSOLE}

uses
  Windows,
  SysUtils,
  nduWlanAPI   in 'nduWlanAPI.pas',
  nduWlanTypes in 'nduWlanTypes.pas';

function DOT11_AUTH_ALGORITHM_To_String( Dummy :Tndu_DOT11_AUTH_ALGORITHM):AnsiString;
begin
    Result:='';
    case Dummy of
        DOT11_AUTH_ALGO_80211_OPEN          : Result:= '80211_OPEN';
        DOT11_AUTH_ALGO_80211_SHARED_KEY    : Result:= '80211_SHARED_KEY';
        DOT11_AUTH_ALGO_WPA                 : Result:= 'WPA';
        DOT11_AUTH_ALGO_WPA_PSK             : Result:= 'WPA_PSK';
        DOT11_AUTH_ALGO_WPA_NONE            : Result:= 'WPA_NONE';
        DOT11_AUTH_ALGO_RSNA                : Result:= 'RSNA';
        DOT11_AUTH_ALGO_RSNA_PSK            : Result:= 'RSNA_PSK';
        DOT11_AUTH_ALGO_IHV_START           : Result:= 'IHV_START';
        DOT11_AUTH_ALGO_IHV_END             : Result:= 'IHV_END';
    end;
end;

function DOT11_CIPHER_ALGORITHM_To_String( Dummy :Tndu_DOT11_CIPHER_ALGORITHM):AnsiString;
begin
    Result:='';
    case Dummy of
  	DOT11_CIPHER_ALGO_NONE      : Result:= 'NONE';
    DOT11_CIPHER_ALGO_WEP40     : Result:= 'WEP40';
    DOT11_CIPHER_ALGO_TKIP      : Result:= 'TKIP';
    DOT11_CIPHER_ALGO_CCMP      : Result:= 'CCMP';
    DOT11_CIPHER_ALGO_WEP104    : Result:= 'WEP104';
    DOT11_CIPHER_ALGO_WPA_USE_GROUP : Result:= 'WPA_USE_GROUP OR RSN_USE_GROUP';
    //DOT11_CIPHER_ALGO_RSN_USE_GROUP : Result:= 'RSN_USE_GROUP';
    DOT11_CIPHER_ALGO_WEP           : Result:= 'WEP';
    DOT11_CIPHER_ALGO_IHV_START     : Result:= 'IHV_START';
    DOT11_CIPHER_ALGO_IHV_END       : Result:= 'IHV_END';
    end;
end;

procedure Scan();
const
WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES =$00000001;
var
  hClient              : THandle;
  dwVersion            : DWORD;
  ResultInt            : DWORD;
  pInterface           : Pndu_WLAN_INTERFACE_INFO_LIST;
  i                    : Integer;
  j                    : Integer;
  pAvailableNetworkList: Pndu_WLAN_AVAILABLE_NETWORK_LIST;
  pInterfaceGuid       : PGUID;
  SDummy               : AnsiString;
begin
  ResultInt:=WlanOpenHandle(1, nil, @dwVersion, @hClient);
   try
    if  ResultInt<> ERROR_SUCCESS then
    begin
       WriteLn('Error Open CLient'+IntToStr(ResultInt));
       Exit;
    end;

    ResultInt:=WlanEnumInterfaces(hClient, nil, @pInterface);
    if  ResultInt<> ERROR_SUCCESS then
    begin
       WriteLn('Error Enum Interfaces '+IntToStr(ResultInt));
       exit;
    end;

    for i := 0 to pInterface^.dwNumberOfItems - 1 do
    begin
     Writeln('Interface       ' + pInterface^.InterfaceInfo[i].strInterfaceDescription);
     WriteLn('GUID            ' + GUIDToString(pInterface^.InterfaceInfo[i].InterfaceGuid));
     Writeln('');
     pInterfaceGuid:= @pInterface^.InterfaceInfo[pInterface^.dwIndex].InterfaceGuid;

        ResultInt:=WlanGetAvailableNetworkList(hClient,pInterfaceGuid,WLAN_AVAILABLE_NETWORK_INCLUDE_ALL_ADHOC_PROFILES,nil,pAvailableNetworkList);
        if  ResultInt<> ERROR_SUCCESS then
        begin
           WriteLn('Error WlanGetAvailableNetworkList '+IntToStr(ResultInt));
           Exit;
        end;

          for j := 0 to pAvailableNetworkList^.dwNumberOfItems - 1 do
          Begin
             WriteLn(Format('Profile         %s',[WideCharToString(pAvailableNetworkList^.Network[j].strProfileName)]));
             SDummy:=PAnsiChar(@pAvailableNetworkList^.Network[j].dot11Ssid.ucSSID);
             WriteLn(Format('NetworkName     %s',[SDummy]));
             WriteLn(Format('Signal Quality  %d ',[pAvailableNetworkList^.Network[j].wlanSignalQuality])+'%');
             //SDummy := GetEnumName(TypeInfo(Tndu_DOT11_AUTH_ALGORITHM),integer(pAvailableNetworkList^.Network[j].dot11DefaultAuthAlgorithm)) ;
             SDummy:=DOT11_AUTH_ALGORITHM_To_String(pAvailableNetworkList^.Network[j].dot11DefaultAuthAlgorithm);
             WriteLn(Format('Auth Algorithm  %s ',[SDummy]));
             SDummy:=DOT11_CIPHER_ALGORITHM_To_String(pAvailableNetworkList^.Network[j].dot11DefaultCipherAlgorithm);
             WriteLn(Format('Auth Algorithm  %s ',[SDummy]));
             Writeln('');
          End;
    end;
   finally
    WlanCloseHandle(hClient, nil);
   end;
end;
begin
  try
    Scan();
    Readln;
  except
    on E:Exception do
      Writeln(E.Classname, ': ', E.Message);
  end;
end.
ScanWifi Image

ScanWifi Image

Author: Rodrigo

Just another Delphi guy.

25 thoughts on “Detecting Wifi Networks Using Delphi and Native Wifi API

  1. Two of us tested this code on 2 different pc’s with Delphi 2007 and Delphi 2010. One pc was in Norway, the other in the United States.

    The code compiles and functions well on both systems… except Delphi 2010 reports 6 warnings:
    [DCC Warning] nduWlanTypes.pas(38): W1012 Constant expression violates subrange bounds
    [DCC Warning] nduWlanTypes.pas(39): W1012 Constant expression violates subrange bounds
    [DCC Warning] nduWlanTypes.pas(62): W1012 Constant expression violates subrange bounds
    [DCC Warning] nduWlanTypes.pas(63): W1012 Constant expression violates subrange bounds
    [DCC Warning] nduWinDot11.pas(32): W1012 Constant expression violates subrange bounds
    [DCC Warning] nduWinDot11.pas(33): W1012 Constant expression violates subrange bounds

    Thank-you for writing the post.

  2. Works fine under Delphi 7 in France XP SP3 / INTEL 5300 AGN

    Would be nice to also get channel number !

  3. Pingback: All about WiFi Networks and WiFi Adapters using the WMI and Delphi « The Road to Delphi – a Blog about programming

  4. Hi,
    I’m trying to download the headers but link is broken… Where can i download it?

    Thank you, and great job!!!

  5. Nice example!!!
    But how to use a WlanGetNetworkBssList???? Dont’t work… help me, please…

  6. Hi, great work, but the SSID returns non readable on Windows 7 64bit, I dont have an XP machine to test at the moment, I found an article relating to this issue in VB but the header translation needs updating http://www.vbforums.com/showthread.php?547916-List-available-wireless-networks-(using-WMI)-Help-pls

    Hope you can help

  7. Hi, great work on the API however there seems to be an issue with the headers I think MS changed the output!, the SSID returns unreadable on windows 7 64bit, I understand this is due to a change in the format of the returned value, I found this related article http://www.vbforums.com/showthread.php?547916-List-available-wireless-networks-(using-WMI)-Help-pls hopefully you will be able to update the header conversation.

    I am using windows 7 64bit and delphi 2010

    Many thanks

  8. Hi, thought I provide an update I changed line 88 to

    SDummy:=AnsiChar(@pAvailableNetworkList^.Network[j].dot11Ssid.ucSSID);

    And it is now returning the correct SSID name.

    • The problem was related to the UNICODE strings, the original post was made using Delphi 2007 which is not UNICODE. I just fix the sample code. Thanks for the report.

  9. Hi Again, is there a method to display the AP MAC Address in the output?

  10. Is there a way to check if one of the connections is active?

    • You must use the WlanQueryInterface method and check if the value of the iState field is wlan_interface_state_connected. for a sample check the MSDN documentation.

      • Hey, Rodrigo.

        It’s working.

        Here is how I used it
        ResultInt := WlanQueryInterface(hClient, pInterfaceGuid,
        wlan_intf_opcode_interface_state, nil, @pdwDataSize,
        @ppData, nil);if (ResultInt = ERROR_SUCCESS) then
        if ResultInt = ERROR_SUCESS then
        WriteLn(Format(‘Status: %s’, [
        WLAN_INTERFACE_STATE_To_String(ppData^.isState)]))

        the function WLAN_INTERFACE_STATE_To_String is simple as:

        function WLAN_INTERFACE_STATE_To_String(
        Dummy: Tndu_WLAN_INTERFACE_STATE): string;
        var
        s: string;
        begin
        s := ”;
        case Dummy of
        wlan_interface_state_not_ready: s :=’Not ready’;
        wlan_interface_state_connected: s := ‘Connected’;
        wlan_interface_state_ad_hoc_network_formed: s := ‘ad hoc network’;
        wlan_interface_state_disconnecting: s := ‘Disconnecting’;
        wlan_interface_state_disconnected: s := ‘Disconnected’;
        wlan_interface_state_associating: s := ‘Associating’;
        wlan_interface_state_discovering: s := ‘Discovering’;
        wlan_interface_state_authenticating: s := ‘Authenticating’;
        end;
        Result := s;
        end;

        That’s it.

        Thank you.

  11. Hello !

    I’m desperately looking for a good sample using right headers and use of WlanGetNetworkBssList !
    The first Tndu_WLAN_BSS_ENTRY item return seems correct but the following ones are completely scrambled….
    I’m unable to download the “fixed” header you provided (broken Dropbox link).
    Would you please post again a full package ?

    Thanks for this excellent site !

    • Hi the updated headers of the Native Wifi API are hosted on github.

      • Then there is a bug in the Tndu_WLAN_BSS_ENTRY definition

        dot11Ssid: Tndu_DOT11_SSID;
        uPhyId: ulong;
        dot11Bssid: Tndu_DOT11_MAC_ADDRESS;
        dot11BssType: Tndu_DOT11_BSS_TYPE;
        //dot11BssPhyType: Tndu_DOT11_PHY_TYPE;
        lRssi: long;
        uLinkQuality: ulong;
        bInRegDomain: Boolean;

        should be replaced by

        dot11Ssid: Tndu_DOT11_SSID;
        uPhyId: ulong;
        dot11Bssid: Tndu_DOT11_MAC_ADDRESS;
        dot11BssType: Tndu_DOT11_BSS_TYPE;
        dot11BssPhyType: ulonglong;
        lRssi: long;
        uLinkQuality: ulong;
        bInRegDomain: Boolean;

        otherwise, data are “shifted” in the record, causing the problem I faced with initial headers.
        With my modification, function works fine.

  12. When using WlanQueryInterface to retrieve stats about my adapter, could you explain to me why multiple PHY are reported for a single adapter ? Where can i find the “description” of these PHY interfaces ?

  13. Another comment, in Tndu_DOT11_PHY_TYPE, there are 2 types missing :
    dot11_phy_type_ht = 7,
    dot11_phy_type_vht = 8,
    as per https://msdn.microsoft.com/en-us/library/windows/desktop/ms706015%28v=vs.85%29.aspx

    required for 802.11n and 802.11ac

  14. Hello,

    Final definition of the currently incorrect nduWlanAPI :

    Tndu_WLAN_BSS_ENTRY = record
    dot11Ssid: Tndu_DOT11_SSID;
    uPhyId: ulong;
    dot11Bssid: Tndu_DOT11_MAC_ADDRESS;
    filler1:array[0..1]of byte;
    dot11BssType: Tndu_DOT11_BSS_TYPE;
    filler2:array[0..2]of byte;
    dot11BssPhyType: ulong;
    lRssi: long;
    uLinkQuality: ulong;
    bInRegDomain: Boolean;
    usBeaconPeriod: ushort;
    ullTimestamp: ulonglong;
    ullHostTimestamp: ulonglong;
    usCapabilityInformation: ushort;
    ulChCenterFrequency: ulong;
    wlanRateSet: Tndu_WLAN_RATE_SET;
    ulIeOffset: ulong;
    ulIeSize: ulong;
    end;

  15. Any update of these source code files ?

Leave a comment