【问题标题】:How can I stop my application from receiving a certain "message"?如何阻止我的应用程序接收某个“消息”?
【发布时间】:2012-01-25 02:37:54
【问题描述】:

找到了可能的解决方案!

我相信我已经找到了解决办法!我将继续测试以确保它确实有效,但我充满希望:) 我已经详细说明了我是如何在编辑问题三中找到解决方案的!

对于任何希望了解我的问题背后的完整背景以及我根据此问题的输入尝试过的内容的人,请参阅: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 的一条 Windows 消息)更改我的屏幕保护程序向我的应用程序发送任何类型的消息?更改屏幕保护程序的另一种消息是否也可能是致命的?

如果还有其他关于我的情况可能有用的信息,请告诉我,我会尽我所能!提前感谢您提供的任何帮助:)

编辑:

如果我只发送 WM_CHANGESETTING 消息,并让我的程序等待我发送消息的 sendmessagetimeout 的超时长度,那么我的程序不会崩溃......看起来响应是我的崩溃节目……有趣。我绝对接近我的解决方案!我正在考虑进行更多测试,以便找出一种方法来确保我的程序不会响应该消息。

编辑二:

我今天发现了一些非常有前途的东西:我完全像这样定义了我的 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 秒似乎增加了我的几率:我触发消息的表单仍然是焦点(顶部栏将是蓝色),就在我按下冻结按钮之后,然后 msgbox 会弹出向上,顶部也是蓝色的(我假设是焦点),它们都仍然“焦点”(至少是蓝色的哈哈)。然后大约 5 秒后,原始表单会失去焦点,看到之后,我会尝试点击 ok。

我目前认为,等待一段时间然后确认消息框有时会使我的程序不会崩溃,因为它正在超时消息所以它不会返回。我不知道为什么返回的消息会影响我的程序实际执行的操作。这是澄清会有所帮助的领域:)

编辑三:

所以我在 Winspector 中寻找更多信息,我发现如果我等待 WM_ERASEBKGND 出现在我的桌面窗口(在 Winspector 中标记为“sysListView32 'FolderView'”的窗口)中,然后点击“OK”在我的msgbox上,那么程序就不会崩溃了,有趣!通常需要接近超时时间才能显示 WM_ERASEBKGND 消息的 sendmessagetimeout。这当然是在我的自制测试应用程序发送 WM_SETTINGCHANGE 消息之后。

所以,在这之后,我决定多看看 Winspector,因为也许我可以找到更多有用的队列?由于显然等待 winspector 显示消息已发送到我的桌面,因此对于我的程序来说根本不是真正的修复。我在我的程序进程下发现了一些异常命名的窗口:一个名为“.NET -BroadcastEventWindow.2.0.0.0.378734a.0”,另一个名为“GDI+ Hook Window Class 'GDI+ Window'”,带有一个名为“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

到目前为止,我也没有注意到这对常规应用程序的使用有任何不利影响!这是有道理的,因为我认为我的程序中没有故意涉及任何用户定义的消息。

在我确定我的解决方案没有问题并且运行良好之前,我会将问题留待更长时间。感谢那些在调试的中间阶段给我一些建议的人:)

【问题讨论】:

  • 无论如何我可以得到我在另一个问题中输入的信息? (因为我认为你也是,这是我需要以某种方式包含的相关信息)
  • 查看我对问题的最后一次编辑,了解我是如何解决问题的。尽管 Olivier Jacot-Descombes 的想法是解决方案的一部分,但我没有将其标记为答案,因为它非常缺乏解决方案的关键细节以及如何以在这种情况下有用的方式实际实施它。跨度>

标签: .net vb.net multithreading messages windows-messages


【解决方案1】:

多年来,我在各种问题中都看到过这个问题。从来没有完全诊断过它,我只会告诉你我所知道的。

这个问题与 SystemEvents 类的初始化方式有关。它与事故有关,因为这是触发在您切换到安全桌面时触发的事件的类。通过屏幕保护程序或锁定工作站(Windows + L 键)。 Winforms 控件通常对 SystemEvents.DisplaySettingsChanged 事件感兴趣,因为当主题或系统颜色发生更改时,它们可能需要重新绘制自己。系统切换桌面时通常也会引发此事件。

一个核心问题是需要在 UI 线程上引发事件。 SystemEvents 需要准确猜测 what 线程实际上是 UI 线程。如果在程序中创建的第一个窗口是在实际上不是 UI 线程的线程上创建的,并且通过将其 COM 单元设置为 STA 来伪装成一个线程,则会出现这种情况。如果线程实际上继续运行,则在该线程上触发事件。如果线程消失了,这种情况并不少见,那么当 SynchronizationContext.Post() 尝试编组调用并失败时会引发异常。异常被吞没,然后在任意线程池线程上引发事件。

无论哪种方式,事件都没有在正确的线程上引发,并且违反了任何 UI 组件的线程要求。这往往会被忽视,出于某种奇怪的原因,在桌面开关上触发的同一事件往往会更频繁地导致死锁或崩溃。

您需要仔细查看程序的初始化代码。到目前为止,最常见的错误是创建自己的启动画面。请务必在 .NET 框架中使用 built-in support 来实现这一点。

【讨论】:

  • 好消息,您知道 WPF 中的任何问题或在主应用程序启动之前调用 WPF SplashScreen 吗?
  • 我的应用程序确实有一个启动画面,虽然我没有做到,所以我会检查它并确保它设置正确!感谢您提供到目前为止的信息。
  • @Hans:所以我尝试在应用程序属性窗口中关闭启动画面,然后运行程序。该错误仍然存​​在,所以我认为这会告诉我这不是我的启动画面的结果;这是正确的,还是闪屏仍然会以某种方式引起问题?
  • 另外,我刚刚测试并发现了一些东西:单独打开屏幕保护程序不会导致我的应用程序崩溃。但是,仅工作站锁定确实会导致错误发生。不确定这是否提供任何见解,但认为提及它不会有什么坏处
【解决方案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
        Return m.Msg = MessageToDiscard
    End Function
End Class

当您的应用程序开始时添加此过滤器

Application.AddMessageFilter(New MyMessageFilter())

【讨论】:

  • 这会过滤掉整个应用程序中的消息还是只过滤主窗体?无论哪种方式,暂时,谢谢!我会试一试,告诉你进展如何!
  • 我认为它会过滤掉整个应用程序中的消息。
  • 所以我刚刚尝试完过滤器。我尝试将一个模块添加到我的源代码中,其中包括 MyMessageFilter 的类定义,除了我将“MessageToDiscard”更改为“26”,即 WM_WININICHANGE 的值。然后我将 Application.AddMessageFilter(New MyMessageFilter()) 插入到处理 mybase.load 的方法中。但是,根据 Winspector 的说法,当我启动程序时,该消息仍在发送到我的应用程序,并返回值为 0 ......有什么想法吗?我也会调查的
  • 该消息仍将发送到您的应用程序。问题是,如果您的应用程序仍在处理它。使用此过滤器,您的表单和控件不应再处理它。
  • 这就是我的想法:我期待我会看到发送的消息,但是没有返回消息或返回不为零。但什么都没有改变,我仍然有 0 的常规回报……嗯。我计划对过滤器的实际工作方式进行更多研究,所以我会看看我在那里找到了什么
猜你喜欢
  • 2019-09-11
  • 1970-01-01
  • 1970-01-01
  • 2012-02-07
  • 2021-08-24
  • 2012-07-12
  • 1970-01-01
  • 1970-01-01
  • 2016-07-25
相关资源
最近更新 更多