VIII. Bôvítômodulok írása C-ben

Автор: Kádár Zsolt

Дата: 22.06.1999

Источник: https://xenia.sote.hu

Язык: Венгерский

Bevezetés

Az eddigi leckék során nagyon sokszor használtuk már az RxFuncAdd függvényt, amellyel DLL-ekben elhelyezkedô függvényeket tettünk elérhetôvé REXX programok számára. A bôvítômodulokat tárgyaló tanfolyamunk utolsó leckéjében azt fogjuk megnézni, hogy hogyan kell a bôvítômodulokat C-ben megírni. A nyelvek közötti különbségekbôl adódik, hogy a C programnak bizonyos szabályoknak kell megfelelni. A legszembetûnôbb különbség az, hogy a REXX a C-vel ellentétben egyetlen adattípust, a karakterláncot ismer. A REXX és C programok együttmûködésekor tehát gondoskodni kell arról, hogy a szükséges adatkonverzió automatikusan végbemenjen. Szerencsére nem kell teljesen a nulláról kezdenünk, mivel az IBM a Devcon CD-ken található Developer's Toolkitben elérhetôvé tette a REXXSAA.H nevû fejlécállományt, amely a feladat egy részét megoldja.

Az RXSTRING adattípus

A fejlécállományban található definíciók közül az egyik leglényegesebb az RXSTRING adattípus, amelyet a REXX karakterláncok C programbeli kezelésére hozták létre. A C program ugyanis ebben az adattípusban kapja meg a REXX program által átadott paramétereket, s természetesen ugyanebben a formában adja vissza az eredményeket is. A C program a mûködése közben persze tetszés szerinti formátumra konvertálhatja az RXSTRING-et. Az alábbiakban megadtuk az RXSTRING definícióját:

typedef struct {
	ULONG	strlength; 	/* A sztring hossza 	 */
	PCH	strptr;  	/* Mutató a sztringre 	 */
}  RXSTRING;
 
typedef RXSTRING *PRXSTRING;  	/* Mutató az RXSTRING-re */

A C függvény megírása

Hogy a C-ben írt függvényt a REXX programok is használni tudják, a következô sorrendet kell betartani a függvény megírásakor:

  • 1. Csatolni kell a REXX függvényeket specifikáló definíciót: #define INCL_RXFUNC
  • 2. Meg kell adni a REXX fejlécállományt: #include <rexxsaa.h>
  • 3. Deklarálni kell a függvényt REXX interfészként a REXXFunctionHandler kulcsszóval.
  • 4. A függvény elején szükség esetén konvertálni kell az RXSTRING-ben kapott adatokat a megfelelô C típusra.
  • 5. A függvény törzsének megírása után vissza kell alakítani a visszaadandó adatokat RXSTRING típusra.

Az alábbiakban bemutatunk egy rövid példaprogramot, amely a Hello függvényt valósítja meg C-ben, amelyet aztán REXX-bôl fogunk meghívni. A függvény nem csinál mást, mint visszaadja a paraméterként átadott karakterláncot a "Hello " és "!" karakterláncok közé ágyazva. Ezáltal a függvényt például REXX-bôl a "World" paraméterrel meghívva a klasszikus "Hello World!' szöveget lehet REXX-bôl a C-ben írt DLL segítségével kinyomtatni. A példa persze kissé erôltetett, hiszen a C bôvítômodul által kínált Hello függvényt REXX-ben is könnyen meg lehetne írni, ám példának tökéletesen megteszi.

/* LECKE08A.C */

/* A szükséges definíciók */
#define INCL_RXFUNC
#define INVALID_ROUTINE 40
#define VALID_ROUTINE   0

/* A szükséges header fájlok */
#include <string.h>
#include "rexxsaa.h"

/* Makró a C függvény által visszaadott RXSTRING elôállítására */
#define BUILDRXSTRING(t, s) { \
    strcpy((t)->strptr,(s));\
    (t)->strlength = strlen((s)); \
  }

/* A függvény prototípusa */
RexxFunctionHandler Hello;

/* A függvény részletezve */
ULONG APIENTRY Hello(
	PUCHAR          Name,                   /* A függvény neve        */
	ULONG           argc,                   /* Az argumentek száma    */
	RXSTRING        argv[],                 /* Az argumentek          */
	PSZ             Queuename,              /* A várakozási sor neve  */
	PRXSTRING       Retstr)                 /* A visszaadott sztring  */
	{
	UCHAR       str[80]; 			/* Ideiglenes változó	  */

	/* A bevitt paraméterek ellenôrzése   */
	if (argc !=1) 	     			/* Maximum 1 paraméter 	  */
		return(INVALID_ROUTINE);	/* Hibás használat   	  */
	if (!RXVALIDSTRING(argv[0])) 		/* Érvényes-e a sztring	  */
		return(INVALID_ROUTINE);	/* Hibás használat   	  */
	if (argv[0].strlength > 70) 		/* Max. 70 hosszú lehet	  */
		return(INVALID_ROUTINE);	/* Hibás használat	  */

	/* A visszaadott sztring elôállítása */
	strcat(str, "Hello ");
	strcat(str, argv[0].strptr);
	strcat(str, "!");

	/* A Rexx visszatérési érték elôállítása */
	BUILDRXSTRING(Retstr, str);

	/* Minden rendben van, így visszatérhetünk */
	return(VALID_ROUTINE);
	}

A példaprogramot az elsô pontban kötelezôen elôírt RXFUNC definícióval kezdjük, amelyet még két másik követ. A 40-es visszatérési érték azt jelzi a REXX program számára, hogy nem megfelelôen használták a függvényt. A 0-ás visszatérési érték a helyes használatot jelenti. A fejlécállományok definiálásánál megadtuk a Hello függvény számára szükséges string.h fájlt, amelyet a 2. pontban kötelezôen elôírt rexxsaa.h csatolása követ. A következô kódrészlet egy makrót definiál, amely megkönnyíti az RXSTRING típusú értékek elôállítását a C-ben használt karakterláncokból. A folytatásban REXX függvényként deklaráljuk a Hello függvényt, majd részletesen kidolgozzuk. A függvény paraméterlistája kötött; a REXX program által átadott adat az argv[] tömbben található. A következô kódrészlet a bevitt adatok számának és típusának ellenôrzését végzi. Figyeljük meg, hogy használunk egy RXVALIDSTRING makrót, amely azt ellenôrzi, hogy érvényes-e a megadott bemeneti adat. Nagyon sok más, a bemeneti paraméterek feldolgozását segítô függvényt találhatunk még a szintén a Developer's Connection részét képezô RXSTRING.LIB fájlban. Példaként az RXTOI függvényt említhetjük meg, amely RXSTRING-et alakít integerré. Amennyiben a bevitt adatok száma és értéke nem felel meg az általunk definiált feltételeknek, akkor az INVALID_ROUTINE értékkel térünk vissza, ami azt fogja jelenteni a REXX programnak, hogy helytelenül használta a függvényt. Ha sikeresen átestünk az ellenôrzéseken, a három strcat utasítással elvégezzük a tulajdonképpeni munkát, a bemeneti adat beékelését a "Hello " és a "!" karakterláncok közé. A BUILDRXSTRING makró segítségével a kapott karakterláncot visszaalakítjuk RXSTRING-re, hogy az elérhetô legyen a REXX függvény számára és a VALID_ROUTINE visszatérési érték mellett zárjuk a C függvényt.

A DLL elôállítása

Ha kész a C kód, akkor már nincs más hátra, mint elkészíteni a DLL-t. Erre a célra elvileg bármelyik C fordító megfelel; én a Borland C for OS/2 2.0-át használtam erre a célra. A DLL elkészítéséhez szükség van még egy DEF fájlra is, amelyben listázzuk a REXX programok számára elérhetô függvényeket:

LIBRARY LECKE08A INITINSTANCE TERMINSTANCE
DESCRIPTION 'Zsolt Kadar LECKE Utilities - (C) Copyright Crucial Applications 1999'
EXPORTS
     HELLO                  = Hello                  @1

Ezek után már nincs más hátra, mint létrehozni egy projectet, hozzáadni a létrehozott fájlokat, a target opciónál beállítani, hogy DLL-t akarunk készíteni, amelyhez az általunk készített DEF fájlt akarjuk használni, és a build gombra bökni. Ha minden jól megy, akkor néhány pillanat múlva kész a DLL, amelyet rögtön tesztelhetünk is REXX-bôl.

A DLL használata

A DLL-ben található függvényt elôször regisztrálni kell az RxFuncAdd utasítással, amelynek elsô és harmadik paramétere rendszerint a függvény neve, a második pedig a DLL neve kell, hogy legyen. A DLL-t természetesen be kell másolni egy olyan könyvtárba, amely benne van a LIBPATH-ban, hogy az RxFuncAdd meg tudja találni. A sikeres regisztráció után pedig minden további nélkül meghívhatjuk a Hello függvényt a World paraméterrel, hogy jól megérdemelt munkánk eredményeként feltûnjön a képernyôn a "Hello World!" szöveg.

/* lecke08a.cmd */

Call RxFuncAdd 'Hello', 'LECKE08A', 'Hello'

Say Hello('World')

EXIT

REXX programok hívása C-bôl

Természetesen a dolog fordítottja, vagyis REXX programok meghívása C-bôl is lehetséges. Sôt mi több, nem is olyan nehéz dolog. Mivel a REXX interpreter maga is egy DLL, a C program egy az egyben meg tudja hívni az interpretert a REXX programok futtatásához. A REXXSAA.H fejlécállományban szerepel egy REXXSTART függvény, amely az interpreter indítására szolgál. Az alábbi példaprogram a REXXSTART függvény használatára mutat példát. A REXXDB2.C program elindítja a GETDB.CMD REXX programot, amely egy adatbázis nevét adja vissza a C programnak, amely aztán kinyomtatja azt:

#define INCL_REXXSAA
#include <rexxsaa.h>
#include <stdio.h>
#include <string.h>
 
int main(void);
 
int main()
     {
     RXSTRING arg;                        /* argument sztring REXX-hez      */
     RXSTRING rexxretval;                 /* REXX-beli visszatérési érték   */
     UCHAR    *str = "";                  /* REXX-nek átadott sztring       */
     APIRET   rc;                         /* REXX visszatérési érték        */
     SHORT    rexxrc = 0;                 /* a függvény visszatérési értéke */
 
     MAKERXSTRING(arg, str, strlen(str)); /* bemeneti paraméter elôállítása */
 
     rc=RexxStart((LONG)      0,          
                 (PRXSTRING)  &arg,       
                 (PSZ)        "GETDB.CMD",
                 (PRXSTRING)  0,          
                 (PSZ)        0,          
                 (LONG)       RXSUBROUTINE,  
                 (PRXSYSEXIT) 0,             
                 (PSHORT)     &rexxrc,       
                 (PRXSTRING)  &rexxretval ); 
 
     Printf("Az adatbázis neve: %s",rexxretval.strptr);
     DosFreeMem(rexxretval.strptr);
 
     return 0;
}

 

REXX GYÍK:

K1. Hol találok további példákat C-ben írt REXX bôvítômodulokra?
V1. Az elôzô leckében már említett hobbes.nmsu.edu ftp site /pub/dev/rexx könyvtárában található shareware/freeware modulok némelyikéhez mellékelték a forráskódot is. Egy ilyen modul például az általunk is tárgyalt RxAsync.

 

Gyakorlatok:

1. Készítsen olyan bôvítômodult, amely az Ön számára a REXX-bôl legjobban hiányzó függvényt valósítja meg!
2. Készítsen egy C programot, amely a parancssorban megadott REXX programot futtatja le!

Kádár Zsolt
1999. 06. 22.