How can I stop my application from receiving a certain “message”?

亡梦爱人 提交于 2019-12-04 15:17:16

问题


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

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 :)

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.

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).

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. I am trying to use Wndproc() to filter out my message like this:

    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!

  2. 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:

    Application.AddMessageFilter(New 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.

  3. 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.

EDIT TWO:

I discovered something VERY promising today: I defined my wndproc function exactly like this:

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:

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.

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.

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.

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'".

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.

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!!!!!!!

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

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 :)


回答1:


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.

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.

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.

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.




回答2:


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())


来源:https://stackoverflow.com/questions/8997216/how-can-i-stop-my-application-from-receiving-a-certain-message

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!