Пишу я в Borland C++ Builder, но, думаю, в данном случае это не имеет особого значения, так как вопрос относится, скорее, не к языку и среде, а к оконной подсистеме Windows и WinAPI. Итак, суть вопроса: требуется, зная описатель окна, сделать его активным, то есть вывести окно на передний план и передать ему клавиатурный фокус. В моей конкретной задаче таковым окном является окно одной из форм приложения. При этом желательно, чтобы код, выполняющий данные действия, срабатывал как для линейки Win9x, так и для линейки WinNT. Если я правильно понял главу 27 "Модель аппаратного ввода и локальное состояние ввода" ("Windows для профессионалов: создание эффективных Win32 приложений с учетом специфики 64-разрядной версии Windows" (с) Джеффри Рихтер), то поставленную задачу должен решить следующий код: Code (Text): bool BringWindowToForeground(HWND hWnd) { if(hWnd==NULL || !IsWindow(hWnd)) return false; HWND hWndActive=GetForegroundWindow(); DWORD dwActiveThreadId=GetWindowThreadProcessId(hWndActive,NULL); DWORD dwOurThreadId = GetCurrentThreadId(); AttachThreadInput(dwOurThreadId, dwActiveThreadId, true); bool Result= SetForegroundWindow(hWnd); AttachThreadInput(dwOurThreadId, dwActiveThreadId, false); return Result; } Однако, вопреки ожиданиям, это срабатывает не всегда. В частности, когда окно, которое мы пытаемся сделать активным, перекрыто окном активного консольного приложения, в панели задач просто начинает мигать пиктограмма приложения, но не более того. (На всякий случай сообщу, что у меня Microsoft Windows 2000 Professional 5.00.2195 Service Pack 4, russian.) Перепробывав кучу различных способов, отправился на поиски решения проблемы в Интернет... Вскоре наткнулся на две аналогичные по своей сути функции, одна написана на Delphi под VCL, другая на MSVC под MFC. Вот ссылка на последнюю: BringWindowToForeground. Я обрадовался - наконец-то! Однако, радость моя была преждевременной. Эта функция также срабатывает не всегда, и, кроме того, в ней есть ряд моментов, которые меня настораживают. В частности: Code (Text): BOOL BringWindowToForeground(CWnd *pWnd) { ... DWORD dwUserInputTimeout; ::SystemParametersInfo( SPI_GETFOREGROUNDLOCKTIMEOUT, 0, &dwUserInputTimeout, 0); ::SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, 0, 0); ... ::SystemParametersInfo( SPI_SETFOREGROUNDLOCKTIMEOUT, 0, (LPVOID) dwUserInputTimeout, 0); return TRUE; } В этом фрагменте кода на время работы функции делается попытка сбросить время таймаута в 0, а затем восстановить оригинальное значение. Однако в MSDN насчет функции SystemParametersInfo сказано, в частности, следующее: Если я правильно перевел последнее предложение, то оно означает следующее: "Вызывающий поток должен иметь возможность изменить приоритетное окно, иначе вызов окончится неудачей." Однако, позвольте, позвольте! Все наши махинации как раз и предназначены для того, чтобы заиметь такую возможность - изменить приоритетное окно! То есть, другими словами, все три вызова этой функции абсолютно бессмысленны, ибо она не сработает! И, действительно, в большинстве случаев она возвращает 0, результат, свидетельствующий об ошибке... Непонятно мне так же, зачем делать следующее: Code (Text): BOOL BringWindowToForeground(CWnd *pWnd) { ... ::SetWindowPos( pWnd->GetSafeHwnd(), HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ... ::SetWindowPos( pWnd->GetSafeHwnd(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE); ... } Какой смысл вызывать эту функцию, устанавливать окну стиль WS_EX_TOPMOST, а затем снова сбрасывать его? Чем это может помочь? И, наконец, зачем там нужен цикл? Кстати говоря, попробовал я сделать вот такое: Code (Text): bool BringWindowToForeground(HWND hWnd) { if(hWnd==NULL || !IsWindow(hWnd)) return false; DWORD dwOurThreadId = GetCurrentThreadId(); HWND hWndActive; while((hWndActive=GetForegroundWindow())!=hWnd) { DWORD dwActiveThreadId=GetWindowThreadProcessId(hWndActive,NULL); AttachThreadInput(dwOurThreadId, dwActiveThreadId, true); SetForegroundWindow(hWnd); AttachThreadInput(dwOurThreadId, dwActiveThreadId, false); Sleep(20); } return true; } И этот код стал срабатывать, вроде бы, всегда! Однако, когда приоритетным окном на момент вызова этой функции является консольное окно, функция срабатывает не сразу - в течение некоторого времени мигает пиктограмма приложения, точнее даже не мигает, а мерцает - это крутится цикл, - но в конце-концов нужное окно вылезает наверх. В Win2k. А под WinXP не сработал. Ну и в чем причина такого поведения?.. Короче говоря, ребята, кто сталкивался с подобной задачей и сумел ее как-то решить, отзовитесь! Буду признателен за любую информацию по данному вопросу!
Ну а толку? Поверх всех других окон должна выводить функция SetForegroundWindow, после того как поток нашего окна будет присоединен к очереди виртуального ввода потока активного на данный момент окна! Конечно, в таком случае, если нашему окну не будет установлен стиль WS_EX_TOPMOST, то оно не отобразится поверх окон с тем же стилем, но в конце-то функции этот стиль сбрасывается! То есть мы возвращаемся в исходное состояние, и, если на экране есть окна со стилем WS_EX_TOPMOST, то они снова перекроют наше окно. Так что я не вижу смысла сначала в установке, а потом сбросе данного флага.
так ты определись, чего хочешь: перевести фокус на своё окно и вынести его наверх или установить его поверх всех навсегда. А SetForegroundWindow не выводит окно поверх других, ты невнимательно читал: The SetForegroundWindow function puts the thread that created the specified window into the foreground and activates the window
Я хочу ...давайте даже на время забудем о существовании окон со стилем WS_EX_TOPMOST... что делать, ? Во всяком случае, у Рихтера написано так: И поступает эта функция именно так.
у рихтера неправильно (или перевод неправильный). SetForegroundWindow не выводит окно наверх. Она только активизирует его. Наверх окно выводит SetWindowPos. Если тебе не нравится, что после использования SetWindowPos флаг WS_EX_TOPMOST сбрасывается, то не сбрасывай его, никто ведь не заставляет. Пусть будет поверх всех окон, не имеющих такого стиля. Что касается тех окон, у которых установлен WS_EX_TOPMOST, то они могут перекрыть твоё окно, если будут активизированы.
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, чем твоя программа. Максимум, что может сделать твоя программа - проинформировать юзера о желании твоей проги получить этот статус при помощи моргания кнопки в таскбаре. А переключиться на твоё приложение или нет - это уже зависит от решения юзера.
2 BreakPointMAN > Перепробывав кучу различных способов, отправился на поиски решения проблемы в Интернет... Кажется, здесь Вы еще не были: rsdn.ru -> Поиск -> SetForegroundWindow. Несметное число обсуждений. Название одной из веток (за 2003): "Как вывести окно в foreground - перепробовал кучу способов"
cresta, прежде, чем давать "конкретные ответы", советую самому более внимательно почитать MSDN и Рихтера. В MSDN обратить внимание на функцию AttachThreadInput, а также на некоторые моменты в описании функции SetForegroundWindow: ...а у Рихтера прочитать главу 27 "Модель аппаратного ввода и локальное состояние ввода", и только после этого что-то советовать. Я просил отозваться тех, , ибо не хочу пересказывать содержание статей MSDN и глав Рихтера.
BreakPointMAN Вот возьми и переведи приведеное тобою же: Что, перевести за тебя? А то, что совсем не значит, что ты наверняка сможешь это сделать. Попытаться - пожалуйста, сколько угодно... А AttachThreadInput вообще здесь ни к месту: даже в своём собственном потоке невозможно передать фокус ввода с клавиатуры своему же окну, если оно неактивно. А ещё и пытаться сделать это для чужого потока при помощи AttachThreadInput - нереально вдвойне. В обчем, успехов вам в вашем безнадёжном предприятии
kero, благодарю. Там я и в самом деле еще не смотрел, и это было большим упущением с моей стороны. PS: но пока тема продолжает оставаться открытой.
Вот возьми и почитай этот факинг мануал, чем читать об использовании недокументированных возможностей. Вот этот код с использованием AttachThreadInput, который якобы работает на 98/2000 (судя по статье на rsdn: http://www.rsdn.ru/article/qna/ui/wndsetfg.xml), благополучно не работает на ХР: Code (Text): invoke GetForegroundWindow mov ebx,eax invoke GetCurrentThreadId mov esi,eax invoke GetWindowThreadProcessId, ebx, 0 mov edi,eax invoke AttachThreadInput, esi, edi, TRUE invoke SetForegroundWindow, hWin invoke AttachThreadInput, esi, edi, FALSE Более того, он даже юзера не информирует морганием в таскбаре.
Вот это должно сработать. ::SendMessage(::GetDesktopWindow(), WM_SYSCOMMAND, (WPARAM) SC_HOTKEY, (LPARAM) hWin); А вообще, терпеть не могу "выскакивающие" окна. Мешают набирать тексты или таскать что-то мышой, нарушают порядок переключения окон по alt-tab. Ведь сделали же в 2k/xp уведомление в трее, чтобы сообщать о чем-нибудь важном...
есть 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
А как это делает ICQ? Скажем, в ICQ2002a есть (Main->Preferences->Contact List->Shortcuts) горячие клавиши, которые позволяют активировать/деактивировать окно аськи, активировать поле WebSearch и т.д. Она не только вылезает наверх, но ей передается и клавиатурный фокус! Причем, насколько я могу судить, горячие клавиши для окна (WM_SETHOTKEY) не задаются. Но она как-то это все-таки делает!