POSSIBLE SOLUTION FOUND!
可能的解决方案了!
I believe I have found a solution! I will be continuing testing to make sure it DOES in fact work, but I'm hopeful :) I have detailed how I found the solution in EDIT THREE of the question!
我相信我找到了解决办法!我将继续进行测试,以确保它确实有效,但我希望:)我已经在编辑问题的三个部分中详细介绍了我是如何找到解决方案的!
For anyone wishing to know the full background behind my problem and what I have kind of tried as a result of input from this question, see this: http://pastebin.com/nTrEAkVj
对于任何希望了解我的问题背后的完整背景的人,以及我从这个问题中输入的结果,请参见http://pastebin.com/nTrEAkVj。
I will be editing this frequently (>3 times a day most weekdays) as I progress my research and situation, so keep checking back if you are interested or have some information or knowledge of my issue :)
我将经常编辑这个(>,大部分工作日每天3次),因为我正在研究和研究我的情况,所以如果你对我的问题感兴趣或者有一些信息或知识,请继续检查:)
Quick Background:
快速的背景:
I have this app I have made that can be crashed by changing my screen saver or locking my work station, and in general whenever a WM_WININICHANGE/WM_SETTINGSCHANGE message is sent to it.
我制作了这个应用程序,可以通过改变屏幕保护程序或锁定我的工作站上来崩溃,一般来说,每当WM_WININICHANGE/WM_SETTINGSCHANGE消息被发送到它时。
If I can consistently crash my app by changing my screensaver, then SOME part of doing that is sending my app SOME kind of message (not necessarily a windows message, I mean message in the most general sense), which in turn is catastrophic to my application. Due to this, I am trying to find a way to block whatever message is causing my problem from being processed by my application. I am aware this isn't the best way to go about a solution, so you don't need to tell me. Look at the background info or ask why if that bothers you (there is a good reason).
如果我可以不断地通过改变屏幕保护程序来崩溃我的应用程序,那么这样做的一部分就是给我的应用程序发送某种消息(不一定是windows消息,我的意思是消息),这反过来对我的应用程序来说是灾难性的。由于这个原因,我正在寻找一种方法来阻止任何导致我的问题被我的应用程序处理的消息。我知道这并不是最好的解决方法,所以你不需要告诉我。看看背景信息,或者问问为什么这会让你感到困扰(这是一个很好的理由)。
My Question:
我的问题:
there are several things that any information about would help me solve my problem, labelled according to relevance (1 being most relevant, 3 slightly less helpful):
有一些信息可以帮助我解决我的问题,根据相关性贴上标签(1是最相关的,3是不太有用的):
-
I am trying to use Wndproc() to filter out my message like this:
我正在尝试使用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
However, according to Windspector, the WM_WININICHANGE message is still being sent to my app (this makes sense), BUT it is also being returned with 0... this shouldn't be happening if it was working properly, it shouldn't return anything, shouldn't it? Information regarding why this isn't working as I expected and how to make it work would be extremely helpful!
然而,根据Windspector, WM_WININICHANGE消息仍然被发送到我的应用程序(这是有道理的),但它也被返回0……如果它运行正常,就不应该发生,它不应该返回任何东西,不是吗?关于为什么它不能像我期望的那样工作以及如何使它工作的信息将是非常有用的!
-
I have also tried using messagefilters:
我也尝试过使用消息过滤器:
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
and then adding to my mybase.load handling method:
然后加上我的mybase。负载处理方法:
Application.AddMessageFilter(New MyMessageFilter())
应用程序。AddMessageFilter(新MyMessageFilter())
however they appear to only filter certain messages, and messages such as mine are not caught in these apparently. information about if it is definately impossible to use any kind of filter to catch a WM_ message or if there are possibly other ways to use message filters to accomplish my goal would also be helpful.
然而,它们似乎只过滤某些消息,而像我这样的消息显然没有被这些消息捕获。关于是否绝对不可能使用任何类型的过滤器来捕获WM_消息,或者是否可能有其他方法来使用消息过滤器来实现我的目标,这些信息也会很有帮助。
-
in what OTHER ways (apart from this one windows message with message.msg = WM_WININICHANGE = 26 that I found) could me changing my screensaver send ANY kind of message to my application? is it possible that another kind of message from changing my screen saver is also fatal?
以什么其他方式(除了这个窗口消息和消息)。我可以修改我的屏保给我的应用程序发送任何信息吗?是否可能改变我的屏幕保护程序的另一种消息也是致命的?
Let me know if there is ANY other information regarding my situation that may be useful, and I will do my best to get it! Thank you in advance for any help you can give :)
如果关于我的情况还有其他有用的信息,请告诉我,我会尽我最大的努力去得到它!提前感谢您的帮助。)
EDIT:
编辑:
It appears if I ONLY send the WM_CHANGESETTING message, and make my program wait over the timeout length of the sendmessagetimeout I sent the message with, then my program doesn't crash... it appears the RESPONSE is what is crashing my program... interesting. I am definitely close to my solution! I'm thinking a little more testing should allow me to figure out a method to make sure my program does not respond to the message.
如果我只发送WM_CHANGESETTING消息,并让我的程序等待我发送消息的sendmessagetimeout的超时时间,那么我的程序就不会崩溃……看来是我的程序崩溃了……有趣。我绝对接近我的解决方案!我正在考虑进行更多的测试,以确保我的程序不会对消息做出响应。
EDIT TWO:
编辑二:
I discovered something VERY promising today: I defined my wndproc function exactly like this:
我今天发现了一个很有前途的东西:我定义了我的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
And then I tried running my program, and then sending a WM_SETTINGCHANGE message using:
然后我尝试运行我的程序,然后发送一个WM_SETTINGCHANGE消息使用:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero, _
SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, IntPtr.Zero)
in another program I made. So what happened you ask? well I tried this several times, and every time the messagebox would pop up (the words I chose for it are insignificant), then I tried waiting different amounts of time before pressing ok, and then I would see what happened to my main form. Well a lot of the time, nothing was different, it would still crash. But occasionally, maybe 1/5 times, the program would still be respoding after! Then if it did, I would try sending the message again, and then again, usually they would fail the second time during the same run of the program, BUT occasionally again, about another 1/5 times it seemed, the program wouldn't crash AGAIN. And then the times I tried to crash it twice. and it didn't either time, it would almost always then never crash no matter how many times I tried sending the message and regardless of how long I waited after the msgbox popped up.
在我做的另一个节目中。你会问,发生了什么?我试了好几次,每次弹出消息框(我为它选择的词是不重要的),然后我试着等待不同的时间按下ok,然后我就会看到我的主表单发生了什么。很多时候,没有什么不同,它仍然会崩溃。但是偶尔,也许是1/5次,程序仍然会重新开始!如果是的话,我会再发送一次消息,然后,通常在同样的程序运行过程中,它们会第二次失败,但偶尔,又会有1/5次,程序不会再次崩溃。然后有次我试着撞了两次。而且它也没有,它几乎总是永远不会崩溃,不管我发了多少次信息,也不管我在msgbox出现后等了多久。
I found waiting about 5 seconds seemed to increase my odds: my form that I trigger the message with would still be in focus (top bar would be blue), right after I pressed the freeze button, and then the msgbox would pop up, with the top also blue (in focus I assume), both of them still "in focus" (at least blue haha). Then after about 5 seconds the original form would lose focus, and after seeing that, I would try hitting ok.
我发现等待约5秒似乎增加几率:我的形式触发消息仍将关注(顶部栏是蓝色),就在我按下停止按钮,然后对话框将弹出,顶部也蓝色焦点(我认为),他们两人还“焦点”(至少蓝色哈哈)。大约5秒后,原来的表单会失去焦点,看到后,我会尝试点击ok。
I am currently thinking that this waiting a bit and then acknowledging the message box is sometimes enabling my program to not crash because it is timing out the message so it does not return. I do NOT know why the message returning or not should have an effect on what my program actually does though. This is the area where clarification would be helpful :)
我现在认为,这个等待了一点,然后确认消息框,有时使我的程序不会崩溃,因为它是超时的消息,所以它不会返回。我不知道为什么返回或不返回的消息会影响我的程序的实际功能。这是需要澄清的地方:)
EDIT THREE:
编辑三:
so I am looking in Winspector a bit more, and I find that if I wait for WM_ERASEBKGND to show up in my desktop window (which is the window labelled as "sysListView32 'FolderView'" in Winspector) before hitting "OK" on my msgbox, then the program will not crash, interesting! It usually takes close to the Timeout for the sendmessagetimeout for the WM_ERASEBKGND message to show up. this is of course after sending the WM_SETTINGCHANGE message from my homemade testing app.
所以我在Winspector上多找了一点,我发现如果我等待WM_ERASEBKGND出现在我的桌面窗口中(窗口在Winspector中被标记为“sysListView32 'FolderView'”),然后在我的msgbox上点击“OK”,那么程序就不会崩溃,有趣!它通常会接近出现WM_ERASEBKGND消息的sendmessagetimeout。当然,这是在我的自制测试应用程序发送WM_SETTINGCHANGE消息之后。
So, after this I decide to look a bit more around Winspector, because maybe there are even more useful queues I can find? Since obviously waiting for winspector to show a message is sent to my desktop isn't an actual fix at all for my program. I find a few unusually named windows under my program process: one is named ".NET -BroadcastEventWindow.2.0.0.0.378734a.0" and another is named "GDI+ Hook Window Class 'GDI+ Window'" with a subwindow called "IME 'Default IME'".
所以,在这之后,我决定多看看Winspector,因为也许我能找到更有用的队列?显然,等待winspector显示消息被发送到我的桌面并不是对我的程序的真正修复。在我的程序过程中,我发现了一些不同寻常的命名窗口:一个是命名的。净-BroadcastEventWindow.2.0.0.0.378734a。另一个名为GDI+ Hook窗口类的GDI+窗口,有一个名为“IME' Default IME”的子窗口。
I decide to look at the messages going to these windows to see if they are receiving any recognizable messages, such as WM_SETTINGCHANGE or WM_ERASEBKGND. Turns out, they do not recieve messages often: GDI+ didn't receive any messages while I watched I don't think, but .NET -BroadcastEventWindow received a few. The ones going to the BroadcastEventWindow were mostly only WM_appactivate when I clicked my application window or another window after it.
我决定查看到这些窗口的消息,看看它们是否接收到任何可识别的消息,比如WM_SETTINGCHANGE或WM_ERASEBKGND。事实证明,他们并不经常收到消息:GDI+在我不认为的情况下没有收到任何消息,但是。net -BroadcastEventWindow收到了一些消息。当我点击我的应用程序窗口或它之后的另一个窗口时,那些进入BroadcastEventWindow的窗口大多只有WM_appactivate。
BUT THEN... I notice .Net BroadcastEventWindow receives my WM_CHANGESETTING message!!!! I look at what other messages show up: not a lot, but I notice when the app crashes because of the bug, there is a message I don't recognize: WM_USER+7194 (0x201A). Hm, lets see what that is. After I google it, I figure out it appears to be an application/user defined message, and then after another search about problems related to it, I notice that someone is able to use a filter to filter this message out and fix a problem of theirs (http://www.pcreview.co.uk/forums/handling-wm_user-messages-t1315625.html). It's worth a try for me at least right? so I re-add the filter I had tried previously, and change the values to be filtered. The app didn't crash!!!!!!!
但后来…我注意到。net广播的窗口接收我的WM_CHANGESETTING消息!!!!我查看了其他的消息:不是很多,但是我注意到当应用程序崩溃的时候,有一个消息我不知道:WM_USER+7194 (0x201A)。嗯,让我看看那是什么。在我谷歌之后,我发现它似乎是一个应用程序/用户定义的消息,然后在另一个关于它的问题的搜索之后,我注意到有人能够使用一个过滤器来过滤这个消息并修复他们的问题(http://www.pcreview.co.uk/forums/handling-wm_user-messages-t1315625.html)。至少对我来说值得一试,对吗?因此,我重新添加之前尝试过的过滤器,并更改要过滤的值。应用程序没有崩溃! ! ! ! ! ! !
Next I try by letting my workstation lock to see if that still crashes it (because previously was only with sending it the lone WM_CHANGESETTING message). turns out, it still did crash :( BUT, I take another look in winspector for that window, and oh huh, two NEW WM_USER messages: WM_USER+7294(0x207E) and WM_USER+7189(0x2015). So I try filtering those out too... and then it doesn't crash on workstation locking either!!! :D
接下来,我尝试让我的工作站锁定看看它是否仍然崩溃(因为以前只发送WM_CHANGESETTING消息)。结果,它还是崩溃了:(但是,我在winspector中查看了另一个窗口,还有两个新的WM_USER消息:WM_USER+7294(0x207E)和WM_USER+7189(0x2015)。所以我也试着过滤掉这些。然后它也不会崩溃在工作站锁定!!!:D
So far I have noticed no adverse affects of this on regular app use too! which makes sense, since I don't think any user defined messages are purposefully involved in my program.
到目前为止,我还注意到在常规的应用程序使用中没有出现这种不良影响!这是有道理的,因为我不认为任何用户定义的消息都有意地参与到我的程序中。
I will be leaving the question open a bit longer until I make sure there is nothing wrong with my solution and it works well. Thanks to those of you who gave me a little bit of advice with how to proceed at the middle stages of my debugging :)
我将把这个问题留得久一点,直到我确定我的解决方案没有问题,并且它运行良好。感谢那些给了我一些建议的人在我调试的中期如何进行
2 个解决方案
#1
6
I've seen this problem mentioned in various questions over the years. Never completely diagnosed it, I'll just tell you what I know about it.
多年来,我在各种问题中都看到了这个问题。从来没有完全诊断出来,我只告诉你我所知道的。
This problem is related to the way the SystemEvents class gets initialized. It is involved in the mishap because that's the class that triggers the event that fires when you switch to the secure desktop. Either through the screen-saver or by locking the workstation (Windows + L key). Winforms controls are in general interested in the SystemEvents.DisplaySettingsChanged event because they might need to redraw themselves when the theme or the system colors were changed. This event is also commonly raised when the system switches desktops.
这个问题与SystemEvents类初始化的方式有关。它与不幸事件有关,因为当您切换到安全桌面时触发事件的类。通过屏幕保护程序或锁定工作站(Windows + L键)。Winforms控件通常对SystemEvents感兴趣。DisplaySettingsChanged事件,因为当主题或系统颜色发生改变时,它们可能需要重新绘制自己。当系统切换桌面时,通常也会引发此事件。
One core issue is that the events needs to be raised on the UI thread. SystemEvents needs to guess exactly what thread is actually the UI thread. This goes wrong when the very first window that is created in the program is created on a thread that is not actually the UI thread and otherwise masquerades as one by having its COM apartment set to STA. If the thread actually keeps running then the event is fired on that thread. If the thread is gone, not uncommon, then an exception is raised when the SynchronizationContext.Post() tries to marshal the call and fails. The exception is swallowed and the event is then raised on an arbitrary threadpool thread.
一个核心问题是事件需要在UI线程上引发。SystemEvents需要准确地猜测什么线程实际上是UI线程。当在程序中创建的第一个窗口在线程上创建时,这是错误的,该线程实际上并不是UI线程,而是通过将COM单元设置为STA来伪装成一个线程。如果线程实际上继续运行,则该线程将触发事件。如果线程消失了,那么当SynchronizationContext.Post()尝试封送调用并失败时,就会引发异常。将接受异常,然后在一个任意的threadpool线程上引发事件。
Either way, the event is not raised on the correct thread and that violates the threading requirements for any UI component. This tends to go unnoticed, for some strange reason the same event fired on the desktop switch tends to cause deadlock or crashes much more often.
无论哪种方式,事件都不会在正确的线程上引发,这违反了任何UI组件的线程需求。这通常不会被注意到,出于某种奇怪的原因,在桌面开关上触发的相同事件往往会导致死锁或更频繁的崩溃。
You'll need to carefully review the initialization code of the program. By far the most common mistake is creating your own splash screen. Be sure to use the built-in support in the .NET framework to get that right.
您需要仔细检查程序的初始化代码。到目前为止,最常见的错误是创建自己的启动屏幕。一定要使用。net框架内的内置支持来正确实现这一点。
#2
4
Implement your own message filter with
实现您自己的消息过滤器
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
Add this filter when your application starts with
在应用程序开始时添加这个过滤器
Application.AddMessageFilter(New MyMessageFilter())
#1
6
I've seen this problem mentioned in various questions over the years. Never completely diagnosed it, I'll just tell you what I know about it.
多年来,我在各种问题中都看到了这个问题。从来没有完全诊断出来,我只告诉你我所知道的。
This problem is related to the way the SystemEvents class gets initialized. It is involved in the mishap because that's the class that triggers the event that fires when you switch to the secure desktop. Either through the screen-saver or by locking the workstation (Windows + L key). Winforms controls are in general interested in the SystemEvents.DisplaySettingsChanged event because they might need to redraw themselves when the theme or the system colors were changed. This event is also commonly raised when the system switches desktops.
这个问题与SystemEvents类初始化的方式有关。它与不幸事件有关,因为当您切换到安全桌面时触发事件的类。通过屏幕保护程序或锁定工作站(Windows + L键)。Winforms控件通常对SystemEvents感兴趣。DisplaySettingsChanged事件,因为当主题或系统颜色发生改变时,它们可能需要重新绘制自己。当系统切换桌面时,通常也会引发此事件。
One core issue is that the events needs to be raised on the UI thread. SystemEvents needs to guess exactly what thread is actually the UI thread. This goes wrong when the very first window that is created in the program is created on a thread that is not actually the UI thread and otherwise masquerades as one by having its COM apartment set to STA. If the thread actually keeps running then the event is fired on that thread. If the thread is gone, not uncommon, then an exception is raised when the SynchronizationContext.Post() tries to marshal the call and fails. The exception is swallowed and the event is then raised on an arbitrary threadpool thread.
一个核心问题是事件需要在UI线程上引发。SystemEvents需要准确地猜测什么线程实际上是UI线程。当在程序中创建的第一个窗口在线程上创建时,这是错误的,该线程实际上并不是UI线程,而是通过将COM单元设置为STA来伪装成一个线程。如果线程实际上继续运行,则该线程将触发事件。如果线程消失了,那么当SynchronizationContext.Post()尝试封送调用并失败时,就会引发异常。将接受异常,然后在一个任意的threadpool线程上引发事件。
Either way, the event is not raised on the correct thread and that violates the threading requirements for any UI component. This tends to go unnoticed, for some strange reason the same event fired on the desktop switch tends to cause deadlock or crashes much more often.
无论哪种方式,事件都不会在正确的线程上引发,这违反了任何UI组件的线程需求。这通常不会被注意到,出于某种奇怪的原因,在桌面开关上触发的相同事件往往会导致死锁或更频繁的崩溃。
You'll need to carefully review the initialization code of the program. By far the most common mistake is creating your own splash screen. Be sure to use the built-in support in the .NET framework to get that right.
您需要仔细检查程序的初始化代码。到目前为止,最常见的错误是创建自己的启动屏幕。一定要使用。net框架内的内置支持来正确实现这一点。
#2
4
Implement your own message filter with
实现您自己的消息过滤器
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
Add this filter when your application starts with
在应用程序开始时添加这个过滤器
Application.AddMessageFilter(New MyMessageFilter())