Two sample applications cover the two sides of Wi-Fi Direct. WiFiDirect uses wclWiFiDirectAdvertiser to advertise a group, accept incoming devices, and manage pairing. WiFiDirectClient uses wclWiFiDirectDeviceWatcher to discover nearby devices and manages paired devices with IsPaired, Unpair, and EnumPairedDevices, and wclWiFiDirectClient to connect to a selected device and disconnect from it.

Wi-Fi Direct: a phone and a laptop connect directly to each other via Wi-Fi signal waves and a bidirectional link; a crossed-out access point below illustrates that no infrastructure is required

Both samples share the same pairing model: a pairing procedure (ppInvitation or ppGroupOwnerNegotiation), a group owner intent, and a set of configuration methods (cmDisplayPin, cmProvidePin, cmPushButton). The code in this article is taken verbatim from the samples.

Advertiser Client ────────── ────── Start() ──► OnStarted Discover() ──► OnDiscoveringStarted OnAcceptDevice ◄── incoming device ◄── OnDeviceFound OnDeviceConnected ──► result Connect(Id) ──► OnDeviceConnected Disconnect() ──► OnDeviceDisconnected Stop() ──► OnStopped Terminate()

Related topics: WLAN XML profiles for the infrastructure side of Wi-Fi, Wi-Fi Framework product overview, and Event processing.


The three Wi-Fi Direct classes

The framework represents Wi-Fi Direct with three classes, one for each role an application can play:

  • wclWiFiDirectAdvertiser — advertises a Wi-Fi Direct group and accepts incoming connections. It exposes Active, Discoverability, AutonomousGroupOwnerEnabled, Count, an indexed accessor for connected devices, and the Start and Stop methods. Its events are OnStarted, OnStopped, OnAcceptDevice, OnDeviceConnected, OnDeviceDisconnected, and the pairing events described below.
  • wclWiFiDirectDeviceWatcher — discovers nearby Wi-Fi Direct devices and manages paired devices. It exposes Discover, Terminate, IsPaired, Unpair, and EnumPairedDevices. Its events are OnDeviceFound, OnDiscoveringStarted, and OnDiscoveringCompleted.
  • wclWiFiDirectClient — connects to a discovered device. It exposes Connect(Id), Disconnect, LocalAddress, and RemoteAddress. Its events are OnDeviceConnected, OnDeviceDisconnected, and the pairing events.

Each connected or discovered device is represented by a device object that exposes Id, Name, LocalAddress, and RemoteAddress. In Delphi and C++ Builder the class is TwclWiFiDirectDevice; in C# and VB.NET it is wclWiFiDirectDevice; in MFC it is CwclWiFiDirectDevice.

Pairing events

The pairing events are identical on the advertiser and the client. They let the application choose the pairing procedure, the group owner intent, the configuration methods, and how to display or collect the PIN:

Event Parameters Purpose
OnPairGetParams GroupOwnerIntent: Smallint, ConfigurationMethods, PairingProcedure Supplies the pairing procedure, group owner intent, and configuration methods for the pairing.
OnPairDisplayPin Pin: String Called when a PIN must be shown to the user.
OnPairProvidePin Pin: String Called when the user must enter a PIN.
OnPairConfirm Confirm: Boolean Asks the user to confirm the pairing.
OnPairCompleted Result: Integer Reports the pairing result.

The Advertiser example

What the example does

The form hosts a wclWiFiDirectAdvertiser component and the following controls: a cbDiscoverability combo box with items Intensive, None, and Normal; an cbAutonomousGroupOwner check box; a cbPairingProcedure combo box with items ppInvitation and ppGroupOwnerNegotiation; an edGroupOwnerIntent edit; the cbDisplayPin, cbProvidePin, and cbPushButton check boxes; a lvDevices list view; and a lbLog list box. The buttons are btStart, btStop, btClear, and btDisconnect.

The advertising side of the example reacts to OnStarted, OnStopped, OnAcceptDevice, OnDeviceConnected, OnDeviceDisconnected, and the pairing events. On form destroy the sample stops the advertiser.

Starting and stopping

btStart checks Active first, then sets Discoverability and AutonomousGroupOwnerEnabled from the UI, and calls Start. btStop calls Stop. Both handlers check the returned error code against WCL_E_SUCCESS.

procedure TfmMain.btStartClick(Sender: TObject);
var
  Res: Integer;
begin
  if wclWiFiDirectAdvertiser.Active then
    MessageDlg('Already running', mtWarning, [mbOK], 0)

  else begin
    case cbDiscoverability.ItemIndex of
      0: wclWiFiDirectAdvertiser.Discoverability := adIntensive;
      1: wclWiFiDirectAdvertiser.Discoverability := adNone;
      2: wclWiFiDirectAdvertiser.Discoverability := adNormal;
    end;
    wclWiFiDirectAdvertiser.AutonomousGroupOwnerEnabled := cbAutonomousGroupOwner.Checked;

    Res := wclWiFiDirectAdvertiser.Start;
    if Res <> WCL_E_SUCCESS then
      MessageDlg('Start error: 0x' + IntToHex(Res, 8), mtError, [mbOK], 0);
  end;
end;
void __fastcall TfmMain::btStartClick(TObject *Sender)
{
  if (wclWiFiDirectAdvertiser->Active)
    MessageDlg("Already running", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    switch (cbDiscoverability->ItemIndex)
    {
      case 0:
        wclWiFiDirectAdvertiser->Discoverability = adIntensive;
        break;
      case 1:
        wclWiFiDirectAdvertiser->Discoverability = adNone;
        break;
      case 2:
        wclWiFiDirectAdvertiser->Discoverability = adNormal;
        break;
    }
    wclWiFiDirectAdvertiser->AutonomousGroupOwnerEnabled = cbAutonomousGroupOwner->Checked;

    int Res = wclWiFiDirectAdvertiser->Start();
    if (Res != WCL_E_SUCCESS)
      MessageDlg("Start error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
  }
}
private void btStart_Click(object sender, EventArgs e)
{
    if (wclWiFiDirectAdvertiser.Active)
        MessageBox.Show("Already running");
    else
    {
        switch (cbDiscoverability.SelectedIndex)
        {
            case 0:
                wclWiFiDirectAdvertiser.Discoverability = wclWiFiDirectAdvertisementDiscoverability.adIntensive;
                break;
            case 1:
                wclWiFiDirectAdvertiser.Discoverability = wclWiFiDirectAdvertisementDiscoverability.adNone;
                break;
            case 2:
                wclWiFiDirectAdvertiser.Discoverability = wclWiFiDirectAdvertisementDiscoverability.adNormal;
                break;
        }
        wclWiFiDirectAdvertiser.AutonomousGroupOwnerEnabled = cbAutonomousGroupOwner.Checked;

        Int32 Res = wclWiFiDirectAdvertiser.Start();
        if (Res != wclErrors.WCL_E_SUCCESS)
            MessageBox.Show("Start error: 0x" + Res.ToString("X8"));
    }
}
Private Sub btStart_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btStart.Click
    If wclWiFiDirectAdvertiser.Active Then
        MessageBox.Show("Already running")
    Else
        Select Case cbDiscoverability.SelectedIndex
            Case 0
                wclWiFiDirectAdvertiser.Discoverability = wclWiFiDirectAdvertisementDiscoverability.adIntensive
            Case 1
                wclWiFiDirectAdvertiser.Discoverability = wclWiFiDirectAdvertisementDiscoverability.adNone
            Case 2
                wclWiFiDirectAdvertiser.Discoverability = wclWiFiDirectAdvertisementDiscoverability.adNormal
        End Select
        wclWiFiDirectAdvertiser.AutonomousGroupOwnerEnabled = cbAutonomousGroupOwner.Checked

        Dim Res As Int32 = wclWiFiDirectAdvertiser.Start()
        If Res <> wclErrors.WCL_E_SUCCESS Then
            MessageBox.Show("Start error: 0x" + Res.ToString("X8"))
        End If
    End If
End Sub
void CWiFiDirectDlg::OnBnClickedButton1()
{
	if (wclWiFiDirectAdvertiser.Active)
		AfxMessageBox(_T("Already running"));
	else
	{
		switch (cbDiscoverability.GetCurSel())
		{
			case 0:
				wclWiFiDirectAdvertiser.Discoverability = adIntensive;
				break;
			case 1:
				wclWiFiDirectAdvertiser.Discoverability = adNone;
				break;
			case 2:
				wclWiFiDirectAdvertiser.Discoverability = adNormal;
				break;
		}
		wclWiFiDirectAdvertiser.AutonomousGroupOwnerEnabled = (cbAutonomousGroupOwner.GetCheck() != FALSE);
		
		int Res = wclWiFiDirectAdvertiser.Start();
		if (Res != WCL_E_SUCCESS)
		{
			CString s;
			s.Format(_T("Start error: 0x%.8X"), Res);
			AfxMessageBox(s);
		}
	}
}

Accepting devices

OnAcceptDevice is called when a remote device asks to connect. The handler sets the Accept out parameter and, if the device is accepted, adds it to lvDevices with the device Id in the second column and the status "Connecting" in the third. In Delphi and C++ Builder the Id and status are stored in SubItems[0] and SubItems[1]; in the C#, VB.NET, and MFC samples they are stored in the second and third columns (SubItems[1] and SubItems[2] in C# and VB.NET, columns 1 and 2 in MFC). This reflects a different ListView column layout in those projects.

procedure TfmMain.wclWiFiDirectAdvertiserAcceptDevice(Sender: TObject;
  const Device: TwclWiFiDirectDevice; out Accept: Boolean);
var
  Item: TListItem;
begin
  lbLog.Items.Add('Accepting device ' + Device.Id);
  Accept := MessageDlg('Accept connection from ' + Device.Name + '?',
    mtConfirmation, [mbYes, mbNo], 0) = mrYes;
  if Accept then begin
    Item := lvDevices.Items.Add;
    Item.Caption := Device.Name;
    Item.SubItems.Add(Device.Id);
    Item.SubItems.Add('Connecting');
  end;
end;
void __fastcall TfmMain::wclWiFiDirectAdvertiserAcceptDevice(
      TObject *Sender, const TwclWiFiDirectDevice *Device, bool& Accept)
{
  Accept = MessageDlg("Accept connection from " + Device->Name + "?",
    mtConfirmation, TMsgDlgButtons() << mbYes << mbNo, 0) == mrYes;
  if (Accept)
  {
    TListItem* Item = lvDevices->Items->Add();
    Item->Caption = Device->Name;
    Item->SubItems->Add(Device->Id);
    Item->SubItems->Add("Connecting");
  }
}
void wclWiFiDirectAdvertiser_OnAcceptDevice(object Sender, wclWiFiDirectDevice Device, out bool Accept)
{
    lbLog.Items.Add("Accepting device " + Device.Id);
    Accept = MessageBox.Show("Accept connection from " + Device.Name + "?", "Connection",
        MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes;
    if (Accept)
    {
        ListViewItem Item = lvDevices.Items.Add(Device.Name);
        Item.SubItems.Add(Device.Id);
        Item.SubItems.Add("Connecting");
    }
}
Private Sub wclWiFiDirectAdvertiser_OnAcceptDevice(ByVal Sender As Object, ByVal Device As wclWiFi.wclWiFiDirectDevice, ByRef Accept As Boolean) Handles wclWiFiDirectAdvertiser.OnAcceptDevice
    lbLog.Items.Add("Accepting device " + Device.Id)
        Accept = MessageBox.Show("Accept connection from " + Device.Name + "?", "Connection", MessageBoxButtons.YesNo, MessageBoxIcon.Question) = DialogResult.Yes
    If Accept Then
        Dim Item As ListViewItem = lvDevices.Items.Add(Device.Name)
        Item.SubItems.Add(Device.Id)
        Item.SubItems.Add("Connecting")
    End If
End Sub
void CWiFiDirectDlg::wclWiFiDirectAdvertiserAcceptDevice(void* Sender,
	CwclWiFiDirectDevice* const Device, bool& Accept)
{
	UNREFERENCED_PARAMETER(Sender);

	tstring s = _T("Accept connection from ") + Device->Name + _T(" ?");
	Accept = (AfxMessageBox(s.c_str(), MB_YESNO | MB_APPLMODAL | MB_ICONQUESTION) == IDYES);
	if (Accept)
	{
		int i = lvDevices.GetItemCount();
		lvDevices.InsertItem(i, Device->Name.c_str());
		lvDevices.SetItemText(i, 1, Device->Id.c_str());
		lvDevices.SetItemText(i, 2, _T("Connecting"));
	}
}

Connection result

OnDeviceConnected receives the device and the result of the connection attempt. On success the sample marks the device as Connected and logs the local and remote IP addresses; on failure it logs the error code and removes the device from the list. The device is located in the ListView by its Id.

procedure TfmMain.wclWiFiDirectAdvertiserDeviceConnected(Sender: TObject;
  const Device: TwclWiFiDirectDevice; const Error: Integer);
var
  Item: TListItem;
begin
  Item := GetItem(Device.Id);
  if Item <> nil then begin
    if Error = WCL_E_SUCCESS then begin
      Item.SubItems[1] := 'Connected';

      lbLog.Items.Add(Device.Name + ' connected');
      lbLog.Items.Add('  Local IP: ' + Device.LocalAddress);
      lbLog.Items.Add('  Remote IP: ' + Device.RemoteAddress);

    end else begin
      lbLog.Items.Add(Device.Name + ' connect error: 0x' + IntToHex(Error, 8));
      Item.Delete;
    end;
  end;
end;
void __fastcall TfmMain::wclWiFiDirectAdvertiserDeviceConnected(
      TObject *Sender, const TwclWiFiDirectDevice *Device, const int Error)
{
  TListItem* Item = GetItem(Device->Id);
  if (Item != NULL)
  {
    if (Error == WCL_E_SUCCESS)
    {
      Item->SubItems->Strings[1] = "Connected";

      lbLog->Items->Add(Device->Name + " connected");
      lbLog->Items->Add("  Local IP: " + Device->LocalAddress);
      lbLog->Items->Add("  Remote IP: " + Device->RemoteAddress);

    }
    else
    {
      lbLog->Items->Add(Device->Name + " connect error: 0x" + IntToHex(Error, 8));
      Item->Delete();
    }
  }
}
void wclWiFiDirectAdvertiser_OnDeviceConnected(object Sender, wclWiFiDirectDevice Device, int Error)
{
    ListViewItem Item = GetItem(Device.Id);
    if (Item != null)
    {
        if (Error == wclErrors.WCL_E_SUCCESS)
        {
            Item.SubItems[2].Text = "Connected";

            lbLog.Items.Add(Device.Name + " connected");
            lbLog.Items.Add("  Local IP: " + Device.LocalAddress);
            lbLog.Items.Add("  Remote IP: " + Device.RemoteAddress);
        }
        else
        {
            lbLog.Items.Add(Device.Name + " connect error: 0x" + Error.ToString("X8"));
            lvDevices.Items.Remove(Item);
        }
    }
}
Private Sub wclWiFiDirectAdvertiser_OnDeviceConnected(ByVal Sender As Object, ByVal Device As wclWiFi.wclWiFiDirectDevice, ByVal [Error] As Integer) Handles wclWiFiDirectAdvertiser.OnDeviceConnected
    Dim Item As ListViewItem = GetItem(Device.Id)
    If Item IsNot Nothing Then
        If [Error] = wclErrors.WCL_E_SUCCESS Then
            Item.SubItems(2).Text = "Connected"

            lbLog.Items.Add(Device.Name + " connected")
            lbLog.Items.Add("  Local IP: " + Device.LocalAddress)
            lbLog.Items.Add("  Remote IP: " + Device.RemoteAddress)
        Else
            lbLog.Items.Add(Device.Name + " connect error: 0x" + [Error].ToString("X8"))
            lvDevices.Items.Remove(Item)
        End If
    End If
End Sub
void CWiFiDirectDlg::wclWiFiDirectAdvertiserDeviceConnected(void* Sender,
	CwclWiFiDirectDevice* const Device, const int Error)
{
	UNREFERENCED_PARAMETER(Sender);

	int Item = GetItem(Device->Id);
	if (Item != -1)
	{
		if (Error == WCL_E_SUCCESS)
		{
			lvDevices.SetItemText(Item, 2, _T("Connected"));
			
			lbLog.AddString(CString(Device->Name.c_str()) + _T(" connected"));
			lbLog.AddString(CString(_T("  Local IP: ")) + Device->LocalAddress.c_str());
			lbLog.AddString(CString(_T("  Remote IP: ")) + Device->RemoteAddress.c_str());
		}
		else
		{
			CString s;
			s.Format(_T(" connect error: 0x%.8X"), Error);
			lbLog.AddString(Device->Name.c_str() + s);
			lvDevices.DeleteItem(Item);
		}
	}
}

Pairing parameters

OnPairGetParams supplies the pairing procedure, the group owner intent, and the configuration methods. The sample reads the pairing procedure from cbPairingProcedure, the group owner intent from edGroupOwnerIntent, and builds the ConfigurationMethods set from the three check boxes. The C# and VB.NET handlers additionally include a default branch — default in C# and Case Else in VB.NET — that selects ppGroupOwnerNegotiation.

procedure TfmMain.wclWiFiDirectAdvertiserPairGetParams(Sender: TObject;
  const Device: TwclWiFiDirectDevice; out GroupOwnerIntent: Smallint;
  out ConfigurationMethods: TwclWiFiDirectConfigurationMethods;
  out PairingProcedure: TwclWiFiDirectPairingProcedure);
begin
  lbLog.Items.Add(Device.Name + ' quering pairing params');

  case cbPairingProcedure.ItemIndex of
    0: PairingProcedure := ppInvitation;
    1: PairingProcedure := ppGroupOwnerNegotiation;
  end;
  GroupOwnerIntent := StrToInt(edGroupOwnerIntent.Text);
  ConfigurationMethods := [];
  if cbDisplayPin.Checked then
    ConfigurationMethods := ConfigurationMethods + [cmDisplayPin];
  if cbProvidePin.Checked then
    ConfigurationMethods := ConfigurationMethods + [cmProvidePin];
  if cbPushButton.Checked then
    ConfigurationMethods := ConfigurationMethods + [cmPushButton];
end;
void __fastcall TfmMain::wclWiFiDirectAdvertiserPairGetParams(
      TObject *Sender, const TwclWiFiDirectDevice *Device,
      short& GroupOwnerIntent,
      TwclWiFiDirectConfigurationMethods& ConfigurationMethods,
      TwclWiFiDirectPairingProcedure& PairingProcedure)
{
  lbLog->Items->Add(Device->Name + " quering pairing params");

  switch (cbPairingProcedure->ItemIndex)
  {
    case 0:
      PairingProcedure = ppInvitation;
      break;
    case 1:
      PairingProcedure = ppGroupOwnerNegotiation;
      break;
  }
  GroupOwnerIntent = StrToInt(edGroupOwnerIntent->Text);
  ConfigurationMethods.Clear();
  if (cbDisplayPin->Checked)
    ConfigurationMethods << cmDisplayPin;
  if (cbProvidePin->Checked)
    ConfigurationMethods << cmProvidePin;
  if (cbPushButton->Checked)
    ConfigurationMethods << cmPushButton;
}
void wclWiFiDirectAdvertiser_OnPairGetParams(object Sender, wclWiFiDirectDevice Device, out short GroupOwnerIntent, out wclWiFiDirectConfigurationMethod ConfigurationMethods, out wclWiFiDirectPairingProcedure PairingProcedure)
{
    lbLog.Items.Add(Device.Name + " quering pairing params");

    switch (cbPairingProcedure.SelectedIndex)
    {
        case 0:
            PairingProcedure = wclWiFiDirectPairingProcedure.ppInvitation;
            break;
        case 1:
            PairingProcedure = wclWiFiDirectPairingProcedure.ppGroupOwnerNegotiation;
            break;
        default:
            PairingProcedure = wclWiFiDirectPairingProcedure.ppGroupOwnerNegotiation;
            break;
    }
    GroupOwnerIntent = Convert.ToInt16(edGroupOwnerIntent.Text);
    ConfigurationMethods = 0;
    if (cbDisplayPIN.Checked)
        ConfigurationMethods |= wclWiFiDirectConfigurationMethod.cmDisplayPin;
    if (cbProvidePIN.Checked)
        ConfigurationMethods |= wclWiFiDirectConfigurationMethod.cmProvidePin;
    if (cbPushButton.Checked)
        ConfigurationMethods |= wclWiFiDirectConfigurationMethod.cmPushButton;
}
Private Sub wclWiFiDirectAdvertiser_OnPairGetParams(ByVal Sender As Object, ByVal Device As wclWiFi.wclWiFiDirectDevice, ByRef GroupOwnerIntent As Short, ByRef ConfigurationMethods As wclWiFi.wclWiFiDirectConfigurationMethod, ByRef PairingProcedure As wclWiFi.wclWiFiDirectPairingProcedure) Handles wclWiFiDirectAdvertiser.OnPairGetParams
    lbLog.Items.Add(Device.Name + " quering pairing params")

    Select Case cbPairingProcedure.SelectedIndex
        Case 0
            PairingProcedure = wclWiFiDirectPairingProcedure.ppInvitation
        Case 1
            PairingProcedure = wclWiFiDirectPairingProcedure.ppGroupOwnerNegotiation
        Case Else
            PairingProcedure = wclWiFiDirectPairingProcedure.ppGroupOwnerNegotiation
    End Select
    GroupOwnerIntent = Convert.ToInt16(edGroupOwnerIntent.Text)
    ConfigurationMethods = 0
    If cbDisplayPIN.Checked Then
        ConfigurationMethods = ConfigurationMethods Or wclWiFiDirectConfigurationMethod.cmDisplayPin
    End If
    If cbProvidePIN.Checked Then
        ConfigurationMethods = ConfigurationMethods Or wclWiFiDirectConfigurationMethod.cmProvidePin
    End If
    If cbPushButton.Checked Then
        ConfigurationMethods = ConfigurationMethods Or wclWiFiDirectConfigurationMethod.cmPushButton
    End If
End Sub
void CWiFiDirectDlg::wclWiFiDirectAdvertiserPairGetParams(void* Sender,
	CwclWiFiDirectDevice* const Device, short& GroupOwnerIntent,
	wclWiFiDirectConfigurationMethods& ConfigurationMethods,
	wclWiFiDirectPairingProcedure& PairingProcedure)
{
	UNREFERENCED_PARAMETER(Sender);

	lbLog.AddString(Device->Name.c_str() + CString(_T(" quering pairing params")));
	
	if (cbPairingProcedure.GetCurSel() == 0)
		PairingProcedure = ppInvitation;
	else
		PairingProcedure = ppGroupOwnerNegotiation;

	CString s;
	edGroupOwnerIntent.GetWindowText(s);
	GroupOwnerIntent = LOWORD(_ttoi(s));
	
	ConfigurationMethods.clear();
	if (cbDisplayPin.GetCheck())
		ConfigurationMethods.insert(cmDisplayPin);
	if (cbProvidePin.GetCheck())
		ConfigurationMethods.insert(cmProvidePin);
	if (cbPushButton.GetCheck())
		ConfigurationMethods.insert(cmPushButton);
}

The remaining pairing handlers follow the same shape in both samples. OnPairDisplayPin shows the PIN to the user, OnPairProvidePin asks the user to enter a PIN, OnPairConfirm asks for confirmation, and OnPairCompleted logs the result. In the MFC samples the PIN is collected through a dedicated dialog: CInputDlg in the Advertiser and CPinDlg in the Client. In the other samples it is collected through InputQuery, InputBox, or Microsoft.VisualBasic.Interaction.InputBox.


The Client example

What the example does

The form hosts a wclWiFiDirectDeviceWatcher and a wclWiFiDirectClient, together with the same pairing controls as the advertiser: cbPairingProcedure, edGroupOwnerIntent, cbDisplayPin, cbProvidePin, and cbPushButton. The buttons are btDiscover, btTerminate, btClear, btConnect, btDisconnect, btIsPaired, btUnpair, and btEnumPaired.

The client side reacts to OnDeviceFound, OnDiscoveringStarted, OnDiscoveringCompleted, OnDeviceConnected, OnDeviceDisconnected, and the same pairing events. On form close the sample calls Client.Disconnect and Watcher.Terminate.

Discovering devices

btDiscover calls Discover on the watcher. btTerminate calls Terminate. Each discovered device is delivered through OnDeviceFound, which adds a row to lvDevices with the device name in the first column and the device Id in the second. OnDiscoveringStarted clears the ListView before a new discovery run; OnDiscoveringCompleted logs the result.

procedure TfmMain.btDiscoverClick(Sender: TObject);
var
  Res: Integer;
begin
  Res := wclWiFiDirectDeviceWatcher.Discover;
  if Res <> WCL_E_SUCCESS then
    MessageDlg('Discover error: 0x' + IntToHex(Res, 8), mtError, [mbOK], 0);
end;

procedure TfmMain.wclWiFiDirectDeviceWatcherDeviceFound(Sender: TObject;
  const Id: string; const Name: string);
var
  Item: TListItem;
begin
  Item := lvDevices.Items.Add;
  Item.Caption := Name;
  Item.SubItems.Add(Id);

  lbLog.Items.Add('Device found');
end;
void __fastcall TfmMain::btDiscoverClick(TObject *Sender)
{
  int Res = wclWiFiDirectDeviceWatcher->Discover();
  if (Res != WCL_E_SUCCESS)
    MessageDlg("Discover error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
}

void __fastcall TfmMain::wclWiFiDirectDeviceWatcherDeviceFound(
      TObject *Sender, const String Id, const String Name)
{
  TListItem* Item = lvDevices->Items->Add();
  Item->Caption = Name;
  Item->SubItems->Add(Id);

  lbLog->Items->Add("Device found");
}
private void btDiscover_Click(object sender, EventArgs e)
{
    Int32 Res = Watcher.Discover();
    if (Res != wclErrors.WCL_E_SUCCESS)
        MessageBox.Show("Discover error: 0x" + Res.ToString("X8"));
}

void Watcher_OnDeviceFound(object Sender, string Id, string Name)
{
    ListViewItem Item = lvDevices.Items.Add(Name);
    Item.SubItems.Add(Id);
    
    lbLog.Items.Add("Device found");
}
Private Sub btDiscover_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btDiscover.Click
    Dim Res As Int32 = Watcher.Discover()
    If Res <> wclErrors.WCL_E_SUCCESS Then
        MessageBox.Show("Discover error: 0x" + Res.ToString("X8"))
    End If
End Sub

Private Sub Watcher_OnDeviceFound(ByVal Sender As Object, ByVal Id As String, ByVal Name As String) Handles Watcher.OnDeviceFound
    Dim Item As ListViewItem = lvDevices.Items.Add(Name)
    Item.SubItems.Add(Id)

    lbLog.Items.Add("Device found")
End Sub
void CWiFiDirectClientDlg::OnBnClickedButtonDiscover()
{
	int Res = Watcher.Discover();
	if (Res != WCL_E_SUCCESS)
	{
		CString s;
		s.Format(_T("Discover error: 0x%.8X"), Res);
		AfxMessageBox(s);
	}
}

void CWiFiDirectClientDlg::wclWiFiDirectDeviceWatcherDeviceFound(void* Sender, 
	const tstring& Id, const tstring& Name)
{
	UNREFERENCED_PARAMETER(Sender);

	int n = lvDevices.GetItemCount();
	lvDevices.InsertItem(n, Name.c_str());
	lvDevices.SetItemText(n, 1, Id.c_str());
	
	lbLog.AddString(_T("Device found"));
}

Connecting

btConnect requires a selected row. It takes the device Id from the second column and passes it to Connect. The result of the connection attempt is delivered through OnDeviceConnected, which logs the result and, on success, the LocalAddress and RemoteAddress of the client.

procedure TfmMain.btConnectClick(Sender: TObject);
var
  Res: Integer;
begin
  if lvDevices.Selected = nil then
    MessageDlg('Select device', mtWarning, [mbOK], 0)

  else begin
    Res := wclWiFiDirectClient.Connect(lvDevices.Selected.SubItems[0]);
    if Res <> WCL_E_SUCCESS then
      MessageDlg('Connect Error: 0x' + IntToHex(Res, 8), mtError, [mbOK], 0);
  end;
end;

procedure TfmMain.wclWiFiDirectClientDeviceConnected(Sender: TObject;
  const Device: TwclWiFiDirectDevice; const Error: Integer);
begin
  lbLog.Items.Add('Connected with result: 0x' + IntToHex(Error, 8));
  if Error = WCL_E_SUCCESS then begin
    lbLog.Items.Add('  Local IP: ' + wclWiFiDirectClient.LocalAddress);
    lbLog.Items.Add('  Remote IP: ' + wclWiFiDirectClient.RemoteAddress);
  end;
end;
void __fastcall TfmMain::btConnectClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    int Res = wclWiFiDirectClient->Connect(lvDevices->Selected->SubItems->Strings[0]);
    if (Res != WCL_E_SUCCESS)
      MessageDlg("Connect Error: 0x" + IntToHex(Res, 8), mtError, TMsgDlgButtons() << mbOK, 0);
  }
}

void __fastcall TfmMain::wclWiFiDirectClientDeviceConnected(
      TObject *Sender, const TwclWiFiDirectDevice *Device, const int Error)
{
  lbLog->Items->Add("Connected with result: 0x" + IntToHex(Error, 8));
  if (Error == WCL_E_SUCCESS)
  {
    lbLog->Items->Add("  Local IP: " + wclWiFiDirectClient->LocalAddress);
    lbLog->Items->Add("  Remote IP: " + wclWiFiDirectClient->RemoteAddress);
  }        
}
private void btConnect_Click(object sender, EventArgs e)
{
    if (lvDevices.SelectedItems.Count == 0)
        MessageBox.Show("Select device");
    else
    {
        Int32 Res = Client.Connect(lvDevices.SelectedItems[0].SubItems[1].Text);
        if (Res != wclErrors.WCL_E_SUCCESS)
            MessageBox.Show("Connect Error: 0x" + Res.ToString("X8"));
    }
}

void Client_OnDeviceConnected(object Sender, wclWiFiDirectDevice Device, int Error)
{
    lbLog.Items.Add("Connected with result: 0x" + Error.ToString("X8"));
    if (Error == wclErrors.WCL_E_SUCCESS)
    {
        lbLog.Items.Add("  Local IP: " + Client.LocalAddress);
        lbLog.Items.Add("  Remote IP: " + Client.RemoteAddress);
    }
}
Private Sub btConnect_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btConnect.Click
    If lvDevices.SelectedItems.Count = 0 Then
        MessageBox.Show("Select device")
    Else
        Dim Res As Int32 = Client.Connect(lvDevices.SelectedItems(0).SubItems(1).Text)
        If Res <> wclErrors.WCL_E_SUCCESS Then
            MessageBox.Show("Connect Error: 0x" + Res.ToString("X8"))
        End If
    End If
End Sub

Private Sub Client_OnDeviceConnected(ByVal Sender As Object, ByVal Device As wclWiFi.wclWiFiDirectDevice, ByVal [Error] As Integer) Handles Client.OnDeviceConnected
    lbLog.Items.Add("Connected with result: 0x" + [Error].ToString("X8"))
    If [Error] = wclErrors.WCL_E_SUCCESS Then
        lbLog.Items.Add("  Local IP: " + Client.LocalAddress)
        lbLog.Items.Add("  Remote IP: " + Client.RemoteAddress)
    End If
End Sub
void CWiFiDirectClientDlg::OnBnClickedButtonConnect()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		CString Id = lvDevices.GetItemText(lvDevices.GetNextSelectedItem(Pos), 1);
		int Res = Client.Connect(tstring(Id));
		if (Res != WCL_E_SUCCESS)
		{
			CString s;
			s.Format(_T("Connect Error: 0x%.8X"), Res);
			AfxMessageBox(s);
		}
	}
}

void CWiFiDirectClientDlg::wclWiFiDirectClientDeviceConnected(void* Sender,
	CwclWiFiDirectDevice* const Device, const int Error)
{
	UNREFERENCED_PARAMETER(Sender);
	UNREFERENCED_PARAMETER(Device);

	CString s;
	s.Format(_T("Connected with result: 0x%.8X"), Error);
	lbLog.AddString(s);
	if (Error == WCL_E_SUCCESS)
	{
		lbLog.AddString(_T("  Local IP: ") + CString(Client.LocalAddress.c_str()));
		lbLog.AddString(_T("  Remote IP: ") + CString(Client.RemoteAddress.c_str()));
	}
}

Disconnecting

btDisconnect calls Disconnect on the client. The disconnect is reported back through OnDeviceDisconnected, which logs the reason code. In the advertiser, the equivalent action is btDisconnect, which finds the selected device by Id among the advertiser's devices and calls Disconnect on that device.

Pairing parameters

The client exposes the same pairing configuration as the advertiser: OnPairGetParams reads the pairing procedure from cbPairingProcedure, the group owner intent from edGroupOwnerIntent, and the configuration methods from the three check boxes. In the client sample there is no default branch: when cbPairingProcedure.SelectedIndex is 0, the handler selects ppInvitation; otherwise it selects ppGroupOwnerNegotiation.

procedure TfmMain.wclWiFiDirectClientPairGetParams(Sender: TObject;
  const Device: TwclWiFiDirectDevice; out GroupOwnerIntent: Smallint;
  out ConfigurationMethods: TwclWiFiDirectConfigurationMethods;
  out PairingProcedure: TwclWiFiDirectPairingProcedure);
begin
  GroupOwnerIntent := StrToInt(edGroupOwnerIntent.Text);

  ConfigurationMethods := [];
  if cbDisplayPin.Checked then
    ConfigurationMethods := ConfigurationMethods + [cmDisplayPin];
  if cbProvidePin.Checked then
    ConfigurationMethods := ConfigurationMethods + [cmProvidePin];
  if cbPushButton.Checked then
    ConfigurationMethods := ConfigurationMethods + [cmPushButton];

  if cbPairingProcedure.ItemIndex = 0 then
    PairingProcedure := ppInvitation
  else
    PairingProcedure := ppGroupOwnerNegotiation;
end;
void __fastcall TfmMain::wclWiFiDirectClientPairGetParams(TObject *Sender,
      const TwclWiFiDirectDevice *Device, short& GroupOwnerIntent,
      TwclWiFiDirectConfigurationMethods& ConfigurationMethods,
      TwclWiFiDirectPairingProcedure& PairingProcedure)
{
  GroupOwnerIntent = StrToInt(edGroupOwnerIntent->Text);

  ConfigurationMethods.Clear();
  if (cbDisplayPin->Checked)
    ConfigurationMethods << cmDisplayPin;
  if (cbProvidePin->Checked)
    ConfigurationMethods << cmProvidePin;
  if (cbPushButton->Checked)
    ConfigurationMethods << cmPushButton;

  if (cbPairingProcedure->ItemIndex == 0)
    PairingProcedure = ppInvitation;
  else
    PairingProcedure = ppGroupOwnerNegotiation;
}
void Client_OnPairGetParams(object Sender, wclWiFiDirectDevice Device, out short GroupOwnerIntent,
    out wclWiFiDirectConfigurationMethod ConfigurationMethods, out wclWiFiDirectPairingProcedure PairingProcedure)
{
    GroupOwnerIntent = Convert.ToInt16(edGroupOwnerIntent.Text);
    
    ConfigurationMethods = 0;
    if (cbDisplayPIN.Checked)
        ConfigurationMethods |= wclWiFiDirectConfigurationMethod.cmDisplayPin;
    if (cbProvidePIN.Checked)
        ConfigurationMethods |= wclWiFiDirectConfigurationMethod.cmProvidePin;
    if (cbPushButton.Checked)
        ConfigurationMethods |= wclWiFiDirectConfigurationMethod.cmPushButton;
    
    if (cbPairingProcedure.SelectedIndex == 0)
        PairingProcedure = wclWiFiDirectPairingProcedure.ppInvitation;
    else
        PairingProcedure = wclWiFiDirectPairingProcedure.ppGroupOwnerNegotiation;
}
Private Sub Client_OnPairGetParams(ByVal Sender As Object, ByVal Device As wclWiFi.wclWiFiDirectDevice, ByRef GroupOwnerIntent As Short, ByRef ConfigurationMethods As wclWiFi.wclWiFiDirectConfigurationMethod, ByRef PairingProcedure As wclWiFi.wclWiFiDirectPairingProcedure) Handles Client.OnPairGetParams
    GroupOwnerIntent = Convert.ToInt16(edGroupOwnerIntent.Text)

    ConfigurationMethods = 0
    If cbDisplayPIN.Checked Then
        ConfigurationMethods = ConfigurationMethods Or wclWiFiDirectConfigurationMethod.cmDisplayPin
    End If
    If cbProvidePIN.Checked Then
        ConfigurationMethods = ConfigurationMethods Or wclWiFiDirectConfigurationMethod.cmProvidePin
    End If
    If cbPushButton.Checked Then
        ConfigurationMethods = ConfigurationMethods Or wclWiFiDirectConfigurationMethod.cmPushButton
    End If

    If cbPairingProcedure.SelectedIndex = 0 Then
        PairingProcedure = wclWiFiDirectPairingProcedure.ppInvitation
    Else
        PairingProcedure = wclWiFiDirectPairingProcedure.ppGroupOwnerNegotiation
    End If
End Sub
void CWiFiDirectClientDlg::wclWiFiDirectClientPairGetParams(void* Sender,
	CwclWiFiDirectDevice* Device, short& GroupOwnerIntent,
	wclWiFiDirectConfigurationMethods& ConfigurationMethods,
	wclWiFiDirectPairingProcedure& PairingProcedure)
{
	UNREFERENCED_PARAMETER(Sender);

	lbLog.AddString(Device->Name.c_str() + CString(_T(" quering pairing params")));
	
	if (cbPairingProcedure.GetCurSel() == 0)
		PairingProcedure = ppInvitation;
	else
		PairingProcedure = ppGroupOwnerNegotiation;

	CString s;
	edGroupOwnerIntent.GetWindowText(s);
	GroupOwnerIntent = LOWORD(_ttoi(s));
	
	ConfigurationMethods.clear();
	if (cbDisplayPin.GetCheck())
		ConfigurationMethods.insert(cmDisplayPin);
	if (cbProvidePin.GetCheck())
		ConfigurationMethods.insert(cmProvidePin);
	if (cbPushButton.GetCheck())
		ConfigurationMethods.insert(cmPushButton);
}

Paired device management

The watcher exposes three paired-device operations: IsPaired returns whether a device with the given Id is paired; Unpair removes the pairing; EnumPairedDevices fills a container with the Ids of all paired devices. The sample binds these to btIsPaired, btUnpair, and btEnumPaired. In C#, VB.NET, and the C++ sample the container type is language-specific (List<String>, List(Of String), and WCL_WFD_DEVICES); in Delphi and C++ Builder it is a TStringList.

procedure TfmMain.btIsPairedClick(Sender: TObject);
var
  Res: Integer;
  IsPaired: Boolean;
begin
  if lvDevices.Selected = nil then
    MessageDlg('Select device', mtWarning, [mbOK], 0)
  else begin
    Res := wclWiFiDirectDeviceWatcher.IsPaired(lvDevices.Selected.SubItems[0],
      IsPaired);
    if Res <> WCL_E_SUCCESS then
      ShowMessage('Get paired error: 0x' + IntToHex(Res, 8))
    else begin
      if IsPaired then
        ShowMessage('Device is paired')
      else
        ShowMessage('Device is NOT paired');
    end;
  end;
end;

procedure TfmMain.btUnpairClick(Sender: TObject);
var
  Res: Integer;
begin
  if lvDevices.Selected = nil then
    MessageDlg('Select device', mtWarning, [mbOK], 0)
  else begin
    Res := wclWiFiDirectDeviceWatcher.Unpair(lvDevices.Selected.SubItems[0]);
    if Res <> WCL_E_SUCCESS then
      ShowMessage('Unpair error: 0x' + IntToHex(Res, 8))
    else
      ShowMessage('Unpaired');
  end;
end;

procedure TfmMain.btEnumPairedClick(Sender: TObject);
var
  Paired: TStringList;
  Res: Integer;
  i: Integer;
  Item: TListItem;
begin
  Paired := TStringList.Create;
  Res := wclWiFiDirectDeviceWatcher.EnumPairedDevices(Paired);
  if Res <> WCL_E_SUCCESS then
    ShowMessage('Enum paired error: 0x' + IntToHex(Res, 8))

  else begin
    lvDevices.Items.Clear;

    if Paired.Count > 0 then begin
      for i := 0 to Paired.Count - 1 do begin
        Item := lvDevices.Items.Add;
        Item.Caption := '';
        Item.SubItems.Add(Paired[i]);
      end;
    end;
  end;
  Paired.Free;
end;
void __fastcall TfmMain::btIsPairedClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    bool IsPaired;
    int Res = wclWiFiDirectDeviceWatcher->IsPaired(
      lvDevices->Selected->SubItems->Strings[0], IsPaired);
    if (Res != WCL_E_SUCCESS)
      ShowMessage("Get paired error: 0x" + IntToHex(Res, 8));
    else
    {
      if (IsPaired)
        ShowMessage("Device is paired");
      else
        ShowMessage("Device is NOT paired");
    }
  }
}

void __fastcall TfmMain::btUnpairClick(TObject *Sender)
{
  if (lvDevices->Selected == NULL)
    MessageDlg("Select device", mtWarning, TMsgDlgButtons() << mbOK, 0);
  else
  {
    int Res = wclWiFiDirectDeviceWatcher->Unpair(
      lvDevices->Selected->SubItems->Strings[0]);
    if (Res != WCL_E_SUCCESS)
      ShowMessage("Unpair error: 0x" + IntToHex(Res, 8));
    else
      ShowMessage("Unpaired");
  }
}

void __fastcall TfmMain::btEnumPairedClick(TObject *Sender)
{
  TStringList* Paired = new TStringList();
  int Res = wclWiFiDirectDeviceWatcher->EnumPairedDevices(Paired);
  if (Res != WCL_E_SUCCESS)
    ShowMessage("Enum paired error: 0x" + IntToHex(Res, 8));
  else
  {
    lvDevices->Items->Clear();

    if (Paired->Count > 0)
    {
      for (int i = 0; i < Paired->Count; i++)
      {
        TListItem* Item = lvDevices->Items->Add();
        Item->Caption = "";
        Item->SubItems->Add(Paired->Strings[i]);
      }
    }
  }
  Paired->Free();
}
private void btIsPaired_Click(object sender, EventArgs e)
{
    if (lvDevices.SelectedItems.Count == 0)
        MessageBox.Show("Select device");
    else
    {
        Boolean IsPaired;
        Int32 Res = Watcher.IsPaired(lvDevices.SelectedItems[0].SubItems[1].Text, out IsPaired);
        if (Res != wclErrors.WCL_E_SUCCESS)
            MessageBox.Show("Get paired error: 0x" + Res.ToString("X8"));
        else
        {
            if (IsPaired)
                MessageBox.Show("Device is paired");
            else
                MessageBox.Show("Device is NOT paired");
        }
    }
}

private void btUnpair_Click(object sender, EventArgs e)
{
    if (lvDevices.SelectedItems.Count == 0)
        MessageBox.Show("Select device");
    else
    {
        Int32 Res = Watcher.Unpair(lvDevices.SelectedItems[0].SubItems[1].Text);
        if (Res != wclErrors.WCL_E_SUCCESS)
            MessageBox.Show("Unpair error: 0x" + Res.ToString("X8"));
        else
            MessageBox.Show("Unpaired");
    }
}

private void btEnumPaired_Click(object sender, EventArgs e)
{
    List<String> Paired = new List<String>();
    Int32 Res = Watcher.EnumPairedDevices(Paired);
    if (Res != wclErrors.WCL_E_SUCCESS)
        MessageBox.Show("Enum paired error: 0x" + Res.ToString("X8"));
    else
    {
        lvDevices.Items.Clear();

        if (Paired.Count > 0)
        {
            foreach (String Id in Paired)
            {
                ListViewItem Item = lvDevices.Items.Add("");
                Item.SubItems.Add(Id);
            }
        }
    }
}
Private Sub btIsPaired_Click(sender As System.Object, e As System.EventArgs) Handles btIsPaired.Click
    If lvDevices.SelectedItems.Count = 0 Then
        MessageBox.Show("Select device")
    Else
        Dim IsPaired As Boolean = False
        Dim Res As Integer = Watcher.IsPaired(lvDevices.SelectedItems(0).SubItems(1).Text, IsPaired)
        If Res <> wclErrors.WCL_E_SUCCESS Then
            MessageBox.Show("Get paired error: 0x" + Res.ToString("X8"))
        Else
            If IsPaired Then
                MessageBox.Show("Device is paired")
            Else
                MessageBox.Show("Device is NOT paired")
            End If
        End If
    End If
End Sub

Private Sub btUnpair_Click(sender As System.Object, e As System.EventArgs) Handles btUnpair.Click
    If lvDevices.SelectedItems.Count = 0 Then
        MessageBox.Show("Select device")
    Else
        Dim Res As Integer = Watcher.Unpair(lvDevices.SelectedItems(0).SubItems(1).Text)
        If Res <> wclErrors.WCL_E_SUCCESS Then
            MessageBox.Show("Unpair error: 0x" + Res.ToString("X8"))
        Else
            MessageBox.Show("Unpaired")
        End If
    End If
End Sub

Private Sub btEnumPaired_Click(sender As System.Object, e As System.EventArgs) Handles btEnumPaired.Click
    Dim Paired As List(Of String) = New List(Of String)()
    Dim Res As Int32 = Watcher.EnumPairedDevices(Paired)
    If Res <> wclErrors.WCL_E_SUCCESS Then
        MessageBox.Show("Enum paired error: 0x" + Res.ToString("X8"))
    Else
        lvDevices.Items.Clear()

        If Paired.Count > 0 Then
            For Each Id As String In Paired
                Dim Item As ListViewItem = lvDevices.Items.Add("")
                Item.SubItems.Add(Id)
            Next
        End If
    End If
End Sub
void CWiFiDirectClientDlg::OnBnClickedButtonIsPaired()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		CString Id = lvDevices.GetItemText(lvDevices.GetNextSelectedItem(Pos), 1);
		bool IsPaired;
		int Res = Watcher.IsPaired(tstring(Id), IsPaired);
		if (Res != WCL_E_SUCCESS)
		{
			CString s;
			s.Format(_T("Get paired error: 0x%.8X"), Res);
			AfxMessageBox(s);
		}
		else
		{
			if (IsPaired)
				AfxMessageBox(_T("Device is paired"));
			else
				AfxMessageBox(_T("Device is NOT paired"));
		}
	}
}

void CWiFiDirectClientDlg::OnBnClickedButtonUnpair()
{
	POSITION Pos = lvDevices.GetFirstSelectedItemPosition();
	if (Pos == NULL)
		AfxMessageBox(_T("Select device"));
	else
	{
		CString Id = lvDevices.GetItemText(lvDevices.GetNextSelectedItem(Pos), 1);
		int Res = Watcher.Unpair(tstring(Id));
		if (Res != WCL_E_SUCCESS)
		{
			CString s;
			s.Format(_T("Unpair error: 0x%.8X"), Res);
			AfxMessageBox(s);
		}
		else
			AfxMessageBox(_T("Unpaired"));
	}
}

void CWiFiDirectClientDlg::OnBnClickedButtonEnumPaired()
{
	WCL_WFD_DEVICES Paired;
	int Res = Watcher.EnumPairedDevices(&Paired);
	if (Res != WCL_E_SUCCESS)
	{
		CString s;
		s.Format(_T("Enum paired error: 0x%.8X"), Res);
		AfxMessageBox(s);
	}
	else
	{
		lvDevices.DeleteAllItems();

		if (Paired.size() > 0)
		{
			for (WCL_WFD_DEVICES::iterator i = Paired.begin(); i != Paired.end(); i++)
			{
				int n = lvDevices.GetItemCount();
				lvDevices.InsertItem(n, _T(""));
				lvDevices.SetItemText(n, 1, (*i).c_str());
			}
		}
	}
}

Enumerations

Three enumerations appear throughout both samples:

Enumeration Values Used by
TwclWiFiDirectAdvertisementDiscoverability
wclWiFiDirectAdvertisementDiscoverability
adIntensive, adNone, adNormal Discoverability on the advertiser
TwclWiFiDirectConfigurationMethods
wclWiFiDirectConfigurationMethod
cmDisplayPin, cmProvidePin, cmPushButton ConfigurationMethods in OnPairGetParams
TwclWiFiDirectPairingProcedure
wclWiFiDirectPairingProcedure
ppInvitation, ppGroupOwnerNegotiation PairingProcedure in OnPairGetParams

In Delphi and C++ Builder, TwclWiFiDirectConfigurationMethods is a set, so the handlers assign an empty set and add members with + [cmDisplayPin]. In C#, VB.NET, and C++ the same type is a bit mask: ConfigurationMethods |= ... in C#, Or in VB.NET, and << on a std::set-like container in C++.


Typical workflow

Pulling the two samples together, a complete Wi-Fi Direct session looks like this:

  1. Advertiser: set Discoverability and AutonomousGroupOwnerEnabled, then call Start. OnStarted confirms the advertiser is running.
  2. Client: call Discover. OnDiscoveringStarted fires, then one OnDeviceFound per discovered device. Call Terminate to stop discovery.
  3. Client: take the Id of a discovered device and call Connect(Id).
  4. Advertiser: OnAcceptDevice asks whether to accept the incoming device. If accepted, the device is added to the list with the status "Connecting".
  5. Both sides: the pairing events fire in order. OnPairGetParams supplies the pairing procedure, group owner intent, and configuration methods; OnPairDisplayPin or OnPairProvidePin handles the PIN; OnPairConfirm confirms the pairing; OnPairCompleted reports the result.
  6. Both sides: OnDeviceConnected reports the connection result and, on success, the local and remote IP addresses. On the client, the addresses are read from the client; on the advertiser, from the device object.
  7. Advertiser: call Disconnect on a specific device to drop the connection, or Stop to stop the advertiser entirely.
  8. Client: call Disconnect to end the connection. Use IsPaired, Unpair, and EnumPairedDevices to inspect and manage the paired-device list.
  9. Cleanup: the samples call Stop, Disconnect, and Terminate on form destroy or form close.
Selected device is required

All actions that operate on a device — Connect, Disconnect (advertiser), IsPaired, Unpair — first check that a row is selected in lvDevices. If nothing is selected, the sample shows a "Select device" message and returns without making the API call.


Frequently asked questions

What is wclWiFiDirectAdvertiser?
wclWiFiDirectAdvertiser is the Wi-Fi Framework class that advertises a Wi-Fi Direct group and accepts incoming connections. It exposes Active, Discoverability, AutonomousGroupOwnerEnabled, Count, and an indexed accessor for connected devices, together with Start, Stop, and the OnStarted, OnStopped, OnAcceptDevice, OnDeviceConnected, OnDeviceDisconnected, and pairing events.
What is wclWiFiDirectDeviceWatcher?
wclWiFiDirectDeviceWatcher is the Wi-Fi Framework class that discovers nearby Wi-Fi Direct devices and manages paired devices. It exposes Discover and Terminate, IsPaired, Unpair, and EnumPairedDevices, and the OnDeviceFound, OnDiscoveringStarted, and OnDiscoveringCompleted events.
What is wclWiFiDirectClient?
wclWiFiDirectClient is the Wi-Fi Framework class that connects to a discovered Wi-Fi Direct device. It exposes Connect(Id) and Disconnect, the LocalAddress and RemoteAddress properties, and the OnDeviceConnected, OnDeviceDisconnected, and pairing events.
What are ppInvitation and ppGroupOwnerNegotiation?
They are the two values of the TwclWiFiDirectPairingProcedure enumeration exposed by the pairing events. ppInvitation selects invitation-based pairing, and ppGroupOwnerNegotiation selects group owner negotiation. Both samples expose the choice through a combo box and pass it back from the OnPairGetParams handler.
How do I enumerate paired devices?
Call wclWiFiDirectDeviceWatcher.EnumPairedDevices, passing a TStringList in Delphi, a List<String> in C#, a List(Of String) in VB.NET, or a WCL_WFD_DEVICES container in C++. The method returns WCL_E_SUCCESS on success and fills the container with the device Ids. The WiFiDirectClient sample clears the ListView and populates it with the returned Ids.