Как сделать окно активным?

Тема в разделе "WASM.WIN32", создана пользователем BreakPointMAN, 29 май 2006.

  1. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia
    Пишу я в Borland C++ Builder, но, думаю, в данном случае это не имеет особого значения, так как вопрос относится, скорее, не к языку и среде, а к оконной подсистеме Windows и WinAPI.



    Итак, суть вопроса: требуется, зная описатель окна, сделать его активным, то есть вывести окно на передний план и передать ему клавиатурный фокус. В моей конкретной задаче таковым окном является окно одной из форм приложения. При этом желательно, чтобы код, выполняющий данные действия, срабатывал как для линейки Win9x, так и для линейки WinNT.



    Если я правильно понял главу 27 "Модель аппаратного ввода и локальное состояние ввода" ("Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows" (с) Джеффри Рихтер), то поставленную задачу должен решить следующий код:


    Код (Text):
    1. bool BringWindowToForeground(HWND hWnd)
    2.    {
    3.     if(hWnd==NULL || !IsWindow(hWnd)) return false;
    4.  
    5.     HWND hWndActive=GetForegroundWindow();
    6.     DWORD dwActiveThreadId=GetWindowThreadProcessId(hWndActive,NULL);
    7.     DWORD dwOurThreadId = GetCurrentThreadId();
    8.  
    9.     AttachThreadInput(dwOurThreadId, dwActiveThreadId, true);
    10.     bool Result=
    11.        SetForegroundWindow(hWnd);
    12.     AttachThreadInput(dwOurThreadId, dwActiveThreadId, false);
    13.  
    14.     return Result;
    15.    }




    Однако, вопреки ожиданиям, это срабатывает не всегда. В частности, когда окно, которое мы пытаемся сделать активным, перекрыто окном активного консольного приложения, в панели задач просто начинает мигать пиктограмма приложения, но не более того. (На всякий случай сообщу, что у меня Microsoft Windows 2000 Professional 5.00.2195 Service Pack 4, russian.)



    Перепробывав кучу различных способов, отправился на поиски решения проблемы в Интернет... Вскоре наткнулся на две аналогичные по своей сути функции, одна написана на Delphi под VCL, другая на MSVC под MFC. Вот ссылка на последнюю: BringWindowToForeground. Я обрадовался - наконец-то! Однако, радость моя была преждевременной. Эта функция также срабатывает не всегда, и, кроме того, в ней есть ряд моментов, которые меня настораживают.



    В частности:
    Код (Text):
    1. BOOL BringWindowToForeground(CWnd *pWnd)
    2. {
    3.       ...
    4.     DWORD dwUserInputTimeout;
    5.  
    6.     ::SystemParametersInfo( SPI_GETFOREGROUNDLOCKTIMEOUT, 0,
    7.                 &dwUserInputTimeout, 0);
    8.     ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0);
    9.  
    10.       ...
    11.  
    12.     ::SystemParametersInfo( SPI_SETFOREGROUNDLOCKTIMEOUT, 0,
    13.                 (LPVOID) dwUserInputTimeout, 0);
    14.  
    15.     return TRUE;
    16. }




    В этом фрагменте кода на время работы функции делается попытка сбросить время таймаута в 0, а затем восстановить оригинальное значение. Однако в MSDN насчет функции SystemParametersInfo сказано, в частности, следующее:







    Если я правильно перевел последнее предложение, то оно означает следующее: "Вызывающий поток должен иметь возможность изменить приоритетное окно, иначе вызов окончится неудачей." Однако, позвольте, позвольте! Все наши махинации как раз и предназначены для того, чтобы заиметь такую возможность - изменить приоритетное окно! То есть, другими словами, все три вызова этой функции абсолютно бессмысленны, ибо она не сработает! И, действительно, в большинстве случаев она возвращает 0, результат, свидетельствующий об ошибке...



    Непонятно мне так же, зачем делать следующее:
    Код (Text):
    1. BOOL BringWindowToForeground(CWnd *pWnd)
    2. {
    3.       ...
    4.     ::SetWindowPos( pWnd->GetSafeHwnd(), HWND_TOPMOST, 0, 0, 0, 0,
    5.             SWP_NOMOVE | SWP_NOSIZE);
    6.       ...
    7.  
    8.     ::SetWindowPos( pWnd->GetSafeHwnd(), HWND_NOTOPMOST, 0, 0, 0, 0,
    9.             SWP_NOMOVE | SWP_NOSIZE);
    10.       ...
    11. }


    Какой смысл вызывать эту функцию, устанавливать окну стиль WS_EX_TOPMOST, а затем снова сбрасывать его? Чем это может помочь?





    И, наконец, зачем там нужен цикл? Кстати говоря, попробовал я сделать вот такое:


    Код (Text):
    1. bool BringWindowToForeground(HWND hWnd)
    2.    {
    3.     if(hWnd==NULL || !IsWindow(hWnd)) return false;
    4.  
    5.     DWORD dwOurThreadId = GetCurrentThreadId();
    6.     HWND hWndActive;
    7.  
    8.     while((hWndActive=GetForegroundWindow())!=hWnd)
    9.        {
    10.         DWORD dwActiveThreadId=GetWindowThreadProcessId(hWndActive,NULL);
    11.         AttachThreadInput(dwOurThreadId, dwActiveThreadId, true);
    12.         SetForegroundWindow(hWnd);
    13.         AttachThreadInput(dwOurThreadId, dwActiveThreadId, false);
    14.         Sleep(20);
    15.        }
    16.     return true;
    17.    }


    И этот код стал срабатывать, вроде бы, всегда! Однако, когда приоритетным окном на момент вызова этой функции является консольное окно, функция срабатывает не сразу - в течение некоторого времени мигает пиктограмма приложения, точнее даже не мигает, а мерцает - это крутится цикл, - но в конце-концов нужное окно вылезает наверх. В Win2k. А под WinXP не сработал. Ну и в чем причина такого поведения?..



    Короче говоря, ребята, кто сталкивался с подобной задачей и сумел ее как-то решить, отзовитесь! Буду признателен за любую информацию по данному вопросу!
     
  2. IceStudent

    IceStudent Active Member

    Публикаций:
    0
    Регистрация:
    2 окт 2003
    Сообщения:
    4.300
    Адрес:
    Ukraine


    При этом стиле окно становится всегда поверх других окон. Позже восстанавливают исходный стиль окна.
     
  3. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia


    Ну а толку? Поверх всех других окон должна выводить функция SetForegroundWindow, после того как поток нашего окна будет присоединен к очереди виртуального ввода потока активного на данный момент окна! Конечно, в таком случае, если нашему окну не будет установлен стиль WS_EX_TOPMOST, то оно не отобразится поверх окон с тем же стилем, но в конце-то функции этот стиль сбрасывается! То есть мы возвращаемся в исходное состояние, и, если на экране есть окна со стилем WS_EX_TOPMOST, то они снова перекроют наше окно. Так что я не вижу смысла сначала в установке, а потом сбросе данного флага.
     
  4. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    так ты определись, чего хочешь: перевести фокус на своё окно и вынести его наверх или установить его поверх всех навсегда.



    А SetForegroundWindow не выводит окно поверх других, ты невнимательно читал:

    The SetForegroundWindow function puts the thread that created the specified window into the foreground and activates the window
     
  5. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia




    Я хочу

    ...давайте даже на время забудем о существовании окон со стилем WS_EX_TOPMOST... что делать,
    ?





    Во всяком случае, у Рихтера написано так:



    И поступает эта функция именно так. :)
     
  6. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    у рихтера неправильно (или перевод неправильный). SetForegroundWindow не выводит окно наверх. Она только активизирует его.

    Наверх окно выводит SetWindowPos. Если тебе не нравится, что после использования SetWindowPos флаг WS_EX_TOPMOST сбрасывается, то не сбрасывай его, никто ведь не заставляет. Пусть будет поверх всех окон, не имеющих такого стиля. Что касается тех окон, у которых установлен WS_EX_TOPMOST, то они могут перекрыть твоё окно, если будут активизированы.
     
  7. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia
    cresta, я понимаю то, что делает функция SetForegroundWindow. Я задал конкретный вопрос.
     
  8. Demon666

    Demon666 New Member

    Публикаций:
    0
    Регистрация:
    19 май 2006
    Сообщения:
    99
    ShowWindow
     
  9. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    BreakPointMAN



    Конкретный ответ:

    То, что ты хочешь сделать - сделать невозможно.

    Чем читать Рихтера, лучше читай msdn, там черным по белому написано:

    an application cannot force a window to the foreground while the user is working with another window. Instead, SetForegroundWindow will activate the window (see SetActiveWindow) and call the FlashWindowEx function to notify the user

    что означает: юзер имеет более высокий приоритет в принятии решения о том, какое окно имеет статус foreground, чем твоя программа. Максимум, что может сделать твоя программа - проинформировать юзера о желании твоей проги получить этот статус при помощи моргания кнопки в таскбаре.

    А переключиться на твоё приложение или нет - это уже зависит от решения юзера.
     
  10. kero

    kero Модератор SOURCES & 2LZ Команда форума

    Публикаций:
    0
    Регистрация:
    4 апр 2006
    Сообщения:
    1.074
    Адрес:
    Москва
    2 BreakPointMAN

    > Перепробывав кучу различных способов, отправился на поиски решения проблемы в Интернет...



    Кажется, здесь Вы еще не были:

    rsdn.ru -> Поиск -> SetForegroundWindow. Несметное число обсуждений.

    Название одной из веток (за 2003): "Как вывести окно в foreground - перепробовал кучу способов" :)
     
  11. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia
    cresta, прежде, чем давать "конкретные ответы", советую самому более внимательно почитать MSDN и Рихтера. В MSDN обратить внимание на функцию AttachThreadInput, а также на некоторые моменты в описании функции SetForegroundWindow:







    ...а у Рихтера прочитать главу 27 "Модель аппаратного ввода и локальное состояние ввода", и только после этого что-то советовать. Я просил отозваться тех,
    , ибо не хочу пересказывать содержание статей MSDN и глав Рихтера.
     
  12. Demon666

    Demon666 New Member

    Публикаций:
    0
    Регистрация:
    19 май 2006
    Сообщения:
    99
    Наверно ShareWare пишет вот и хочет мигающее окошко всем показывать?
     
  13. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    BreakPointMAN

    Вот возьми и переведи приведеное тобою же:





    Что, перевести за тебя?



    А то, что


    совсем не значит, что ты наверняка сможешь это сделать.

    Попытаться - пожалуйста, сколько угодно...



    А AttachThreadInput вообще здесь ни к месту: даже в своём собственном потоке невозможно передать фокус ввода с клавиатуры своему же окну, если оно неактивно. А ещё и пытаться сделать это для чужого потока при помощи AttachThreadInput - нереально вдвойне.



    В обчем, успехов вам в вашем безнадёжном предприятии :)
     
  14. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia
    kero, благодарю. :) Там я и в самом деле еще не смотрел, и это было большим упущением с моей стороны.



    PS: но пока тема продолжает оставаться открытой.
     
  15. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia
    cresta, RTFM :)
     
  16. cresta

    cresta Active Member

    Публикаций:
    0
    Регистрация:
    13 июн 2004
    Сообщения:
    2.257
    Вот возьми и почитай этот факинг мануал, чем читать об использовании недокументированных возможностей.

    Вот этот код с использованием AttachThreadInput, который якобы работает на 98/2000 (судя по статье на rsdn: http://www.rsdn.ru/article/qna/ui/wndsetfg.xml), благополучно не работает на ХР:
    Код (Text):
    1.                 invoke  GetForegroundWindow
    2.                 mov     ebx,eax
    3.                 invoke  GetCurrentThreadId
    4.                 mov     esi,eax
    5.                 invoke  GetWindowThreadProcessId, ebx, 0
    6.                 mov     edi,eax
    7.                 invoke  AttachThreadInput, esi, edi, TRUE
    8.                 invoke  SetForegroundWindow, hWin
    9.                 invoke  AttachThreadInput, esi, edi, FALSE


    Более того, он даже юзера не информирует морганием в таскбаре.
     
  17. Nothing

    Nothing New Member

    Публикаций:
    0
    Регистрация:
    4 авг 2003
    Сообщения:
    139
    Адрес:
    Russia
    Вот это должно сработать.



    ::SendMessage(::GetDesktopWindow(), WM_SYSCOMMAND, (WPARAM) SC_HOTKEY, (LPARAM) hWin);



    А вообще, терпеть не могу "выскакивающие" окна. Мешают набирать тексты или таскать что-то мышой, нарушают порядок переключения окон по alt-tab. Ведь сделали же в 2k/xp уведомление в трее, чтобы сообщать о чем-нибудь важном...
     
  18. khv_test

    khv_test New Member

    Публикаций:
    0
    Регистрация:
    30 июн 2004
    Сообщения:
    135
    есть SwitchToThisWindow.

    но Minimum operating systems Windows 2000.



    The SwitchToThisWindow function is called to switch focus to a specified window and bring it to the foreground.



    Syntax



    VOID SwitchToThisWindow( HWND hWnd,

    BOOL fAltTab

    );

    Parameters



    hWnd

    [in] Handle to the window being switched to.

    fAltTab

    [in] A TRUE for this parameter indicates that the window is being switched to using the Alt/Ctl+Tab key sequence. This parameter should be FALSE otherwise.

    Return Value



    None.





    Remarks



    This function is typically called to maintain window z-ordering.



    Although you can access this function by using LoadLibrary and GetProcAddress combined in Microsoft Windows versions prior to Windows XP, the function is not accessible using the standard Include file and library linkage. The header files included in Windows XP Service Pack 1 (SP1) and Windows Server 2003 document this function and make it accessible using the appropriate Include file and library linkage. However, this function is deprecated and not intended for general use. It is recommended that you do not use it in new programs because it might be altered or unavailable in subsequent versions of Windows.



    Function Information



    Minimum DLL Version user32.dll

    Header Declared in Winuser.h, include Windows.h

    Import library User32.lib

    Minimum operating systems Windows 2000
     
  19. khv_test

    khv_test New Member

    Публикаций:
    0
    Регистрация:
    30 июн 2004
    Сообщения:
    135
  20. BreakPointMAN

    BreakPointMAN New Member

    Публикаций:
    0
    Регистрация:
    26 июн 2005
    Сообщения:
    42
    Адрес:
    Russia
    А как это делает ICQ? Скажем, в ICQ2002a есть (Main->Preferences->Contact List->Shortcuts) горячие клавиши, которые позволяют активировать/деактивировать окно аськи, активировать поле WebSearch и т.д. Она не только вылезает наверх, но ей передается и клавиатурный фокус! Причем, насколько я могу судить, горячие клавиши для окна (WM_SETHOTKEY) не задаются. Но она как-то это все-таки делает! :)