Viele Programmierer verspüren immer wieder den Wunsch, Daten zwischen verschiedenen Programmen auf 'legale' Weise austauschen zu können. Leider ist dieser Datenaustausch nicht genormt. Trotzdem ist es möglich, wenn die Programme aufeinander abgestimmt worden sind, Daten über GEM (unglaublich, aber wahr) auszutauschen. Dies soll unser Thema in diesem Monat sein.
Einige der im GEM vorhandenen Befehle sind inzwischen weitläufig bekannt und beschrieben worden, und trotzdem findet man immer wieder eine gewisse Scheu und Unsicherheit, wenn es um die Anwendung ganz bestimmter Befehle geht. Zu diesen Befehlsgruppen gehören die APPL ,SHELL- und SCRAP-Befehle. Mit den SHELL- und SCRAP-Befehlen werden wir uns ein anderes Mal beschäftigen. Diese ST-Ecke soll den Befehlen appl_write(), Appl_read() und Appl_find() gewidmet sein. Zu Appl_tplay() und Appl_trecord() läßt sich folgendes sagen: Selbst nach längerem Ausprobieren ( von einigen Programmierern ) ist es uns nicht gelungen, diese Routinen vernünftig zum Laufen zu bekommen. Es scheint so, als würden nicht die Aktionen, sondern die Adressen der Routinen, die diese Aktionen ausrühren, aufgezeichnet werden, was natürlich völlig unbrauchbar ist! Sollte es dennoch einem unter Ihnen gelungen sein, eine vernünftige Anwendung aus diesen Routinen herauszufinden, bin ich natürlich sehr daran interessiert.
Nun aber zu den guten Nachrichten: Die anderen ApplRoutinen funktionieren hervorragend. Zu Appl_init() gibt es eigentlich recht wenig zu sagen. Diese Routine meldet Ihr Programm bei GEM an und sorgt so dafür, daß Sie Routinen des AES ausführen können. Dabei revanchiert sich GEM mit einer Identifikationsnummer. An sich sollte dieser Wert als Rückgabeparameter im Register D0 zu finden sein, was aber . nicht der Fall ist. Stattdessen existiert eine schon vordefinierte Variable gl_apid (globale Applikations-Identifikation), die sie ( unter C ) nur noch als 'extern' deklarieren müssen, und in der dieser Wert zu finden ist. Sie werden im weiteren Verlauf des Artikel noch erkennen, welchen Zweck diese Nummer erfüllt. Appl_exit() hat den Sinn, die Applikation wieder abzumelden und die ID-Nummer wieder freizugeben. Beachten Sie aber, daß selbst dann, wenn Sie keine Verwendung für die ID haben, trotzdem vor dem Aufruf eine AES-Routine Appl_init() durchgeführt werden muß, da sonst ihr GEM-Programm einfach abstürzt.
Nun wollen wir aber zu den interessanteren Routinen kommen, mit denen auch Informationen ausgetauscht werden können. Das Prinzip ist eigentlich ganz einfach: Weiß man die Applikations-identifikation eines anderen Programmes - beispielsweise die einer Accessory, so kann man diesem mit appl_write() Daten über eine sogenannte Pipeline schicken. Durch die Angabe der ID sorgt GEM dafür, daß diese Informationen nur an diese spezielle Applikation übermittelt wird.
Natürlich werden Sie aber einwenden, daß Sie diese ID nicht wissen, denn die Programme teilen sie einem nicht mit. Das ist aber weiter kein Problem, sofern Sie wissen, unter welchem Programmnamen ihr anzusprechendes Programm geladen worden ist. Diesen Namen merkt sich GEM in Zusammenhang mit seiner ID. Um diese ID zu erfahren, benutzen wir den Befehl Appl-find(), dessen Parameter folgendermaßen aussehen:
ap_id appl-find(ap-filename);
int ap_id: ID, der gesuchten Applikation
char* ap-filename: Dateiname, der gesuchten Datei - auf 8 Zeichen erweitert (siehe Text)
Zu dem Dateinamen sind nun folgende Bemerkungen zu machen: Der Extender wie auch der Pfad des Dateinamens sind unwichtig und müssen daher entfernt werden. Sollte der übriggebliebene Name kürzer als 8 Buchstaben sein, muß er mit Spaces aufgefüllt werden. Weiterhin müssen die Buchstaben, falls es kleine sind, in große umgewandelt werden. Ist dann dieses Programm geladen worden, findet man sicher die entsprechende Applika-tions-Identifikation. Wie verschickt man nun Nachrichten an andere Applikationen ?
Dazu schauen wir uns noch einmal die Struktur des Ereignis-Puffers an, der bei evnt_mesag() oder auch evnt_multi() eine entscheidende Rolle spielt. Dieser Puffer ist ein Feld, das aus sechzehn Bytes besteht: WortO:
Enthält das Kommando, an dem die Applikation erkennt, um welches Ereignis es sich handelt. Beispielsweise eine Redraw-Meldung oder eine Meldung, daß ein Menü angeklickt wurde.
Wort 1:
Dieses Wort enthält die ID des Prozesses, der die Meldung geschickt hat.
Wort 2:
Ist dieses Wort ungleich 0, liegen mehr als 16 Werte vor, die dann auch gelesen werden müssen. Mehr dazu im Text.
appl_write() gibt uns nun die Möglichkeit, in die Pipeline hineinzuschreiben, die dann an die bestimmte Applikation weitergeleitet wird. Das Empfangen der Daten wird unten näher beschrieben. Wie eben erwähnt, handelt es sich dabei um eine sechzehn Byte lange Botschaft. Zunächst also schreiben wir in das erste Wort (zwei Bytes ) die Nummer der Botschaft. Dabei ist zu beachten, daß diverse Botschaftsnummern von GEM schon belegt worden sind: Dies sind die Nummern 10, 20-29, 40 und 4l. Die Bedeutung dieser Botschaften schauen sie bitte in der einschlägigen Literatur nach, da sie den Rahmen der ST-Ecke sprengen würde. Denkbar wären also alle anderen Zahlen, so könnte beispielsweise eine 50 bedeuten, daß Sie Koordinaten übertragen wollen, während eine 111 eine Übertragung eines Zeigers, der auf einen Text zeigt, ankündigt. In das zweite Wort schreibt man die ID des sendenden Programms, so daß der Empfänger eventuell herausbekommen kann, wo die Nachricht herkommt. Nun kommt es vielleicht vor, daß Sie einmal eine Nachricht senden wollen, die größer als 16 Bytes ist. Dies erkennt der Empfänger an Wort 2! Ist der Inhalt von Wort 2 gleich Null, bedeutet das, daß die Nachricht 16 Bytes lang ist; wollen Sie aber beispielsweise 35 Zeichen senden, so schreiben Sie in dieses Wort die Zahl 19 - also 35-16 Bytes. Dies würde bedeuten, daß der Empfänger außer den 16 Bytes noch zusätzliche 19 Bytes lesen muß.
Der Nachteil ist, daß diese ganzen Bytes über den Umweg GEM erst an ihren Empfänger gelangten. Eleganter ist folgende Idee. Sie kopieren ihre zu übertragenden Daten in ihrem Sendeprogramm zusammen in einen Puffer. Dann übertragen Sie nur die Adresse ihres Puffers. Dadurch werden über den Umweg GEM nur wenige Bytes -die Adresse- übertragen. Da aber der Sender die Adresse der Daten, die im Speicher zu finden sind, kennt, kann es sich diese direkt aus demselben holen. Anhand des Sende-Beispielprogramms können Sie erkennen, daß ab dem vierten Feldelement die Adresse des Textes nicht aber der gesamte Text eingeschrieben und dann übertragen wird.
Nachdem Sie wissen, wie das Feld des Puffers zu füllen ist, wollen wir uns dem Senden, das sehr einfach ist, zuwenden. Dazu verwenden wir appl_write()) das wie folgt definiert ist:
ap_ret = appl_write(ap_id_empf, laenge, puffer)
int ap_ret:
Ist im Fehlerfall 0, sonst gibt das AES einen positiven Wert zurück.
int ap_id-empf: Applikations-ID des Programmes, das die Daten empfangen soll. Diese ID haben Sie durch appl_find() ermittelt.
int laenge:
Dort steht die Anzahl der zu übertragenden Bytes, allerdings wird hier die volle Anzahl angegeben. Im Normalfall steht hier aber die Zahl 16.
char *puffer:
Hier setzen Sie die Adresse ihrer zu sendenden Daten ein.
Achten Sie bitte darauf, daß Sie als Länge keinesfalls eine Null einsetzen, sonst stürzt der Rechner ab. Jetzt sind die Daten abgeschickt, allerdings soll eine zweite Applikation diese Daten auch empfangen. Dies geschieht genauso, wie Sie auch sonst Botschaften emp fangen, und zwar mit evnt_mesag() bzw. evnt_multi()- In diesen beiden Routinen benötigen Sie einen 16 Byte großen Puffer, und genau jener Puffer enthält ihre Botschaft. Rufen Sie beispielsweise evnt_mesag() auf, wartet dieses solange, bis es eine Botschaft empfängt. Dabei ist es völlig unerheblich, ob dies eine Botschaft ist, die vom Desktop stammt oder von einer fremden Applikation. Wichtig ist nur, daß etwas in die Pipeline geschrieben wurde, und auch genau diese Applikation gemeint war ( siehe oben bei der Erklärung von appl_write() Die Empfangs-Applikation muß eigentlich nur noch das erste Byte des Puffers überprüfen und schon weiß sie, welche Meldung sie bekommen hat und kann Sie entsprechend auswerten. Einen wichtigen Punkt darf man. allerdings nicht außer acht lassen. Wurde eine Meldung geschickt, die länger als sechzehn Bytes ist, was man aber sehr elegant umgehen kann, so müssen diese restlichen Bytes auf jeden Fall gelesen werden, was mit appl_read() geschieht. Nach Erkennen, daß Wort 2 ungleich Null ist, führt man folgenden Befehl aus:
ap_ret appl_read(ap_id, laenge, puffer )
int ap_ret:
ist im Fehlerfall 0, sonst ein positiver Wert.
int ap_id:
Die Identifikation der Applikation, die gelesen werden soll. Im Normalfall wird das die eigene Applikations-ID, also gl_apid sein, da man zusätzliche Daten aus der eigenen Pipeline liest.
int laenge:
Anzahl der aus der Pipeline zu lesenden Bytes.
char* puffer:
Adresse, an die die gelesenen Daten geschrieben werden sollen.
Der Anwendung dieses Verfahrens sind kaum Grenzen gesetzt. Es soll allerdings nicht verschwiegen werden, daß dieses Verfahren auch Nachteile hat: Daten können nur dann erfragt oder empfangen werden, wenn Sender und Empfänger aufeinander abgestimmt sind. So ist die einzige Normung durch die Befehle des GEM gegeben. Diese Befehle sollten daher von allen Applikationen verstanden werden. Denkbar wäre es auch, über diese Verfahrensweise einer Accessory vorzutäuschen, sie wäre angeklickt worden (AC-OPEN). Sie finden sicherlich einige Dinge, die ihren Reiz besitzen. Eine interessante Tatsache ist übrigens, daß bei DEGAS durch Angabe eines bestimmten Befehls durch eine Accessory über die Pipeline, die Adressen der im Computer befindlichen Bilder zu erfahren sind. Umso erstaunlicher ist es, daß das auch schon bei alten Versionen von DEGAS funktioniert. Denkbar sind sogar diverse Accessories, die miteinander kommunizieren.
Ich wünsche noch viel Spaß beim Austauschen von Daten und möchte nur noch kurz erwähnen, daß das häufig erfragte Problem, die Maussteuerung zu manipulieren, gelöst ist. Sie können sich daher auf dieses Thema freuen, das ich in den nächsten Monaten vorstellen werde.