Доброго времени суток!
Вот вкратце моя проблема:
Есть написанный на С++ инсталлятор. В процессе работы необходимо оттуда запустить установку BDE. запускаю стандартно:
if ( ! CreateProcess( NULL,
Path_BDE_SETUP,
NULL,
NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi)
)
{
MessageBeep( MB_ICONERROR );
}
WaitForSingleObject( pi.hProcess, INFINITE );
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess );
все бы хорошо, но на WaitForSingleObject — весь процесс замирает, пока не "срублю" поток инсталлятора, тогда и продолжается установка BDE. Я так понял, это происходит из-за того, что инсталшилд вначале стартует виртуальную дос-машину, зачем-то (я смотрел по pi.hProcess ) и WaitForSingleObject ловит его хендл и впадает в бесконечное ожидание...
Кто-нибудь сталкивался с подобным?
Решил сделать по-другому:
"Выковырял" BdeInst.dll, гружу и вызываю DllRegisterServer — и все-бы хорошо, но под WinXP SP3 — возникает ошибка во время регистрации, пишет, что не хватает места на диске и спрашивает, продолжить ли? Когда соглашаешься — то дальше все идет нормально. Но неприятно пугает пользователей. Может кто-нибудь и с этим сталкивался?
09.12.08 01:20: Перенесено модератором из 'C/C++. Прикладные вопросы' — Кодт
Проблема решилась следующим образом:
if ( ! CreateProcess( NULL,
Path_BDE_SETUP,
NULL,
NULL,
TRUE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi)
)
{
// GetLastError
}
else
{
DWORD exitCode;
MSG msg;
do
{
WaitForSingleObject(pi.hProcess, 10);
if ( (PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE)) && (msg.message == WM_PAINT) )
{
::TranslateMessage(&msg);
::DispatchMessage(&msg);
}
GetExitCodeProcess(pi.hProcess, &exitCode);
} while ( exitCode == STILL_ACTIVE );
}
Здравствуйте, sublihim, Вы писали:
S>Проблема решилась следующим образом:
S>S>if ( ! CreateProcess( NULL,
S> Path_BDE_SETUP,
S> NULL,
S> NULL,
S> TRUE,
S> NORMAL_PRIORITY_CLASS,
S> NULL,
S> NULL,
S> &si,
S> &pi)
S> )
S> {
S> // GetLastError
S> }
S> else
S> {
S> DWORD exitCode;
S> MSG msg;
S> do
S> {
S> WaitForSingleObject(pi.hProcess, 10);
S> if ( (PeekMessage(&msg, m_hWnd, 0, 0, PM_REMOVE)) && (msg.message == WM_PAINT) )
S> {
S> ::TranslateMessage(&msg);
S> ::DispatchMessage(&msg);
S> }
S> GetExitCodeProcess(pi.hProcess, &exitCode);
S> } while ( exitCode == STILL_ACTIVE );
S> }
S>
PS. надо только сменить
NORMAL_PRIORITY_CLASS на
CREATE_SEPARATE_WOW_VDM
S>S> WaitForSingleObject(pi.hProcess, 10);
S>
Лучше MsgWaitForMultipleObject вместо этого таймаута
Здравствуйте, Аноним, Вы писали:
A>>>Лучше MsgWaitForMultipleObject вместо этого таймаута
S>>почему, аргументируйте?
А>1) Более быстрым просыпанием потока при приходе оконного сообщения.
А>2) Отсутствием отжора процессора на пустой цикл каждые 10 мсек.
А>3) Polling — быдлокодерство ( изза 1) и 2) )
Вообще, любой модальный цикл должен быть как-то предъявлен пользователю как модальный.
А то разница между Wait и MsgWait (или тупым поллингом) лишь в том, замёрзнет окошко или не замёрзнет.
Удобно предъявлять модальность... да, конечно: модальным диалогом!
Одна беда: ::DialogBox (или надстройка над ним, obj.DoModal), помимо дисабления нижележащих окон, крутит собственный цикл. А создавать немодальный диалог и вручную превращать его в модальный — много возни.
Поэтому я предпочёл бы сделать так:
1) вынес процедуру вызова дочернего процесса в отдельный поток
2) создал бы модальный диалог (с кнопкой "Cancel вдребезги пополам")
3) познакомил бы диалог и тред между собой (или даже пусть диалог из OnInitDialog запустит тред, отдав ссылку на себя)
4) по "Вдребезги" диалог посылает треду эвент, и тот прекращает ждать процесса (WaitForMultipleObjects), и делает ему TerminateProcess
5) в конце работы тред посылает диалогу сообщение о закрытии
А если всё это кажется слишком сложным, то у нас есть ещё один способ показать модальность. Это песочные часы!
1) включаем песочные часы
2) дисаблим окно приложения
3) запускаем процесс
4) устраиваем прокачку, всё, как описано выше с MsgWait...
5) энаблим окно обратно
6) восстанавливаем курсор в исходное положение
Минус в том, что при зависании дочернего процесса остаётся висеть (хотя и не замерзает) основной процесс. И чтобы разрулить ситуацию, придётся запускать таскменеджер.
Да, кстати.
Оба подхода — с модальным диалогом и с песочными часами — прекрасно оформляются для повторного использования.
Точка кастомизации — это создание и утилизация хэндла синхрообъекта.
Причём создавать и утилизировать можно за пределами собственно модальной процедуры
API будет выглядеть вот так
BOOL WaitWithDialog( // возвращает false, если пользователь прервал работу
HANDLE hToWait, // кого ждём
HWND hwndParent, // родительское окно для диалога
LPCTSTR strText, LPCTSTR strCaption, // содержание диалога
BOOL bHasCancelButton // ну и собственно, есть ли чудо-кнопка
);
void WaitWithCursor(
HANDLE hToWait, // кого ждём
HWND hwnd // какое окно (точнее, его owner'а) дисаблить
);
Тогда
CreateProcess(.......);
if(!WaitWithDialog(pi.hProcess, hwnd, _T("Installing BDE... Please wait"), _T("My Cool Installer"), true))
TerminateProcess(pi.hProcess);
CloseHandle(pi.hProcess);