Motivation

Bei der Entwicklung von Arduino-Programmen gibt man üblicherweise Debug-Information über die serielle Schnittstelle aus. Zur Anzeige dieser Daten nutze ich meist mein selbst geschriebenes Programm VbTermie. Meist vergesse ich jedoch, die Verbindung vor dem Upload zu schließen. Der Uploader, mit dem ich dann ein geändertes Programm hochladen möchte, bricht ab, weil der COM-Port blockiert ist.

VbTermie wurde so verändert, dass man der laufenden Applikation eine Nachricht schicken kann, wenn der Uploader den Port benötigt. VbTermie soll darauf hin die Verbindung trennen und, nachdem das Programm hochgeladen wurde, angestoßen durch eine neue Nachricht, wieder aufbauen.

Für den Nachrichtenaustausch wurde eine Visual-Basic-Bibliothek entworfen, die man in ein beliebiges .NET-Programm einbauen kann und entsprechende Funktionen bereit stellt. Die Solution enthält die Bibliothek AppMsgSendReceive und je ein Programm zum Testen der Sende- und der Empfangsfunktionen.

In­halts­ver­zeich­nis

Bibliothek AppMsgSendReceive

Öffentliche Elemente

Senderseite

Sender-Testprogramm

Empfängerseite

Empfänger-Testprogramm

Erweiterungs-Ideen

Download


Bibliothek AppMsgSendReceive

Nachrichten sind Texte, die von einer Applikation zu einer anderen gesandt wird, genauer von einer Applikation zu einem Fenster.

Öffentliche Elemente

Klassendiagramm

Element Funktion Anmerkung
DefaultMessageID Die ID-Nummer der Meldung, die standardmäßig zum Senden und Empfangen verwandt wird, wenn im Konstruktor keine andere Angabe gemacht wird. Der Wert dieser Konstanten ist 0x400. Dies ist WM_USER.
MessageID Liefert die ID-Nummer der Meldung, die bei dieser Instanz zum Senden und Empfangen verwandt werden soll. Der Wert dieser Eigenschaft wird im Konstruktor festgelegt.
New Konstruktor. Initialisiert eine neue Instanz der AppSendReceiveService-Klasse. SSoll eine eigene ID für die Nachrichtekennung genutzt werden, muss diese hier angegeben werden.
PostString Sendet den angegebenen Text an das angegebene Fenster mit der eingestellten MessageID.

Das Zielfenster kann auf zwei Arten angegeben werden:

  • Angabe eines Fenster-Handles, dass extern ermittelt wurde
  • Angabe eines Fester-Titels. Die Nachricht wird an alle Fenster mit diesem Titel gesandt.
    Dies ist eine Änderung zur Version 1.0. Dort wurde die Nachricht nur an ein Fenster übermittelt.
WindowExists Prüft, ob ein Fenster mit dem angegeben Titel (Caption) existiert.  
DataReceived Dieses Ereignis wird ausgelöst, wenn eine Nachricht komplett empfangen wurde. Das Ereignis wird von der Methode HandleWindowMsg ausgelöst. HandleWindowMsg muss in die Methode Control.WndProc des empfangenden Fensters eingebunden werden (s.u.).

Für weitere Details siehe die im Download enthaltene Hilfedatei bzw. die Code-Dokumentation.

Senderseite

Auf der Senderseite wird die Windows-API-Funktion PostMessage (Tutorial: So werden in Windows Nachrichten versandt). Der Text wird zeichenweise über diese Funktion versandt.

PostMessage hat vier Parameter:

In diesem Programm wird lParam benutzt um die einzelnen Zeichen zu übergeben. wParam hart dabei den Wert 1. Wenn der komplette Text übertragen wurde, wird eine abschließender PostMessage-Aufruf mit wParam = 0 gemacht. Details hierzu kann man dem Quellcode entnehmen.

Die Einbindung in ein Programm geschieht wie folgt:

ReadOnly mAppMessage As New AppSendReceiveService

Private Sub cmdSend_Click(sender As Object, e As EventArgs) Handles cmdSend.Click
   mAppMessage.PostString(txtReceiver.Text, txtMessage.Text)
End Sub

Sender-Testprogramm

SendertestDas Testprogramm besitzt ein Eingabefeld "Empfänger" für den Fenstertitel des Empfängerfensters. Nach jeder Veränderung wird überprüft, ob ein Fenster mit dem angegebenen Namen existiert. Ist dies nicht der Fall, wird die Schaltfläche "Nachricht senden" blockiert.

Die zu versendende Nachricht kann im Feld "Nachricht" eingegeben werden. Ist das Feld leer, wird die Schaltfläche "Nachricht senden" blockiert.

Existiert das bezeichnete Fenster und ist das Nachrichtenfeld nicht leer, kann über die Schaltfläche "Nachricht senden" die Nachricht verschickt werden.

Ein Rückmeldung, ob die Nachricht erfolgreich verarbeitet wurde, erfolgt nicht. Man kann natürlich auch die Empfangsapplikation so gestalten, dass sie dem Sender, jetzt aber mit vertauschten Rollen, eine passende Nachricht zukommen lässt. Dazu benötigt sie das Fensterhandle oder den Fenstertitel des ursprünglichen Senders. Den sollte dieser als eine der ersten Nachrichten senden und ggf. wiederholen.

Empfängerseite

Auf der Empfängerseite hängt man sich in die Methode WndProc ein, in der eingehende Nachrichten verarbeitet werden. Der Funktion werden als Parameter die bei PostMessage gemachten Angaben Hwnd, wParam und lParam (s.o.), übergeben.

Trifft eine Nachricht wird geschaut, ob die Message-ID die vereinbarte ist. Aus den Nachrichten mit der passenden ID werden nach und nach die Zeichen entnommen und zum ursprüngliche Text zusammengebaut. Wird eine Nachricht mit wParam = 0 empfangen, ist dies das Zeichen für das Nachrichtenende. Es wird nun das Ereignis DataReceived ausgelöst, mit dem der empfangene Text verteilt wird.

Die Einbindung in ein Programm geschieht wie folgt:

Private WithEvents mReceiver As New AppSendReceiveService

Protected Overrides Sub WndProc(ByRef m As Windows.Forms.Message)
   If Not mReceiver.HandleWindowMsg(m) Then
      MyBase.WndProc(m)
   End If
End Sub

Private Sub mReceiver_DataReceived(Message As String) Handles mReceiver.DataReceived
   ' Mache etwas mit Message
End Sub

Empfänger-Testprogramm

EmpfängertestDas Empfänger-Programm ist etwas trickreicher aufgebaut, weil ich den Fenstertitel des Hauptfensters nicht fest vergeben wollte. Häufig werden in dem Fenstertitel der Name der in Bearbeitung befindlichen Datei angezeigt. Dann wird es schwierig, das Fenster zu finden. Ggf. könnte man noch die Fensterklasse benutzen, aber die ist in .Net-Programmen leider nicht zugänglich.

Fenstertitel

Im diesem Umstand Rechnung zu tragen habe ich ein verborgenes Fenster mit dem passenden Fenstertitel eingerichtet, dass die Nachrichten empfängt und an das Hauptfenster weiter leitet. Dieses Fenster wird kurz angezeigt und sofort wieder geschlossen. Damit dieses "kurz" und "sofort" garantiert unsichtbar bleibt, wird das Fenster außerhalb des sichtbaren Bildschirms geöffnet (x = -1000).

Verborgenes Fenster

Der Code zum verstecken des Fensters ist wie folgt:

Private WithEvents mHiddenReceiver As New HiddenReceiver
Private Sub EmpfängerTest_Load(sender As Object, e As EventArgs) Handles MyBase.Load
   mHiddenReceiver.Show()
   mHiddenReceiver.Visible = False
End Sub

Im Form-Desinger sind dann folgende Einstellungen für das minimalistische Fenster abgelegt:

Me.ControlBox = False
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Location = New System.Drawing.Point(-1000, 0)
Me.MinimizeBox = False
Me.ShowIcon = False
Me.ShowInTaskbar = False
Me.WindowState = System.Windows.Forms.FormWindowState.Minimized

Das unsichtbare Fenster reicht die empfangene Nachricht einfach weiter:

Public Event DataReceived(Message As String)
Private Sub mReceiver_DataReceived(Message As String) Handles mReceiver.DataReceived
   RaiseEvent DataReceived(Message)
End Sub

Erweiterungs-Ideen

Der Beim Senden eines Zeichen wird wParam nur in zwei Ausprägungen genutzt, 0 und 1. Hier besteht die Möglich, weitere Ausprägung zur Implementierung von mehreren Postfächern nutzen. wParam erhielte dann die Postfach-ID. Für jedes Postfach müsste jeweils eine unterschiedliche Ausprägung für "Zeichen" und Nachrichtenende" vereinbart werden.

Download

Quellcode
Demo (Binary)
API-Dokumentation (Hilfe-Datei)