Atarium: Identitätsfragen

Diesen Monat möchte ich mich einer Reihe von Problemen zuwenden, die zwar seit langer Zeit bekannt sind, aber aufs neue auftauchen. Immer wieder wollen Programmierer wissen, wie man herausbekommt, unter welchem Namen ein Programm gestartet worden ist, ob es vom Desktop aus gestartet worden ist usw. Bei genauer Betrachtung sind diese Fragen aber meist schlecht gestellt, und nicht immer gibt es eine befriedigende Antwort. Auch damit muß man manchmal leben: manche Probleme lassen sich einfach nach dem aktuellen Stand der Technik nicht sauber lösen.

Die Fragen nach Programmname und Desktop lassen sich in der Regel auf mehrere Einzelfragen zurückführen:

(1) Kann ich AES-Aufrufe machen? (2) Soll ich AES-Aufrufe machen? (3) Soll nach Programmende auf einen Tastendruck gewartet werden? (4) Wie ist der genaue Name der Programmdatei, und in welchem Verzeichnis liegt sie? (5) Bin ich als Accessory oder als Programm gestartet worden? (6) Wie kann ich Fehler bei ‚rsrc_load()’ besser erkennen?

Die erste Frage läßt sich im Gegensatz zu den meisten anderen Fragen leicht beantworten. Dazu benutzt man einfach die Methode, die ATARI selbst auch für den Mausbeschleuniger Maccel benutzt hat und die sowohl im Profibuch ([1]) als auch von ATARI dokumentiert worden ist. Sie macht sich die Tatsache zunutze, daß appl_init() nur dann das global-Feld ausfüllt, wenn die Funktion auch erfolgreich durchlaufen wurde. Man setzt also das erste Element des Feldes auf 0 und testet anschließend, ob dort noch immer 0 steht. Da das erste Element des Feldes die AES-Versionsnummer aufnimmt, ist das Verfahren auf jeden Fall sicher - ein AES mit der Versionsnummer 0 gibt es nicht.

Dieses Verfahren kann man immer dann gut einsetzen, wenn sich ein Programm im Autoordner anders als sonst verhalten soll. Man erfragt nämlich genau die Information, auf die es ankommt (AES-Aufrufe sind erst nach Abarbeitung des Autoordners möglich).

Übrigens: nur diese Methode funktioniert zuverlässig und ist auch von ATARI abgesegnet.

PRG oder APP?

Noch zwei Hinweise: Programme, die sowohl mit als auch ohne AES funktionieren, sollten die Namenserweiterung PRG tragen dadurch kann man sie im Autoordner ausFühren lassen. Programme, die zwingend die Anwesenheit des AES erfordern, sollten hingegen die Namenserweiterung APP tragen - dadurch beugt man überflüssigen Verwechslungen vor. VDI-Aufrufe sind übrigens immer möglich: man muß nur darauf achten, daß man, wenn das AES noch nicht aktiv ist (siehe Listing 1) anstelle einer virtuellen eine physikalische Bildschirmworkstation öffnen muß - genau dasselbe macht das AES ja schließlich auch.

Die zweite Frage - ‘Soll ich GEM-Aufrufe machen?’ - ist erheblich schwieriger zu beantworten. Viele Programmierer sind fälschlicherweise der Meinung, daß es eine strikte Unterscheidung zwischen Programmen, die ‘als GEM-Programm' gestartet worden sind, und anderen gibt. Tatsächlich sieht der AES-Aufruf ‘shel_write()’ ja auch solch einen Parameter vor, doch erstens kann man dies später nicht erfragen, und zweitens werden Programme nun mal eben nicht immer per ‘shel_write()’ gestartet. Und selbst ein unter MultiTOS vom ATARI-Desktop als ‘TOS-Programm' gestartetes TTP kann dann - im Miniwin-Fenster laufend - ohne weiteres GEM-Aufrufe machen (zu möglichen Problemen später mehr).

Fazit: Zur Laufzeit kann dies einfach nicht problemlos unterschieden werden. Programmierer sind gut beraten, zwei verschiedene Versionen ihrer Software zu compilieren - einmal als TTP mit textuellen Ein- und Ausgaben per GEMDOS und einmal als APP. So kann es keine Irrtümer geben.

‘Soll nach Programmende auf einen Tastendruck gewartet werden?’. Dieses Problem hat natürlich viel mit Frage Nummer 2 zu tun. Es geht letztendlich darum, ob die Bildschirmausgaben eines TTPs nach Beendigung sichtbar bleiben (etwa in der Gemini-Konsole) oder sofort verschwinden (ATARI-Desktop unter Single-TOS). Auch dies kann man nicht sauber erfragen. Wer also darauf angewiesen ist, daß das Programm auch von ‘unintelligenten’ Desktops aus gestartet werden kann, hat mehrere Alternativen:

Einmal kann man eine spezielle Option anbieten, mit der eben das Warten auf einen Tastendruck eingeschaltet wird. Zusätzlich kann man ein Programm mitliefern, das speziell für den Aufruf von TTPs entworfen ist und optional am Programmende auf einen Tastendruck wartet.

‘Moderne’ Desktops haben normalerweise sowieso eine solche Konfigurationsmöglichkeit.

Wo bin ich?

Auch Frage 4 - ‘Wie ist der genaue Name der Programmdatei, und in welchem Verzeichnis liegt sie?’ - hat mit dieser Problematik zu tun: Viele Programmierer sind der Ansicht, daß der Name einen Rückschluß auf die bereits besprochenen Probleme zuläßt. Wie man sich allerdings leicht überlegen kann, ist dies falsch: weder GEMDOS noch AES interessieren sich für die Namenserweiterungen der Programme. Es ist allenfalls das Desktop, das aus der Extension Schlüsse zieht, aber dieses Verhalten kann man leicht durch Umkonfigurieren ändern.

Eine sichere Methode zur Abfrage gibt es nur in MiNT, und das auch erst seit einiger Zeit. Man öffnet die eigene Prozeßdatei und benutzt den ‘Fcntl()’-Opcode PLOADINFO, um die gewünschten Informationen zu erfragen (siehe Abbildung 2). Wenn verfügbar, ist dies mit Abstand die beste und vor allem die einzig sichere Methode.

Wenn der Prozeß unter Zuhilfenahme des ARGV-Verfahrens gestartet worden ist, gibt es einen weiteren Anhaltspunkt. Dann enthält nämlich ‘argv[0]’ normalerweise den vollen Namen der Programmdatei. Doch die Betonung liegt auf ‘normalerweise’! Dem aufrufenden Programm steht es völlig frei, dort auch etwas anderes einzutragen. Auf Unix-Systemen ist es beispielsweise üblich, bei Shells, die als Login-Shells gestartet werden, als erstes Zeichen einen Bindestrich einzutragen.

Was kann man sonst tun? Wenn man weiß, daß man AES-Aufrufe machen kann, und mit Sicherheit weiß, daß per ‘shel_write()’ gestartet worden ist, dann kann man auch *shel_read()’ probieren. Grundsätzlich gilt aber: wenn PLOADINFO nicht da ist, gibt es keine absolut verläßliche Methode.

Übrigens: vielen ist unbekannt, daß das AES (seit TOS 1.04) beim Programmstart per shel_write()’ automatisch das Verzeichnis, in dem das gestartete Programm liegt, seinem internen Suchpfad hinzufügt. Das heißt, daß die AES-Funktionen ‘shel_find()’ und ’rsrc_load()’ auch dann Dateien in diesem Verzeichnis finden, wenn es gar nicht das aktuelle Arbeitsverzeichnis des Programms ist.

Was bin ich?

Mit Frage 5 (‘Bin ich als Accessory oder als Programm gestartet worden?’) sind wir wieder im ‘sicheren’ Bereich. Bei als Accessory gestarteten Programmen ist A0 ungleich Null. Bei den meisten Compilern setzt der Startup-Code automatisch eine globale Variable, mit der man dies abfragen kann (PureC: *_app'). Achtung: andere Methoden sind nicht von ATARI abgesegnet und funktionieren auch nicht zuverlässig.

Wie kann ich Fehler bei ’rsrc_load()’ besser erkennen? ’rsrc_load()’ liefert ja einen Fehlercode, man kann aber nicht zwischen den verschiedenen Ursachen unterscheiden. Daher bietet es sich an, die Datei zunächst per 4shel_find()’ zu suchen. Damit kann man schon mal sicherstellen, daß die Datei überhaupt auffindbar ist. Den so ermittelten Dateinamen übergibt man an ’rsrc_load()’. Wenn es dann zu einem Fehler kommt, war zuwenig Speicher frei, die Datei nicht lesbar oder der Inhalt defekt. Auf jeden Fall kann man in diesem Fall immerhin den vollen Dateinamen anzeigen; oft kommt es nämlich zu Problemen, weil das AES die Resource-Datei an einer ganz anderen Stelle gefunden hat, als man geplant hatte.

Ein Programm, mit dem Resource-Dateien auf die richtige Version abgeprüft werden können, wurde übrigens im Februar-Heft ([2]) beschrieben.

Kommen wir abschließend zu dem Problem von unter Multi-TOS gestarteten GEM-Programmen. ATARI selbst dokumentiert, daß man dazu ‘shel_write()’ benutzen muß. Warum?

Durch diesen Mechanismus ist das gestartete Programm ein Tochterprozeß des AES, nicht des Aufrufers. Dadurch kann das AES auf einfache Art und Weise feststellen (per Signal), wann das Programm beendet ist. Dieses Wissen ist wichtig, weil das AES direkt auf Speicherbereiche des Programms zugreift. Stürzt das Programm ab, kann es sonst dazu kommen, daß das AES weitere Zugriffe macht, was zu fatalen Fehlem führen kann. Diese Erleichterung für das AES wird allerdings durch schwerwiegende Probleme an anderer Stelle erkauft. So werden fast alle GEMDOS-Eigenschaften (offene Dateien, Benutzerkennung, Ausgabeumlenkung), die normalerweise an nachgestartete Programme vererbt werden, eben nicht vererbt. Insbesondere ist die Forderung, daßGEM-Programme nur per ‘shel_write()’ gestartet werden dürfen und TOS-Programme keine GEM-Aufrufe machen sollen natürlich insofern lächerlich, da sonst niemals eine in einem Miniwin-Fenster laufende Shell (ein TOS-Programm) GEM-Programme starten kann.

Fazit: man kann GEM-Programme getrost auch per ‘Pexec()’ starten. Schlimmstenfalls kommt es zu Problemen, wenn das Programm abstürzt und das AES dies nicht rechtzeitig merkt.

Quellennachweis:

[1] Jankowski/Rabich/Reschke:
„ATARI Profibuch ST-STE-TT“, 12. Auflage,
Sybex Düsseldorf 1992, ISBN 3-88745-888-5

[2] Julian Reschke:
„Debugging: mit den richtigen Werkzeugen geht es besser“,
ST-Computer 2/94, Seite 94

int appl_id;        /* meine Application-ID */
int aes_callable;   /* AES-Aufrufe möglich /
_GemParBlk[0] = 0;
appl_id = appl_init();
aes_callable = (_GemParBlk.global[0] != 0);

Listing 1: Abfrage, ob AES-Aufrufe möglich sind (PureC)

/*
    pinfo.c
    (c)1994 by MAXON-Computer 
    Autor: Julian P. Reschke 
    31. September 1994
*/

#include <stddef.h> 
#include <stdio.h> 
#include <tos.h>

#ifndef PLOADINFO 
#define PLOADINFO   (('P'= 8) | 12)
#endif

struct ploadinfo { 
    /* passed */
    short fnamelen;
    /* returned */
    char *cmdlin, *fname;
};

int
main (void)
{
    struct ploadinfo pl; 
    char fname[PATH_MAX]; 
    char cmdlin[PATH_MAX]; 
    long handle;
    long ret;

    pl.fnamelen = sizeof (fname); 
    pl.cmdlin = cmdlin;
    pl.fname = fname;

    handle = Fopen ("u:\\proc\\x.-1", 0);

    if (handle < 0) return 1;

    ret = Fcntl ((int) handle, (long) &pl, PLOADINFO);

    Fclose ((int) handle);

    if (ret == 0)
        printf ("Dateiname: '%s'\n", pl.fname);

    return 0; 
}

listing 2: Wie man den Programmnamen erfragt


Julian F. Reschke
Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]