Информационный портал Media Systems & Bear Corp.

Главная Новости Delphi C&C++ Tеория Графов Web-Design Математика Исходники и Проекты Лисп и Пролог Ссылки

Портал :: Программирование на С и С++
Порождение процессов и контроль за ними

 

Те, кто читали статью «Использование Plug-in'ов … » вероятно видели среди комментариев к коду фразу о подключении обработчика OnIdle с ссылкой на подготавливаемую к выпуску статью. Вот она!!!

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

Очень часто возникает необходимость вызывать из работающего приложения другие исполняемые модули. В таких случаях возможны несколько случаев, определяемые сутью решаемых задач, а именно: требуется ли ожидание завершения работы порожденного процесса, и если требуется, то в синхронном или асинхронном режиме? Рассмотрим несколько случаев.

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

2)                      Приложение должно продолжать работу, не обращая внимания на поведение порожденного процесса: ненамного интереснее. Используются либо те же spawn функции, либо ShellExecute, либо WinExec.

3)                      Вариант следующий: после вызова внешней задачи приложение выполняет какие-либо действия, а потом ожидает завершения порожденного процесса. И тут пригодятся функции spawn… , но уже с параметром P_NOWAIT или  P_DETACH. В таком случае в системе сохраняются идентификаторы дочерних процессов, завершение которых становится возможным отслеживать. Для этого применяются функции wait и cwait.

Функция

Синтаксис

Header

wait

int wait(int *statloc)

process.h

cwait

int cwait(int *statloc, int pid, int action)

process.h

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

биты 0 – 7

Нули

биты 8 – 15

Старшие разряды кода возврата порожденного процесса( значение переданное в функцию exit  или в return)

При аварийном завершении биты статуса означают:

биты 0 – 7

1

неисправимая ошибка

2

генерация исключения

3

прерывание внешним сигналом

биты 8 – 15

Нули

 

При нормальном завершении wait возвращает идентификатор порожденнного процесса. При неудаче возвращается –1, а переменная errno равна EINTR – ненормальное завершение процесса, или ECHILD – порожденного процесса нет.

Функция cwait подобна wait, но даёт большую гибкость. Если параметр pid = 0, то происходит ожидание окончания любого порожденного процесса, но может быть указан и идентификатор конкретного процесса для ожидания его завершения. Параметр action может принимать значения WAIT_CHILD для ожидания только дочернего процесса или WAIT_GRANDCHILD для ожидания окончания все процессов, запущенных из дочернего.

4)                 Напоследок, самое любопытное. Допустим, что программа должна отследить завершение порождённого процесса, но при этом не должна приостанавливать своей работы (как это происходит в случае использования функций wait  и cwait). Я предлагаю использовать следующий подход: необходимо сохранить идентификатор порождённого процесса. Он возвращается функциями spawn… или может быть получен из структуры PROCESS_INFORMATION, передаваемой в функцию CreateProcess, которая, кстати, является рекомендованной к применению в Windows’9x. Далее необходимо подключить обработчик события OnIdle класса TApplication. Следующий фрагмент взят из моего проекта Tree Checker ( см. раздел по теории графов)

STARTUPINFO StartUp={sizeof (STARTUPINFO), NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0,
                                              STARTF_USESHOWWINDOW, SW_HIDE, 0, NULL, NULL, NULL};

PROCESS_INFORMATION Inform;//Заполняем структуры, необходимые для порождения нового процесса………………………………………………………

int res=CreateProcess(NULL, RunAllName.c_str(), NULL, NULL, 0, REALTIME_PRIORITY_CLASS, NULL, RootDir.c_str(), &StartUp, Inform);

if(res){// если процесс корректно порождён……..

……………………………

  fmMain->hProcess=Inform.hProcess;// сохраняем handler порожденного процесса…..

  Application->OnIdle=&fmMain->IdleProc;// подключаем обработчик OnIdle

…………………………………………………………………………………………….

Разумеется, этот обработчик должен быть заранее подготовлен и иметь, например, следующий вид:

void _fastcall TfmMain::IdleProc(TObject *Sender,bool& Done){

unsigned long test;

GetExitCodeProcess(hProcess,&test);//получаем статус процесса

if (test!=STILL_ACTIVE){// если завершён…………

hProcess=0;//обнуляем идентификатор

DeleteFile(RootDir+TempFileName );//удаляем временный файл

miRunSearch->Enabled=true;

miGenerate->Enabled=true;

miRunBatch->Enabled=true;//активизируем пункты меню

sbBar->SimpleText="Done!!!";

Application->OnIdle=NULL; // отключаем ненужный теперь обработчик

Stat=new TfmStat(Application);

Stat->lbTotProc->Caption=Processes;

Stat->lbCurProc->Caption=ProcessName;

Stat->CurTime=Stat->CurTime.CurrentDateTime();//формируем и выводим окно статистики………

Stat->lbTime->Caption=(Stat->CurTime-ProcBeg).TimeString() + " :: " + (AnsiString)(( (int) ((double)(Stat2->
                                           CurTime - ProcBeg)*24*3600*100) )%100);

Stat->lbTime->Enabled=true;

Stat->lbTimeEl->Enabled=true;

Stat->ShowModal();


delete Stat;

Stat=NULL;

if (ShowRep)//и если надо, показываем отчёт

if (RepType==Substitutions) OpenReport(ReportName);

}

}

Такой подход даёт наиболее гибкие возможности по контролю порождённых процессов и может свободно применяться в средах Delphi и C++ Builder.

       Пишите сюда: bearoleg@online.ru

                                                                                                     Bear. (1.03.2001)

     
  Гостевая книга