В таких случаях возможны несколько случаев, определяемые сутью решаемых задач, а именно: требуется ли ожидание завершения работы порожденного процесса, и если требуется, то в синхронном или асинхронном режиме? Рассмотрим несколько случаев.
1. Работа приложения не должна продолжаться, пока не закончит работу порожденный процесс: самый неинтересный вариант. Для запуска процесса можно воспользоваться функциями семейства spawn... c параметром 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.
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. |