Share via



Juli 2016

Band 31, Nummer 7

Moderne Apps – Entwickeln eines WLAN-Scanners auf der UWP

Von Frank La La

Seit ca. 10 Jahren sind WLANs allgegenwärtig. Viele Geschäfte und Restaurants bieten Kunden mittlerweile einen kostenlosen WLAN-Zugriff. Praktisch alle Hotels bieten ihren Gästen einen drahtlosen Zugriff auf das Internet. Die meisten von uns haben zu Hause ein WLAN. Nur noch wenige Tablets und Mobilgeräte haben Ethernet-Buchsen, sodass WLAN zu einem integralen Bestandteil unseres modernen Lebens geworden ist. Darüber hinaus denken wir nicht viel darüber nach.

Es stellen sich aber zahlreiche Fragen. Was ist mit der schiere Menge von WLANs um uns herum? Wie viele gibt es? Sind sie geschützt? Auf welchen Kanal werden sie betrieben? Wie heißen sie? Können wir sie kartieren? Können wir aus den WLAN-Metadaten Erkenntnisse gewinnen?

Als ich vor Kurzem mit meinen Hunden spazieren ging, warf ich zufällig auf einem Handy einen Blick auf den Bildschirm mit den WLAN-Verbindungen und bemerkte einige witzige Netzwerknamen. Deshalb fragte ich mich, wie viele andere eher einen lustigen als einen praktischen Namen gewählt hatten. Dann kam mir die Idee, die WLANs in meiner näheren Nachbarschaft zu scannen und zu kartieren. Wenn ich diesen Vorgang automatisieren könnten, könnte ich sogar WLANs beim Pendeln zur Arbeit scannen und kartieren. Im Idealfall hätte ich ein Programm auf einem Raspberry Pi, das in regelmäßigen Abständen drahtlos einen Scan durchführt und diese Daten in einem Webdienst aufzeichnet. Das wäre mit Sicherheit praktischer, als zwischendurch einen Blick auf mein Smartphone zu werfen.

Wie sich herausstellt, bietet die universelle Windows-Plattform (UWP) über Klassen im Namespace „Windows.Devices.WiFi“ einen umfassenden Zugriff auf WLAN-Daten. Wie Sie wissen, kann eine UWP-App nicht nur auf Smartphones und PCs, sondern auch auf Raspberry Pi 2 mit Windows 10 IoT Core ausgeführt werden. Nun hatte ich alles, was ich brauchte, um mein Projekt zu erstellen.

In dieser Kolumne untersuche ich die Grundlagen des Scannens von WLANs mithilfe der direkt in die UWP integrierten APIs.

Der Namespace „Windows.Devices.WiFi“

Die Klassen im Namespace „Windows.Devices.WiFi“ enthalten alles, was zum Scannen und Erkunden von Funkadaptern und -netzwerken in der näheren Umgebung benötigt wird. Fügen Sie nach einem Erstellen eines neuen UWP-Projekts in Visual Studio eine neue Klasse namens „WifiScanner“ und die folgende Eigenschaft hinzu:

public WiFiAdapter WiFiAdapter { get; private set; }

Da mehrere WLAN-Adapter in einem bestimmten System möglich sind, müssen Sie den gewünschten WLAN-Adapter auswählen. Die „InitializeFirstAdapter“-Methode ruft den ersten vom System aufgezählten Adapter ab (siehe Abbildung 1).

Abbildung 1: Suchen und Initialisieren des ersten mit dem System verbundenen WLAN-Adapters

private async Task InitializeFirstAdapter()
{
  var access = await WiFiAdapter.RequestAccessAsync();
  if (access != WiFiAccessStatus.Allowed)
  {
    throw new Exception("WiFiAccessStatus not allowed");
  }
  else
  {
    var wifiAdapterResults =
      await DeviceInformation.FindAllAsync(WiFiAdapter.GetDeviceSelector());
  if (wifiAdapterResults.Count >= 1)
    {
      this.WiFiAdapter =
        await WiFiAdapter.FromIdAsync(wifiAdapterResults[0].Id);
    }
    else
    {
      throw new Exception("WiFi Adapter not found.");
    }
  }
}

Hinzufügen der WLAN-Funktion

Wie Sie ggf. bemerkt haben, gibt es eine Zugriffsprüfung für das WLAN. Der Code löst eine Ausnahme aus, wenn die „RequestAccessAsync“-Methode „false“ zurückgibt. Der Grund ist, dass die App eine Gerätefunktion benötigt, die das Scannen nach und Verbinden mit WLANs zulässt. Diese Funktion ist im Editor für die Manifesteigenschaften auf der Registerkarte „Funktionen“ nicht aufgeführt. Um diese Funktion hinzuzufügen, klicken Sie mit der rechten Maustaste auf die Datei „Package.appxmanager“, und wählen Sie dann „Code anzeigen“.

Sie sehen nur die rohe XML der Datei „Package.appxmanager“. Fügen Sie dem Knoten „Capabilities“ den folgenden Code hinzu:

<DeviceCapability Name="wifiControl" />

Speichern Sie nun die Datei. Ihre App hat nun Berechtigungen für den Zugriff auf die WLAN-APIs.

Untersuchen von WLANs

Nun da Sie über den Code zum Bestimmen des zu verwendenden WLAN-Adapters und die Berechtigung für den Zugriff darauf verfügen, ist der nächste Schritt das Scannen nach Netzwerken. Zum Glück ist der Code hierfür recht einfach. Es handelt sich bloß um einen Aufruf der „ScanAsync“-Methode für das „WifiAdapter“-Objekt. Fügen Sie der „WifiScanner“-Klasse die folgende Methode hinzu:

public async Task ScanForNetworks()
{
  if (this.WiFiAdapter != null)
  {
    await this.WiFiAdapter.ScanAsync();
  }
  }

Sobald „ScanAsync“ ausgeführt wird, wird die „NetworkReport“-Eigenschaft des „WifiAdapter“-Objekts aufgefüllt. „NetworkReport“ ist eine Instanz der „WiFiNetworkReport“-Klasse, die „AvailableNetworks“ (List<WiFiAvailableNetwork>) enthält. Das „WiFiAvailableNework“-Objekt enthält zahlreiche Datenpunkte zu einem bestimmten Netzwerk. Ohne mit dem Netzwerk verbunden zu sein, können Sie die SSID (Service Set Identifier), Signalstärke, Verschlüsselungsmethode und Betriebszeit des Zugriffspunkts herausfinden.

Das Durchlaufen der verfügbaren Netzwerke ist recht einfach: Sie erstellen ein POCO (Plain Old CLR Object), dem einige der Daten aus den „WiFiAvailableNetwork“-Objekten hinzugefügt werden (siehe den folgenden Code):

foreach (var availableNetwork in report.AvailableNetworks)
{
  WiFiSignal wifiSignal = new WiFiSignal()
  {
    MacAddress = availableNetwork.Bssid,
    Ssid = availableNetwork.Ssid,
    SignalBars = availableNetwork.SignalBars,
    ChannelCenterFrequencyInKilohertz =
      availableNetwork.ChannelCenterFrequencyInKilohertz,
    NetworkKind = availableNetwork.NetworkKind.ToString(),
    PhysicalKind = availableNetwork.PhyKind.ToString()
  };
}

Erstellen der Benutzeroberfläche

Wenngleich ich vorhabe, die App im endgültigen Projekt ohne eine Benutzeroberfläche auszuführen, ist es für die Entwicklung und Problembehandlung nützlich, die Netzwerke in der näheren Umgebung und die zu ihnen gehörenden Metadaten anzuzeigen. Es ist auch nützlich für Entwickler, die ggf. derzeit keinen Raspberry Pi haben, aber dennoch fortfahren möchten. Wie Abbildung 2 zeigt, ist die XAML für das Projekt unkompliziert, und es gibt ein mehrzeiliges Textfeld (TextBox) zum Speichern der Ausgabe des Scans.

Abbildung 2: Die XAML für die Benutzeroberfläche

<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
  <Grid.RowDefinitions>
    <RowDefinition Height="60"/>
      <RowDefinition Height="60"/>
      <RowDefinition Height="*"/>
  </Grid.RowDefinitions>
  <TextBlock FontSize="36" Grid.RowSpan="2" >WiFi Scanner</TextBlock>
  <StackPanel Name="spButtons" Grid.Row="1" Orientation="Horizontal">
    <Button Name="btnScan" Click="btnScan_Click" Grid.Row="1">Scan For
      Networks</Button>
  </StackPanel>
  <TextBox Name="txbReport" TextWrapping="Wrap" AcceptsReturn="True"
    Grid.Row="2"></TextBox>
  </Grid>
</Page>

Erfassen von Standortdaten

Um einen zusätzlichen Nutzen zu bieten, sollte bei jedem Scan des WLAN auch der Standort des Scans aufgezeichnet werden. Dies ermöglicht später interessante Einblicke und Datenvisualisierungen. Das Hinzufügen von Standortinformationen zu UWP-Apps ist einfach. Sie müssen jedoch Ihrer App zuvor die Standortfunktion hinzufügen. Doppelklicken Sie dazu im Projektmappen-Explorer auf die Datei „Package.appxmanifest“, klicken Sie auf die Registerkarte „Funktionen“, und aktivieren Sie dann in der Liste „Funktionen“ das Kontrollkästchen „Standort“.

Der folgende Code ruft den Standort mithilfe der in die UWP integrierten APIs ab:

Geolocator geolocator = new Geolocator();
Geoposition position = await geolocator.GetGeopositionAsync();

Nun da Sie über einen Standort verfügen, können Sie die Standortdaten speichern. Es folgt die „WiFiPointData“-Klasse, in der Standortdaten sowie Informationen zu Netzwerken am Standort gespeichert werden:

public class WiFiPointData
{
  public DateTimeOffset TimeStamp { get; set; }
  public double Latitude { get; set; }
  public double Longitude { get; set; }
  public double Accuracy { get; set; }
  public List<WiFiSignal> WiFiSignals { get; set; }
  public WiFiPointData()
  {
    this.WiFiSignals = new List<WiFiSignal>();
  }
}

An dieser Stelle ist der Hinweis wichtig, dass wenn Ihr Gerät keine GPS-Unterstützung bietet, die App eine WLAN-Verbindung mit dem Internet braucht, um den Standort aufzulösen. Ohne integrierten GPS-Sensor benötigen Sie einen mobilen Hotspot und müssen sicherstellen, dass Ihr Laptop oder Raspberry Pi 2 damit verbunden ist. Dies bedeutet auch, dass der gemeldete Standort weniger genau ist. Weitere Informationen zu bewährten Methoden zum Erstellen von UWP-Apps mit Standortbestimmung finden Sie im Windows Dev Center-Artikel „Richtlinien für Apps mit Standortbestimmung“ unter bit.ly/1P0St0C.

Wiederholtes Scannen

Für das Scannen und Kartieren während der Pendlerfahrt muss die App regelmäßig nach WLANs scannen. Hierzu müssen Sie einen DispatcherTimer zum Scannen nach WLANs in regelmäßigen Zeitabständen verwenden. Wenn Sie mit der Funktionsweise von DispatcherTimer nicht vertraut sind, konsultieren Sie die Dokumentation unter bit.ly/1WPMFcp.

Wichtig ist der Hinweis, dass ein WLAN-Scan abhängig von Ihrem System mehrere Sekunden dauern kann. Der folgende Code legt einen DispatcherTimer so fest, dass alle 10 Sekunden ein Ereignis ausgelöst wird, was selbst für das langsamste System ausreichend Zeit ist:

DispatcherTimer timer = new DispatcherTimer();
timer.Interval = new TimeSpan(0, 0, 10);
timer.Tick += Timer_Tick;
timer.Start();

Alle 10 Sekunden führt der Timer den Code in der „Timer_Tick“-Methode aus. Der folgende Code scannt nach WLANs und fügt die Ergebnisse anschließend an das Textfeld auf der Benutzeroberfläche an:

private async void Timer_Tick(object sender, object e)
{
  StringBuilder networkInfo = await RunWifiScan();
  this.txbReport.Text = this.txbReport.Text + networkInfo.ToString();
}

Melden von Scanergebnissen

Wie bereits erwähnt, werden die Ergebnisse des Scans nach Aufrufen der „ScanAsync“-Methode in „List<WiFiAvailableNetwork>“ gespeichert. Um zu diesen Ergebnissen zu gelangen, muss lediglich die Liste durchlaufen werden. Der Code in Abbildung 3 macht genau das und legt die Ergebnisse in einer Instanz der „WiFiPointData“-Klasse ab.

Abbildung 3: Code zum Durchlaufen aller während eines Scans gefundenen Netzwerke

foreach (var availableNetwork in report.AvailableNetworks)
{
  WiFiSignal wifiSignal = new WiFiSignal()
  {
    MacAddress = availableNetwork.Bssid,
    Ssid = availableNetwork.Ssid,
    SignalBars = availableNetwork.SignalBars,
    NetworkKind = availableNetwork.NetworkKind.ToString(),
    PhysicalKind = availableNetwork.PhyKind.ToString(),
    Encryption = availableNetwork.SecuritySettings.NetworkEncryptionType.ToString()
  };
  wifiPoint.WiFiSignals.Add(wifiSignal);
  }

Um die Benutzeroberfläche einfach zu halten und dennoch eine umfassende Datenanalyse zu ermöglichen, können Sie die „WiFiPointData“ in ein CSV-Format umwandeln und den Text des Textfelds auf der Benutzeroberfläche festlegen. CSV (Comma Separated Value, durch Trennzeichen getrennte Werte) ist ein relativ einfaches Format, das zu Analysezwecken in Excel und Power BI importiert werden kann. In Abbildung 4 sehen Sie den Code zum Konvertieren von „WiFiPointData“.

Abbildung 4: Konvertieren von „WiFiPointData“

private StringBuilder CreateCsvReport(WiFiPointData wifiPoint)
{
  StringBuilder networkInfo = new StringBuilder();
  networkInfo.AppendLine("MAC,SSID,SignalBars,Type,Lat,Long,Accuracy,Encryption");
  foreach (var wifiSignal in wifiPoint.WiFiSignals)
  {
    networkInfo.Append($"{wifiSignal.MacAddress},");
    networkInfo.Append($"{wifiSignal.Ssid},");
    networkInfo.Append($"{wifiSignal.SignalBars},");
    networkInfo.Append($"{wifiSignal.NetworkKind},");
    networkInfo.Append($"{wifiPoint.Latitude},");
    networkInfo.Append($"{wifiPoint.Longitude},");
    networkInfo.Append($"{wifiPoint.Accuracy},");
    networkInfo.Append($"{wifiSignal.Encryption}");
    networkInfo.AppendLine();
  }
  return networkInfo;
}

Visualisieren der Daten

Als Nächstes habe ich meinen Clouddienst zum Anzeigen und Visualisieren der Daten eingerichtet. Ich habe die von der App generierten CSV-Daten kopiert und in eine Textdatei eingefügt. Diese Datei habe ich dann mit der Erweiterung CSV gespeichert. Als Nächstes habe ich die Daten in Power BI Desktop importiert. Power BI Desktop kann von powerbi.microsoft.com kostenlos heruntergeladen werden und vereinfacht das Visualisieren und Untersuchen von Daten.

Zum Importieren der Daten aus der App klicken Sie auf dem Begrüßungsbildschirm von Power BI auf „Daten abrufen“.

Wählen Sie auf dem folgenden Bildschirm „CSV“ aus, und klicken Sie auf „Verbinden“. Wählen Sie im Dialogfeld zur Dateiauswahl die CSV-Datei mit den Daten, die Sie aus der App kopiert und eingefügt haben.

Nach dem Laden sehen Sie rechts auf dem Bildschirm eine Liste mit Feldern. Ein vollständiges Tutorial zu Power BI Desktop würde den Rahmen dieses Artikels sprengen. Doch es braucht nicht viel, um eine Visualisierung zu erstellen, die den Standort von WLANs, ihre SSIDs und die von ihnen genutzten Verschlüsselungsprotokolle zeigt (siehe Abbildung 5).

Power BI-Visualisierung der mit der WLAN-Scanner-App gesammelten Daten
Abbildung 5: Power BI-Visualisierung der mit der WLAN-Scanner-App gesammelten Daten

Verblüffenderweise ist ca. ein Drittel der Netzwerke völlig unverschlüsselt. Während es sich bei einigen um von verschiedenen Firmen eingerichtete Gastnetzwerke handelt, ist dies bei anderen nicht der Fall.

Praktische Anwendungen

Eigentlich war meine ursprüngliche Absicht, das technische Können meiner Nachbarn zu messen. Doch dieses Projekt hat auch einen interessanten praktischen Nutzen. Die Fähigkeit, Stärke und Standort von WLAN-Signalen mühelos und automatisch zu kartieren, eröffnet interessante Anwendungsmöglichkeiten. Was könnte eine Stadt tun, wenn jeder Linienbus mit einem IoT-Gerät mit dieser App ausgestattet wäre, die darauf ausgeführt wird? Städte könnten die Verbreitung von WLANs messen und diese Daten mit Einkommensdaten des jeweiligen Wohngebiets korrelieren. Mandatsträger könnten dann basierend auf diesen Daten informierte politische Entscheidungen treffen. Wenn eine Kommune überall oder in bestimmten Gebieten öffentliches WLAN bereitstellt, kann die Signalstärke in Echtzeit gemessen werden, ohne dass zusätzliche Kosten für das Beauftragen von Technikern anfallen. Gemeinden könnten auch bestimmen, wo ungesicherte Netzwerke verbreitet sind, und zielgerichtete Programme starten, um für mehr Bewusstsein für Sicherheit im Cyberspace zu sorgen.

Etwas kleiner gedacht, kann die Fähigkeit, WLAN-Metadaten schnell zu scannen, beim Einrichten Ihres eigenen Netzwerks praktisch sein. Viele Router bieten Benutzern die Möglichkeit, den Kanal zu ändern, auf dem sie senden. Ein gutes Beispiel hierfür ist eine App namens „Wi-Fi Analyzer“ (bit.ly/25ovZ0Q), die u. a. die Stärke und Frequenz von WLANs in der Nähe anzeigt. Dies ist praktisch beim Einrichten eines WLAN an einem neuen Standort.

Zusammenfassung

Das Kopieren und Einfügen von Textdaten von der Benutzeroberfläche lässt sich nicht skalieren. Wenn darüber hinaus das Ziel die Ausführung der App auf einem IoT-Geräte ohne Anzeigemöglichkeit ist, muss die App Daten ohne jegliche Benutzeroberfläche in die Cloud senden. In meiner Kolumne im nächsten Monat erfahren Sie, wie Sie einen Clouddienst einrichten, der alle diese Daten erfasst. Außerdem erfahren Sie, wie Sie die Lösung auf einem Raspberry Pi 2 mit ausgeführtem Windows IoT Core bereitstellen.


Frank La Vigneist ein IT-Experte im Microsoft Technology and Civic Engagement-Team. Sein Ziel ist es, Benutzern bei der Nutzung von IT-Technologie zu helfen, um für eine bessere Erfahrung zu sorgen. Auf FranksWorld.com führt er einen Blog, sein YouTube-Kanal heißt Frank’s World TV (youtube.com/FranksWorldTV).

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Rachel Appel, Robert Bernstein und Jose Luis Manners