Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система MS-DOS

© Александр Фролов, Григорий Фролов
Том 1, книги 1-2, М.: Диалог-МИФИ, 1991.

[Назад] [Содеожание] [Дальше]

3.4. Запуск программ из программ

Ваша программа может при необходимости запустить другую программу формата EXE или COM. Для ассемблерных программ существует функция 4Bh прерывания INT 21h, для программ, составленных на языке Си - разнообразные функции, входящие в состав стандартной библиотеки. Сначала рассмотрим запуск программ при помощи функции 4Bh прерывания INT 21h.

Содержимое регистров перед вызовом прерывания:

AH = 4BH
AL - код подфункции (0, 1, 2, 3)
DS:DX - указатель на путь к запускаемой программе
ES:BX - указатель на блок параметров EPB

После возврата из прерывания флаг CF устанавливается в 0, если ошибок не было, и в 1 при обнаружении ошибок. Регистр AX в случае наличия ошибок содержит код ошибки:

1 неверный код подфункции;
2 файл запускаемой программы не найден;
3 путь не найден;
4 слишком много открытых файлов;
5 нет доступа;
8 нет памяти для загрузки программы;
10 длина блока среды больше 32 килобайт;
11 плохой формат запускаемого EXE-файла.

Функция 4Bh прерывания 21h имеет четыре подфункции с номерами от 0 до 3:

0 загрузить и выполнить программу;
1 загрузить, но не выполнять программу (внутренняя подфункция для DOS 3.х);
2 загрузить, но не выполнять программу (внутренняя подфункция для DOS 2.х);
3 загрузить программу как оверлей (не создавать PSP).

Для функции 0 регистры DS:DX должны указывать на полный путь запускаемой программы в формате ASCIIZ ( т.е. текстовая строка, закрытая двоичным нулем). Блок параметров EPB (Exec Parameter Block) в этом случае имеет следующий формат:

(0) 2 seg_env сегментный адрес среды, которая создается родительской программой для запускаемой программы. Если в этом поле находится 0, то для запускаемой программы копируется среда родительской программы
(+2) 4 cmd FAR-адрес строки параметров для запускаемой программы. Эта строка должна иметь такой же формат, как и в PSP, т.е. вначале идет байт со значением, равным количеству символов в строке параметров, а затем - сама строка параметров
(+6) 4 fcb1 адрес блока FCB, который будет помещен в PSP со смещением 5Ch (в PSP помещается блок, а не адрес!)
(+10) 4 fcb2 адрес блока FCB, который будет помещен в PSP со смещением 6Ch.

Запущенной программе доступны все файлы, открытые родительской программой.

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

Приведем простую программу, которая запускает программу с именем PARM.COM из текущего каталога. Программу PARM.COM мы только что рассматривали, эта программа выводит на экран полученные ей в командной строке параметры.

                  .MODEL  small
                  DOSSEG

                  .STACK  100h

                  .DATA

path         db "PARM.COM",0
command_line db 8,"Parm Str"

epb      dw 0
cmd_off  dw ?
cmd_seg  dw ?
fcb1     dd ?
fcb2     dd ?

                  .CODE
                  .STARTUP

                  mov     bx,OFFSET command_line ; адрес командной
                  mov     cmd_off,bx       ; строки для блока EPB
                  mov     cmd_seg,ds

                  mov     ax,ds
                  mov     es,ax

                  mov     bx,OFFSET epb  ; ES:BX указывают на EPB
                  mov     dx,OFFSET path ; DS:DX указывают на путь
                                         ; запускаемой программы

                  mov     ax, 4B00h ; AH = 4Bh
                                             ; AL = 0 загрузить и выполнить
                  int     21h

                  .EXIT   0

                  END

Эта программа использует модель памяти SMALL, и ее загрузочный модуль имеет формат EXE. При редактировании был указан стандартный для Quick C 2.01 размер памяти, требуемый для программы. Если попытаться использовать формат COM в модели TINY, то окажется, что вся память распределена COM-программе и для дочерней программы не осталось места.

Следующая программа освобождает всю неиспользуемую ей память, после чего на освободившееся место загружает программу PARM.COM:

                  .MODEL  tiny
                  DOSSEG

                  .STACK  100h

                  .DATA

path         db "PARM.COM",0
command_line db 8,"Parm Str"

epb      dw 0
cmd_off  dw ?
cmd_seg  dw ?
fcb1     dd ?
fcb2     dd ?

                  .CODE
                  .STARTUP
;
; Освобождаем лишнюю память за концом программы
;
                  mov     bx,OFFSET last ; смещение конца
                                         ; программы

                  mov     cl,4           ; вычисляем длину в
                                         ; параграфах
                  shr     bx,cl

                  add     bx,17        ; добавляем 1 параграф для
                                       ; выравнивания и 256 байт
                                       ; для стека

                  mov     ah, 4Ah    ; изменяем размер выделенного
                  int     21h        ; блока памяти

                  mov     ax,bx      ; установка нового значения
                  shl     ax,cl      ;  указателя стека
                  dec     ax
                  mov     sp,ax

                  mov     bx,OFFSET command_line ; адрес командной
                  mov     cmd_off,bx             ; строки для
                                                 ; блока EPB
                  mov     cmd_seg,ds

                  mov     ax,ds
                  mov     es,ax

                  mov     bx,OFFSET epb  ; ES:BX указывают на EPB
                  mov     dx,OFFSET path ; DS:DX указывают на путь
                                         ; запускаемой программы

                  mov     ax, 4B00h      ; AH = 4Bh
                                         ; AL = 0 загрузить и
                                         ; выполнить
                  int     21h

                  .EXIT   0
last:   db ?
                  END

Для изменения размера выделенного программе блока памяти мы использовали функцию 4Ah прерывания 21h.

Подфункции 1 и 2 прерывания 4Bh используются DOS (это внутренние подфункции DOS). Мы приведем недокументированный формат блока EBP для этих функций.

Для подфункнкции 1:

(0) 2 seg_env сегментный адрес среды, которая создается родительской программой для запускаемой программы. Если в этом поле находится 0, то для запускаемой программы копируется среда родительской программы
(+2) 4 cmd FAR-адрес строки параметров для запускаемой программы.
(+6) 4 fcb1 адрес блока FCB, который будет помещен в PSP со смещением 5Ch
(+10) 4 fcb2 адрес блока FCB, который будет помещен в PSP со смещением 6Ch.
(+14) 4 ss_sp это поле будет содержать значение SS:SP после возврата
(+18) 4 entry_p адрес точки входа в загруженную программу (CS:IP)

Для подфункции 2:

(0) 2 seg_env сегментный адрес среды, которая создается родительской программой для запускаемой программы. Если в этом поле находится 0, то для запускаемой программы копируется среда родительской программы
(+2) 4 cmd FAR-адрес строки параметров для запускаемой программы.
(+6) 4 fcb1 адрес блока FCB, который будет помещен в PSP со смещением 5Ch
(+10) 4 fcb2 адрес блока FCB, который будет помещен в PSP со смещением 6Ch.

Подфункция 3 используется для загрузки программных оверлеев. Оверлей загружается в адресное пространство родительской программы, поэтому DOS не заказывает дополнительной памяти и не строит PSP. Формат EPB для этой подфункции:

(0) 2 seg_env сегментный адрес, по которому загружается программа
(+2) 4 reloc фактор перемещения, аналогичен элементу таблицы перемещений в заголовке EXE-файла

Следующая демонстрационная программа загружает программу PARM.COM_как оверлей без передачи ей управления:

                  .MODEL  small
                  DOSSEG

                  .STACK  100h

          .DATA

path    db "PARM.COM",0

epb     dw 0
reloc   dd 0

          .CODE
          .STARTUP


          mov     ax,ds
          mov     es,ax

          mov     bx,SEG buff
          mov     epb,bx

          mov     bx,OFFSET epb  ; ES:BX указывают на EPB
          mov     dx,OFFSET path ; DS:DX указывают на путь
                                 ; загружаемой программы

          mov     ax, 4B03h ; AH = 4Bh
                            ; AL = 0 загрузить оверлей
          int     21h

          .EXIT   0

buff:   dd 100 dup(?)

          END

Программа загружается в буфер buff.

Пользователи языка Си имеют в своем распоряжении три возможности запустить программу.

Самый простой способ - использовать функцию system(). Эта функция может выполнить любую команду DOS или любую программу, пакетный файл. Например:

system("FORMAT A:");

При использовании этой функции должен быть доступен COMMAND.COM. К сожалению, хотя system и возвращает код завершения, по нему нельзя сделать вывод о том, как была выполнена запускаемая программа. Если в качестве аргумента функции будет передано неправильное имя, на экране появится сообщение:

Bad command or file name

Код возврата в этом случае будет 0 - как будто все нормально!

Другие две возможности запустить программу - использовать функции spawn и exec. Функция spawn и ее разновидности запускают программу как дочерний процесс. Функция exec загружает новую программу как оверлей на место старой и передает ей управление без возврата. После завершения дочерней программе управление будет передано COMMAND.COM или программе, которая запустила родительскую программу.

Семейство функций spawn обеспечивает запуск дочерней программы с родительской или со специально сформированной средой. Кроме того, в файле process.h описаны параметры, которые можно передать функции spawn:

P_WAIT выполнение родительской программы задерживается до завершения дочерней программы.
P_NOWAIT родительская программа продолжает выполнение сразу после запуска дочерней. Этот параметр имеет смысл только для операционных систем OS/2, UNIX, в которых поддерживается мультизадачность.
P_OVERLAY загружает программу как оверлей и передает ей управление. Этот режим соответствует функции exec в том смысле, что родительская программа не получит управления после завершения дочерней.

В качестве примера использования функций запуска программы рассмотрим возможное решение проблемы создания HELP-системы для прикладной программы.

С помощью текстового редактора можно создать справочную базу данных в формате утилиты Microsoft HELPMAKE, затем, запуская в нужный момент диалоговую утилиту работы с базой данных Microsoft Quick Help QH.EXE, можно получить нужную справку.

Утилита QH использует базы данных, описанные в переменной среды HELPFILES. Мы будем использовать либо родительскую среду, где находится значение переменной HELPFILES по умолчанию, либо указывать новое значение для этой переменной.

Приведенная ниже программа используется для получения справки о функции стандартной библиотеки printf, поиск производится в HELP-базе QuickC:

#include <stdio.h>
#include <conio.h>
#include <process.h>

main()
{
         int r;

         // Получаем справку о функции printf,
         //  справочная база данных расположена
         //  в каталоге d:\qc2\bin

         r = help("HELPFILES=d:\\qc2\\bin;","printf");

         if( r == -1 )
                  printf( "Невозможно запустить процесс" );
         else
                  printf( "\nПроцесс завершен" );
         exit(r);
}


/**
*.Name      help
*
*.Title     Получить справку по заданному контексту
*
*.Descr     Функция получает в качестве параметров
*           переменную среды, указывающую на путь
*           к справочной базе данных и указатель
*           на строку контекста для поиска в базе.
*           Затем запускается как дочерний процесс
*           утилита Microsoft Quick Help QH.EXE, для
*           которой формируются среда и параметры.
*
*.Params    int help(char *help_file, char *help_topic);
*
*              help_file  - переменная среды, указывающая
*                           на путь к справочной базе
*
*              help_topic - контекст для поиска в базе
*
*
*.Return    0 при успешном запуске процесса
*           -1 не удалось запустить процесс
**/


int help(char *help_file, char *help_topic) {

         char *env[] = { "", NULL }; // Среда, которую
                                                    // получит QH при запуске

         if(*help_file != 0) {
                env[0] = help_file; // Формируем среду для QH

                // Запускаем утилиту

                return(spawnlpe(P_WAIT,"QH","QH",
                         "-u",help_topic,NULL,env));
         }
         else {

                // Если переменная среды не задана,
                //   используем родительскую среду

                return(spawnlp(P_WAIT,"QH","QH",
                         "-u",help_topic,NULL));
         
        }
}

Подробная информация об использовании утилит HELPMAKE и QH приводится в документации на Microsoft C 6.0.

[Назад] [Содеожание] [Дальше]