Программирование субпроцессов




16.6.1.1. Процедура SwapVectors. При запуске среды Турбо Паскаль или созданного в ней выполнимого файла первым делом происходит смена ряда системных векторов прерываний на векторы отладчика среды и системной библиотеки. Однако адреса системных векторов не теряются, а запоминаются в переменных типа Pointer с именами SaveIntNN, где NN — номер прерывания (эти переменные являются предопределенными и привносятся вместе с системной библиотекой).

Пока программа работает сама по себе, нет особой необходимости восстанавливать системные векторы. Но если в программе организуется запуск субпроцесса, то будет разумнее, если он будет использовать именно системные векторы прерываний, а не специальные.

Процедура SwapVectors восстанавливает векторы прерываний, которые сохранены в переменных SaveIntNN, записывая одновременно в эти же переменные предыдущие векторы. Поэтому второй вызов SwapVectors вновь восстановит «отключенные» процедуры прерываний и сохранит в переменных последние активные адреса. Вообще говоря, можно даже взять за правило, чтобы в текстах программ соблюдалось требование четности вызовов процедуры SwapVectors.

Обычно эта процедура используется как «обрамляющая» для вызова Exec и в тех случаях, когда надо вернуться на время к исходным векторам, т.е. к тем, что были до запуска программы. О {379} номерах сохраняемых прерываний можно справиться в интерактивной подсказке среды программирования по модулю System.

16.6.1.2. Процедура Ехес(ExeFile, ComLine: String). Эта процедура служит для запуска субпроцесса. Программа, в которой используются вызовы процедуры Exec, должна иметь в своем начале директиву распределения памяти {$М...}. Кроме того, рекомендуется до вызова Exec и сразу после него вставлять процедуру SwapVectors.

В процедуру передается два строковых аргумента: ExeFile — имя файла или полное имя файла (в обоих случаях обязательно указывать расширение имени) — это просто имя того файла, который нужно «запустить» из программы; ComLine — строка из аргументов, которые передаются запускаемому файлу.

Рассмотрим пример. Пусть в командной строке MS-DOS дается команда

С:\> format a: /s

Для подачи такой же команды из файла, т.е. организации субпроцесса форматирования, надо использовать команду Exec:

Ехес('format.com', 'a: /s');

или даже

Ехес('c:\dos\format.com', 'a: /s');

если файл format.com лежит не в текущем каталоге, а в C:\DOS.

Строка ComLine может быть и пустой. Это означает, что в запускаемую программу не передаются никакие параметры. Имя запускаемого файла всегда должно присутствовать.

Второй пример: в строке MS-DOS слияние файлов задается командой

С:\> copy a.txt + b.txt c.txt

Но COPY — встроенная команда командного процессора и не является запускаемым файлом. Чтобы реализовать ее как субпроцесс, необходимо запускать командный процессор COMMAND.COM и передавать ему текст команды в виде параметров:

Ехес('connmand.com', '/с copy a.txt+b.txt c.txt');

Важно не забывать включить в командную строку первым по счету ключ /c для командного процессора. Если забыть это сделать, то получится «выход в DOS», и вернуться из субпроцесса можно будет только через подачу команды EXIT с клавиатуры. Ключ /р тоже не годится для субпроцесса, поскольку заставляет выполниться файл AUTOEXEC.BAT, что вряд ли к месту при запуске субпроцесса. А {380} ключ /c выполнит команды из строки и автоматически завершит субпроцесс.

При запуске командного процессора через процедуру Exec более правильным будет вставлять полное его имя, а оно, в свою очередь, может быть получено автоматически через функцию модуля DOS GetEnv. В этом случае организация выхода в DOS для свободной работы с возвратом по команде EXIT, например, запишется следующим образом:

Ехес(GetEnv('COMSPEC'), '');

Можно через командный процессор запускать и самостоятельные файлы. Так, пример с форматированием может быть переписан в виде

Ехес(GetEnv('COMSPEC'), '/с format a: /s');

Обращаем внимание, что здесь расширение '.СОМ' у команды format уже не обязательно. Подобный запуск имеет свои особенности. Первая — это незначительный перерасход памяти, и им можно пренебречь. Вторая особенность важнее: если запускается субпроцесс, то можно при помощи функции DosExitCode проанализировать, чем и как он закончился. Написав, например, в программе

Exec('subproc.exe', Parameters);

можно быть уверенным, что анализ завершения субпроцесса будет соответствовать действительности. Но запуск

Exec(GetEnv('COMSPEC'), '/с subproc '+Parameters);

скорее всего даст нормальное завершение субпроцесса, даже если subproc.exe сломает дисковод, сожжет монитор и завершится фатальной ошибкой. Просто в этом случае будет рассматриваться работа самого процессора COMMAND.COM, а не того субпроцесса, который он запускает и выполняет. А процессор редко дает сбои. Следует помнить об этом внутреннем различии, хотя внешний эффект будет неразличим (если, конечно, речь идет не о сжигании мониторов!).

О функции DosExitCode речь еще пойдет ниже. Кроме нее, можно анализировать ход выполнения субпроцесса через системную переменную модуля DOS DosError. После выполнения вызова Exec переменная DosError может содержать значения:

0 — все в порядке, нормальное выполнение;

2 — не найден файл-субпроцесс;

8 — не хватает памяти для запуска;

10 — несоответствие среды DOS;

11 — ошибка в формате команд. {381}

Появление значения DosError, равного 8, говорит о том, что надо повысить значение максимального размера кучи в директиве компилятора {$М... }.

Сбой в субпроцессе или даже невозможность его запустить зачастую не приводят ни к каким внешним эффектам — просто ничего не происходит. И определить, в чем ошибка, можно только через переменную DosError и функцию DosExitCode. Пример программы, запускающей различные субпроцессы, дан после описания функции DosExitCode (рис. 16.17):

16.6.1.3. Функция DosExitCode: Word. Эта функция анализирует завершение субпроцесса. В возвращаемом значении типа Word скомбинированы два значения. Старший байт содержит одно из значений, приведенных в табл. 16.8.

Таблица 16.8

Hi Значение кода
  Субпроцесс был прерван нажатием Ctrl+Break (по прерыванию 23Н)
  Субпроцесс был прерван из-за ошибки какого-либо устройства
  Субпроцесс завершился процедурой Keep и остался резидентным

Младший байт содержит код завершения программы-субпроцесса, переданный через процедуру завершения: Halt(n) или Keep(n), где n — код окончания. Если таких команд в программе не было, то код завершения будет равен 0.

На рис. 16.17 приведен пример, объединяющий процедуры и функции организации субпроцессов.

{$М 1512, 0, 0 ресурсы для запускающей программы } USES DOS, CRT; {Функция запускает файл ExeFile с параметрами Parameters и возвращает логическое значение True, если запуск был удачен. Коды завершения субпроцесса возвращаются в переменных ErrorLevel и ExitHiByte. }

Рис. 16.17 {382}

FUNCTION Execute(ExeFile, Parameters: String; VAR ErrorLevel, ExitHiByte: Byte): Boolean; VAR Wrd: Word; { промежуточная переменная } BEGIN SwapVectors; { установка векторов DOS } Exec(ExeFile, Parameters); { сам запуск субпроцесса } SwapVectors; { возврат векторов TURBO } Wrd:= DosExitCode; { запомним код завершения } ErrorLevel:= Lo(Wrd); { код выхода из процесса } ExitHiByte:= Hi(Wrd); { код способа выхода } Execute:= False; { пусть сначала будет так } case DosError of { анализ вызова Exec } 0: begin { все в порядке } Execute:= True; { Меняем значение функции } Exit { и выходим из нее } end; 2: WriteLn(#10'He найден файл ', ExeFile); 8: WriteLn(#10'He хватает памяти для субпроцесса) else WriteLn(#10'Ошибка DOS номер ', DosError) end {case} END; VAR { ===== ПРИМЕР ВЫЗОВОВ ==== } Er, Ex: Byte; Ch: Char; BEGIN ClrScr; { очистка экрана } CheckBreak:= True; ' Repeat { "вечный" цикл } WriteLn('Нажмите:'); WriteLn{ ' ':15, '[D] - для выхода в DOS'); WriteLn(‘’:15, '[S] - для запуска субпроцесса'); WriteLn(‘’:15, '[Q] - для завершения работы'); repeat Ch:= UpCase(ReadKey) { Выборочный опрос } until (Ch in [ 'D','S','Q']); { клавиатуры. } case Ch of { Действия: } 'D': begin { 1.Выход в MS-DOS. } HighVideo; Write(#10'Для возврата введите EXIT...'); LowVideo; if Execute(GetEnv('COMSPEC'),' ',Er,Ex) then; end;

Рис. 16.17 {383}

'S': begin { 2. Запуск файла. } if not Execute('outer.exe',' ', Er, Ex) then Halt; { запуск неудачен } if Ex = 1 then { Вы нажали ^Break:} WriteLn(#10'Процесс прерван с консоли.'); end; 'Q': Exit { 3. Выход из программы. } end; {case} until False { условие "вечного" цикла } END.

Рис. 16.17 (окончание)

16.6.2. Процедура Keep и резидентные программы

Процедура Кеер(ExitCode: Word), пожалуй, наименее описанная в руководстве по Турбо Паскалю. Ее назначение — завершать выполнение программы, выдавая в DOS код, заданный параметром ExitCode и оставлять ее в памяти ПЭВМ, т.е. делать программу резидентно находящейся в памяти. Ставится эта процедура в тексте программы последней по очередности выполнения. Внешне она аналогична процедуре Halt(n), но в отличие от последней резервирует память. Программы, разрабатываемые как резидентные, должны обязательно иметь в первых строках директиву распределения памяти {$М... }, в которой указываются необходимые для резервирования объемы памяти под стек и кучу (динамические объекты и данные).

Организация резидентных программ — дело достаточно сложное и требующее хороших системных знаний. Ведь мало оставить программу в памяти ПЭВМ — надо еще «заставить» ее реагировать на прерывания, возвращать управление и т.п. Это подразумевает наличие в тексте вставок машинных кодов и процедур с директивой interrupt, что вовсе не упрощает написание программ. Тяжело дается и отладка «резидентов» — после каждой неудачи, как правило, приходится перезапускать ПЭВМ.

Тем не менее ниже мы приводим пример резидентной программы. Она использует ряд функций модуля CRT и специальные приемы определения начала видеопамяти (см. разд. 20.1) и копирования экрана процедурой Move (рис. 16.18). {384}

{$М 1024, 0, 0} { директивы распределения памяти } PROGRAM HideScr; { Резидентная программа скрытия экрана от "любопытных глаз" во время отсутствия программиста. Работает во всех режимах текста и использует пароль (если задан) для возврата } USES CRT, DOS; VAR OldAttr: Byte; { последний цвет символов } WX, WY: Byte; { последнее место курсора } ScrAddr: Word; { сегмент начала экрана } Buffer: Array [1..8000] of Byte; {буфер для экрана } PS: String[20]; { нужна для ввода пароля } b: Boolean; { значение параметра BREAK } CONST Password: String[20] = ' '; { задаваемый пароль входа } {$F+} PROCEDURE MyInt05H; INTERRUPT; { процедура прерывания } VAR с: Char; BEGIN GetCBreak(b); { запоминание статуса BREAK } SetCBreak(False); { отключение проверки ^Break } OldAttr:= TextAttr; { запоминание последнего цвета } WX:= WhereX; { запоминание позиции курсора } WY:= WhereY; TextAttr:= 7; { неяркий цвет } if (Mem[0:$410] and $30) = $30 { начало экрана: } then ScrAddr:= $B000 { моно – режим } else ScrAddr:= $B800; { цветной режим } Move(Mem[ScrAddr:0],buffer,8000); { экран -> в буфер } FillChar(Mem[ScrAddr:0], 8000, 0); { затемнение экрана } repeat {цикл опроса пароля } ClrScr; { гашение экрана } repeat until KeyPressed; { ждать до нажатия } while KeyPressed do с:= ReadKey;{ сброс нажатия } Write(#10'Пароль возврата? '); { Ввод пароля, но } TextAttr:= 0; ReadLn(PS); TextAttr:= 7; { вслепую! } until PS=Password; { Пароль введен? } Move(buffer, Mem[ScrAddr:0],8000); { буфер-> на экран } GotoXY(WX, WY); { курсор на место } TextAttr:= OldAttr; { снова старый цвет } SetCBreak(b); { восстановление статуса BREAK } END; {$F-}

Рис. 16.18 {385}

{ Запускающая часть программы } BEGIN CheckBreak:= False; Write(#10#13'Программа закрытия экрана'#10#13 Запуск'); Write(#10' HIDESCR [пароль возврата]'#10#10#13); WriteLn('Для включения нажмите PrintScreen'#10#13); Password:= ParamStr(1); {пароль из командной строки } SetIntVec($00,SaveInt00); {Необходимые операции подго- } SetIntVec($1B,SaveInt1B); {товки резидентной работы } SetIntVec($05,@MyInt05H); {подстановка прерывания 05 } Кеер(0); { <-- То, ради чего построен пример! } END.

Рис. 16.18 (окончание) {386}



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2022-11-01 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: