RDM/2 The Russian Electronic Developer Magazine  
RDM/2 Русский электронный журнал разработчика  
ДомойОт редактораПишите намОбратная связьRU/2

Написание библиотек для REXX на Virtual Pascal/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 - исходные тексты библиотеки и простой пример ее использования.
Виктор Кустов AKA VikingX
kvv@muka.pptus.ru

---
Интересные ссылки:

---

---
Комментариев к странице: 0 | Добавить комментарий
---
Редактор: Дмитрий Бан
Оформление: Евгений Кулешов


(C) Russian Underground/2