среда, 19 октября 2011 г.

Продолжаем создание инсталляционного комплекта

В прошлом посте я начал тему создания инсталляционного комплекта. Один из вопросов, который остался нераскрытым:

Ожидание завершения всех процессов, запущенных из программы установки.

Это должна быть простая программа, которая не требует установки дополнительных компонентов, поэтому язык 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));
    }
}

 


Связанные материалы:



  1. Создание комплекта для инсталляции приложения

  2. Обновление манифеста приложения

Комментариев нет:

Отправить комментарий