Написание библиотек для REXX на Virtual Pascal/2
Автор: Виктор Кустов AKA VikingX
Источник: RDM/2
Все началось с того, что я решил написать сторож процессов на рексе - скриптик, который следил бы за определенными процессами, и если один из этих процессов умрет, то запускал бы его заново. Потратив минут 20, я набросал скрипт, который список процессов загонял в queue со стандартного вывода ps.exe. Но это показалось не очень хорошим решением и родилась идея написать DLL-ку с реализацией функции получения списка процессов.
В VP/2 оказался пример, реализующий библиотеку для REXX (в examples\rexx). Там описана функция VPTouch, входными параметрами для которой являются одна или несколько масок (или имен файлов), результатом являются действия (изменение timestamp файла(ов)) и код возврата (текстовая переменная).
Что полезного можно почерпнуть из этого примера? Ну во-первых заголовок функции:
Function VPTouch( FuncName : PChar;
ArgC : ULong;
Args : pRxString;
QueueName : pChar;
Var Ret : RxString ) : ULong; export;
Этот заголовок должен быть одинаков для всех функций, вызываемых из рекс-программ.
FuncName - имя вызываемой функции.
ArgC - количество передаваемых аргументов.
Args - сами аргументы (в примере очень хорошо показано, как с ними обращаться, их может быть много ;)
QueueName - имя очереди (непонятно, как ее использовать, но по всей видимости результат выполнения функции может помещаться в очереди, и/или читаться из нее).
Ret - результат работы функции (при вызове var = RxMyFunc( a, b, ... ) он будет помещен в var).
И код возврата типа ULong.
Обратите внимание! Если вы не вернете 0 при удачном выполнении функции, то REXX ругнется на ошибку выполнения.
Как видно из примера функция возвращает только одну простую строку в качестве результата ( Var Ret : RxString ). Возвращать список процессов в одной строке неудобно, поэтому лучше использовать другой вариант. Для этого можно использовать запись в файл, очередь и еще тот замечательный способ, который использует, к примеру SysFileTree - стем. Отлично, в Watcom 10.0 как раз есть пример DLL-ки SysUtils, которая содержит эту функцию (WATCOM\SAMPLES\TOOLKT2X\REXX\REXXUTIL\). Оттуда мы и выясняем, что такая переменная создается и обрабатывается функцией RexxVariablePool (см. VP\SOURCE\RTL\OS2REXX.PAS):
function RexxVariablePool(var Pool: ShvBlock): ApiRet; { Указатель на список SHVBLOCK-ов}
Параметром для нее служит переменная типа:
PShvBlock = ^ShvBlock;
ShvBlock = record
shvnext: PShvBlock; { pointer to the next block }
shvname: RxString; { Pointer to the name buffer }
shvvalue: RxString; { Pointer to the value buffer }
shvnamelen: ULong; { Length of the name value }
shvvaluelen: ULong; { Length of the fetch value }
shvcode: Byte; { Function code for this block }
shvret: Byte; { Individual Return Code Flags }
end;
Ее нужно соответствующим образом заполнить и вызвать RexxVariablePool. Что я и сделал в предлагаемом примере - библиотеке для работы с процессамми. В этой записи следует обратить на поле shvcode, которое может принимать значения:
rxshv_Set = $00; { Set var from given value }
rxshv_Fetch = $01; { Copy value of var to buffer }
rxshv_DropV = $02; { Drop variable }
rxshv_SySet = $03; { Symbolic name Set variable }
rxshv_SyFet = $04; { Symbolic name Fetch variable }
rxshv_SyDro = $05; { Symbolic name Drop variable }
rxshv_NextV = $06; { Fetch "next" variable }
rxshv_Priv = $07; { Fetch private information }
rxshv_Exit = $08; { Set function exit value }
Это команды функции RexxVariablePool. Из них более или менее понятны первые три (задать значение, считать значение, уничтожить переменную - именованый буфер) и $06 - считать следующее по цепочке значение. Таким образом, стем (а точнее именованый буфер) представляет собой цепочку таких блоков. Идентификатором такого буфера является его имя (shvname.strptr). Из каких-то соображений длина и имени стема и его значения повторяется дважды - один раз в составе переменной типа RxString, второй раз в соответствующем поле shvnamelen и shvvaluelen.
Собственно ничего сложного в написании подобной DLL нет, но пример с именованым буфером в Виртуале отсутствует, равно как и подробная документация по написанию DLL для REXX. Существуют также неочевидные моменты - везде в REXX используются null-terminated строки, поэтому нужен глаз да глаз ;), тем более что отлаживать такую библиотеку сложно, так как вызывается она не из EXE-ника...
Теперь о самой библиотеке. Она реализует одну-единственную функцию - ProcList, формат вызова которой:
call RxFuncAdd 'PrcLoadFuncs', 'RXPROCSS', 'PrcLoadFuncs'
call PrcLoadFuncs
call ProcList 'PROCESS' /* самвызов */
say 'Процессов 'PROCESS.0 /* там хранится число процессов*/
do i=1 to PROCESS.0
say PROCESS.i /* первое слово - PID процесса, второе его название*/
end
Функцию определения имени процесса по PID и наоборот легко реализовать на самом REXX на основе полученного списка.
В DLL реализована функция PrcLoadFuncs, назначение которой - подключить все функции данной библиотеки. На данный момент таковая только одна, поэтому наличие этой функции необязательно. Если ее закомментировать, то подключение из рекс-скрипта будет выглядеть так:
call RxFuncAdd 'ProcList', 'RXPROCSS', 'ProcList'
Программа использует недокументированную функцию DosQuerySysState, за информацию о ней спасибо DrChaos, iN8Malice, zuko, Euxx. zuko - особенно, так как он прислал исходник прямо на виртуале, избавив от необходимости медитации над сишными исходниками. OS2OK - только по его настоянию стал писать эту статью. Большое спасибо joseph за тестирование скриптов и sunlover за помощь в подготовке статьи. Ни в коем случае не претендую на авторство или оригинальность - это чистая компиляция уже проделанной работы.
Что можно усовершенствовать/открыть? Обработку ошибок добавить, у меня ее просто нет. Разобраться как с очередью из функции работать. Команды для RexxVariablePool поизучать. Научиться подключать рексовые длл-ки к паскалевским программам (т.е. обратная задача. Во-первых интересно, во-вторых в отладке поможет).
watchdog1.zip - программа-сторож процессов на рексе. Если какой процесс из списка охраняемых умрет, то она его рестартует. Список процессов получает перенаправлением вывода ps.exe в очередь.
watchdog13.zip - то же, но более продвинутый вариант. Конфиг расширен, процессы могут стартовать или детачится из их рабочих каталогов, и не сами екзешники, а батники, к примеру. Список процессов получает посредством библиотеки, использующей недокументированную функцию OS/2 API.
rxprclst.zip - исходные тексты библиотеки и простой пример ее использования.