WPF-Renderthreadfehler
In diesem Dokument werden Fehler im Renderthread von WPF erläutert, insbesondere diejenigen, die eine Ausnahme verursachen SyncFlush NotifyPartitionIsZombie oder dazu führen, dass Anwendungen hängen bleiben WaitForNextMessage oder SynchronizeChannel .
Ursprüngliche Produktversion: .NET Framework 4.8
Fehler in SyncFlush, WaitForNextMessage, SynchronizeChannel und NotifyPartitionIsZombie
Entwickler haben häufig Probleme im Zusammenhang mit dem Rendern von Threadfehlern mit 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.
Der Aufrufstapel 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 mit einem Aufrufstapel hängen SynchronizeChannel bleiben, 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 die Symptome eines Fehlers im Renderthread. Dies ist ein schwieriges Problem zu diagnostizieren, da die empfangenen Ausnahmen und Aufrufstapel generisch sind. Renderthreadfehler generieren einen der oben gezeigten Aufrufstapel (oder eine kleinere Abweichung), unabhängig von der Ursache. Dies macht die Diagnose des Problems oder sogar die Erkennung, wenn zwei Abstürze oder Blockaden von derselben Ursache stammen, besonders schwierig.
Beschreibung des WPF-Renderthreads und seine Abweichungen vom UI-Thread
Jede WPF-Anwendung kann über einen oder mehrere UI-Threads verfügen, die ihre eigene Nachrichtenmeldung ( Dispatcher.Run ) ausführen. Jeder UI-Thread ist für die Verarbeitung von Fenstermeldungen aus der Nachrichtenwarteschlange des Threads und deren Verteilung an Fenster zuständig, die im Besitz dieses Threads sind. Jede WPF-Anwendung verfügt nur über einen Renderthread. Es handelt sich um einen separaten Thread, der mit DirectX/D3D (und/oder GDI kommuniziert, wenn die Softwarerenderingpipeline verwendet wird). Für WPF-Inhalte sendet jeder UI-Thread detaillierte Anweisungen an den Renderthread, was gezeichnet werden soll. Der Renderthread übernimmt dann diese Anweisungen und rendert den Inhalt.
Ursachen der oben genannten Fehler
Die oben erwähnten Ausnahmen und Blockaden treten in einem UI-Thread als Folge des WPF-Renderthreads auf, der einen schwerwiegenden Fehler aufweist. Es gibt mehrere mögliche Ursachen für diese Fehler, aber der Renderthread gibt diese Informationen nicht für den UI-Thread frei. Da diese Ausnahmen und Blockaden nicht auf einen einzelnen Stammfehler oder ein Problem zurückzuführen sind, gibt es keine spezifische Möglichkeit, sie zu beheben.
Der Renderthread von WPF überprüft den Rückgabewert auf Erfolg oder Fehler, wenn ein Aufruf an eine andere Komponente wie DirectX/D3D, User32 oder GDI32 ausgeführt wird. Wenn ein Fehler erkannt wird, "löscht" 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 z. B. der WPF-Renderthread aufgrund einer Nichtspeicherbedingung fehlgeschlagen ist, wird der Fehler einem Fehler zugeordnet, System.OutOfMemoryException und dies ist die Ausnahme, die im UI-Thread angezeigt wird. Der Renderthread wird nur an wenigen Stellen mit dem UI-Thread synchronisiert, sodass die oben genannten Aufrufstapel in der Regel dort angezeigt werden, wo das Problem auftritt, nicht an der Stelle, an der er 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 "Kanal"-Nachricht aus dem Renderthread verarbeitet.
Die Ausnahmen und Aufrufstapel im UI-Thread sind entwurfsbedingt 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 würde uns helfen 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 etwas besser, dies in einer postirtem Benutzerabbilddatei zu debuggen. Der Renderthread behält einen Kreispuffer des fehlerhaften Aufrufstapels bei, den wir mithilfe einer proprietären Debuggererweiterung und privater Debugsymbole intern rekonstruieren können, um den ungefähren Anfangspunkt des Fehlers anzuzeigen. Zum Zeitpunkt des Fehlers haben wir jedoch keinen Zugriff auf den kritischen Zustand, z. B. lokal, Stapelvariablen und Heap-Objekte. In der Regel führen wir die Anwendung erneut aus, um nach Fehlern bei den von uns angenommenen Anrufen zu suchen.
Häufige Ursachen der Fehler
Der gängigste Bucket von WPF-Renderthreadfehlern ist mit Videohardware- oder Treiberproblemen verknüpft. Wenn WPF den Videotreiber nach Funktionen über DirectX fragt, kann der Treiber seine Funktionen falsch angeben, was dazu führt, dass WPF einen Codepfad einnimmt, der zu einigen DirectX/D3D-Fehlern führt. Manchmal gibt der Treiber seine Funktionen nicht falsch an, wurde jedoch nicht ordnungsgemäß implementiert. Der Großteil der Renderthreadfehler wird durch WPF-Versuche verursacht, die Hardwarerenderingpipeline so zu nutzen, dass einige Fehler im Treiber verfügbar gemacht werden. Dies kann bei modernen Versionen von Windows mit modernen Grafikgeräten und Treibern passieren, obwohl dies nicht so üblich ist wie in den frühen Tagen von WPF. Aus diesem Grund besteht einer unserer ersten Vorschläge zum Testen und/oder Umgehen eines Renderthreadfehlers darin, die Hardwarebeschleunigung in WPF zu deaktivieren.
Es ist auch möglich, dass ein Fehler durch eine App verursacht wird, die zum Rendern einer Szene auffordert, die für den Treiber (oder DirectX) zu komplex ist. Dies ist bei modernen Treibern nicht üblich, aber jedes Gerät hat Einschränkungen, und es ist nicht unmöglich, sie zu überschreiten.
Eine weitere historische Quelle von Renderthreadfehlern ist die Verwendung der Eigenschaften "Window.AllowsTransparency" oder "Popup.AllowsTransparency" in WPF, wodurch übereinander angeordnete Fenster verwendet werden. Ältere Versionen von Windows hatten Probleme mit mehrstufigen Fenstern, die meisten wurden jedoch mit der Einführung des Desktopfenster-Managers (DESKTOP Window Manager, OLEM) in Windows Vista behoben.
Wenn ein Renderthreadfehler manifestiert wird, System.OutOfMemoryException war der Renderthread wahrscheinlich ein Opfer des Prozesses, der einige Ressourcen aufgebraucht hat. Der Renderthread, der in eine API aufgerufen Win32/DX wird, die versucht hat, eine Ressource zuzuordnen, aber fehlgeschlagen ist. WPF maps return values like E_OUTOFMEMORY or ERROR_NOT_ENOUGH_MEMORY to a System.OutOfMemoryException . Obwohl sich die Ausnahme auf "Speicher" 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 Ressourcenzuweisungsfehlern
Zwei Hinweise gelten für System.OutOfMemoryException Fehler und alle Ressourcenzuweisungsfehler.
Die Ursache liegt möglicherweise nicht im Code, der auf den Fehler trifft. Stattdessen kann es anderen Code in dem Prozess geben, der die Ressource überlastet und keinen für eine normalerweise erfolgreiche Anforderung übrig lässt.
Wenn die Anforderung ungewöhnlich groß ist, kann der Fehler trotz einer ressource auftreten, die zahlreich erscheint. A
System.OutOfMemoryExceptioncould occur even when the system has large of memory, if there's a request for a large amount of (contiguous) memory. Hier ist ein beispiel aus der Praxis: Ein Visual Studio Plug-In bereitete sich darauf vor, das Fenster aus einem Zustand wiederherzustellen, der in einer vorherigen Sitzung gespeichert wurde. Es wurde falsch angepasst, um den Unterschied zwischen dem DPI-Wert zwischen dem vorherigen und dem aktuellen Monitor zu berücksichtigen, der sich durch Anpassungen aus mehreren Ebenen von WPF-, WindowsForms- und VS-Fensterhostingkomponenten zusammensetzen lässt, um die Größe des Fensters um das 16-fache zu ändern, als es hätte sein sollen. Der Renderthread hat versucht, einen 256-mal größeren Hintergrundpuffer zuzuweisen als erforderlich, und es ist ein Fehler aufgetreten, obwohl mehr als genügend Arbeitsspeicher für die erwartete Zuordnung verfügbar war.
Allgemeine Empfehlungen
Deaktivieren Sie das Hardwarerendering mithilfe des Registrierungswerts DisableHWAcceleration, der in der Option "Hardwarebeschleunigung deaktivieren"erläutert wird. Dies wirkt sich auf alle WPF-Anwendungen auf Ihrem Computer aus. verwenden Sie dies nur, 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 programmgesteuert auf einer präziseren Ebene deaktivieren. Dies kann pro Fenster mithilfe der HwndTarget.RenderMode-Eigenschaft oder pro Prozess mithilfe der RenderOptions.ProcessRenderMode-Eigenschaft erfolgen.
Aktualisieren Sie Ihre Videotreiber, und/oder testen Sie verschiedene Videohardware auf den problematischen Computern.
Upgrade auf die neueste Version und Service Pack-Ebene von .NET, die für Ihre Zielplattform verfügbar sind.
Upgrade auf das neueste Betriebssystem.
Deaktivieren Sie die Verwendung von
Windows.AllowsTransparencyundPopup.AllowsTransparencyin Ihrer Anwendung.Wenn
System.OutOfMemoryExceptionsgemeldet wird, überwachen Sie die Speicherauslastung des Prozesses im Leistungsmonitor, insbesondere die \ Prozess-Virtual Bytes, Process \ Private Bytes und .NET CLR Memory \ # Bytes in All Heaps counters. Überwachen Sie die Benutzerobjekte und GDI-Objekte für den Prozess auch im Windows Task-Manager. Wenn Sie feststellen, dass eine bestimmte Ressource aufgebraucht ist, behandeln Sie eine Problembehandlung für die Anwendung, um den übermäßigen Ressourcenverbrauch zu beheben. Beachten Sie die beiden obigen Anmerkungen zu Problemen bei der Ressourcenzuweisung.Wenn Sie über ein reproduzierens Szenario verfügen, das plattformübergreifend oder mit unterschiedlichen Kombinationen aus Videohardware und -treibern 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. Ein Aufrufstapel 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 der Beschreibung der Umgebung – Betriebssystem, .NET und Grafiken.
- Eine Ablaufverfolgung des Problems im Zeit-/Reisedebugging.
- Ein vollständiges Absturzabbild.