Перейти из форума на сайт.

НовостиФайловые архивы
ПоискАктивные темыТоп лист
ПравилаКто в on-line?
Вход Забыли пароль? Первый раз на этом сайте? Регистрация
Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Как переместить модальный диалог на передний план ?

Модерирует : ShIvADeSt

 Версия для печати • ПодписатьсяДобавить в закладки

Открыть новую тему     Написать ответ в эту тему

mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
Есть очень большая программа.
В определенной ситуации она ''выкидывает'' модальный диалог.
Проблема в том, что иногда если юзер не прореагировал на него, через определенное время ''смежники'' выбрасывают
из другого потока еще один модальный диалог.
Так вот после истечения таймаута надо вернуть фокус первому модальному диалогу и закрыть его с возвращением на строку  
if (pDlg->DoModal() == IDOK) в коде родителя.
 
Я применил для этого следующий код:  
в OnClose() модального диалога при срабатывании таймера:  

Код:
 if (GetForegroundWindow() != this)
    {
        HWND hCurrWnd;
        int iMyThreadID, iCurrThreadID;
 
        hCurrWnd = ::GetForegroundWindow();
        iMyThreadID   = GetCurrentThreadId();
        iCurrThreadID = GetWindowThreadProcessId(hCurrWnd, 0);
 
        AttachThreadInput(iMyThreadID, iCurrThreadID, TRUE);
 
        SetForegroundWindow();
 
        AttachThreadInput(iMyThreadID, iCurrThreadID, FALSE);
    }
 
 
 
В 95% случаев работает ''на ура''.  
Но иногда обваливается Debug Assertion Failed: file: wincore.cpp line: 4486.  
А именно в строчке ASSERT(ContinueModal()):

Код:
     // phase2: pump messages while available
                do
                {
                        ASSERT(ContinueModal());

Что он желает?
Kaкие будут идеи?

----------
Мы знаем: время растяжимо. Оно зависит от того,
Какого рода содержимым Вы заполняете его. (C. Маршак)

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 21:01 25-01-2005 | Исправлено: mihas83, 21:02 25-01-2005
ShIvADeSt



Moderator
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83

Цитата:
Так вот после истечения таймаута надо вернуть фокус первому модальному диалогу и закрыть его с возвращением на строку  
if (pDlg->DoModal() == IDOK) в коде родителя.

Для чего возвращать фокус мне не понятно, если же просто надо закрыть диалог, то не проще ли через FindWindowEx() найти кнопку ок, нажать ее и все будет ок
Если именно это тебе нужно.

----------
И создал Бог женщину... Существо получилось злобное, но забавное...

Всего записей: 3956 | Зарегистр. 29-07-2003 | Отправлено: 03:24 26-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
ShIvADeSt

Цитата:
FindWindowEx() найти кнопку ок, нажать ее и все будет ок [Bl]  
Если именно это тебе нужно.

Это сделать нет проблем.

Цитата:
Для чего возвращать фокус мне не понятно

Иначе управление не попадает назад к родителю, в точку вызова (pDlg->DoModal())...

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 08:55 26-01-2005
ShIvADeSt



Moderator
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору

Цитата:
Иначе управление не попадает назад к родителю, в точку вызова (pDlg->DoModal())...

Хм странно, если поток ждет когда будет реакция на диалог, то ему должно быть по барабану, был установлен на него фокус.

Цитата:
if (GetForegroundWindow() != this)  
    {  
        HWND hCurrWnd;  
        int iMyThreadID, iCurrThreadID;  
 
        hCurrWnd = ::GetForegroundWindow();  
        iMyThreadID   = GetCurrentThreadId();  
        iCurrThreadID = GetWindowThreadProcessId(hCurrWnd, 0);  
 
        AttachThreadInput(iMyThreadID, iCurrThreadID, TRUE);  
 
        SetForegroundWindow();  
 
        AttachThreadInput(iMyThreadID, iCurrThreadID, FALSE);  
    }

Это если я не ошибаюсь принудительный вынос окна на передний план ( по крайней мере одна из его реализаций, кстати тот человек который ее предлагал не советовал ее использовать).


----------
И создал Бог женщину... Существо получилось злобное, но забавное...

Всего записей: 3956 | Зарегистр. 29-07-2003 | Отправлено: 09:24 26-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
ShIvADeSt

Цитата:
Это если я не ошибаюсь принудительный вынос окна на передний план

Совершенно верно.

Цитата:
тот человек который ее предлагал не советовал ее использовать

Это где и когда?

Цитата:
Хм странно, если поток ждет когда будет реакция на диалог, то ему должно быть по барабану, был установлен на него фокус.  

Если было бы не так, то нет смысла играться...
Видимо, ''управление'' перехватывает верхний модальный диалог.
Если его нет - никаких проблем.

----------
Мы знаем: время растяжимо. Оно зависит от того,
Какого рода содержимым Вы заполняете его. (C. Маршак)

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 09:56 26-01-2005 | Исправлено: mihas83, 09:57 26-01-2005
ShIvADeSt



Moderator
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83
Вот код который там предлагался, и который не рекомендуют юзать. ИМХО небольшие отличия с твоим. Попробуй его.

Цитата:
 
function ForceForegroundWindow(Const hWnd: THandle): Boolean;
  var
          hCurWnd: THandle;
    begin
 
       Result := False;
 
       if not isWindow(hWnd) then Exit;
 
       hCurWnd := GetForegroundWindow;
 
       if (hCurWnd=hWnd) then begin
         Result := True;
         Exit;
       end;
 
       if (GetWindowLong( hWnd, GWL_STYLE ) and WS_MINIMIZE)<>0
       then ShowWindow(hWnd, SW_SHOWNOACTIVATE);
 
       AttachThreadInput(    GetWindowThreadProcessId(hCurWnd, nil),
       GetCurrentThreadId, True);
 
       Application.ProcessMessages;
 
       SetForegroundWindow(hWnd);
 
       AttachThreadInput(    GetWindowThreadProcessId(hCurWnd, nil),
       GetCurrentThreadId, False);
 
       Result := GetForegroundWindow=hWnd;
 
end;
 
 



----------
И создал Бог женщину... Существо получилось злобное, но забавное...

Всего записей: 3956 | Зарегистр. 29-07-2003 | Отправлено: 11:47 26-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
ShIvADeSt

Цитата:
Вот код который там предлагался, и который не рекомендуют юзать.  

Где предлагался, и где не рекомендуют юзать?  
Почему?

----------
Мы знаем: время растяжимо. Оно зависит от того,
Какого рода содержимым Вы заполняете его. (C. Маршак)

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 19:12 26-01-2005
ShIvADeSt



Moderator
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83

Цитата:
Где предлагался, и где не рекомендуют юзать?    
Почему?

Я не помню где это было, просто данный код давно у меня вроде бы это было в ФИДОшной эхе, врать не буду. просто вот два варианта, один из которых твой, второй почему то не работает на NT системах

Цитата:
 
Если это твоя пpога, можешь поюзать SwitchToThisWindow(Window, true).
Если же испpавить этот глюк в системе, то вот:
 
===========================================================
While rooting around the MSDN Library CD i noticed that
SystemParametersInfo has a couple of new flags, among them these two:
 
SPI_GETFOREGROUNDFLASHCOUNT
TWindows NT 5.0 and later, Windows 98: Indicates the number of times
SetForegroundWindow will flash the taskbar button when rejecting a
foreground switch request. The pvParam parameter must point to a DWORD
variable that receives the value.
 
SPI_GETFOREGROUNDLOCKTIMEOUT
Windows NT 5.0 and later, Windows 98: Indicates the amount of time
following user input, in milliseconds, during which the system will not
allow applications to force themselves into the foreground. The pvParam
parameter must point to a DWORD variable that receives the time.
 
There are also two matching SPI_SET flags. Now, this indicates that it
might be possible to
 
 - get the old LOCKTIMEOUT
 - set the LOCKTIMEOUT to 0
 - call SetForegroundWindow
 - restore the old LOCKTIMEOUT
 
and thus get your window to front even if the user is hammering away at
the keyboard in another app. Of course doing so would be antisocial
behaviour, but anyway: could you test this notion? I do not have Win98
or NT 5 beta installed anywhere
 
// 1-й способ (корректный)
Procedure ForceForegroundWindow( Wnd : HWND );
      // by "Peter Below (TeamB)" <100113.1101@compuXXserve.com>
     Const
        SPI_GETFOREGROUNDLOCKTIMEOUT = $2000;
        SPI_SETFOREGROUNDLOCKTIMEOUT = $2001;
 Var
            timeout, temp: DWORD;
            needsHack: Boolean;
    Begin
            needsHack := { Is it Win98 or NT 5 or later? }
              ((Win32Platform = VER_PLATFORM_WIN32_NT)
               and
               (Win32MajorVersion > 4)
              )
              or
              ((Win32Platform = VER_PLATFORM_WIN32_WINDOWS)
               and
               ((Win32MajorVersion > 4)
                or
                ((Win32MajorVersion = 4)
                 and
                 (Win32MinorVersion > 0)
                )
               )
              );
            If needsHack Then Begin
 
ParametersInfo(
                SPI_GETFOREGROUNDLOCKTIMEOUT,
                0, @timeout, 0);
              temp := 0;
              SystemParametersInfo(
                SPI_SETFOREGROUNDLOCKTIMEOUT,
                0, @temp, 0);
            End; { If }
            SetForegroundWindow( Wnd );
            If needsHack Then Begin
              SystemParametersInfo(
                SPI_SETFOREGROUNDLOCKTIMEOUT,
                timeout, @timeout, 0);
              { Note: the platform SDK docs indicate that the second parameter
                is not used in setting the timeout but that does not match
                with the common use of this parameter for other SPI_SET flags.
                If it is not used then specifying it should do no harm, if
                the docs are off we have covered both bases <g>.}
        End; { If }
End; { ForceForegroundWindow }
 
 
// 2-й способ (работает, но ...)
 
function ForceForegroundWindow(Const hWnd: THandle): Boolean;
  var
          hCurWnd: THandle;
    begin
 
       Result := False;
 
       if not isWindow(hWnd) then Exit;
 
       hCurWnd := GetForegroundWindow;
 
       if (hCurWnd=hWnd) then begin
         Result := True;
         Exit;
       end;
 
       if (GetWindowLong( hWnd, GWL_STYLE ) and WS_MINIMIZE)<>0
       then ShowWindow(hWnd, SW_SHOWNOACTIVATE);
 
       AttachThreadInput(    GetWindowThreadProcessId(hCurWnd, nil),
       GetCurrentThreadId, True);
 
       Application.ProcessMessages;
 
       SetForegroundWindow(hWnd);
 
       AttachThreadInput(    GetWindowThreadProcessId(hCurWnd, nil),
       GetCurrentThreadId, False);
 
       Result := GetForegroundWindow=hWnd;
 
end;
 

как видно рекомендуют юзать именно первый способ. По крайней мере я так понял.


----------
И создал Бог женщину... Существо получилось злобное, но забавное...

Всего записей: 3956 | Зарегистр. 29-07-2003 | Отправлено: 02:41 27-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
ShIvADeSt

Цитата:
 не помню где это было,  

Жаль, хотелось глянуть обсуждение ''узких мест''...

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 10:23 27-01-2005
segeich

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83

Цитата:
из другого потока еще один модальный диалог

А правильно ли это, что диалоги из разных потоков выводятся (да еще и одновременно)? Имхо лучше когда интерфейсом занимается один выделенный поток. Или, на крайний случай, потоки координируют свои действия (юзер, он всё-таки, разделяемый ресурс )
 

Цитата:
Цитата:Хм странно, если поток ждет когда будет реакция на диалог, то ему должно быть по барабану, был установлен на него фокус.  
 
Если было бы не так, то нет смысла играться...  
Видимо, ''управление'' перехватывает верхний модальный диалог.  
Если его нет - никаких проблем.

Может стоит вот с этой проблемой разобраться, а не искать обходных путей?
Какой поток пытается закрыть первый диалог и каким образом?
Что за потоки (с message loop или без)?

Всего записей: 112 | Зарегистр. 03-01-2003 | Отправлено: 01:57 28-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
segeich

Цитата:
А правильно ли это, что диалоги из разных потоков выводятся (да еще и одновременно)? Имхо лучше когда интерфейсом занимается один выделенный поток. Или, на крайний случай, потоки координируют свои действия (юзер, он всё-таки, разделяемый ресурс)  

Они выводятся не одновременно. Смотри первый пост.

Цитата:
Какой поток пытается закрыть первый диалог и каким образом?  

После истечения таймаута он сам же  

Код:
void CDlg::OnTimer(UINT nIDEvent)  
{
 PostMessage(WM_CLOSE);
 CDialog::OnTimer(nIDEvent);
}
 
Надо вернуть фокус первому модальному диалогу и закрыть его с возвращением на строку if (pDlg->DoModal() == IDOK) в коде родителя.  
Кстати, предлагался вариант:
 ::SendMessage(::GetDesktopWindow(), WM_SYSCOMMAND, (WPARAM) SC_HOTKEY, (LPARAM) this->m_hWnd);
 

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 14:45 28-01-2005
segeich

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83

Цитата:
Они выводятся не одновременно. Смотри первый пост.

Если я правильно понял твой первый пост, то второй диалог выводится из другого потока, при чем в это же время первый диалог еще существует. Т.е. одновременно висят 2 модальных диалога, выданных из разных потоков? Или не так?
 

Цитата:
PostMessage(WM_CLOSE);  

А зачем так хитро? Чем EndDialog() не устраивает?

Всего записей: 112 | Зарегистр. 03-01-2003 | Отправлено: 18:25 28-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
segeich

Цитата:
второй диалог выводится из другого потока, при чем в это же время первый диалог еще существует. Т.е. одновременно висят 2 модальных диалога, выданных из разных потоков? Или не так?  

Совершенно верно.
Но он ''возникает'' не всегда, а только при определенных условиях...segeich

Цитата:
Чем EndDialog() не устраивает?  

Надо однозначно и гарантированно попасть на DoModal() в коде родителя.

----------
Мы знаем: время растяжимо. Оно зависит от того,
Какого рода содержимым Вы заполняете его. (C. Маршак)

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 23:06 28-01-2005
segeich

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83

Цитата:
Надо однозначно и гарантированно попасть на DoModal() в коде родителя.

Дык EndDialog для этого и предназначен. Уничтожает модальный диалог и заставляет DoModal вернуть указанный код возврата.
 
Посмотри код функции RunModalLoop в исходниках MFC (в ней как раз и сидит ASSERTна ContinueModal). Там вроде бы все достаточно очевидно.
 
Если в очереди есть сообщение, оно обрабатывается ( PumpMessage ). Поскольку обработчик сообщения может вызвать EndDialog, то после вызова PumpMessage проверяется, не пора ли завершать модальный цикл ( if (!ContinueModal()) goto ExitModal; )
 
В нормальной ситуации, когда все сообщения из очереди диалога проходят через этот цикл, EndDialog может случиться только внутри PumpMessage.
 
Срабатывание ASSERT(ContinueModal() может означать, например, что
- EndDialog был вызван напрямую извне (напр. из другого потока)
- Сообщение в диалог было доставлено напрямую (SendMessage), минуя очередь сообщений. И обаботчик этого сообщения позвал EndDialog.

Всего записей: 112 | Зарегистр. 03-01-2003 | Отправлено: 23:38 29-01-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
segeich

Цитата:
Посмотри код функции RunModalLoop в исходниках MFC (в ней как раз и сидит ASSERTна ContinueModal).  

Смотрел. Ничего ''опасного'' не нашел и для WM_CLOSE не нашел.

Цитата:
EndDialog для этого и предназначен. Уничтожает модальный диалог и заставляет  

EndDialog не закрывает блок диалога сама, а только устанавливает флаг, чтобы закрыть его, как только закончится обработка текущего сообщения.
Цитата:
Срабатывание ASSERT(ContinueModal() может означать,

В 95% случаев все работает ''на ура''.
Оставшихся 5% не понятно - что ему не нравится...

----------
Мы знаем: время растяжимо. Оно зависит от того,
Какого рода содержимым Вы заполняете его. (C. Маршак)

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 09:10 30-01-2005
segeich

Junior Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
mihas83

Цитата:
EndDialog не закрывает блок диалога сама, а только устанавливает флаг, чтобы закрыть его, как только закончится обработка текущего сообщения.  

Кроме установки флага CDialog::EndDialog еще и виндовый EndDialog вызывает. Непонятно правда, нафига они это делают. Ведь модальные диалоги в MFC реализованы через немодальные (виндовую ::DialogBox они не используют).
 

Цитата:
В 95% случаев все работает ''на ура''.  
Оставшихся 5% не понятно - что ему не нравится...  

Тут то все понятно, как раз
 
Интересующий нас кусок функции CWnd::RunModalLoop выглядит примерно так

Код:
 
do {
    ASSERT(ContinueModal());              // (1)  
    AfxGetThread()->PumpMessage();   // (2) обработать сообщение
    if (!ContinueModal())                       // (3) проверить признак выхода
       goto ExitModal;
} while (::PeekMessage());                  // продолжать пока очередь не пуста
 

В точке (1) утверждается, что признак выхода из модального диалога может быть установлен только в точке (2), т.е. внутри обработчика сообщения. Признак проверяется после завершения обработчика, в точке (3).
 
Когда нарушается это требование?  
- Когда признак встает либо после (1), но до (2), либо после (2), но до (1).
 
Когда такое может случиться?  
- см. мой предыдущий пост:

Цитата:
- EndDialog был вызван напрямую извне (напр. из другого потока)  
- Сообщение в диалог было доставлено напрямую (SendMessage), минуя очередь сообщений. И обработчик этого сообщения позвал EndDialog.

Это всего лишь пара идей, пришедших в голову. Наверняка,  могут быть и другие ситуации.
 
Скорее всего WM_CLOSE тут не причем, и твоя проблема в чем то другом. Ставь точку останова на все места, где сбрасывается флаг (их вроде всего 2), и смотри откуда идет вызов в 5% случаев.
 
Почему в 95% случаев все работает, а в 5% - нет?
- Потому что на самом деле не работает в 100% случаев Просто большую часть времени код тратит внутри (2), поэтому в 95% случаев "бяка" происходит после (1), но до (3), и ее не замечают.

Всего записей: 112 | Зарегистр. 03-01-2003 | Отправлено: 01:11 01-02-2005
mihas83



Gold Member
Редактировать | Профиль | Сообщение | Цитировать | Сообщить модератору
segeich

Цитата:
Непонятно правда, нафига они это делают. Ведь модальные диалоги в MFC реализованы через немодальные  

Toже задавал себе такой вопрос...

Всего записей: 7832 | Зарегистр. 15-07-2003 | Отправлено: 08:55 01-02-2005
Открыть новую тему     Написать ответ в эту тему

Компьютерный форум Ru.Board » Компьютеры » Прикладное программирование » Как переместить модальный диалог на передний план ?


Реклама на форуме Ru.Board.

Powered by Ikonboard "v2.1.7b" © 2000 Ikonboard.com
Modified by Ru.B0ard
© Ru.B0ard 2000-2024

BitCoin: 1NGG1chHtUvrtEqjeerQCKDMUi6S6CG4iC

Рейтинг.ru