Программирование PC Очень часто возникает необходимость вызывать из работающего приложения другие исполняемые модули. Tue, May 30 2017  

Поделиться

нашли опечатку?

Пожалуйста, сообщите об этом - просто выделите ошибочное слово или фразу и нажмите Shift Enter.


Очень часто возникает необходимость вызывать из работающего приложения другие исполняемые модули. Печать
Добавил(а) microsin   

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

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.

 

Добавить комментарий


Защитный код
Обновить

Top of Page