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.
Inhaltsverzeichnis
Nachrichten sind Texte, die von einer Applikation zu einer anderen gesandt wird, genauer von einer Applikation zu einem Fenster.
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:
|
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.
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
Das 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.
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
Das 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.
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).
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
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.