Как мне создать окно, которое будет располагаться поверх всех окон и никогда не будет перекрываться другими окнами, которые отображаются поверх всех окон?
Как мы уже знаем, нельзя создать окно, которое будет всегда находиться поверх всех окон, включая другие окна, которые помечены как отображающиеся поверх всех окон (topmost-окна). Применение правила «Что, если это случилось в двух программах сразу? » демонстрирует, что это невозможно, потому что какой бы вы трюк с размещением окна выше topmost-окон не использовали, другая программа может воспользоваться тем же трюком, и теперь у вас два окна поверх всех topmost-окон. И каким должен быть результат?
Клиент, который не смог понять этот принцип, спросил, есть ли какой-нибудь способ установить свое окно как «супер-крутое topmost-окно». Они даже придумали ответ на риторический вопрос: «И каким должен быть результат?», заданный выше.
Мы переопределили методы OnLostFocus и OnPaint для переустановки свойств TopLevel и TopMost, а также для вызова методов BringToFront и Activate. В результате наше приложение начинает бороться с другими приложениями, в которых реализовано схожее поведение, за право быть выше. Мы пытались установить глобальный перехватчик событий и подавлять события отрисовки и установки фокуса для всех приложений, кроме нашего (и таким образом не давать этим приложениям возможности устанавливать свойство TopMost, чтобы быть выше нашего окна), но мы обнаружили, что это приводит к аварийному завершению этих приложений. Мы подумываем об установке таймера и переустановки свойства TopMost во время срабатывания таймера. Есть ли какой-либо более эффективный способ для реализации такой возможности?
Это все равно, что сказать: «Иногда я очень спешу и хочу убедиться, что в этом гастрономе меня обслужат следующим. Для этого я нахожу покупателя с наименьшим номерком очереди, бью его по голове, чтобы он потерял сознание, а затем краду его билетик. Но иногда появляется кто-то еще, кто так же спешит. Этот человек бьет меня по голове и крадет мой билетик. Мой план заключается в установке будильника, который периодически будит меня, и каждый раз, когда я просыпаюсь, я нахожу человека с наименьшим номерком, бью его по голове, чтобы он потерял сознание, и краду его билет. Есть ли какой-либо более эффективный способ?»
Более эффективный способ — перестать бить друг друга по голове и красть друг у друга билеты.
Клиент (через службу поддержки клиентов) предоставил контекст своего вопроса.
Это приложение не будет приложением общего назначения. Оно будет запускаться на выделенных компьютерах, которые управляют гигантскими мониторами в магазинах розничной торговли. На компьютерах установлены и другие приложения, которые отображают серию рекламных роликов и другой информации.
Мы разрабатываем приложение, которое тоже будет запускаться на этих компьютерах. Большую часть времени оно ничего не делает, но время от времени нашему приложению нужно появиться на переднем плане и отобразить свое сообщение, вне зависимости от того, что отображают другие приложения. (К примеру, это может быть какая-либо акция с ограниченным временем действия, которую нужно отобразить поверх обычных рекламных роликов).
К сожалению, все эти сторонние приложения были написаны разными разработчиками, и между ними нет никакого взаимодействия, чтобы договариваться, кто будет контролировать экран. Мы надеялись, что есть какой-либо способ пометить наше окно как «супер-topmost окно», так, чтобы когда оно начинает конфликтовать с окном другого приложения, наше приложение выигрывало, а другое приложение — проигрывало.
Я подумываю порекомендовать, чтобы все разработчики пришли к соглашению о координации доступа к экрану, так, чтобы приложения могли договариваться и не начинать войну за перехват фокуса. (Легко сказать, но сложно сделать: все эти приложения написаны разными разработчиками)...
Поскольку координация между этими приложениями отсутствует, вам, фактически, ничего не остается, кроме как играть в игру стен и лестниц, надеясь, что ваша лестница окажется выше, чем стены всех остальных. Клиент нашел, бесспорно, самую высокую лестницу, которую предоставляет оконный менеджер. Но флага «супер-topmost окно» нет.
Безусловно, вы можете попробовать перейти на другой уровень системы, скажем, создать собственный рабочий стол, но это всего лишь более высокая лестница. А затем какое-нибудь из других приложений решит: «Мне нужно отобразить сообщение на всех мониторах магазина: «Просим менеджера пройти в мясной отдел, менеджера — в мясной отдел, спасибо», чтобы оно отображалось поверх всех сообщений, даже тех сообщений про акции с ограниченным временем действия». И им придется прибегать к более грязным методам, вроде получения списка всех окон в системе и вызова ShowWindow(SW_HIDE).
А затем другое приложение скажет: «Мне нужно отобразить важное объявление от сотрудников службы безопасности на всех мониторах магазина: «Владелец белой Honda Civic, регистрационный номер 037-MSN, пожалуйста, подойдите к своему автомобилю», чтобы оно отображалось поверх всех сообщений, даже тех сообщений для сотрудников магазина». И тогда оно попробует сделать что-нибудь еще более грязное, вроде установки программы в качестве экранной заставки с отключением клавиатуры и мыши и запуска экранной заставки на защищенном рабочем столе.
А затем другое приложение скажет: «Мне нужно отобразить критическое объявление на всех мониторах магазина: «ПОЖАР В АВТОМОБИЛЬНОМ ОТДЕЛЕ МАГАЗИНА. ПРОСЬБА ВСЕМ НЕМЕДЛЕННО ПОКИНУТЬ ПОМЕЩЕНИЕ», чтобы оно отображалось поверх всех сообщений, даже если это сообщение от сотрудников службы безопасности». И оно попробует сделать что-то еще более грязное, например, получение списка всех процессов в системе, подключение к каждому из них с привилегиями отладки и приостановку всех потоков.
Остановите безумие. Единственный разумный способ разрешить данную ситуацию — это заставить все приложения кооперироваться для определения, кто должен контролировать экран в каждый конкретный момент времени.
В ответ на мою гипотетическую игру в стены и лестницы один из моих коллег написал: «Заметка для себя: не соревнуйся в игре в стены и лестницы с Рэймондом».