tiempo - llamar evento click desde codigo c#
¿Cómo evitar la reentrada del controlador de eventos WPF durante la llamada al método ActiveX? (5)
Estamos llamando a métodos en un componente ActiveX desde una aplicación WPF y STA. Esta llamada se realiza con retraso mediante:
res = ocx.GetType().InvokeMember(methodName, flags, null, ocx, args);
... donde ocx es el objeto ActiveX recuperado con el método System.Windows.Forms.AxHost.GetOcx ().
Esta llamada se realiza desde un controlador de eventos de WPF, digamos "clic con el mouse".
Ahora el problema. Si hacemos doble clic en el evento ''mouse clicked'' se activará, ejecutando InvokeMember (). Sin embargo, durante esta llamada, vemos que el evento "hecho clic en el mouse" se vuelve a ingresar. Entonces, en el mismo hilo, vemos el controlador de eventos dos veces en la pila de llamadas. Esto es muy inesperado, y estamos tratando de prevenir eso. ¿Cómo podemos evitar que esto suceda?
La única razón por la que podemos pensar por qué sucede es:
- El objeto COM se crea en otra STA, por lo que estamos realizando una llamada cross-STA que debe organizarse
- cross-thread STA calls usa un mensaje de Windows para enviar una solicitud RPC al componente COM
- las llamadas STA de subprocesos utilizan la bomba Mensaje de Windows para recibir una respuesta RPC
- Durante la espera, entra otro tipo de evento (como "clic del mouse"), y esto se maneja antes de que se maneje la respuesta RPC.
- Esta respuesta RPC se maneja
Cosas que tratamos de solucionar el problema:
- use lock () en todos los controladores de eventos. Esto no funciona, ya que lock () bloqueará un hilo, y en este caso es el mismo hilo que vuelve a entrar en el controlador de eventos.
- use un bloqueo personalizado como ''bool locked = false; if (! locked) {locked = true; InvokeMethod (); ...; bloqueado = falso; } ''. Esto funciona parcialmente: descarta los eventos en lugar de ponerlos en cola para más adelante, y necesita un cambio extenso a todos nuestros controladores de eventos, lo cual no es agradable de hacer.
- use Dispatcher.DisableProcessing para evitar que se procesen (otros) mensajes. Esto no ayuda: arroja una excepción porque los mensajes se procesan de todos modos.
- crea un segundo despachador en un nuevo subproceso y ejecuta ocx.InvokeMehod () a través de Dispatcher.Invoke () para que otro subproceso lo maneje. Esto da ''Un evento no pudo invocar a ninguno de los suscriptores (Excepción de HRESULT: 0x80040201)'' (Sí, también estamos suscritos a los eventos COM del objeto ActiveX).
- use Dispatcher.PushFrame () para detener el manejo de eventos. Esto también falla.
Una idea disparatada que podría funcionar, pero no se sabe cómo implementar esto, sería la creación de una nueva bomba de mensajes como la bomba de mensajes WPF, que se puede configurar para manejar temporalmente solo las llamadas RPC. Esto está en la línea de http://jmorrill.hjtcentral.com/Home/tabid/428/EntryId/430/WPF-MediaKit-Updates.aspx , pero aún algo diferente de esta situación.
Entonces, ¿la pregunta se reduce a cómo podemos hacer que la llamada ActiveX sea sincronizada como esperábamos que fuera en lugar de asincrónica?
Actualizar
Para dejar en claro que el mecanismo involucrado no se trata solo de eventos de mouse, sino que el problema más genérico de ''se maneja un nuevo evento mientras se está ejecutando el antiguo'', le daré otro ejemplo con un seguimiento de pila:
Contexto: tenemos una Cuadrícula WPF en la que obtenemos un clic del mouse (Grid_MouseDown), tenemos un objeto ActiveX sobre el cual realizamos el método ''CloseShelf''. La apertura del estante llevará tiempo, por lo que estamos suscritos al evento ''EventShelfClosed'', que en el caso de que el manejador de EventShelfClosed llame a ''ListShelf'' para saber qué estanterías quedan.
Así es como se ve la traza de pila administrada (Hans pidió una pila de pila no administrada, pero no sé cómo obtener una):
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 53 C#
MyAxWrapper.dll!MyAxWrapper.LoggingMyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 151 + 0x14 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMethod(string methodName, object[] args) Line 92 + 0x18 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxAppWrapper.ListShelfs(string CanvasPageId) Line 300 + 0x42 bytes C#
PACS.dll!PACS.MyAxDatabase.GetShelfIdsOn(string canvasPageId) Line 223 + 0xf bytes C#
MyAxCanvas.dll!MyAxCanvas.MyAxCanvasPlugin.UpdateTimeLineSelection(string canvasPageId) Line 123 + 0x10 bytes C#
MyAxCanvas.dll!MyAxCanvas.MyAxCanvasPlugin.EventShelfClosed(string canvasPageId, string shelfId) Line 180 + 0xb bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxAppWrapper.FireEvent(string eventName, object[] args) Line 21 + 0x73 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxEventForwarder.EventShelfClosed(string CanvasPageID, string ShelfID) Line 177 + 0x58 bytes C#
[Native to Managed Transition]
[Native to Managed Transition]
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 75 + 0x2b bytes C#
MyAxWrapper.dll!MyAxWrapper.LoggingMyAxWrapper.InvokeMember(string methodName, System.Reflection.BindingFlags flags, object[] args, int refArgIdx) Line 151 + 0x14 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxWrapper.InvokeMethod(string methodName, object[] args) Line 92 + 0x18 bytes C#
MyAxWrapper.dll!MyAxWrapper.MyAxAppWrapper.CloseShelf(string a) Line 218 + 0x42 bytes C#
MyAxCanvas.dll!MyAxCanvas.MyAxCanvasPlugin.EventCanvasPageCreated.AnonymousMethod__0(DataModel.Item exam) Line 110 + 0x1d bytes C#
ItemPresenter.dll!ItemPresenter.ItemPresenter.OnItemClicked(DataModel.Item study) Line 36 + 0x14 bytes C#
ItemPresenter.dll!ItemPresenter.ItemPresenter.ItemPresenterPerYearControls_Click(object sender, System.Windows.RoutedEventArgs e) Line 215 + 0x1e bytes C#
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x78 bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1ae bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x79 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs e) + 0x17 bytes
ItemPresenter.dll!ItemPresenter.ItemPresenterControl.Grid_MouseDown(object sender, System.Windows.Input.MouseButtonEventArgs e) Line 47 + 0x29 bytes C#
PresentationCore.dll!System.Windows.Input.MouseButtonEventArgs.InvokeEventHandler(System.Delegate genericHandler, object genericTarget) + 0x31 bytes
PresentationCore.dll!System.Windows.RoutedEventArgs.InvokeHandler(System.Delegate handler, object target) + 0x29 bytes
PresentationCore.dll!System.Windows.RoutedEventHandlerInfo.InvokeHandler(object target, System.Windows.RoutedEventArgs routedEventArgs) + 0x3e bytes
PresentationCore.dll!System.Windows.EventRoute.InvokeHandlersImpl(object source, System.Windows.RoutedEventArgs args, bool reRaised) + 0x1ae bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEventImpl(System.Windows.DependencyObject sender, System.Windows.RoutedEventArgs args) + 0x79 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseTrustedEvent(System.Windows.RoutedEventArgs args) + 0x41 bytes
PresentationCore.dll!System.Windows.UIElement.RaiseEvent(System.Windows.RoutedEventArgs args, bool trusted) + 0x2c bytes
PresentationCore.dll!System.Windows.Input.InputManager.ProcessStagingArea() + 0x1ff bytes
PresentationCore.dll!System.Windows.Input.InputManager.ProcessInput(System.Windows.Input.InputEventArgs input) + 0x45 bytes
PresentationCore.dll!System.Windows.Input.InputProviderSite.ReportInput(System.Windows.Input.InputReport inputReport) + 0x62 bytes
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.ReportInput(System.IntPtr hwnd, System.Windows.Input.InputMode mode, int timestamp, System.Windows.Input.RawMouseActions actions, int x, int y, int wheel) + 0x263 bytes
PresentationCore.dll!System.Windows.Interop.HwndMouseInputProvider.FilterMessage(System.IntPtr hwnd, MS.Internal.Interop.WindowMessage msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x46d bytes
PresentationCore.dll!System.Windows.Interop.HwndSource.InputFilterMessage(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0x75 bytes
WindowsBase.dll!MS.Win32.HwndWrapper.WndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam, ref bool handled) + 0xbe bytes
WindowsBase.dll!MS.Win32.HwndSubclass.DispatcherCallbackOperation(object o) + 0x7d bytes
WindowsBase.dll!System.Windows.Threading.ExceptionWrapper.InternalRealCall(System.Delegate callback, object args, int numArgs) + 0x53 bytes
WindowsBase.dll!MS.Internal.Threading.ExceptionFilterHelper.TryCatchWhen(object source, System.Delegate method, object args, int numArgs, System.Delegate catchHandler) + 0x42 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.InvokeImpl(System.Windows.Threading.DispatcherPriority priority, System.TimeSpan timeout, System.Delegate method, object args, int numArgs) + 0xb4 bytes
WindowsBase.dll!MS.Win32.HwndSubclass.SubclassWndProc(System.IntPtr hwnd, int msg, System.IntPtr wParam, System.IntPtr lParam) + 0x104 bytes
[Native to Managed Transition]
[Managed to Native Transition]
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrameImpl(System.Windows.Threading.DispatcherFrame frame) + 0xc1 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.PushFrame(System.Windows.Threading.DispatcherFrame frame) + 0x49 bytes
WindowsBase.dll!System.Windows.Threading.Dispatcher.Run() + 0x4c bytes
PresentationFramework.dll!System.Windows.Application.RunDispatcher(object ignore) + 0x17 bytes
PresentationFramework.dll!System.Windows.Application.RunInternal(System.Windows.Window window) + 0x6f bytes
PresentationFramework.dll!System.Windows.Application.Run(System.Windows.Window window) + 0x26 bytes
PresentationFramework.dll!System.Windows.Application.Run() + 0x1b bytes
MyAxCanvasStandalone.exe!MyAxCanvasStandalone.App.Main(string[] args) Line 37 + 0xa bytes C#
[Native to Managed Transition]
[Managed to Native Transition]
mscorlib.dll!System.AppDomain.ExecuteAssembly(string assemblyFile, System.Security.Policy.Evidence assemblySecurity, string[] args) + 0x6d bytes
Microsoft.VisualStudio.HostingProcess.Utilities.dll!Microsoft.VisualStudio.HostingProcess.HostProc.RunUsersAssembly() + 0x2a bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart_Context(object state) + 0x63 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool ignoreSyncCtx) + 0xb0 bytes
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) + 0x2c bytes
mscorlib.dll!System.Threading.ThreadHelper.ThreadStart() + 0x44 bytes
[Native to Managed Transition]
Lo que sucede es que el método ''CloseShelf'' cerrará el estante, pero en este caso ''CloseShelf'' es tan rápido que el evento ''EventShelfClosed'' se emite y maneja durante la llamada a CloseShelf. Ahora CloseShelf llamará a ListShelfs, pero ListShelfs fallará y devolverá nulo porque el componente ActiveX está bloqueado por la llamada ''CloseShelf'' que todavía está activa.
¿Por qué es esto un problema? Debido a que el programador no espera que una llamada a método sea asincrónica. Esto nos golpea después de crear un programa grande, que ahora significa auditar todas las llamadas para un comportamiento inesperado.
¿Qué nos gustaría ver en este caso? Nos gustaría ver que ''CloseShelf'' regrese sin manejar otros eventos durante la llamada. El método debe ser sincrónico y cualquier evento pendiente manejado durante el ciclo de mensaje principal (no recursivo).
Tener un tipo de booleano ''lock'' no ayudará aquí, ya que nos faltarían eventos aquí, lo que bloquea la aplicación.
He tratado con problemas similares en el pasado, aunque no desde WPF.
En una aplicación win32, el enfoque recomendado era utilizar IMessageFilter :: MessagePending: esto se podía configurar para indicar qué tipos de mensajes se podían manejar cuando una llamada STA saliente ya estaba en progreso. Aquí tenía que tener cuidado para asegurarse de que las devoluciones de llamada de su objeto llamado se aceptaran y manejaran.
http://msdn.microsoft.com/en-us/library/windows/desktop/ms694352(v=vs.85).aspx
En WPF esto no está disponible. Creo que su enfoque para usar otro hilo es la forma correcta de hacerlo.
En principio, usted quiere que su hilo principal se bloquee en un hilo hijo. El subproceso secundario puede realizar la llamada COM saliente. Probablemente desee hacer que el hijo enhebre un STA para evitar introducir otros problemas desconocidos. Es importante que los mensajes se bombeen en el subproceso secundario y que los punteros o tipos estén correctamente organizados ya que el subproceso secundario estará en un apartamento COM diferente. Se evita la reentrada porque las retrollamadas son lo único que intentará enviar un mensaje al hilo que está bombeando.
En WPF, creo que un despachador debe proporcionar toda la funcionalidad que necesita.
No estoy seguro de por qué su intento de hacer esto con un Dispatcher falló; podría estar relacionado con este problema conocido: http://support.microsoft.com/kb/926997
Si ya tiene un comportamiento asincrónico, PowerThreading
biblioteca PowerThreading
Jeffrey Richter. Tiene AsyncEnumerator
para simplificar la programación asincrónica. Y también tiene primitiva de bloqueo que puede ayudarte a implementar tu escenario. Hasta donde yo sé, esta primitiva difiere de la clase regular de Monitor
de esa manera que no permite volver a ingresar su código ni siquiera en el mismo hilo, por lo que podría serle útil. Lamentablemente, no he probado ese primitivo, así que no puedo agregarle mucho.
Aquí hay un artículo sobre esta primitiva: http://msdn.microsoft.com/en-us/magazine/cc163532.aspx
Usted tiene la respuesta!
¿Es solo un viejo truco? No, no lo es, es un procedimiento operativo estándar cuando se trata de cualquier tipo de reentrada. Esto ha funcionado a la perfección para mí en más casos de los que puedo recordar, desde humildes pop-ups VB3 de un solo panel hasta enormes aplicaciones de administración de empresas MVVM / DDD.
Es lo que mencionaste: ''usa bloqueo personalizado como'' static bool locked = false; if (! locked) {locked = true; InvokeMethod (); ...; bloqueado = falso; } ''.
EDITAR
Tenga en cuenta los comentarios de la OP. OK, así que esto no resolverá el problema! El segundo evento no es un clic espurio; es un evento diferente, crítico para el funcionamiento correcto del sistema.
Por favor, mira mi siguiente respuesta para algunos intentos más. # 3 es el más feo, pero debería funcionar.
Sé que esta no es una respuesta completa, pero me gustaría aclarar cómo funcionan los eventos. Esto te ayudará a comprender que el reingreso no es un problema y es la forma en que funcionan los eventos.
- Las llamadas a los manejadores de Envent son siempre sincrónicas, pero eso no significa que no haya otros eventos en la pila.
- Supongamos que hace clic en un botón dentro de un cuadro de lista y levanta manualmente un evento de cambio de lista y en algún lugar se llama a alguna llamada.
Su típica llamada a un evento similar se ve así:
.... OnClick(...)
{
if(SelectionChanged!=null)
SelectionChanged(...)
}
Tenga en cuenta que la llamada OnClick todavía está en la pila, mientras que el evento SelectionChanged se activa, OnClick no saldrá de la pila hasta que se complete la llamada SelectionChanged.
¿Qué nos gustaría ver en este caso? Nos gustaría ver que ''CloseShelf'' regrese sin manejar otros eventos durante la llamada. El método debe ser sincrónico y cualquier evento pendiente manejado durante el ciclo de mensaje principal (no recursivo).
¿Cómo es posible si está generando eventos dentro de CloseShelf?
La única forma en que desearía hacer esto es haciendo cola en el controlador de eventos como en,
.... OnClick(...)
{
Dispatcher.BeginInvoke(delegate(){
if(SelectionChanged!=null)
SelectionChanged(...)
});
}
Esto generará un evento después de que OnClick termine, en este caso no verás OnClick en la pila mientras se realiza SelectionChanged.
<tldr> prueba el siguiente código en Attempt # 3. No había nada en la televisión cuando me senté a escribir esto. </ Tldr>
¡Gracias por la aclaración! Veo que solo hay un hilo aquí; y también, dado que el marco CloseShelf aún está en la pila, parece que la llamada COM está realmente bloqueando.
Desde el seguimiento de pila, parece que el objeto com llama a GetMessage o PeekMessage API (o si es VB6, DoEvents o similar) que verificará la cola de mensajes y los mensajes de PROCESO en ella, independientemente de si causará la reincorporación. AKA ''bombeando la cola de mensajes'' - pero si usa peekmessage, no se bloqueará si no hay mensajes, pero aún ejecutará los mensajes. En tu pila, estas llamadas podrían estar ocultas en la parte nativa invisible. El rastro de la pila en realidad demuestra que de alguna manera, ¡la llamada COM vuelve a llamar a tu código! Parece que estás enterado de esto. Si hay algo de niebla para algunos lectores aquí hay un par de enlaces:
http://discuss.joelonsoftware.com/default.asp?joel.3.456478.15
http://en.wikipedia.org/wiki/Event_loop
Multitarea cooperativa (un ciclo de mensajes para todo el sistema operativo como win3.0): http://en.wikipedia.org/wiki/Computer_multitasking#Cooperative_multitasking.2Ftime-sharing
La cita "tiempo cedido voluntariamente ..." se realiza en realidad mediante estas llamadas. ¡Y TODAVÍA sucede todo el tiempo en el hilo de GUI de cada aplicación de Windows!
Dado que la llamada real está en el COM, si no puede editarla, aún debe codificarla. Probablemente sea solo este método (con suerte).
A veces, los programas y componentes comprueban el ciclo de mensajes a propósito, a veces para que las cosas respondan o vuelven a pintar la pantalla. Al igual que este cartel está tratando de hacer:
¿Hay una función como PeekMessage que no procesa mensajes?
La cita sobre ''El sistema también puede procesar eventos internos'' te está traicionando aquí.
observe donde dice ''el programador no espera que una llamada a método sea asincrónica'' - no es asíncrona, o la traza de la pila se vería diferente. está ''recursando'' de nuevo en tu código. Como un viejo programador de Win3.1, ¡este era el infierno con el que lidiamos para TODAS las aplicaciones del sistema!
Encontré ese enlace en Google para ''verificar la cola de mensajes peekmessage'' mientras trataba de ver si Get / Peekmessage, etc. podría evitar el procesamiento de mensajes. Puedes ver desde esta documentación ...
http://msdn.microsoft.com/en-us/library/windows/desktop/ms644943(v=vs.85).aspx
... que envía algo como PM_QS_INPUT | PM_QS_PAINT evitaría el problema. Desafortunadamente, dado que no lo está llamando, ¡no puede cambiarlo! Y no vi ninguna forma de establecer un indicador para controlar el procesamiento posterior de mensajes de llamadas posteriores.
Si un lector todavía está confundido (si no se salta este código) ... Para un ejemplo simple, haga una aplicación VB Winforms y haga un botón y haga doble clic y pegue este código en - (Digo VB porque application.doevents es el la forma más práctica de invocar esta desagradable comprobación de cola de mensajes):
For i As Integer = 0 To 20
Text = i.ToString
System.Threading.Thread.Sleep(100)
Application.DoEvents()
Next
Ahora haz clic en el botón. Tenga en cuenta que puede agrandar la ventana y volver a dibujar el fondo, ya que los eventos permiten que sucedan estos eventos al verificar la cola de mensajes (REM out the doevents y ''esperar'' hasta que finalice el conteo; también intente hacer clic 3x y obtendrá 3 cuenta en una fila).
AHORA ... el pateador. Haga clic en el botón w / Doevents NO comentado. Haga clic en el botón 3 veces: las cuentas regresivas se interrumpen entre sí, luego continúen como termina la anterior. Puede pausar el IDE y ver 3 clic en callstacks.
¡Sabroso!
También veo que intentó un par de cosas para evitar que los mensajes o eventos se procesen. Sin embargo, si el código que dispara EventShelfClosed recibe una llamada de la reentrada causada por PeekMessage o ''Doevents'', es posible que no lo afecte.
Tenga en cuenta que esta práctica tiene sus detractores: http://www.codinghorror.com/blog/2005/08/is-doevents-evil-revisited.html
Lo mejor sería cambiar el COM para que no haga ninguna llamada API que esté verificando el ciclo del mensaje.
¡Buena suerte con eso!
Otra forma de cambiarlo sería pasar de eventos que controlan EventShelfClosed y llamarlos explícitamente una vez que se cierra la llamada a CloseShelf (ya que la llamada com está realmente sucediendo). Desafortunadamente, la arquitectura de su programa probablemente no permitirá eso sin grandes cambios y / o mayor cohesión y otras cosas que ensucian a los modelos bonitos (no a los modelos de moda:).
Una forma diferente sería hacer que un nuevo objeto de subproceso apunte a una función que hace que com invoque, luego iniciarlo, luego unirlo, con la esperanza de que algo como PeekMessage no encuentre un mensaje de bomba en el nuevo subproceso y por lo tanto no interfiera con cosas. Parece que algunos de ustedes intentaron este tipo de cosas. Desafortunadamente, si el COM atiende los mensajes, y no hay mensaje de bomba en el hilo, kaboom. Probablemente explote en lugar de simplemente ignorar cosas. Parece que eso es lo que sucedió. Además, si el COM depende de otros elementos a los que solo se debe acceder desde el hilo GUI / messagepump, tiene problemas con las llamadas entre hilos (lo cual sería cierto si el COM interactúa con la UI o cualquier objeto de UI )
Si no puede detener la verificación de la cola de mensajes, o evitar que EventShelfClosed se active hasta más adelante, no tiene más remedio que llamar al EventShelfClosed. Pero lo que puede hacer es esperar, y luego caiga cuando CloseShelf finalice.
Por lo tanto, aún debe tener un campo booleano de nivel de clase establecido / desarmado por CloseShelf para que EventShelfClosed sepa que se está ejecutando.
Desafortunadamente, simplemente verificando esto en un ciclo while, incluso con un reposo, se bloqueará el único hilo que tienes, y se congelará la aplicación. Puede intentar que EventShelfClosed vuelva a subir y salir de la función mientras esté configurado el bool; pero como RaiseEvent se mantiene dentro de managed, ejecuta el código inmediatamente y no comprueba la cola de mensajes, se bloqueará con un . Si puede averiguar cómo volver a subir EventShelfClosed llamando a la API de PostMessage (no a SendMessage, que la ejecuta de inmediato), eso seguirá poniendo en la cola de mensajes del subproceso de la GUI tantas veces como la llamada COM haga que Windows lo verifique. A menos que dicho COM espere que la cola esté vacía por alguna razón estúpida: otro bloqueo. No recomendando
Entonces ... puedes luchar contra el fuego con fuego. Aquí estoy implementando otro ciclo de verificación de mensajes para permitir que sus eventos sucedan mientras espera que el bool se borre.
Tenga en cuenta que esto solo solucionará este problema en este caso. Auditando todas las llamadas ... esto no es una bala mágica. Mi conjetura es que no hay ninguno. Muy desordenado y esto es un truco total.
Intento # 3
No es realmente el intento # 3, es más como la posibilidad # 8. Pero hice referencia a esto en mi respuesta anterior y soy demasiado flojo para editar eso.
Boolean InCloseShelf
function CloseShelf(...)
InCloseShelf=True;
try
{
com call and all else
}
finally
InCloseShelf=False
function EventShelfClosed(...
while (InCloseShelf)
{
DoEvents
}
Ahora, por supuesto, no hay DoEvents en WPF, está en la ''aplicación'' de winforms. Este blog tiene una implementación
http://dedjo.blogspot.com/2007/08/how-to-doevents-in-wpf.html
void DoEvents(){
DispatcherFrame f = new DispatcherFrame();
Dispatcher.CurrentDispatcher.BeginInvoke(DispatcherPriority.Background,
(SendOrPostCallback)delegate(object arg) {
DispatcherFrame fr = arg as DispatcherFrame;
fr.Continue=True;
}, f);
Dispatcher.PushFrame(frame);
}
No probado, por supuesto! Tenga en cuenta que esto es de la corrección en los comentarios:
static void DoEvents()
{
DispatcherFrame frame = new DispatcherFrame(true);
Dispatcher.CurrentDispatcher.BeginInvoke
(
DispatcherPriority.Background,
(SendOrPostCallback) delegate(object arg)
{
var f = arg as DispatcherFrame;
f.Continue = false;
},
frame
);
Dispatcher.PushFrame(frame);
}
O siempre puede hacer referencia a WinForms y llamar a Application.DoEvents.
Supongo que ya sabes esto, pero estás en un mal lugar en este momento. Si esto no funciona, ¡buena suerte! Si encuentras una manera mejor, actualiza la publicación, porque, bueno, los ataques del mal como este apestan, pero ahora puedes ver por qué la gente dice que revise la cola de mensajes con moderación.