.net - ¿Cómo puedo evitar que mi aplicación reciba un cierto "mensaje"?
vb.net multithreading (2)
He visto este problema mencionado en varias preguntas a lo largo de los años. Nunca lo diagnostiqué por completo, solo te diré lo que sé sobre él.
Este problema está relacionado con la forma en que se inicializa la clase SystemEvents. Está involucrado en el error porque esa es la clase que desencadena el evento que se activa cuando se cambia al escritorio seguro. Ya sea a través del protector de pantalla o bloqueando la estación de trabajo (tecla Windows + L). Los controles de Winforms en general están interesados en el evento SystemEvents.DisplaySettingsChanged porque es posible que necesiten volver a dibujarse cuando cambien los colores del tema o del sistema. Este evento también se produce comúnmente cuando el sistema cambia de escritorio.
Un problema central es que los eventos deben plantearse en el hilo de la interfaz de usuario. SystemEvents necesita adivinar exactamente qué hilo es en realidad el hilo de la interfaz de usuario. Esto falla cuando la primera ventana que se crea en el programa se crea en un subproceso que no es realmente el subproceso de la interfaz de usuario y se enmascara como uno al tener su apartamento COM configurado en STA. Si el hilo realmente se ejecuta, entonces el evento se dispara en ese hilo. Si el hilo se ha perdido, no es raro, se genera una excepción cuando SynchronizationContext.Post () intenta coordinar la llamada y falla. La excepción se traga y el evento se genera en una cadena de subprocesos arbitraria.
De cualquier manera, el evento no se genera en el subproceso correcto y eso infringe los requisitos de subprocesamiento para cualquier componente de la interfaz de usuario. Esto tiende a pasar desapercibido, por alguna extraña razón, el mismo evento disparado en el interruptor del escritorio tiende a provocar un bloqueo o bloqueos con mucha más frecuencia.
Deberá revisar cuidadosamente el código de inicialización del programa. Con mucho, el error más común es crear su propia pantalla de bienvenida. Asegúrese de utilizar el soporte integrado en .NET Framework para hacerlo bien.
POSIBLE SOLUCIÓN ENCONTRADA!
¡Creo que he encontrado una solución! Continuaré probando para asegurarme de que SI FUNCIONA, pero estoy esperanzado :) ¡He detallado cómo encontré la solución en EDITAR TRES de la pregunta!
Para cualquiera que desee conocer los antecedentes completos de mi problema y lo que he intentado como resultado de las aportaciones de esta pregunta, consulte esto: http://pastebin.com/nTrEAkVj
Voy a editar esto con frecuencia (> 3 veces al día la mayoría de los días de la semana) a medida que avance mi investigación y mi situación, así que no dejes de consultar si estás interesado o si tienes alguna información o conocimiento de mi problema :)
Antecedentes rápidos:
Tengo esta aplicación que he hecho que puede bloquearse cambiando mi protector de pantalla o bloqueando mi estación de trabajo, y en general cada vez que se le envía un mensaje WM_WININICHANGE / WM_SETTINGSCHANGE.
Si puedo bloquear constantemente mi aplicación cambiando mi protector de pantalla, ALGUNA parte de eso es enviar mi aplicación ALGUN tipo de mensaje (no necesariamente un mensaje de Windows, me refiero a un mensaje en el sentido más general), que a su vez es catastrófico para mi solicitud. Debido a esto, estoy tratando de encontrar la forma de bloquear cualquier mensaje que esté causando que mi aplicación procese mi problema. Soy consciente de que esta no es la mejor manera de buscar una solución, así que no es necesario que me lo diga. Mira la información de fondo o pregunta por qué si eso te molesta (hay una buena razón).
Mi pregunta:
Hay varias cosas sobre las cuales cualquier información me ayudaría a resolver mi problema, etiquetada de acuerdo con la relevancia (1 siendo más relevante, 3 ligeramente menos útil):
Estoy tratando de usar Wndproc () para filtrar mi mensaje de esta manera:
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
Sin embargo, según Windspector, el mensaje WM_WININICHANGE todavía se está enviando a mi aplicación (esto tiene sentido), PERO también se devuelve con 0 ... esto no debería estar sucediendo si funcionaba correctamente, no debería volver. cualquier cosa, ¿no es así? ¡La información con respecto a por qué esto no está funcionando como esperaba y cómo hacerlo funcionar sería extremadamente útil!
También he intentado usar 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
y luego agregando a mi método de manejo mybase.load:
Application.AddMessageFilter (New MyMessageFilter ())
sin embargo, parece que solo filtran ciertos mensajes, y mensajes como el mío no aparecen en estos aparentemente. información sobre si definitivamente es imposible usar cualquier tipo de filtro para capturar un mensaje WM_ o si hay posiblemente otras maneras de usar filtros de mensajes para lograr mi objetivo también sería útil.
de qué OTRAS maneras (aparte de este mensaje de Windows con message.msg = WM_WININICHANGE = 26 que encontré) ¿podría cambiar el protector de pantalla enviando ALGÚN tipo de mensaje a mi aplicación? ¿Es posible que otro tipo de mensaje de cambiar mi protector de pantalla también sea fatal?
¡Avíseme si hay CUALQUIER otra información sobre mi situación que pueda ser útil, y haré todo lo posible para obtenerla! Gracias de antemano por cualquier ayuda que puedas dar :)
EDITAR:
Parece que SOLAMENTE envío el mensaje WM_CHANGESETTING y hago que mi programa espere sobre la duración del tiempo de envío de sendmessagetimeout con el que envié el mensaje, entonces mi programa no se cuelga ... parece que RESPUESTA es lo que está bloqueando mi programa. interesante. ¡Definitivamente estoy cerca de mi solución! Estoy pensando en que un poco más de prueba debería permitirme encontrar un método para asegurarme de que mi programa no responda al mensaje.
EDITAR DOS:
Descubrí algo MUY prometedor hoy: definí mi función wndproc exactamente así:
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
Y luego intenté ejecutar mi programa y luego enviar un mensaje WM_SETTINGCHANGE usando:
SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero, _
SendMessageTimeoutFlags.SMTO_ABORTIFHUNG, 5000, IntPtr.Zero)
en otro programa que hice. Entonces, ¿qué pasó, preguntas? así lo intenté varias veces, y cada vez que aparecía el mensaje (las palabras que elegí para él son insignificantes), luego intenté esperar diferentes cantidades de tiempo antes de presionar "ok", y luego vería lo que sucedió con mi formulario principal. Bueno, muchas veces, nada era diferente, todavía se colgaba. Pero de vez en cuando, tal vez 1/5 veces, ¡el programa todavía estaría respoding después! Entonces, si lo hiciera, trataría de enviar el mensaje nuevamente, y luego otra vez, por lo general, fallarían la segunda vez durante la misma ejecución del programa, PERO ocasionalmente otra vez, aproximadamente otra vez 1/5 veces, el programa no lo haría chocar OTRA VEZ. Y luego las veces que intenté colgarlo dos veces. y tampoco en ningún momento, casi nunca se bloqueaba, sin importar cuántas veces intenté enviar el mensaje e independientemente de cuánto tiempo esperé después de que apareciera el mensaje de texto.
Descubrí que esperar unos 5 segundos parecía aumentar mis probabilidades: mi forma con la que desencadenaría el mensaje seguiría enfocada (la barra superior sería azul), justo después de presionar el botón de congelación, y luego aparecería el mensaje de texto, con la parte superior también es azul (en foco, supongo), ambos todavía "en foco" (al menos azul jaja). Luego, después de unos 5 segundos, la forma original perdería el foco, y después de ver eso, trataría de golpear bien.
Actualmente estoy pensando que esperar un poco y luego reconocer el cuadro de mensaje a veces permite que mi programa no se cuelgue porque está agotando el mensaje para que no vuelva. NO sé por qué el mensaje que regresa o no debe tener un efecto en lo que realmente hace mi programa. Esta es el área donde la aclaración sería útil :)
EDITAR TRES:
así que estoy buscando en Winspector un poco más, y me parece que si espero que aparezca WM_ERASEBKGND en la ventana de mi escritorio (que es la ventana etiquetada como "sysListView32 ''FolderView''" en Winspector) antes de presionar "OK" en mi msgbox , entonces el programa no se bloqueará, ¡interesante! Por lo general, se tarda cerca del tiempo de espera para que aparezca el mensaje de envío de tiempo para el mensaje WM_ERASEBKGND. esto es, por supuesto, después de enviar el mensaje WM_SETTINGCHANGE desde mi aplicación de prueba casera.
Entonces, después de esto, decido mirar un poco más por Winspector, porque tal vez haya colas aún más útiles que pueda encontrar. Dado que, obviamente, esperar a que winspector muestre que se envió un mensaje a mi escritorio no es una solución real para mi programa. Encuentro algunas ventanas inusualmente nombradas en mi proceso de programa: una se llama ".NET -BroadcastEventWindow.2.0.0.0.378734a.0" y otra se llama "GDI + ventana de la ventana de gancho ''GDI + ventana''" con una subventana llamada "IME" IME predeterminado ''".
Decido mirar los mensajes que van a estas ventanas para ver si están recibiendo mensajes reconocibles, como WM_SETTINGCHANGE o WM_ERASEBKGND. Resulta que no reciben mensajes a menudo: GDI + no recibió ningún mensaje mientras yo miraba, pero .NET -BroadcastEventWindow recibió algunos. Los que iban a BroadcastEventWindow eran en su mayoría solo WM_appactivate cuando hacía clic en la ventana de mi aplicación u otra ventana después de ella.
PERO LUEGO ... Noté que .Net BroadcastEventWindow recibe mi mensaje WM_CHANGESETTING !!!! Miro qué otros mensajes aparecen: no mucho, pero me doy cuenta cuando la aplicación falla debido al error, hay un mensaje que no reconozco: WM_USER + 7194 (0x201A). Hm, veamos qué es eso. Después de buscar en Google, descubro que parece ser un mensaje definido por la aplicación / usuario, y luego de otra búsqueda sobre problemas relacionados con él, noté que alguien puede usar un filtro para filtrar este mensaje y solucionar un problema de el suyo ( http://www.pcreview.co.uk/forums/handling-wm_user-messages-t1315625.html ). Merece la pena intentarlo al menos ¿no? entonces vuelvo a agregar el filtro que intenté previamente, y cambio los valores para filtrar. La aplicación no se coló !!!!!!!
A continuación, intento de permitir que mi estación de trabajo se bloquee para ver si todavía falla (porque anteriormente solo era con enviar el único mensaje WM_CHANGESETTING). Resulta que todavía se colgó :( PERO, eché otra ojeada a winspector para esa ventana, y ah, dos mensajes NUEVOS WM_USER: WM_USER + 7294 (0x207E) y WM_USER + 7189 (0x2015). Así que intento filtrarlos ¡¡¡también ... y luego no se bloquea en el bloqueo de la estación de trabajo !!!: D
¡Hasta ahora, no he notado ningún efecto adverso de esto en el uso regular de la aplicación! lo cual tiene sentido, ya que no creo que ningún mensaje definido por el usuario esté involucrado intencionalmente en mi programa.
Dejaré la pregunta abierta un poco más hasta que me asegure de que no haya nada de malo en mi solución y funcione bien. Gracias a aquellos de ustedes que me dieron un pequeño consejo sobre cómo proceder en las etapas intermedias de mi depuración :)
Implemente su propio filtro de mensajes con
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
Agregue este filtro cuando su aplicación comience con
Application.AddMessageFilter(New MyMessageFilter())