Важно не забывать включить в командную строку первым по счету ключ /c для командного процессора. Если забыть это сделать, то получится «выход в DOS», и вернуться из субпроцесса можно будет только через подачу команды EXIT с клавиатуры. Ключ /р тоже не годится для субпроцесса, поскольку заставляет выполниться файл AUTOEXEC.BAT, что вряд ли к месту при запуске субпроцесса. А
- 381 -
ключ /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 — ошибка в формате команд.
- 382 -
Появление значения DosError, равного 8, говорит о том, что надо повысить значение максимального размера кучи в директиве компилятора {$М ... }.
Сбой в субпроцессе или даже невозможность его запустить зачастую не приводят ни к каким внешним эффектам — просто ничего не происходит. И определить, в чем ошибка, можно только через переменную DosError и функцию DosExitCode. Пример программы, запускающей различные субпроцессы, дан после описания функции DosExitCode (рис. 16.17):
16.6.1.3. Функция DosExitCode : Word. Эта функция анализирует завершение субпроцесса. В возвращаемом значении типа Word скомбинированы два значения. Старший байт содержит одно из значений, приведенных в табл. 16.8.
Таблица 16.8
Hi | Значение кода |
0 | Нормальное завершение и возврат управления |
1 | Субпроцесс был прерван нажатием Ctrl+Break (по прерыванию 23Н) |
2 | Субпроцесс был прерван из-за ошибки какого-либо устройства |
3 | Субпроцесс завершился процедурой Keep и остался резидентным |
Младший байт содержит код завершения программы-субпроцесса, переданный через процедуру завершения: Halt(n) или Keep(n), где n — код окончания. Если таких команд в программе не было, то код завершения будет равен 0.
На рис. 16.17 приведен пример, объединяющий процедуры и функции организации субпроцессов.
| { $М 1512, 0, 0 ресурсы для запускающей программы }
| USES DOS, CRT;
| {Функция запускает файл ExeFile с параметрами Parameters и возвращает логическое значение True, если запуск был удачен. Коды завершения субпроцесса возвращаются в переменных ErrorLevel и ExitHiByte.}
Рис. 16.17
- 383 -
| 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 ); { код способа выхода }