Solucionar fallos del subproceso de representación de WPF

En este artículo se describen los errores en el subproceso de representación de Windows Presentation Foundation (WPF). Este artículo se centra en las excepciones que se producen en SyncFlush o NotifyPartitionIsZombie y en las situaciones de bloqueo que ocurren en WaitForNextMessage o SynchronizeChannel.

Las aplicaciones wpF pueden tener uno o varios subprocesos de interfaz de usuario que ejecutan su propia bomba de mensajes (Dispatcher.Run). Cada subproceso de interfaz de usuario es responsable de procesar mensajes de ventana desde la cola de mensajes del subproceso y enviarlos a ventanas que posee el subproceso. Cada aplicación WPF tiene solo un subproceso de representación. Ese subproceso independiente se comunica con Microsoft DirectX D3D (o GDI, si se usa la canalización de representación de software). Para el contenido de WPF, cada subproceso de interfaz de usuario envía instrucciones detalladas al subproceso de representación sobre qué dibujar. A continuación, el subproceso de representación sigue esas instrucciones para representar el contenido.

Se aplica a: .NET Framework 4.8

Errores en SyncFlush, WaitForNextMessage, SynchronizeChannel y NotifyPartitionIsZombie

Los desarrolladores suelen encontrar problemas relacionados con errores de subprocesos de representación que se producen en aplicaciones WPF. Los usuarios pueden informar de que su aplicación produce una excepción, como:

  • System.Runtime.InteropServices.COMException: UCEERR_RENDERTHREADFAILURE (Excepción de HRESULT: 0x88980406)
  • System.InvalidOperationException: se produjo un error no especificado en el subproceso de representación.
  • System.OutOfMemoryException: memoria insuficiente para continuar con la ejecución del programa.

La pila de llamadas relacionada se inicia en SyncFlush o NotifyPartitionIsZombie. Por ejemplo:

   at System.Windows.Media.Composition.DUCE.Channel.SyncFlush()  
   at System.Windows.Interop.HwndTarget.UpdateWindowSettings(Boolean enableRenderTarget, Nullable\`1 channelSet)  
   at System.Windows.Interop.HwndTarget.UpdateWindowSettings(Boolean enableRenderTarget)  
   at System.Windows.Interop.HwndTarget.UpdateWindowPos(IntPtr lParam)  
   at System.Windows.Interop.HwndTarget.HandleMessage(WindowMessage msg, IntPtr wparam, IntPtr lparam)  
   at System.Windows.Media.MediaContext.NotifyPartitionIsZombie(Int32 failureCode)  
   at System.Windows.Media.MediaContext.NotifyChannelMessage()  
   at System.Windows.Interop.HwndTarget.HandleMessage(Int32 msg, IntPtr wparam, IntPtr lparam)  

La aplicación podría dejar de responder en WaitForNextMessage o SynchronizeChannel y generar una pila de llamadas, como:

   ntdll.dll!NtWaitForMultipleObjects
   kernelbase.dll!WaitForMultipleObjectsEx
   kernelbase.dll!WaitForMultipleObjects
   wpfgfx_v0400.dll!CMilChannel::WaitForNextMessage
   wpfgfx_v0400.dll!MilComposition_WaitForNextMessage
   presentationcore.dll!System.Windows.Media.MediaContext.CompleteRender
   kernelbase.dll!WaitForSingleObject
   wpfgfx_v0400.dll!CMilConnection::SynchronizeChannel
   wpfgfx_v0400.dll!CMilChannel::SyncFlush
   presentationcore.dll!System.Windows.Media.Composition.DUCE+Channel.SyncFlush
   presentationcore.dll!System.Windows.Media.MediaContext.CompleteRender
   presentationcore.dll!System.Windows.Interop.HwndTarget.OnResize
   presentationcore.dll!System.Windows.Interop.HwndTarget.HandleMessage

Estas pilas de llamadas son síntomas de un error en el subproceso de representación. Se trata de un problema complicado para diagnosticar porque las excepciones y las pilas de llamadas son genéricas. Los errores de subproceso de representación generan una de las pilas de llamadas que se enumeran aquí (o una variación secundaria de las pilas de llamadas) independientemente de la causa principal. Por lo tanto, puede ser difícil diagnosticar el problema o reconocer una situación en la que los incidentes independientes de no respuesta tienen la misma causa principal.

Causas de los errores en SyncFlush, WaitForNextMessage, SynchronizeChannel y NotifyPartitionIsZombie

Excepciones y situaciones en las que el software deja de responder en un subproceso de interfaz de usuario si el subproceso de representación de WPF experimenta un error irrecuperable. Estos errores tienen varias causas posibles, pero el subproceso de representación no comparte esa información junto con el subproceso de interfaz de usuario. Dado que estos errores no están relacionados con un único error raíz o problema, no tienen ninguna solución específica.

El subproceso de representación de WPF comprueba si el valor devuelto es correcto o no cuando realiza una llamada a otro componente, como DirectX D3D, User32 o GDI32. Cuando se detecta un fallo, WPF "zombifica" la partición de renderizado y notifica al subproceso de la interfaz de usuario sobre el fallo cuando se sincronizan los dos subprocesos. El subproceso de representación intenta asignar el error que recibe a una excepción administrada adecuada. Por ejemplo, si se produjo un error en el subproceso de representación de WPF debido a una condición de memoria insuficiente, asigna el error a .System.OutOfMemoryException Esa excepción se muestra en el subproceso de la interfaz de usuario. El subproceso de representación se sincroniza con el subproceso de interfaz de usuario en solo algunas ubicaciones. Por lo tanto, las pilas de llamadas que se mencionan en la sección anterior suelen aparecer cuando observa síntomas del problema, no donde se produce realmente el problema. La sincronización se produce normalmente en ubicaciones donde la configuración de una ventana se actualiza (tamaño, posición, etc.) o donde el subproceso de la interfaz de usuario controla un mensaje de "canal" del subproceso de representación.

Por diseño, las excepciones y las pilas de llamadas en el subproceso de la interfaz de usuario no son recursos útiles para ayudarle a diagnosticar el problema. Cuando se produce la excepción, el hilo de renderizado ya ha pasado el punto de error. El estado crítico del subproceso de representación le podría ayudar a comprender dónde y por qué se produjo el error, pero ya se ha perdido. Debido a esta situación, el autor de una aplicación WPF no puede saber por qué se produjo el error o cómo evitarlo. En lugar de analizar excepciones y pilas de llamadas, depuramos el problema en un archivo de volcado de usuario posterior. Aunque este método solo es ligeramente más útil, el subproceso de representación mantiene un búfer circular de la pila de llamadas con errores. Podemos reconstruir el búfer internamente mediante una extensión del depurador propietario y símbolos de depuración privados para mostrar el punto inicial aproximado de error. Sin embargo, no tenemos acceso al estado crítico, como variables locales, variables de pila y objetos de montón en el momento del error. Por lo general, se vuelve a ejecutar la aplicación para buscar errores en las llamadas que sospechamos que están implicadas.

Errores con hardware o controladores de vídeo

El grupo más común de fallos de subprocesos de representación de WPF está relacionado con problemas de hardware o de controladores de vídeo. Cuando WPF consulta el controlador de vídeo para obtener funcionalidades a través de DirectX, el controlador podría informar erróneamente de sus funcionalidades. Esta acción hace que WPF tome un camino de ejecución del código provocando algunos errores de DirectX D3D. Es posible que el controlador también se implemente incorrectamente. La mayoría de los fallos del hilo de renderizado se producen porque WPF intenta usar la canalización de renderizado de hardware de una manera que expone alguna falla en el controlador. Esta condición puede producirse en versiones modernas de Windows que usan controladores y dispositivos gráficos modernos, aunque lo habitual es que se produzca en los primeros días de WPF. Por este motivo, al empezar a probar o solucionar un error de subproceso de representación, se recomienda deshabilitar primero la aceleración de hardware en WPF.

También puede producirse un fallo si una app solicita una escena demasiado compleja para que el controlador (o DirectX) lo renderice. Esta situación no es común para los conductores modernos. Sin embargo, cada dispositivo tiene límites que se pueden superar.

Otro origen histórico de errores de subproceso de representación son las propiedades Window.AllowsTransparency o Popup.AllowsTransparency en WPF. Estas propiedades hacen que se usen ventanas superpuestas . Las ventanas superpuestas provocaron problemas en versiones anteriores de Windows. Sin embargo, la mayoría de estos problemas fueron resueltos por la introducción del Administrador de ventanas de escritorio (DWM) en Windows Vista.

Si un error de subproceso de representación se manifiesta como System.OutOfMemoryException, ese error suele indicar que el proceso agotó algún recurso. En esta situación, el hilo de renderizado llamó a una API Win32/DX que intentó sin éxito asignar algún recurso. WPF asigna valores devueltos como E_OUTOFMEMORY o ERROR_NOT_ENOUGH_MEMORY a un System.OutOfMemoryException. Aunque la entrada de excepción hace referencia a "memoria", esta mención podría referirse a cualquier tipo de recurso, como identificadores de objetos GDI, otros identificadores del sistema, memoria gpu, memoria RAM estándar, etc.

Comentarios sobre errores de asignación de recursos

** Los siguientes comentarios se aplican a fallos de System.OutOfMemoryException y a cualquier fallo en la asignación de recursos.

  • Es posible que la causa principal no esté relacionada con el código que experimenta el error. Otro código en el proceso puede consumir demasiado el recurso y no dejar nada para el código que, de otro modo, se ejecutaría con éxito.

  • Si la solicitud es inusualmente grande, el error podría producirse a pesar de un recurso que parece ser abundante. Una solicitud de una gran cantidad de memoria (contigua) podría causar un System.OutOfMemoryException aunque el sistema tenga memoria disponible. Este es un ejemplo real: un complemento de Visual Studio se prepara para restaurar su ventana a partir de un estado que se guardó en una sesión anterior. El complemento se ajusta incorrectamente a la diferencia de DPI entre el monitor anterior y el actual. Dado que este error se compone de ajustes de varias capas de componentes de WPF, WindowsForms y de hospedaje de ventanas de VS, el complemento establece su tamaño de ventana en 16 veces mayor que el tamaño correcto. A continuación, el subproceso de representación intenta asignar un búfer de reserva que sea 256 veces mayor que el necesario. Por lo tanto, se produce un error en el proceso aunque haya suficiente memoria disponible para la asignación esperada.

Recomendaciones generales

  1. Desactivar la representación de hardware. Use el valor del Registro DisableHWAcceleration que se describe en Opción para deshabilitar la aceleración de hardware. Esta acción afecta a todas las aplicaciones WPF en su ordenador. Realice este paso solo para probar si el problema está relacionado con el hardware gráfico o los controladores. Si es así, puede solucionar el problema deshabilitando mediante programación la aceleración de hardware en un nivel más granular. Este paso se puede realizar por ventana mediante la propiedad HwndTarget.RenderMode o por proceso mediante la propiedad RenderOptions.ProcessRenderMode .

  2. Actualice los controladores de vídeo o pruebe otro hardware de vídeo en los equipos con problemas.

  3. Actualice a la versión más reciente y el nivel de Service Pack de Microsoft .NET Framework que está disponible para la plataforma de destino.

  4. Actualice al sistema operativo más reciente.

  5. Deshabilite la capacidad de usar Windows.AllowsTransparency y Popup.AllowsTransparency en la aplicación.

  6. Si se notifican System.OutOfMemoryExceptions, supervise el uso de memoria del proceso en el Monitor de recursos. Específicamente, supervise los contadores Process\Virtual Bytes, Process\Private Bytes y .NET CLR Memory\# Bytes en todos los heaps. Supervise también los objetos de usuario y los objetos GDI para el proceso en el Administrador de tareas de Windows. Si determina que se está agotando un recurso específico, solucione los problemas de la aplicación para corregir el consumo excesivo de recursos. Como guía, siga las dos observaciones de la sección anterior sobre los problemas de asignación de recursos.

  7. Si tiene un escenario reproducible que se produce entre plataformas o en diferentes combinaciones de hardware o controlador de vídeo, es posible que tenga un error de WPF. Asegúrese de recopilar información suficiente para habilitar una investigación antes de notificar el problema a Microsoft. Una pila de llamadas por sí sola no es suficiente. Necesitamos información más detallada, como:

    • Una solución de VS completa que incluye pasos para reproducir el problema, incluida una descripción del entorno (SO, .NET y gráficos).
    • Un Time-Travel seguimiento de depuración del problema.
    • Un archivo de volcado de memoria completo.