共用方式為


WPF 轉譯線程失敗

本文件討論 WPF 轉譯線程中的失敗,特別注意那些在 或 NotifyPartitionIsZombie 中造成應用程式停止WaitForNextMessage響應的SynchronizeChannel失敗SyncFlush

原始產品版本: .NET Framework 4.8

SyncFlush、WaitForNextMessage、SynchronizeChannel 和 NotifyPartitionIsZombie 失敗

開發人員通常會面臨與 Windows Presentation Foundation (WPF) 應用程式呈現線程失敗相關的問題。 使用者可能會回報其應用程式擲回例外狀況,例如:

  • System.Runtime.InteropServices.COMException: UCEERR_RENDERTHREADFAILURE (HRESULT 的例外狀況:0x88980406)
  • System.InvalidOperationException:轉譯線程上發生未指定的錯誤。
  • System.OutOfMemoryException:記憶體不足,無法繼續執行程式。

例外狀況的呼叫堆疊會從 或 NotifyPartitionIsZombie開始SyncFlush。 例如:

   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)  

應用程式也可能在 或SynchronizeChannelWaitForNextMessage停止回應,呼叫堆疊,例如:

   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

這些是轉譯線程失敗的徵兆。 這是診斷的一個具有挑戰性的問題,因為收到的例外狀況和呼叫堆疊是泛型的。 無論根本原因為何,轉譯線程失敗都會產生上述其中一個呼叫堆棧(或稍有變化)。 這使得診斷問題,甚至識別兩個當機或停止回應源於相同的根本原因,特別是困難的。

WPF 轉譯線程的描述,以及它與 UI 線程有何不同

每個 WPF 應用程式可能都有一或多個 UI 線程執行自己的訊息幫浦 (Dispatcher.Run)。 每個UI線程都負責處理線程消息佇列中的視窗訊息,並將其分派至該線程所擁有的視窗。 每個 WPF 應用程式只有一個轉譯線程。 它是與 DirectX/D3D 通訊的個別線程,如果正在使用軟體轉譯管線,則為 /或 GDI。 針對WPF內容,每個UI線程都會將詳細指示傳送至要繪製的轉譯線程。 轉譯線程接著會採用這些指示並轉譯內容。

上述失敗的原因

上述例外狀況和停止回應會在UI線程中發生,因為 WPF 轉譯線程發生嚴重錯誤。 這些錯誤有數個可能的原因,但轉譯線程不會與UI線程共用該資訊。 由於這些例外狀況和停止回應不會源自單一根錯誤或問題,因此沒有特定的方法來修正它們。

WPF 的轉譯線程會在呼叫其他元件,例如 DirectX/D3D、User32 或 GDI32 時,檢查傳回值是否成功或失敗。 偵測到失敗時,WPF 會「殭屍」轉譯分割區,並在兩個線程同步處理時通知失敗的UI線程。 轉譯線程會嘗試將它收到的失敗對應至適當的Managed 例外狀況。 例如,如果 WPF 轉譯線程因為記憶體不足而失敗,則它會將失敗對應至 System.OutOfMemoryException ,而且會在 UI 線程上顯示例外狀況。 轉譯線程只會在幾個位置與UI線程同步處理,因此上述呼叫堆疊通常會出現在您注意到問題的位置,而不是實際發生的位置。 它們最常在窗口設定更新的位置同步處理(大小、位置等),或UI線程處理轉譯線程中的「通道」訊息的位置。

根據設計,UI 線程上的例外狀況和呼叫堆疊對診斷問題沒有説明。 這是因為在擲回例外狀況時,轉譯線程已經傳遞失敗點。 轉譯線程的重要狀態可協助我們了解失敗發生的位置和原因,但已遺失。 這讓撰寫 WPF 應用程式的人幾乎不可能知道發生失敗的原因,或如何避免它。 針對Microsoft,在事後用戶傾印檔案中偵錯此專案只會稍微好一些。 轉譯線程會保留失敗呼叫堆疊的循環緩衝區,我們可以透過專屬調試程式延伸模組和私人偵錯符號在內部重建,以顯示大約的初始失敗點。 不過,在失敗時,我們無法存取重要狀態,例如局部變數、堆疊變數和堆積物件。 我們通常會再次執行應用程式,以尋找我們懷疑涉及的呼叫失敗。

失敗的常見原因

最常見的 WPF 轉譯線程失敗貯體與視訊硬體或驅動程式問題相關聯。 當 WPF 透過 DirectX 查詢視訊驅動程式以取得功能時,驅動程式可能會誤報其功能,導致 WPF 取得最終導致某些 DirectX/D3D 失敗的程式代碼路徑。 有時候,驅動程式不會誤報其功能,但未正確實作。 大部分的轉譯線程失敗是由 WPF 嘗試利用硬體轉譯管線的方式所造成,因而在驅動程式中暴露出一些缺陷。 這可能發生在具有新式圖形裝置和驅動程式的新式 Windows 版本上,雖然它不像 WPF 早期那樣常見。 這就是為什麼我們測試及/或解決轉譯線程失敗的第一個建議之一是在 WPF 中停用硬體加速。

也可能是因為應用程式要求轉譯場景太複雜而無法處理驅動程式 (或 DirectX) 所造成的失敗。 這並不常見於新式驅動程式,但每個裝置都有限制,而且無法超過它們。

轉譯線程失敗的另一個歷程記錄來源是使用 WPF 中的 Window.AllowsTransparency 或 Popup.AllowsTransparency 屬性,這會導致使用分層視窗。 舊版 Windows 有分層式窗口的問題,但大部分都已透過 Windows Vista 中的桌面視窗管理員 (DWM) 簡介來解決。

如果轉譯線程失敗顯示為 System.OutOfMemoryException,則轉譯線程可能是部分資源耗盡進程的受害者。 呼叫至 Win32/DX 嘗試配置某些資源的 API 的轉譯線程,但失敗。 WPF 會將 類似 E_OUTOFMEMORYERROR_NOT_ENOUGH_MEMORY 的值對應至 System.OutOfMemoryException。 雖然例外狀況是指「記憶體」,但失敗可能是指任何類型的資源,例如 GDI 物件句柄、其他系統句柄、GPU 記憶體、一般 RAM 記憶體等。

資源配置失敗的備註

兩個備註適用於 System.OutOfMemoryException 失敗,以及任何資源配置失敗。

  • 根本原因可能不在於遇到失敗的程式代碼中。 相反地,程式中可能會有其他程式碼過度取用資源,讓一般成功的要求沒有留下任何程序代碼。

  • 如果要求非常大,即使資源看起來很豐富,仍可能會發生失敗。 System.OutOfMemoryException即使系統有足夠的記憶體,如果有大量(連續)記憶體的要求,也可能會發生 。 以下是真實世界的範例:Visual Studio 外掛程式正準備從先前會話中儲存的狀態還原其視窗。 它針對上一個和目前監視器之間的 DPI 差異而調整不正確,這與 WPF、WindowsForms 和 VS 視窗裝載元件的數層調整,設定其視窗大小比應該大 16 倍。 轉譯線程嘗試配置比所需的后緩衝區多 256 倍,即使有足夠的記憶體可供預期的配置,還是失敗。

一般建議

  1. 使用停用硬體加速選項中所討論的 DisableHWAcceleration 登錄值來停用硬體轉譯。 這會影響您計算機上的所有 WPF 應用程式;這樣做只是用來測試您的問題是否與圖形硬體或驅動程序有關。 如果是這種情況,您可以透過以程序設計方式在更細微的層級停用硬體加速來解決此問題。 這可以透過使用 HwndTarget.RenderMode 屬性,或使用 RenderOptions.ProcessRenderMode 屬性,以個別進程為基礎來完成。

  2. 更新您的視訊驅動程式,並在問題機器中嘗試不同的視訊硬體。

  3. 升級至目標平臺可用之 .NET 的最新版本和 Service Pack 層級。

  4. 升級至最新的操作系統。

  5. 停用 在應用程式中使用 Windows.AllowsTransparencyPopup.AllowsTransparency

  6. 如果System.OutOfMemoryExceptions報告,請監視 效能監視器 中的進程記憶體使用量;特別是所有堆積計數器中的 Process\Virtual Bytes、Process\Private Bytes 和 .NET CLR Memory\# Bytes。 監視 Windows 任務管理器中進程的用戶物件和 GDI 物件。 如果您判斷特定資源已用盡,請針對應用程式進行疑難解答,以修正過度的資源耗用量。 請記住上述關於資源配置問題的兩句話。

  7. 如果您有跨平臺或不同視訊硬體/驅動程式組合發生的可重現案例,您可能會有 WPF Bug。 請務必收集足夠的資訊,以便在向Microsoft報告問題之前允許調查。 呼叫堆疊不夠。 Microsoft需要更詳細的資訊,例如:

    • 完整 VS 解決方案,其中包含重現問題的步驟,包括環境的描述 - OS、.NET 和圖形。
    • 問題的 「時間移動偵錯」追蹤
    • 完整損毀傾印。