В прошлом посте я начал тему создания инсталляционного комплекта. Один из вопросов, который остался нераскрытым:
Ожидание завершения всех процессов, запущенных из программы установки.
Это должна быть простая программа, которая не требует установки дополнительных компонентов, поэтому язык C# не подходит (на компьютере может отсутствовать .NET Framework). Я выбрал для её реализации С++ с использованием статической библиотеки STL.
При запуске программа сразу запускает процесс setup.exe (исполняемый файл, получаемый в результате сборки Setup Project) и начинает наблюдение за ним:
//структура описывает запущенный процесс
struct PROC2WAIT
{
DWORD procID; //<идентификатор процесса
HANDLE procH; //<хэндл процесса
};
//--------------------------------------------------------------------------//
if (!::CreateProcess(setupPath, NULL, NULL, NULL, FALSE, 0U, NULL, NULL, &;sti, &pri))
{
int err = GetLastError();
throw exception("CreateProcess failed", err);
}
PROC2WAIT rootProc = {0};
rootProc.procH = pri.hProcess;
rootProc.procID = pri.dwProcessId;
//ожидаем завершения процесса и его потомков
WaitProcTree(rootProc);
Функция ожидания завершения процессов очень проста:
void WaitProcTree( PROC2WAIT rootProc)
{
list<;PROC2WAIT> pids;
pids.push_back(rootProc);
//продолжаем цикл, пока в наборе есть хотя бы один процесс
while (pids.size() >; 0)
{
//ждем пока закроется хотя бы один
WaitAnyClose(pids);
//добавляем новые процессы, которые были порождены завершенным или запущенными
AddNewLeaves(pids);
//удаляем из набора завершенные процессы
RemoveClosed(pids);
}
}
Остальные функции не более сложные:
///ожидание завершения любого из процессов
void WaitAnyClose( const list<PROC2WAIT> &pids )
{
HANDLE * phandles = new HANDLE[pids.size()];
int i = 0;
for (list<PROC2WAIT>::const_iterator it = pids.begin(); it != pids.end(); it++)
phandles[i++] = (*it).procH;
WaitForMultipleObjects(pids.size(), phandles, FALSE, INFINITE);
delete[] phandles;
}
//удаление из набора завершенных процессов
void RemoveClosed( list<PROC2WAIT> & pids )
{
list<PROC2WAIT>::iterator it = pids.begin();
while(it != pids.end())
{
bool IsCompleted = (WaitForSingleObject((*it).procH, 0) == WAIT_OBJECT_0);
if (!IsCompleted)
{
it++;
continue;
}
list<PROC2WAIT>::iterator rem = it++;
CloseHandle(rem->procH);
pids.erase(rem);
}
}
На этом фоне чуть более сложной выглядит функция добавления в набор новых процессов, порожденных из уже запущенных. В основе её работы лежит использование функции CreateToolhelp32Snapshot, которая позволяет получить состояние запущенных процессов, с их последующим анализом и сравнением с имеющимся набором.
void AddNewLeaves( list<PROC2WAIT> & pids )
{
HANDLE hsnap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0U);
if (hsnap == INVALID_HANDLE_VALUE)
throw exception("CreateToolhelp32Snapshot failed", GetLastError());
PROCESSENTRY32 ProcEntry = {0};
ProcEntry.dwSize = sizeof(PROCESSENTRY32);
if (Process32First(hsnap, &ProcEntry))
{
do
{
CompareID x(ProcEntry.th32ParentProcessID);
list<PROC2WAIT>::iterator hasparent = find_if(pids.begin(), pids.end(), x);
if (hasparent != pids.end())
{
PROC2WAIT p = {0};
p.procH = OpenProcess(SYNCHRONIZE, FALSE, ProcEntry.th32ProcessID);
p.procID = ProcEntry.th32ProcessID;
if (p.procH != NULL)
pids.push_back(p);
}
} while (Process32Next(hsnap, &ProcEntry));
}
}
Комментариев нет:
Отправить комментарий