Freigeben über


WPF-Renderthreadfehler

In diesem Dokument werden Fehler im WPF-Renderthread behandelt, wobei besonderes Augenmerk auf Fehler liegt, die eine Ausnahme in SyncFlush oder NotifyPartitionIsZombie verursachen oder dazu führen, dass Anwendungen in WaitForNextMessage oder SynchronizeChannelhängen bleiben.

Ursprüngliche Produktversion: .NET Framework 4.8

Fehler in SyncFlush, WaitForNextMessage, SynchronizeChannel und NotifyPartitionIsZombie

Entwickler haben häufig Probleme im Zusammenhang mit Renderthreadfehlern bei Windows Presentation Foundation (WPF)-Anwendungen. Benutzer können melden, dass ihre Anwendung Ausnahmen auslöst, z. B.:

  • System.Runtime.InteropServices.COMException: UCEERR_RENDERTHREADFAILURE (Ausnahme von HRESULT: 0x88980406)
  • System.InvalidOperationException: Im Renderthread ist ein nicht angegebener Fehler aufgetreten.
  • System.OutOfMemoryException: Nicht genügend Arbeitsspeicher, um die Ausführung des Programms fortzusetzen.

Die Aufrufliste der Ausnahme beginnt bei SyncFlush oder NotifyPartitionIsZombie. Zum Beispiel:

   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)  

Die Anwendung kann auch in WaitForNextMessage oder SynchronizeChannelhängen bleiben, mit einer Aufrufliste, z. B.:

   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

Dies sind Symptome eines Fehlers im Renderthread. Dies ist ein schwieriges Problem, das diagnostiziert werden muss, da die empfangenen Ausnahmen und Aufruflisten generisch sind. Renderthreadfehler generieren unabhängig von der Grundursache einen der oben gezeigten Aufruflisten (oder eine geringfügige Variation davon). Dies macht es besonders schwierig, das Problem zu diagnostizieren oder sogar zu erkennen, wenn zwei Abstürze oder Hängen aus derselben Grundursache stammen.

Beschreibung des WPF-Renderthreads und wie er sich vom UI-Thread unterscheidet

Jede WPF-Anwendung kann über einen oder mehrere Ui-Threads verfügen, die einen eigenen Nachrichtenpump (Dispatcher.Run) ausführen. Jeder UI-Thread ist für die Verarbeitung von Fenstermeldungen aus der Nachrichtenwarteschlange des Threads und die Verteilung an Fenster im Besitz dieses Threads verantwortlich. Jede WPF-Anwendung verfügt nur über einen Renderthread. Es handelt sich um einen separaten Thread, der mit DirectX/D3D kommuniziert (und/oder GDI, wenn die Softwarerenderingpipeline verwendet wird). Für WPF-Inhalte sendet jeder UI-Thread detaillierte Anweisungen zum Zeichnen an den Renderthread. Der Renderthread übernimmt dann diese Anweisungen und rendert den Inhalt.

Ursachen der oben genannten Fehler

Die oben genannten Ausnahmen und Hänger treten in einem UI-Thread auf, da der WPF-Renderthread auf einen schwerwiegenden Fehler stößt. Es gibt mehrere mögliche Ursachen für diese Fehler, aber der Renderthread gibt diese Informationen nicht für den UI-Thread weiter. Da diese Ausnahmen und Hänger nicht auf einen einzelnen Fehler oder Ein problem beruhen, gibt es keine spezifische Möglichkeit, sie zu beheben.

Der Renderthread von WPF überprüft den Rückgabewert auf Erfolg oder Fehler, wenn er eine andere Komponente wie DirectX/D3D, User32 oder GDI32 aufruft. Wenn ein Fehler erkannt wird, "zombiet" WPF die Renderpartition und benachrichtigt den UI-Thread über den Fehler, wenn die beiden Threads synchronisiert werden. Der Renderthread versucht, den empfangenen Fehler einer entsprechenden verwalteten Ausnahme zuzuordnen. Wenn der WPF-Renderthread beispielsweise aufgrund einer Nicht genügend Arbeitsspeicherbedingung fehlgeschlagen ist, wird der Fehler einem System.OutOfMemoryException zugeordnet, und dies ist die Ausnahme, die im UI-Thread angezeigt wird. Der Renderthread wird nur an einigen Stellen mit dem UI-Thread synchronisiert, sodass die obigen Aufruflisten in der Regel dort angezeigt werden, wo Sie das Problem bemerken, nicht an dem Ort, an dem es tatsächlich aufgetreten ist. Sie werden am häufigsten an Orten synchronisiert, an denen die Einstellungen eines Fensters aktualisiert werden (Größe, Position usw.) oder wo der UI-Thread eine Kanalmeldung aus dem Renderthread verarbeitet.

Standardmäßig sind die Ausnahmen und Aufruflisten im UI-Thread nicht hilfreich, um das Problem zu diagnostizieren. Dies liegt daran, dass der Renderthread zum Zeitpunkt des Auslösens der Ausnahme bereits den Fehlerpunkt überschritten hat. Der kritische Zustand des Renderthreads hilft uns zu verstehen, wo und warum der Fehler aufgetreten ist, aber er ist bereits verloren. Dies macht es praktisch unmöglich, dass jemand, der eine WPF-Anwendung schreibt, weiß, warum der Fehler aufgetreten ist oder wie er vermieden werden kann. Für Microsoft ist es nur ein wenig besser, dies in einer nachhergestellten Benutzerabbilddatei zu debuggen. Der Renderthread behält einen kreisförmigen Puffer der fehlerhaften Aufrufliste bei, den wir intern mithilfe einer proprietären Debuggererweiterung und privater Debugsymbole rekonstruieren können, um den ungefähren Anfangspunkt des Fehlers anzuzeigen. Zum Zeitpunkt des Fehlers haben wir jedoch keinen Zugriff auf den kritischen Zustand wie lokale Elemente, Stapelvariablen und Heapobjekte. Wir führen die Anwendung im Allgemeinen erneut aus, um nach Fehlern bei den Aufrufen zu suchen, von denen wir vermuten, dass sie beteiligt sind.

Häufige Fehlerursachen

Der häufigste Bucket von WPF-Renderthreadfehlern ist mit Videohardware- oder Treiberproblemen verbunden. Wenn WPF den Videotreiber über DirectX nach Funktionen abfragt, kann der Treiber seine Funktionen falsch berichtigen, was dazu führt, dass WPF einen Codepfad verwendet, der einige DirectX/D3D-Fehler verursacht. Manchmal berichtet der Treiber seine Funktionen nicht falsch, wurde aber nicht ordnungsgemäß implementiert. Die meisten Renderthreadfehler werden dadurch verursacht, dass WPF versucht, die Hardwarerenderingpipeline auf eine Weise zu nutzen, die einen Fehler im Treiber offenlegt. Dies kann bei modernen Versionen von Windows mit modernen Grafikgeräten und Treibern der Fall sein, obwohl es nicht so häufig ist wie in den frühen Tagen von WPF. Aus diesem Grund ist einer unserer ersten Vorschläge zum Testen und/oder Umgehen eines Fehlers des Renderthreads das Deaktivieren der Hardwarebeschleunigung in WPF.

Es ist auch möglich, dass ein Fehler dadurch verursacht wird, dass eine App aufgefordert wird, eine Szene zu rendern, die für den Treiber (oder DirectX) zu komplex ist. Dies ist bei modernen Treibern nicht üblich, aber jedes Gerät hat Grenzwerte, und es ist nicht unmöglich, diese zu überschreiten.

Eine weitere historische Ursache von Renderthreadfehlern ist die Verwendung der Eigenschaften Window.AllowsTransparency oder Popup.AllowsTransparency in WPF, wodurch mehrstufige Fenster verwendet werden. Ältere Versionen von Windows hatten Probleme mit mehrstufigen Fenstern, aber die meisten von ihnen wurden mit der Einführung des Desktopfenster-Managers (DWM) in Windows Vista behoben.

Wenn sich ein Renderthreadfehler als System.OutOfMemoryExceptionmanifestiert, war der Renderthread wahrscheinlich Opfer des Prozesses, der eine Ressource überlastet hat. Der Renderthread, der in eine Win32/DX API aufgerufen wurde, die versucht hat, eine Ressource zuzuweisen, aber fehlgeschlagen ist. WPF ordnet Rückgabewerte wie E_OUTOFMEMORY oder ERROR_NOT_ENOUGH_MEMORY einem zu System.OutOfMemoryException. Obwohl sich die Ausnahme auf "Arbeitsspeicher" bezieht, kann sich der Fehler auf jede Art von Ressource beziehen, z. B. GDI-Objekthandles, andere Systemhandles, GPU-Speicher, normaler RAM-Speicher usw.

Hinweise zu Ressourcenzuordnungsfehlern

Zwei Hinweise gelten für System.OutOfMemoryException Fehler und für fehler bei der Ressourcenzuordnung.

  • Die Grundursache liegt möglicherweise nicht im Code, bei dem der Fehler auftritt. Stattdessen gibt es möglicherweise anderen Code im Prozess, der die Ressource überlastet, sodass für eine normalerweise erfolgreiche Anforderung keiner mehr übrig bleibt.

  • Wenn die Anforderung ungewöhnlich groß ist, kann der Fehler auftreten, obwohl eine Ressource reichlich vorhanden ist. Ein System.OutOfMemoryException kann auch dann auftreten, wenn das System über ausreichend Arbeitsspeicher verfügt, wenn eine große Menge an (zusammenhängendem) Arbeitsspeicher erforderlich ist. Hier ist ein beispiel aus der Praxis: Ein Visual Studio-Plug-In bereitete die Wiederherstellung seines Fensters aus einem Zustand vor, der in einer vorherigen Sitzung gespeichert wurde. Es wurde falsch für den Dpi-Unterschied zwischen dem vorherigen und dem aktuellen Monitor angepasst, der mit Anpassungen aus mehreren Ebenen von WPF-, WindowsForms- und VS-Fensterhostingkomponenten zusammengesetzt wurde, um die Größe des Fensters 16-mal größer festzulegen, als es hätte sein sollen. Der Renderthread hat versucht, einen 256-mal größeren Backpuffer als erforderlich zuzuordnen, und ist fehlgeschlagen, obwohl mehr als genügend Arbeitsspeicher für die erwartete Zuordnung verfügbar war.

Allgemeine Empfehlungen

  1. Deaktivieren Sie das Hardwarerendering mithilfe des Registrierungswerts DisableHWAcceleration , der unter Option "Hardwarebeschleunigung deaktivieren" erläutert wird. Dies wirkt sich auf alle WPF-Anwendungen auf Ihrem Computer aus. Führen Sie dies nur aus, um zu testen, ob Ihr Problem mit Grafikhardware oder Treibern zusammenhängt. Wenn dies der Fall ist, können Sie das Problem umgehen, indem Sie die Hardwarebeschleunigung auf einer präziseren Ebene programmgesteuert deaktivieren. Dies kann auf Fensterbasis mithilfe der HwndTarget.RenderMode-Eigenschaft oder prozessbezogen mithilfe der RenderOptions.ProcessRenderMode-Eigenschaft erfolgen.

  2. Aktualisieren Sie Ihre Videotreiber, und/oder probieren Sie andere Videohardware auf den Problemcomputern aus.

  3. Führen Sie ein Upgrade auf die neueste Version und Service Pack-Ebene von .NET durch, die für Ihre Zielplattform verfügbar ist.

  4. Führen Sie ein Upgrade auf das neueste Betriebssystem durch.

  5. Deaktivieren Sie die Verwendung von Windows.AllowsTransparency und Popup.AllowsTransparency in Ihrer Anwendung.

  6. Wenn System.OutOfMemoryExceptions gemeldet wird, überwachen Sie die Speicherauslastung des Prozesses in Leistungsmonitor, insbesondere die Indikatoren Process\Virtual Bytes, Process\Private Bytes und .NET CLR Memory\# Bytes in All Heaps. Überwachen Sie die Benutzerobjekte und GDI-Objekte für den Prozess auch im Windows-Task-Manager. Wenn Sie feststellen, dass eine bestimmte Ressource erschöpft ist, führen Sie eine Problembehandlung für die Anwendung durch, um den übermäßigen Ressourcenverbrauch zu beheben. Beachten Sie die beiden obigen Hinweise zu Problemen bei der Ressourcenzuordnung.

  7. Wenn Sie ein reproduzierbares Szenario haben, das plattformübergreifend oder in verschiedenen Kombinationen aus Videohardware und -treiber auftritt, liegt möglicherweise ein WPF-Fehler vor. Stellen Sie sicher, dass Sie genügend Informationen sammeln, um eine Untersuchung zu ermöglichen, bevor Sie das Problem an Microsoft melden. Eine Aufrufliste reicht nicht aus. Microsoft benötigt ausführlichere Informationen, z. B.:

    • Eine vollständige VS-Lösung mit Schritten zum Reproduzieren des Problems, einschließlich einer Beschreibung der Umgebung – Betriebssystem, .NET und Grafiken.
    • Eine Zeitreisedebugging-Ablaufverfolgung des Problems.
    • Ein vollständiges Absturzabbild.