Im letzten Artikel der Serie wurde das WindowsUri-Protokoll vorgestellt, um URIs über NFC auszutauschen. Neben WindowsUri gibt es noch zahlreiche weitere Protokolle, die zum Austausch anderer Inhaltsarten über NFC verwendet werden können. In diesem Artikel der Serie werden somit zwei dieser Protokolle vorgestellt. Der Fokus wird hierbei auf WindowsMime gelegt, das unter anderem für Texte, Bilder und vCards verwendet werden kann und auf das Windows-Protokoll, das für beliebige binäre Nachrichten verwendet wird.

Hinweis: Für die folgenden Beispiele muss das Projekt in Visual Studio so eingerichtet werden, wie bereits im ersten Artikel der Serie beschrieben.

1. Windows Mime

Das WindowsMime-Protokoll wird verwendet, um Daten zu lesen und zu veröffentlichen, die durch einen Mime-Typ dargestellt werden. Die Mime-Typen können hierbei vielseitige Inhalte beinhalten, ein Auszug ist in folgender Tabelle dargestellt:

Inhalt Mime-Typ
Text text/plain
Bild image/jpeg, image/gif, image/png
vCard text/vcard

Wird eine Nachricht über WindowsMime veröffentlicht, muss der Mime-Typ als SubTyp zusätzlich zum Protokoll mit angegeben werden. Wird das Protokoll hingegen abonniert, kann das Protokoll WindowsMime ohne zusätzlichen Subtyp angegeben werden. Das ProximityDevice reagiert dann auf alle möglichen Mime-Typen. Beide Herangehensweisen werden in den nächsten Abschnitten vorgestellt.

1.1 Plain text per NFC lesen und schreiben

Für einfache Textinhalte wird der Mime-Typ text/plain verwendet. Um einen Text von einen beliebigen Gerät oder NFC-Tag zu lesen, wird über die Methode SubscribeForMessage das Protokoll WindowsMime.text/plain abonniert. Der empfange Buffer muss dann lediglich in ein ByteArray und daraufhin über die entsprechende Encoding-Funktion in einen String konvertiert werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    var device = ProximityDevice.GetDefault();
    if (device != null)
    {
        // Subscribe to mime text/plain type messages
        this.subscribedMessageId = device.SubscribeForMessage("WindowsMime.text/plain", this.MessageReceivedHandler);
    }
    else
    {
        await new MessageDialog("Your phone has no NFC or it is disabled").ShowAsync();
    }
}

private async void MessageReceivedHandler(ProximityDevice device, ProximityMessage message)
{
    // Get the data
    var buffer = message.Data.ToArray();
    var data = Encoding.UTF8.GetString(buffer, 0, buffer.Length);

    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
    {
        this.TextArea.Text = data;
    });
}

Um einen Text per NFC für andere Geräte zu veröffentlichen, wird das gleiche Protokoll wie auch zum Lesen verwendet. Um die Nachricht statt dessen für NFC-Tags zu veröffentlichen, wird das Protokoll um den Parameter WriteTag wie folgt ergänzt: WindowsMime:WriteTag.text/plain.

1
2
3
4
5
6
7
8
9
10
11
var device = ProximityDevice.GetDefault();
if (device != null)
{
    var buffer = Encoding.UTF8.GetBytes("Hello world!");

    // Write the text to a nfc tag
    this.publishedMessageId = this.device.PublishBinaryMessage("WindowsMime:WriteTag.text/plain", buffer.AsBuffer(), this.PublishedHandler);

    // Publish the text to nfc devices
    // this.publishedMessageId = this.device.PublishBinaryMessage("WindowsMime.text/plain", buffer.AsBuffer(), this.PublishedHandler);
}

1.2 Bilder per NFC lesen und schreiben

Wie oben in der Tabelle bereits dargestellt, können verschiedene Bildformate über NFC ausgetauscht werden. In den folgenden Beispielen wird das PNG-Format verwendet, der entsprechende Mime-Type ist image/png. Für den Lesevorgang wird hierbei dementsprechend das Protokoll WindowsMime.image/png abonniert. Zum Anzeigen eines Bildes muss der ankommende Buffer erst in einen Stream des Typs RandomAccessStream konvertiert werden und kann daraufhin als BitmapImage angezeigt werden.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    var device = ProximityDevice.GetDefault();
    if (device != null)
    {
        // Subscribe to mime text/plain type messages
        this.subscribedMessageId = device.SubscribeForMessage("WindowsMime.image/png", this.MessageReceivedHandler);
    }
    else
    {
        await new MessageDialog("Your phone has no NFC or it is disabled").ShowAsync();
    }
}

private async void MessageReceivedHandler(ProximityDevice device, ProximityMessage message)
{
    // Get the data
    var buffer = message.Data.ToArray();
    byte[] array = new byte[buffer.Length];
    System.Buffer.BlockCopy(buffer, 0, array, 0, buffer.Length);

    using (var stream = new InMemoryRandomAccessStream())
    {
        using (var writer = new DataWriter(stream))
        {
            writer.WriteBytes(array);
            await writer.StoreAsync();
            writer.DetachStream();
        }

        stream.Seek(0);

        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            var image = new BitmapImage();
            image.SetSource(stream);

            this.ImageRead.Source = image;
        });
    }
}

Beim Veröffentlichen von Bildern für NFC-Tags muss beachtet werden, dass diese nur eine begrenzte Größe besitzen. Die Speicherkapazität kann über die Methode in folgendem Artikel abgefragt werden. Die dort zurückgegebene Größe ist ein Brutto-Wert, von dem ca. 75% nutzbar sind. Der Rest ist für Headerdaten reserviert.

Zum Veröffentlichen eines Bildes wird das Protokoll WindowsMime.image/png, bzw. WindowsMime:WriteTag.image/png verwendet. Das Bild muss als ByteArray vorliegen, um dann in den nötigen Buffer konvertiert werden zu können.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var device = ProximityDevice.GetDefault();
if (device != null)
{
    var file = await StorageFile.GetFileFromApplicationUriAsync(new Uri("ms-appx:///Assets/tuna-icon.png", UriKind.RelativeOrAbsolute));
    using (Stream stream = await file.OpenStreamForReadAsync())
    {
        byte[] array = new byte[stream.Length];
        stream.Read(array, 0, (int)stream.Length);

        // Write the image to a nfc tag
        this.publishedMessageId = this.device.PublishBinaryMessage("WindowsMime:WriteTag.image/png", array.AsBuffer(), this.PublishedHandler);

        // Publish the image to nfc devices
        // this.publishedMessageId = this.device.PublishBinaryMessage("WindowsMime.image/png", array.AsBuffer(), this.PublishedHandler);
    }
}

1.3 Allgemeine Abonnierung des WindowsMime-Protokolls

Wie bereits erwähnt, muss zum Abonnieren von Mime-Nachrichten kein spezieller Mime-Typ angegeben werden. Es reicht das Protokoll WindowsMime anzugeben. Hierbei ist zu beachten, dass in diesem Fall die ersten 256 Bytes den Mime-Typ enthalten und mit einem \0-Zeichen abgeschlossen werden. Diese müssen separat ausgelesen werden. Daraufhin können die restlichen Daten gelesen und entschieden werden, wie mit den Daten umzugehen ist.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    var device = ProximityDevice.GetDefault();
    if (device != null)
    {
        // Subscribe to uri messages
        this.subscribedMessageId = device.SubscribeForMessage("WindowsMime", this.MessageReceivedHandler);
    }
}

private async void MessageReceivedHandler(ProximityDevice device, ProximityMessage message)
{
    // Get the data
    var buffer = message.Data.ToArray();
    int mimesize = 0;

    // Search first '\0' character
    for (mimesize = 0; mimesize < 256; ++mimesize)
    {
        if (buffer[mimesize] == 0)
        {
            break;
        }
    };

    // Extract mimetype
    var mimeType = Encoding.UTF8.GetString(buffer, 0, mimesize);

    // Handle mime message according to the mime type
    if (mimeType == "text/plain")
    {
        // Convert data to string, cut off the mime header
        var data = Encoding.UTF8.GetString(buffer, 256, buffer.Length - 256);

        // ...
    }
    else if (mimeType == "image/png")
    {
        // ...
    }
}

1.5 Experience in Windows Phone beim Lesen von Mime-Tags

Alle eingehenden NFC-Nachrichten des Typs WindowsMime können vom Windows Phone verarbeitet werden. Es wird die je zum Mime-Typ passende App geöffnet (Word, Bildvorschau, Kontaktansicht, …)

Ein als WindowsMime.image/png gespeichertes Bild wird in Windows Phone empfangen Ein als WindowsMime.image/png gespeichertes Bild wird in Windows Phone empfangen

2. Binäre Nachrichten mit dem Windows-Protokoll

Über NFC können auch binäre Nachrichten ausgetauscht werden, auf die innerhalb einer App reagiert werden kann. Diese Nachrichten basieren auf dem _Windows-_Protokoll mit einem beliebigen Subtyp. Außerhalb der zugehörigen App werden solche Nachrichten von Windows Phone ignoriert, da das Betriebssystem nicht entscheiden kann, wie es mit den binären Daten umzugehen hat.

In folgendem Beispiel sollen Objekte des benutzerdefinierten Typs Person über NFC ausgetauscht werden. Die Klasse Person besteht aus den Attributen FirstName und LastName. Vor dem Veröffentlichen der Objekte werden diese mithilfe eines DataContractSerializers serialisiert.

public class Person
{
    public string LastName { get; set; }
    public string FirstName { get; set; }
}

2.1 Daten veröffentlichen

Als Subtyp für das Protokoll wird hier der Bezeichner Person gewählt. Dadurch ergibt sich das folgende Protokoll, das zur Veröffentlichung verwendet wird: Windows.Person. Zum Veröffentlichen der Nachrichten wird ein Objekt des Typs Person erzeugt und mit dem DataContractSerializer serialisiert. Daraufhin wird es in ein ByteArray geschrieben und kann veröffentlicht werden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
private async void WriteCustom_Tapped(object sender, TappedRoutedEventArgs e)
{
    var device = ProximityDevice.GetDefault();
    if (device != null)
    {
        var stream = new MemoryStream();

        // Create new payload object
        var person = new Person { FirstName = "John", LastName = "Doe" };

        // Serialize object
        var serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(Person));
        serializer.WriteObject(stream, person);

        // Convert to array
        byte[] array = new byte[stream.Length];
        stream.Position = 0;
        stream.Read(array, 0, (int)stream.Length);

        // Write the text to a nfc tag
        this.publishedMessageId = this.device.PublishBinaryMessage("Windows:WriteTag.Person", array.AsBuffer(), this.PublishedHandler);

        // Publish the text to nfc devices
        // this.publishedMessageId = this.device.PublishBinaryMessage("Windows.Person", buffer.AsBuffer(), this.PublishedHandler);
    }
}

 2.2 Daten lesen

Um die entsprechenden Daten zu empfangen, wird das gleiche Protokoll Windows.Person abonniert. Die empfangenen Daten müssen wieder über einen DataContractSerializer in ein Person-Objekt deserialisiert werden:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
protected async override void OnNavigatedTo(NavigationEventArgs e)
{
    // Get the nfc device
    var device = ProximityDevice.GetDefault();
    if (device != null)
    {
        // Subscribe to the windows protocol and the custom subtype
        this.subscribedMessageId = device.SubscribeForMessage("Windows.Person", this.MessageReceivedHandler);
    }
}

private async void MessageReceivedHandler(ProximityDevice device, ProximityMessage message)
{
    // Get the data
    var buffer = message.Data.ToArray();

    byte[] array = new byte[buffer.Length];
    using (MemoryStream stream = new MemoryStream(buffer))
    {
        // Deserialize object
        var serializer = new System.Runtime.Serialization.DataContractSerializer(typeof(Person));
        var item = (Person)serializer.ReadObject(stream);

        // Display result in text area - use the dispatcher, as the reading occurs in a separate thread
        await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
        {
            this.TextBlockFirstName.Text = item.FirstName;
            this.TextBlockLastName.Text = item.LastName;
        });

    }
}

3. Weitere Protokolle

Es gibt noch ein paar weitere Protokolle zum Datenaustausch über NFC, die bisher noch nicht betrachtet worden sind.

Über das LaunchApp-Protokoll kann eine benutzerdefinierte App mit optionalen Startparametern geöffnet werden. Ist die App nicht installiert, wird diese im Store geöffnet. Dieses Verhalten kann jedoch auch über einen Link und das WindowsUri-Protokoll abgebildet werden, das bereits im ersten Artikel der Serie beschrieben wurde. Wie man eine App mit bestimmten Parametern über NFC und das WindowsUri-Protokoll starten kann, ist sehr gut in folgendem Msdn-Artikel beschrieben.

Alle bisher genannten Protokolle waren auf die Windows-Plattform beschränkt. Das sogenannte NDEF-Protokoll (NFC Data Exchange Format) ist ein plattformunabhängiger Standard zum Nachrichtenaustausch über NFC, der von vielen verschiedenen mobilen Betriebssystemen unterstützt wird.

4. Zusammenfassung

In diesem Artikel wurde zum einen das WindowsMime-Protokoll vorgestellt, durch das sich viele verschiedene Inhalte über NFC austauschen lassen. Zum anderen wurde auch das Windows-Protokoll vorgestellt, über das binäre Nachrichten ausgetauscht werden können. Durch die ersten beiden Teile der Artikelserie wurden somit bereits die meisten Nachrichtenprotokolle für NFC unter der Windows-Plattform betrachtet. Im nächsten Teil der Artikelserie wird der Verbindungsaufbau zwischen 2 Windows-Geräten über NFC vorgestellt, um dadurch eine dauerhafte Verbindung zum Datenaustausch zu erhalten.

Beispielprojekt: Lesen und Schreiben von Mime und benutzerdefinierten Daten über NFC

Das Beispielprojekt ist hier auf GitHub zu finden.