Как я могу запретить моей заявке получать определенное «сообщение»? - PullRequest
27 голосов
/ 25 января 2012

ВОЗМОЖНОЕ РЕШЕНИЕ НАЙДЕНО!

Я считаю, что нашел решение! Я буду продолжать тестирование, чтобы убедиться, что оно действительно работает, но я надеюсь :) Я подробно описал, как я нашел решение в РЕДАКТИРОВАТЬ ТРИ вопроса!

Для всех, кто хочет узнать всю историю моей проблемы и то, что я пытался в результате ввода этого вопроса, см. http://pastebin.com/nTrEAkVj

Я буду редактировать это часто (> 3 раза в день в течение большинства будних дней), когда буду продвигать свои исследования и ситуацию, поэтому продолжайте проверять, интересуетесь ли вы или имеете некоторую информацию или знание моей проблемы:)

Быстрый фон:

У меня есть приложение, которое я сделал, которое может быть разбито при смене заставки или блокировке моей рабочей станции, и в целом всякий раз, когда ему отправляется сообщение WM_WININICHANGE / WM_SETTINGSCHANGE.

Если я могу постоянно аварийно завершить работу моего приложения, изменив заставку, то НЕКОТОРАЯ часть выполнения этого - отправка моего приложения НЕКОТОРЫХ сообщений (не обязательно сообщений Windows, я имею в виду сообщения в самом общем смысле), что, в свою очередь, катастрофически для моего приложения. Из-за этого я пытаюсь найти способ заблокировать любое сообщение, которое вызывает мою проблему от обработки моим приложением. Я знаю, что это не лучший способ найти решение, поэтому вам не нужно говорить мне. Посмотрите на справочную информацию или спросите, почему, если это вас беспокоит (есть веская причина).

Мой вопрос:

есть несколько вещей, о которых любая информация помогла бы мне решить мою проблему, помеченных в соответствии с релевантностью (1 является наиболее релевантным, 3 немного менее полезным):

  1. Я пытаюсь использовать Wndproc () для фильтрации моего сообщения следующим образом:

    Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
        If CInt(m.Msg) <> CInt(26) then
            MyBase.WndProc(m)
        end if
    End Sub
    

    Однако, согласно Windspector, сообщение WM_WININICHANGE по-прежнему отправляется в мое приложение (это имеет смысл), НО оно также возвращается с 0 ... этого не должно происходить, если оно работает должным образом, оно не должно ничего не вернуть, не так ли? Информация о том, почему это не работает так, как я ожидал, и о том, как заставить это работать, была бы чрезвычайно полезной!

  2. Я также пытался использовать фильтры сообщений:

    Public Class MyMessageFilter
        Implements IMessageFilter
        Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
            ' Return true for messages that you want to stop  << someone elses comment       
            Return m.Msg = 26
        End Function
    End Class
    

    и добавление к моему методу обработки mybase.load:

    Application.AddMessageFilter (New MyMessageFilter ())

    однако они, похоже, фильтруют только определенные сообщения, и такие сообщения, как мое, по-видимому, не попадают в них. информация о том, что определенно невозможно использовать какой-либо фильтр для перехвата сообщения WM_ или есть ли другие способы использования фильтров сообщений для достижения моей цели, также будет полезна.

  3. Каким ДРУГИМ способом (кроме того, что я обнаружил одно оконное сообщение с message.msg = WM_WININICHANGE = 26), мог ли я изменить заставку, чтобы отправить ЛЮБОЕ сообщение в мое приложение? возможно ли, что другой вид сообщения от изменения заставки также будет фатальным?

Дайте мне знать, если какая-либо другая информация о моей ситуации может быть полезной, и я сделаю все возможное, чтобы получить ее! Заранее благодарю за любую помощь, которую вы можете оказать:)

EDIT:

Появляется, если я ТОЛЬКО отправляю сообщение WM_CHANGESETTING и заставляю мою программу ждать в течение тайм-аута sendmessage timeout, с которым я отправил сообщение, тогда моя программа не падает ... кажется, что мой сбой вызывает ОТВЕТ программа ... интересная. Я определенно близок к моему решению! Я думаю, что немного больше тестирования должно позволить мне найти метод, чтобы убедиться, что моя программа не отвечает на сообщение.

РЕДАКТИРОВАТЬ ВТОРОЕ:

Сегодня я обнаружил кое-что ОЧЕНЬ многообещающее: я определил свою функцию wndproc так:

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    If CInt(m.Msg) <> CInt(26) Then
        MyBase.WndProc(m)
    Else
        MessageBox.Show("Get to work!", "Attention", MessageBoxButtons.OK, MessageBoxIcon.Exclamation, MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification)
    End If
End Sub

А потом я попытался запустить мою программу, а затем отправил сообщение WM_SETTINGCHANGE, используя:

SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero, _
             SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, IntPtr.Zero)

в другой программе, которую я сделал.Так что случилось, спросите вы?ну, я попробовал это несколько раз, и каждый раз появлялось окно сообщения (слова, которые я выбрал для него, не имеет значения), затем я пытался ждать различное количество времени, прежде чем нажимать ОК, и затем я видел, что случилось с моей основной формой.Ну, в большинстве случаев, ничего не было иначе, это все равно падало.Но иногда, может быть, 1/5 раз, программа все еще будет переподписывать после!Затем, если это произойдет, я попытаюсь отправить сообщение снова, и затем, опять же, обычно они терпят неудачу во второй раз во время одного и того же запуска программы, НО время от времени снова, кажется, что примерно в 1/5 раз программа не будетСНОВАИ тогда я дважды пытался разбить его.и это не было ни разу, то почти всегда тогда никогда не произойдет сбой, независимо от того, сколько раз я пытался отправить сообщение и независимо от того, сколько времени я ждал после появления сообщения msgbox.

Я ожидал около 5 секундказалось, увеличил мои шансы: моя форма, с которой я запускаю сообщение, все еще будет в фокусе (верхняя полоса будет синей), сразу после того, как я нажму кнопку замораживания, и затем появится окно с сообщением, а верхняя также синяя (вФокус, я предполагаю), они оба все еще "в фокусе" (по крайней мере, синий хаха).Затем, примерно через 5 секунд, исходная форма потеряла фокус, и, увидев это, я попытаюсь нажать «ОК».

В настоящее время я думаю, что это ожидание, а затем подтверждение того, что окно сообщения иногда позволяет моей программене сбой, потому что это время ожидания сообщения, поэтому оно не возвращается.Я НЕ знаю, почему возвращаемое или нет сообщение должно влиять на то, что на самом деле делает моя программа.Это область, в которой было бы полезно разъяснение:)

РЕДАКТИРОВАТЬ ТРИ:

, поэтому я смотрю в Winspector немного больше и нахожу, что если я буду ждатьWM_ERASEBKGND будет отображаться в окне моего рабочего стола (это окно, помеченное как "sysListView32 'FolderView'" в Winspector), прежде чем нажать "OK" на моем msgbox, тогда программа не завершится сбоем, интересно!Обычно для отправки сообщения WM_ERASEBKGND требуется время, близкое к Тайм-ауту для sendmessage timeout.это, конечно, после отправки сообщения WM_SETTINGCHANGE из моего самодельного приложения для тестирования.

Итак, после этого я решил немного больше взглянуть на Winspector, потому что, может быть, есть еще более полезные очереди, которые я могу найти?Поскольку очевидно, что ожидание, пока winspector покажет сообщение, отправленное на мой рабочий стол, для моей программы совсем не является исправлением.Я обнаружил несколько необычно именованных окон в процессе моей программы: одно называется ".NET -BroadcastEventWindow.2.0.0.0.378734a.0", а другое - "GDI + Класс окна перехвата 'GDI + Window'" с подокном под названием "IME".IME по умолчанию '".

Я решил посмотреть сообщения, отправляемые в эти окна, чтобы узнать, получают ли они какие-либо распознаваемые сообщения, такие как WM_SETTINGCHANGE или WM_ERASEBKGND.Оказывается, они не получают сообщения часто: GDI + не получал никаких сообщений, пока я смотрел, я не думаю, но .NET -BroadcastEventWindow получил несколько.Те, что шли в BroadcastEventWindow, были в основном только WM_appactivate, когда я щелкал свое окно приложения или другое окно после него.

НО ТО ... Я заметил .Net BroadcastEventWindow получает мое сообщение WM_CHANGESETTING !!!!Я смотрю на то, что появляются другие сообщения: не много, но я замечаю, когда приложение вылетает из-за ошибки, появляется сообщение, которое я не распознаю: WM_USER + 7194 (0x201A).Хм, давайте посмотрим, что это такое.После того, как я загадаю его, я выясняю, что оно выглядит как приложение / пользовательское сообщение, а затем, после очередного поиска проблем, связанных с ним, я замечаю, что кто-то может использовать фильтр для фильтрации этого сообщения и устранения проблемыих (http://www.pcreview.co.uk/forums/handling-wm_user-messages-t1315625.html). Стоит попробовать для меня, по крайней мере, верно? поэтому я заново добавляю фильтр, который пробовал ранее, и изменяю значения для фильтрации. Приложение не вылетало !!!!!!!

Затем я пытаюсь разрешить моей рабочей станции заблокировать, чтобы увидеть, не сбивается ли это по-прежнему (потому что ранее это было только при отправке единственного сообщения WM_CHANGESETTING).Оказывается, он все-таки потерпел крах :( НО, я еще раз посмотрю на winspector для этого окна и, о, да, два НОВЫХ сообщения WM_USER: WM_USER + 7294 (0x207E) и WM_USER + 7189 (0x2015). Поэтому я пытаюсь отфильтровать ихтоже ... и при этом не происходит сбой при блокировке рабочей станции !!!: D

До сих пор я не заметил никаких негативных последствий этого при обычном использовании приложения! Это имеет смысл, так как я неЯ не думаю, что любые пользовательские сообщения целенаправленно участвуют в моей программе.

Я оставлю вопрос открытым немного дольше, пока не убедлюсь, что в моем решении нет ничего плохого, и оно работает хорошо. Спасибо всем вамкоторый дал мне небольшой совет о том, как действовать на средних этапах моей отладки:)

Ответы [ 2 ]

6 голосов
/ 25 января 2012

Я видел эту проблему, упомянутую в различных вопросах на протяжении многих лет. Никогда полностью не диагностировал это, я просто скажу вам, что я знаю об этом.

Эта проблема связана со способом инициализации класса SystemEvents. Это связано с неудачей, потому что это класс, который вызывает событие, которое срабатывает при переключении на защищенный рабочий стол. Либо через заставку, либо заблокировав рабочую станцию ​​(клавиша Windows + L). Элементы управления Winforms в целом заинтересованы в событии SystemEvents.DisplaySettingsChanged, поскольку им может потребоваться перерисовать себя при изменении темы или системных цветов. Это событие также обычно возникает, когда система переключает рабочие столы.

Одна из основных проблем заключается в том, что события должны возникать в потоке пользовательского интерфейса. SystemEvents должен точно угадать какой поток на самом деле является потоком пользовательского интерфейса. Это идет не так, когда самое первое окно, созданное в программе, создается в потоке, который на самом деле не является потоком пользовательского интерфейса, и в противном случае маскируется как одно, установив для своей COM-квартиры значение STA. Если поток действительно продолжает работать, то событие запускается в этом потоке. Если поток пропал, что не редкость, то возникает исключение, когда SynchronizationContext.Post () пытается маршализовать вызов и завершается неудачно. Исключение проглатывается, и затем событие вызывается в произвольном потоке потоков.

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

Вам необходимо внимательно просмотреть код инициализации программы. Безусловно, самая распространенная ошибка - создание собственного заставки. Обязательно используйте встроенную поддержку в .NET Framework, чтобы получить это право.

4 голосов
/ 25 января 2012

Реализуйте свой собственный фильтр сообщений с помощью

Public Class MyMessageFilter
    Implements IMessageFilter

    Public Function PreFilterMessage(ByRef m As Message) As Boolean Implements IMessageFilter.PreFilterMessage
        ' Return true for messages that you want to stop
        Return m.Msg = MessageToDiscard
    End Function
End Class

Добавьте этот фильтр, когда ваше приложение запускается с

Application.AddMessageFilter(New MyMessageFilter())
...