Errores de subprocesos de representación de WPF

En este documento se analizan los errores en el subproceso de representación de WPF, con especial atención a aquellos que causan una excepción en o que hacen que las aplicaciones se SyncFlush NotifyPartitionIsZombie cuelgan WaitForNextMessage o SynchronizeChannel .

Versión del producto original:   .NET Framework 4.8

Errores en SyncFlush, WaitForNextMessage, SynchronizeChannel y NotifyPartitionIsZombie

A menudo, los programadores se enfrentan a problemas relacionados con los errores de subprocesos Windows Presentation Foundation (WPF). Los usuarios pueden informar de que su aplicación genera excepciones 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 de la excepción comienza 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 también puede estar o WaitForNextMessage , con una pila de llamadas SynchronizeChannel 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

Estos son síntomas de un error en el subproceso de representación. Este es un problema difícil de diagnosticar porque las excepciones y las pilas de llamadas recibidas son genéricas. Los errores de subprocesos de representación generarán una de las pilas de llamadas mostradas anteriormente (o una variación menor de las mismas) independientemente de la causa raíz. Esto hace que diagnosticar el problema o incluso reconocer cuando dos bloqueos o bloqueos se derivan de la misma causa raíz, especialmente difícil.

Descripción del subproceso de representación wpf y cómo difiere del subproceso de interfaz de usuario

Cada aplicación WPF puede tener uno o más subprocesos de interfaz de usuario que ejecuten su propia bomba de mensajes ( Dispatcher.Run ). Cada subproceso de interfaz de usuario es responsable de procesar los mensajes de ventana de la cola de mensajes del subproceso y distribuirlos a las ventanas que pertenecen a ese subproceso. Cada aplicación WPF tiene solo un subproceso de representación. Es un subproceso independiente que se comunica con DirectX/D3D (o GDI si se usa la canalización de representación de software). Para el contenido wpf, cada subproceso de interfaz de usuario envía instrucciones detalladas al subproceso de representación sobre lo que se va a dibujar. A continuación, el subproceso de representación toma esas instrucciones y representa el contenido.

Causas de los errores mencionados anteriormente

Las excepciones y las cuelgues mencionadas anteriormente se producen en un subproceso de interfaz de usuario como consecuencia de que el subproceso de representación WPF encuentra un error grave. Existen varias causas posibles para estos errores, pero el subproceso de representación no comparte esa información con el subproceso de interfaz de usuario. Dado que estas excepciones y cuelgues no se derivan de un solo error o problema raíz, no hay ninguna forma específica de corregirlas.

El subproceso de representación de WPF comprueba si el valor devuelto es correcto o error cuando realiza una llamada a otro componente como DirectX/D3D, User32 o GDI32. Cuando se detecta un error, WPF "zombies" la partición de representación y notifica al subproceso de interfaz de usuario del error cuando se sincronizan los dos subprocesos. El subproceso de representación intentará asignar el error que recibe a una excepción administrada adecuada. Por ejemplo, si se produjo un error en el subproceso de representación wpf debido a una condición de falta de memoria, se asignará el error a a y esa será la excepción que se muestra en el subproceso de interfaz System.OutOfMemoryException de usuario. El subproceso de representación solo se sincroniza con el subproceso de interfaz de usuario en unas cuantas ubicaciones, por lo que las pilas de llamadas anteriores suelen aparecer donde se observa el problema, no donde se produjo realmente. Normalmente se sincronizan en ubicaciones donde se actualiza la configuración de una ventana (tamaño, posición, etc.) o donde el subproceso de 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 interfaz de usuario no son útiles para diagnosticar el problema. Esto se debe a que cuando se produce la excepción, el subproceso de representación ya ha pasado el punto de error. El estado crítico del subproceso de representación nos ayudará a comprender dónde y por qué se produjo el error, pero ya se perdió. Esto hace prácticamente imposible que alguien que escriba una aplicación WPF sepa por qué se produjo el error o cómo evitarlo. Para Microsoft, solo es ligeramente mejor depurar esto en un archivo de volcado de usuario postmortem. El subproceso de representación mantiene un búfer circular de la pila de llamadas con errores, que podemos reconstruir internamente mediante una extensión de depurador propietaria 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 locales, variables de pila y objetos de montón en el momento del error. Por lo general, ejecutamos la aplicación de nuevo para buscar errores en las llamadas que sospechemos que están implicadas.

Causas comunes de los errores

El cubo más común de errores de subprocesos de representación de WPF está asociado con problemas de hardware de vídeo o controladores. Cuando WPF consulta al controlador de vídeo para obtener funcionalidades a través de DirectX, el controlador puede desinformar sus capacidades, lo que hace que WPF tome una ruta de acceso de código que termine provocando algunos errores de DirectX/D3D. En ocasiones, el controlador no informa erróneamente de sus capacidades, pero no se implementó correctamente. La mayoría de los errores del subproceso de representación se debe a que WPF intenta usar la canalización de representación de hardware de una manera que expone algún defecto en el controlador. Esto puede suceder en las versiones modernas de Windows con controladores y dispositivos gráficos modernos, aunque no es tan común como en los primeros días de WPF. Por este motivo, una de nuestras primeras sugerencias para probar y/o evitar un error de subproceso de representación es deshabilitar la aceleración de hardware en WPF.

También es posible que un error se deba a que una aplicación pida representar una escena demasiado compleja para que el controlador (o DirectX) controle. Esto no es común con los controladores modernos, pero cada dispositivo tiene límites y no es imposible superarlos.

Otro origen histórico de errores de subprocesos de representación es el uso de las propiedades Window.AllowsTransparency o Popup.AllowsTransparency en WPF, lo que hará que se usen ventanas en capas. Las versiones anteriores de Windows tenían problemas con las ventanas en capas, pero la mayoría de ellas se han abordado con la introducción del Administrador de ventanas de escritorio (DWM) en Windows Vista.

Si un error de subproceso de representación se manifesta como un , el subproceso de representación probablemente fue una víctima del proceso que System.OutOfMemoryException agote algún recurso. El subproceso de representación llamó a una Win32/DX API que intentó asignar algún recurso, pero falló. Los mapas WPF devuelven valores E_OUTOFMEMORY como o ERROR_NOT_ENOUGH_MEMORY a System.OutOfMemoryException . Aunque la excepción hace referencia a "memoria", el error podría hacer referencia a cualquier tipo de recurso, como controladores de objetos GDI, otros controladores del sistema, memoria GPU, memoria RAM normal, etc.

Comentarios sobre errores de asignación de recursos

Dos observaciones se aplican System.OutOfMemoryException a errores y a cualquier error de asignación de recursos.

  • Es posible que la causa raíz no se encuentre en el código que encuentra el error. En su lugar, puede haber otro código en el proceso que consume en exceso el recurso, lo que no deja ninguna para una solicitud normalmente correcta.

  • Si la solicitud es inusualmente grande, el error podría producirse a pesar de un recurso que parece abundante. Puede producirse incluso cuando el sistema tiene mucha memoria, si hay una solicitud de una gran cantidad de System.OutOfMemoryException memoria (contigua). Este es un ejemplo real: un complemento de Visual Studio se preparaba para restaurar su ventana desde un estado guardado en una sesión anterior. Se ajustó incorrectamente por la diferencia de PPP entre los monitores anteriores y actuales, que se comieron con ajustes de varias capas de wpf, WindowsForms y vs componentes de hospedaje de ventanas para establecer el tamaño de su ventana 16 veces más grande de lo que debería haber sido. El subproceso de representación intentó asignar un búfer de reserva 256 veces mayor que el necesario y falló aunque había memoria más que suficiente disponible para la asignación esperada.

Recomendaciones generales

  1. Deshabilitar la representación de hardware mediante el valor del Registro DisableHWAcceleration que se describe en Disable Hardware Acceleration Option. Esto afectará a todas las aplicaciones WPF del equipo; haga esto solo como una forma de probar si el problema está relacionado con controladores o hardware de gráficos. Si ese es el caso, puede solucionar el problema deshabilitando mediante programación la aceleración de hardware a un nivel más granular. Esto se puede hacer por ventana mediante la propiedad HwndTarget.RenderMode o por proceso mediante la propiedad RenderOptions.ProcessRenderMode.

  2. Actualiza los controladores de vídeo o prueba hardware de vídeo diferente en los equipos con problemas.

  3. Actualice a la versión más reciente y al nivel de Service Pack de .NET disponible para la plataforma de destino.

  4. Actualizar al sistema operativo más reciente.

  5. Deshabilite el Windows.AllowsTransparency uso de y en la Popup.AllowsTransparency aplicación.

  6. Si se informa, supervise el uso de memoria del proceso en el Monitor de rendimiento; en particular, los bytes virtuales de proceso, bytes privados de proceso y bytes de memoria CLR de .NET en todos los contadores de System.OutOfMemoryExceptions \ \ \ # montón. Supervise los objetos de usuario y los objetos GDI para el proceso en Windows de tareas. Si determina que se está agotando un recurso específico, solucione los problemas de la aplicación para corregir el consumo excesivo de recursos. Tenga en cuenta los dos comentarios anteriores acerca de los problemas de asignación de recursos.

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

    • Una solución completa de VS con pasos para reproducir el problema, incluida la descripción del entorno: sistema operativo, .NET y gráficos.
    • Seguimiento de depuración de viajes en tiempo del problema.
    • Un volcado de memoria completo.