Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to detect when WiFi (re)connects?

Tags:

winapi

delphi

Sorry, I don't have any code to show. That might make this question a candidate for Programmers, but they don't have language specific areas here and I am seeking a Delphi solution, so I will ask here. I hope that that is acceptable.

Basically, I want to auto-launch my VPN whenever WiFi becomes connected. How would I detect that? Google, it seems, is not my friend in this matter.

like image 255
Mawg says reinstate Monica Avatar asked May 04 '15 11:05

Mawg says reinstate Monica


1 Answers

The required WinApi calls are not implemented in Delphi - you have to define and call them yourself. In this case, the relevant functions are :


WlanOpenHandle (MSDN)

WlanRegisterNotification (MSDN)

WLAN_NOTIFICATION_SOURCE structure (MSDN)


You would define these as :

const
  wlanapi = 'wlanapi.dll';
  WLAN_NOTIFICATION_SOURCE_ACM = $00000008;

type
  GUID = TGUID;
  HANDLE = THandle;

  PWLanNotificationData = ^TWLanNotificationData;
  TWLanNotificationData = record
    NotificationSource: DWORD;
    NotificationCode: DWORD;
    InterfaceGuid: GUID;
    dwDataSize: DWORD;
    pData: PVOID;
  end;

  TWLanNotificationCallback = procedure(data: PWLanNotificationData; context: PVOID); stdcall;

function WlanOpenHandle(dwClientVersion: DWORD; pReserved: PVOID;
  out pdwNegotiatedVersion: DWORD; out phClientHandle: HANDLE): DWORD; stdcall;
  external wlanapi name 'WlanOpenHandle';

function WlanRegisterNotification(hClientHandle: HANDLE; dwNotifSource: DWORD;
  bIgnoreDuplicate: BOOL; funcCallback: TWLanNotificationCallback; pCallbackContext: PVOID;
  pReserved: PVOID; out pdwPrevNotifSource: DWORD): DWORD; stdcall;
  external wlanapi name 'WlanRegisterNotification';

And call them as :

// Define a callback procedure to handle the notifications
// You can supply a context pointer to any user information the callback
// may require (pointer to object, etc)
procedure OnWlanNotify(data : PWLanNotificationData; context : PVOID); stdcall;
begin
  // ShowMessage(IntToStr(data^.NotificationCode));  etc...
end;

// Register for notification
procedure TForm1.FormCreate(Sender: TObject);
var
  negotiatedVer : DWORD;
  prevSource : DWORD
begin
  if WlanOpenHandle(2, nil, negotiatedVer, FHandle) <> ERROR_SUCCESS then begin
    // handle error...
  end;
  // check negotiatedVersion if needed
  if WlanRegisterNotification(FHandle,
                       WLAN_NOTIFICATION_SOURCE_ACM,
                       LongBool(true),
                       @OnWlanNotify,
                       nil,
                       nil,
                       prevSource) <> ERROR_SUCCESS then begin
  end;

This registers you for WLAN notifications. Your callback function will execute upon receiving notifications and the nature of the notification can be found in the TWlanNotificationData structure passed to that method.

You should also un-register for notifications when no longer needed. I've only implemented rudimentary error checking here. The details for the various error codes can be found in the referenced MSDN documentation.

A (partial) list of ACM notifications :

const
  WLAN_NOTIFICATION_ACM_AUTOCONF_ENABLED = $00000001; 
    //Autoconfiguration is enabled.
  WLAN_NOTIFICATION_ACM_AUTOCONF_DISABLED = $00000002;
    //Autoconfiguration is disabled.
  WLAN_NOTIFICATION_ACM_BACKGROUND_SCAN_ENABLED = $00000003; 
    //Background scans are enabled.
  WLAN_NOTIFICATION_ACM_BACKGROUND_SCAN_DISABLED = $00000004;
    //Background scans are disabled.
  WLAN_NOTIFICATION_ACM_BSS_TYPE_CHANGE = $00000005;
    //The BSS type for an interface has changed.
  WLAN_NOTIFICATION_ACM_POWER_SETTING_CHANGE = $00000006;
    //The power setting for an interface has changed.
  WLAN_NOTIFICATION_ACM_SCAN_COMPLETE = $00000007;
    //A scan for networks has completed.
  WLAN_NOTIFICATION_ACM_SCAN_FAIL = $00000008;
    //A scan for connectable networks failed.
  WLAN_NOTIFICATION_ACM_CONNECTION_START = $00000009;
    //A connection has started to a network in range.
  WLAN_NOTIFICATION_ACM_CONNECTION_COMPLETE = $0000000a;
    //A connection has completed.
  WLAN_NOTIFICATION_ACM_CONNECTION_ATTEMPT_FAIL = $0000000b;
    //A connection attempt has failed.
  WLAN_NOTIFICATION_ACM_FILTER_LIST_CHANGE = $0000000c;
    //A change in the filter list has occurred, either through group policy or a call to the WlanSetFilterList function.
  WLAN_NOTIFICATION_ACM_INTERFACE_ARRIVAL = $0000000d;
    //A wireless LAN interface is been added to or enabled on the local computer.
  WLAN_NOTIFICATION_ACM_INTERFACE_REMOVAL = $0000000e;
    //A wireless LAN interface has been removed or disabled on the local computer.
  WLAN_NOTIFICATION_ACM_PROFILE_CHANGE = $0000000f;
    //A change in a profile or the profile list has occurred, either through group policy or by calls to Native Wifi functions.
  WLAN_NOTIFICATION_ACM_PROFILE_NAME_CHANGE = $00000010;
    //A profile name has changed, either through group policy or by calls to Native Wifi functions.
  WLAN_NOTIFICATION_ACM_PROFILES_EXHAUSTED = $00000011;
    //All profiles were exhausted in an attempt to autoconnect.
  WLAN_NOTIFICATION_ACM_NETWORK_NOT_AVAILABLE = $00000012;
    //The wireless service cannot find any connectable network after a scan.
  WLAN_NOTIFICATION_ACM_NETWORK_AVAILABLE = $00000013;
    //The wireless service found a connectable network after a scan, the interface was in the disconnected state, and there is no compatible auto-connect profile that the wireless service can use to connect .
  WLAN_NOTIFICATION_ACM_DISCONNECTING = $00000014;
    //The wireless service is disconnecting from a connectable network.
  WLAN_NOTIFICATION_ACM_DISCONNECTED = $00000015;
    //The wireless service has disconnected from a connectable network.
  WLAN_NOTIFICATION_ACM_ADHOC_NETWORK_STATE_CHANGE = $00000016;
    //A state change has occurred for an adhoc network.

Second example callback with more detail :

procedure OnWlanNotify(data : PWLanNotificationData; context : PVOID); stdcall;
begin
  case data^.NotificationCode of
    WLAN_NOTIFICATION_ACM_AUTOCONF_ENABLED : 
      TForm1(context^).Memo1.Lines.Add('Autoconfiguration is enabled.');
    WLAN_NOTIFICATION_ACM_AUTOCONF_DISABLED : 
      TForm1(context^).Memo1.Lines.Add('Autoconfiguration is disabled.');
    WLAN_NOTIFICATION_ACM_BACKGROUND_SCAN_ENABLED : 
      TForm1(context^).Memo1.Lines.Add('Background scans are enabled.');
    WLAN_NOTIFICATION_ACM_BACKGROUND_SCAN_DISABLED : 
      TForm1(context^).Memo1.Lines.Add('Background scans are disabled.');
    WLAN_NOTIFICATION_ACM_BSS_TYPE_CHANGE : 
      TForm1(context^).Memo1.Lines.Add('The BSS type for an interface has changed.');
    WLAN_NOTIFICATION_ACM_POWER_SETTING_CHANGE : 
      TForm1(context^).Memo1.Lines.Add('The power setting for an interface has changed.');
    WLAN_NOTIFICATION_ACM_SCAN_COMPLETE : 
      TForm1(context^).Memo1.Lines.Add('A scan for networks has completed.');
    WLAN_NOTIFICATION_ACM_SCAN_FAIL : 
      TForm1(context^).Memo1.Lines.Add('A scan for connectable networks failed.');
    WLAN_NOTIFICATION_ACM_CONNECTION_START : 
      TForm1(context^).Memo1.Lines.Add('A connection has started to a network in range.');
    WLAN_NOTIFICATION_ACM_CONNECTION_COMPLETE : 
      TForm1(context^).Memo1.Lines.Add('A connection has completed.');
    WLAN_NOTIFICATION_ACM_CONNECTION_ATTEMPT_FAIL : 
      TForm1(context^).Memo1.Lines.Add('A connection attempt has failed.');
    WLAN_NOTIFICATION_ACM_FILTER_LIST_CHANGE : 
      TForm1(context^).Memo1.Lines.Add('A change in the filter list has occurred, either through group policy or a call to the WlanSetFilterList function.');
    WLAN_NOTIFICATION_ACM_INTERFACE_ARRIVAL : 
      TForm1(context^).Memo1.Lines.Add('A wireless LAN interface is been added to or enabled on the local computer.');
    WLAN_NOTIFICATION_ACM_INTERFACE_REMOVAL : 
      TForm1(context^).Memo1.Lines.Add('A wireless LAN interface has been removed or disabled on the local computer.');
    WLAN_NOTIFICATION_ACM_PROFILE_CHANGE : 
      TForm1(context^).Memo1.Lines.Add('A change in a profile or the profile list has occurred, either through group policy or by calls to Native Wifi functions.');
    WLAN_NOTIFICATION_ACM_PROFILE_NAME_CHANGE : 
      TForm1(context^).Memo1.Lines.Add('A profile name has changed, either through group policy or by calls to Native Wifi functions.');
    WLAN_NOTIFICATION_ACM_PROFILES_EXHAUSTED : 
      TForm1(context^).Memo1.Lines.Add('All profiles were exhausted in an attempt to autoconnect.');
    WLAN_NOTIFICATION_ACM_NETWORK_NOT_AVAILABLE : 
      TForm1(context^).Memo1.Lines.Add('The wireless service cannot find any connectable network after a scan.');
    WLAN_NOTIFICATION_ACM_NETWORK_AVAILABLE : 
      TForm1(context^).Memo1.Lines.Add('The wireless service found a connectable network after a scan, the interface was in the disconnected state, and there is no compatible auto-connect profile that the wireless service can use to connect.');
    WLAN_NOTIFICATION_ACM_DISCONNECTING : 
      TForm1(context^).Memo1.Lines.Add('The wireless service is disconnecting from a connectable network.');
    WLAN_NOTIFICATION_ACM_DISCONNECTED : 
      TForm1(context^).Memo1.Lines.Add('The wireless service has disconnected from a connectable network.');
    WLAN_NOTIFICATION_ACM_ADHOC_NETWORK_STATE_CHANGE : 
      TForm1(context^).Memo1.Lines.Add('A state change has occurred for an adhoc network.');
  end;
end;

In the above example, a pointer to Form1 was passed in the user context parameter. This is passed back in the callback, allowing you to access that data from within the method.

  if WlanRegisterNotification(FHandle,
                           WLAN_NOTIFICATION_SOURCE_ACM,
                           LongBool(true),
                           @OnWlanNotify,
                           @Form1,  // Pass in form pointer
                           nil,
                           prevSource) <> ERROR_SUCCESS then begin
    // handle error
  end;
like image 96
J... Avatar answered Sep 28 '22 11:09

J...