Vous aurez conclu que ce protocole Document HiSTory est géré par le petit Start (à partir de la version 8.0). MultiStrip le gère aussi, pour ne pas faire de jaloux :)
Aussi, au lieu de regarder l'ap_id& de START ou de MultiStrip (lequel est le serveur DHST ?), il faut aller regarder dans la Cookie-Jar et chercher la chaine "DHST". La valeur du cookie contient (dans le mot de poids faible) l'ap_id& du serveur DHST. C'est aussi simple. Je vous donne ma fonction pour regarder les cookies :
FUNCTION test_cookie(cookie_name$,VAR cookie_valeur%) LOCAL read_cook%,nom_cook%,cookie% ' nom_cook%=CVL(cookie_name$) cookie%=LPEEK(&H5A0) cookie_valeur%=0 ' IF cookie%<>0 REPEAT read_cook%=LPEEK(cookie%) cookie_valeur%=LPEEK(ADD(cookie%,4)) ADD cookie%,8 UNTIL read_cook%=0 OR read_cook%=nom_cook% IF read_cook%=nom_cook% RETURN TRUE ELSE RETURN FALSE ENDIF ELSE RETURN FALSE ENDIF ENDFUNCComme cela, on sait qui appeler avec notre sempiternel APPL_WRITE. Il faudra donc utiliser le tube GEM, et donner au serveur DHST une adresse vers une structure. En C, cela donne la structure (qui doit être en mémoire partagée) :
typedef struct { char *appname, // adresse vers le nom de l'application gérant le document *apppath, // adresse vers le nom complet (chemin+nom) de l'appliction *docname, // adresse vers le nom du document *docpath; // adresse vers le nom complet (chemin+nom) du document } DHSTINFO
IF magic! OR mint! dhst_mem%=GEMDOS(68,L:1056,W:3) ! allocation en mémoire partagée dhst_appname%=ADD(dhst_mem%,16) ! on réserve 256 octets pour chaque dhst_apppath%=ADD(dhst_mem%,272) ! item dhst_docname%=ADD(dhst_mem%,528) dhst_docpath%=ADD(dhst_mem%,784) ' LONG{dhst_mem%}=dhst_appname% ! on remplie la "structure" LONG{ADD(dhst_mem%,4)}=dhst_apppath% ! par les adresses LONG{ADD(dhst_mem%,8)}=dhst_docname% ! LONG{ADD(dhst_mem%,12)}=dhst_docpath% ! ' CHAR{dhst_appname%}="Joe" ! par exemple CHAR{dhst_apppath%}=chemin$+"JOE.PRG" ! nom complet CHAR{dhst_docname%}="" ! rempli avant appel CHAR{dhst_docpath%}="" ! rempli avant appel ENDIF
Reste plus qu'à prévenir le serveur DHST qu'on a utilisé tel document.
La doku, écrite par Thomas MUCH lui-même, indique qu'il faut faire
l'appel à chaque chargement d'un nouveau fichier. Okay, mais si l'on
sauvegarde le fichier sous un nouveau nom ? Là je ne sais pas trop.
L'appel est de la forme (tiré du source de Joe ;-) :
IF @test_cookie("DHST",dhst_id%)=TRUE ! toujours tester avant l'appel dhst_id&=WORD(dhst_id%) ! on récupère l'ap_id& dans le mot de poids faible ' remplisage des buffers de DHSTINFO avec le nom du document CHAR{dhst_docname%}=LEFT$(MID$(nom_htm$,SUCC(RINSTR(nom_htm$,"\"))),254) CHAR{dhst_docpath%}=LEFT$(nom_htm$,254) IF dhst_id&>0 INT{m_adr%}=56029 ! DHST_ADD ou 0xDADD INT{ADD(m_adr%,2)}=ap_id& ! ap_id& de votre programme INT{ADD(m_adr%,4)}=0 LONG{ADD(m_adr%,6)}=dhst_mem% ! adresse de la struture INT{ADD(m_adr%,10)}=0 INT{ADD(m_adr%,12)}=0 INT{ADD(m_adr%,14)}=0 ~APPL_WRITE(dhst_id&,16,m_adr%) ENDIF ENDIFVoilà ! Au cas où vous voulez effacer la struture et les buffers après cet appel, le serveur DHST vous renvoie un DHST_ACK (56030 ou 0xDADE) par l'EVNT_MULTI principal de votre programme. Le message correspond presque exactement aux 16 octet que vous avez envoyé avec APPL_WRITE, c'est à dire l'adresse de DHSTINFO dans LONG{msg_adr%+3}. Il y a en plus dans INT{msg_adr%+14} une réponse : si c'est nul alors il y a un blème, sinon les informations ont été indexées dans une petite base attenante à Start ou MultiStrip.
Oh ! j'oubliais qu'il faut la version 8.0 de Start et que vous devez
rajouter les lignes dans le start.set (SMUSetup ne gérant pas encore
cela) :
/documents /documents_max 20 /documents_maxperapp 5
Atariste Phone Home
Ainsi parmi les variables d'environnement, ajoutez $HOME sous la forme : #_ENV HOME=C:\GEMSYS\HOME\ dans MAGX.INF par exemple. Les programmes modernes prennent en compte ce répertoire et sauvent leurs fichiers de configuration dans celui-ci. (On a vu comment récupérer une variable d'environnement dans les chapitres précédents de ce fanzine merveilleux qu'est le STimulus)
Avantages ?
Bref, on a le beurre (sans l'avoir rance :) et l'argent du beurre. Mais que reste-t-il donc à zin00 ? Les ActiveX ? Ben non : on les a aussi ! Toujours grâce à Monsieur MUCH (too much, ce gars ;-).
Comment je contrôle une application : GEMScript
GEMScript est une application indépendante qui peut interpréter des scripts (fichiers *.SIC) et les éxécuter. Le langage ressemble au C, mais est très accessible. On peut ainsi lancer avec une instruction une application comme Texel et lui faire faire plein de choses par l'intermédiaire du script, comme des copier/coller, chargement de fichiers, etc.
GEMScript s'occupe de traduire votre code et de l'envoyer, via un protocole spécial, à l'application voulue. Il est nécessaire que l'application supporte ce protocole, ainsi que les commandes usuelles qui lui seront fournies par GEMScript.
Je ne fais ici que déchiffrer la documentation, n'ayant pas encore intégré ce protocole dans Joe. msg% est un buffer de réception de l'EVNT_MULTI ou d'envoi de APPL_WRITE :
GS_REQUEST : GEMScript vers votre application (reçu par EVNT_MULTI)
INT{msg%}-> 4944 INT{ADD(msg%,2)}-> ap_id& ! de GEMScript INT{ADD(msg%,4)}-> 0 LONG{ADD(msg%,6)}-> gs_info% ! adresse de la structure GS_INFO INT{ADD(msg%,10)}-> 0 INT{ADD(msg%,12)}-> 0 INT{ADD(msg%,14)}-> gems_id& ! votre identité sous GEMScript
GS_REPLY : vous supportez GEMScript et vous écrivez avec APPL_WRITE
INT{msg%}=4945 INT{ADD(msg%,2)}=ap_id& ! de votre application INT{ADD(msg%,4)}=0 LONG{ADD(msg%,6)}=gs_info% ! même adresse de la struture GS_INFO reçue INT{ADD(msg%,10)}=0 INT{ADD(msg%,12)}=0 ! OK, vous supportez le protocole de communication >1 ! une autre valeur sinon. INT{ADD(msg%,14)}=gems_id& ! votre id& sous GEMScript
Pour information, la struture GS_INFO est de la forme :
typedef struct { long len; /* longueur de la struture en octets */ int version; /* numéro de version (si MKI$ donne 0110 alors c'est la 1.1) */ int msgs; /* Bitmap des fonctionnalités GSM_xxx */ long ext; /* extension du fichier script : MKL$ donne ".SIC" */ } GS_INFO;
Pour les fonctionnalités, veuillez vous taper la doc en allemand, merci. Il existe plusieurs protocoles, dont l'écriture de macros entières !!! En ce qui nous concerne ici, on ne s'occupe que des commandes vers une application, qui est supporté si le bit 0 est à 1, c-a-d les valeur de msgs impaires.
Passé l'étape de reconnaissance, notre application peut recevoir des ordres via des commandes, toujours données par GEMScript, via le tube GEM. Ces commandes sont de la forme :
GS_COMMAND : GEMScript vers notre application
INT{msg%}->4946 INT{ADD(msg%,2)}-> ap_id& ! de GEMScript INT{ADD(msg%,4)}-> 0 LONG{ADD(msg%,6)}-> adresse vers une chaine de commande en mémoire non protégée INT{ADD(msg%,10)}-> 0 INT{ADD(msg%,12)}-> 0 INT{ADD(msg%,14)}-> votre identifiant donné par GS_REQUEST
Attention, car la chaine de commande est formatée d'une façon particulière : tous les paramètres sont séparés par un null-byte, la fin de chaîne étant terminée elle-même par 2 null-bytes, comme dans la chaîne d'environement d'un SHEL_WRITE (voir épisodes précédents du Stimulus)
"Commande"+CHR$(0)+"Paramètre 1"+CHR$(0)+"Paramètre n"+CHR$(0)+CHR$(0)
Faites donc attention, il n'y a pas forcément 1 paramètre, et il faut manipuler CHAR{} avec précaution. Ci-dessous les commandes standard que toute application digne de ce nom se doit de supporter. Vous pouvez, si votre programme gère d'autres fonctions, les faire commander par un script. Il faut alors déclarer les paramètres de commande dans votre documentation pour que l'utilisateur les emploie dans ses scripts.
Commande | Paramètre 1 | Retour 1 | Commentaire |
Close | nom du fichier (optionnel) | - | |
Copy | nom du fichier (optionnel) | - | fonction du clipboard |
Cut | nom du fichier (optionnel) | - | fonction du clipboard |
Delete | nom du fichier (optionnel) | - | fonction du clipboard |
GetFront | - | nom du fichier | on demande le fichier géré en avant plan |
New | - | - | ouverture d'un fichier vierge |
Open | nom du fichier | - | |
Paste | nom du fichier (optionnel) | - | fonction du clipboard |
nom du fichier (optionnel) | - | ||
Quit | - | - | |
Save | nom du fichier (optionnel) | - | |
SaveAs | nom du fichier (obligatoire) | - | |
SelectAll | nom du fichier (optionnel) | - | fonction du clipboard |
ToFront | nom du fichier | - | mettre en avant plan le fichier |
Undo | nom du fichier (optionnel) | - | |
AppGetLongName | - | nom de l'application | pour le mettre dans MultiStrip ou autre |
Vous aurez compris que, si vous devez supporter GEMScript, votre application doit au moins obéir à ces commandes et agir, c'est-à-dire sauver le fichier EN VRAI si la commande est "Save".
D'autres commandes sont décrites dans la doc de GEMScript : il s'agit des commandes pour faire charger et écécuter un script par GEMScript ! C'est le serpent qui se mort la queue :)
Mais on a pas fini. Car il faut renvoyer un ACK, c'est-à-dire le message de retour disant j'ai bien compris et éxécuté la chose (ou non), avec renvoi de certaines informations (d'où la colonne Retour 1 dans notre tableau).
GS_ACK : de l'application vers GEMScript
INT{msg%}=4947 INT{ADD(msg%,2)}=ap_id& ! de votre application INT{ADD(msg%,4)}=0 LONG{ADD(msg%,6)}=commande_adr% ! adresse de la chaine de commande qui vous a été envoyée LONG{ADD(msg%,10)}=retour_adr% ! chaine de retour, en mémoire non protégée INT{ADD(msg%,14)}=0 ! GSACK_OK-> c'est tout bon 1 ! GSACK_UNKNOWN-> commande inconnue 2 ! GSACK_ERROR-> erreurOn donne donc l'adresse d'une chaine de retour. Celle-ci est formatée comme la chaine de commande, c'est-à-dire avec 1 null-byte entre les propositions et 2 null-bytes finaux. La chaine de retour peut contenir un message d'erreur si l'opération ne s'est pas bien passée.
Et on est propre sur soi. Quand on quitte, on oublie pas de prévenir GEMScript par :
GS_QUIT : application vers GEMSscript
INT{msg%}=4948 INT{ADD(msg%,2)}=ap_id& ! de votre application INT{ADD(msg%,4)}=0 INT{ADD(msg%,6)}=0 INT{ADD(msg%,8)}=0 INT{ADD(msg%,10)}=0 INT{ADD(msg%,12)}=0 INT{ADD(msg%,14)}=gems_id& ! votre identifiant sous GEMScriptIl y a donc un début et une fin : une intialisation et terminaison pour le protocole GEMScript. Les plus malins verront qu'on peut très bien s'en passer ;-) . En effet, GEMScript ne sert qu'à analyser vos scripts et envoyer des commandes à l'application voulue.
Mais on peut très bien envisager une autre application qui se servirait de la vôtre par l'intermédiaire des commandes, sans passer par l'initialisation et terminaison GEMScript.
Par exemple, un module de Joe pourrait dire à celui-ci : cherche la
balise <BODY...>, met-la dans un buffer mémoire et donne moi
l'adresse. J'analyse ça et affiche les paramètres dans ma boîte
de dialogue en fenêtre. Quand l'utilisateur ferme et veut intégrer ça dans le texte édité sous Joe,
alors je lui demande d'effacer son <BODY..> et de lire le tag que je
lui donnerai.
Lumineux, non ?
Il y a donc plein d'applications possibles du concept de commandes introduit par GEMScript et son protocole. Le tout est de bien l'utiliser, car une application ou des gens mal intentionnés pourraient en faire mauvais usage. Des macro-virus sur Atari ? Zut ! notre système commence à devenir un peu trop moderne ! Il faut songer, si l'on passe des commandes sans passer par GEMScript de protéger les actions d'une manière quelconque.
Je vous conseille de lire la doc de GEMScript. Après ces quelques lignes, l'allemand vous sera plus facile à déchiffrer :) Et n'oubliez pas d'ajouter dans les variables d'environnement :
#_ENV GEMSCRIPT=chemin+nom de GEMScript
A la prochaine !
Rajah Lone
écrit le 19 Août 1999