次の方法で共有


WPF のレンダリング スレッドエラー

このドキュメントでは、WPF のレンダリング スレッドでのエラーについて説明します。特に、 SyncFlush または NotifyPartitionIsZombie で例外が発生したり、アプリケーションが WaitForNextMessage または SynchronizeChannelでハングしたりする原因となるエラーに注意してください。

元の製品バージョン: .NET Framework 4.8

SyncFlush、WaitForNextMessage、SynchronizeChannel、NotifyPartitionIsZombie でのエラー

開発者は、多くの場合、Windows Presentation Foundation (WPF) アプリケーションでのスレッドエラーのレンダリングに関連する問題に直面します。 ユーザーは、アプリケーションが次のような例外をスローすることを報告できます。

  • System.Runtime.InteropServices.COMException: UCEERR_RENDERTHREADFAILURE (HRESULT からの例外: 0x88980406)
  • System.InvalidOperationException: レンダリング スレッドで未指定のエラーが発生しました。
  • System.OutOfMemoryException: プログラムの実行を続行するためのメモリが不足しています。

例外の呼び出し履歴は、 SyncFlush または NotifyPartitionIsZombieから始まります。 例えば次が挙げられます。

   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)  

アプリケーションは、次のような呼び出し履歴を使用して、 WaitForNextMessage または SynchronizeChannelでハングする場合もあります。

   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

レンダリング スレッドでエラーが発生した場合の症状です。 これは、受信した例外と呼び出し履歴が汎用的であるため、診断に困難な問題です。 レンダー スレッドエラーは、根本原因に関係なく、上記の呼び出し履歴の 1 つ (またはその小さなバリエーション) を生成します。 これにより、問題を診断したり、2 つのクラッシュやハングが同じ根本原因から発生した場合の認識が特に困難になります。

WPF レンダリング スレッドの説明と UI スレッドとの違い

各 WPF アプリケーションには、独自のメッセージ ポンプ (Dispatcher.Run) を実行する 1 つ以上の UI スレッドが含まれる場合があります。 各 UI スレッドは、スレッドのメッセージ キューからのウィンドウ メッセージを処理し、そのスレッドが所有するウィンドウにディスパッチする役割を担います。 各 WPF アプリケーションには、レンダリング スレッドが 1 つだけ含まれます。 これは、DirectX/D3D (ソフトウェア レンダリング パイプラインが使用されている場合は GDI) と通信する別のスレッドです。 WPF コンテンツの場合、各 UI スレッドは描画する内容に関する詳細な命令をレンダリング スレッドに送信します。 その後、レンダー スレッドはこれらの命令を受け取り、コンテンツをレンダリングします。

上記のエラーの原因

上記の例外とハングは、WPF レンダリング スレッドで致命的なエラーが発生した結果として UI スレッドで発生します。 これらのエラーにはいくつかの原因が考えられますが、レンダリング スレッドはその情報を UI スレッドと共有しません。 これらの例外とハングは、1 つのルートバグや問題に起因しないため、それらを修正する特定の方法はありません。

WPF のレンダリング スレッドは、DirectX/D3D、User32、GDI32 などの別のコンポーネントへの呼び出しを行うときに、戻り値の成功または失敗をチェックします。 エラーが検出されると、WPF はレンダー パーティションを "ゾンビ" し、2 つのスレッドが同期されたときにエラーを UI スレッドに通知します。 レンダー スレッドは、受け取ったエラーを適切なマネージド例外にマップしようとします。 たとえば、メモリ不足の状態のために WPF レンダリング スレッドが失敗した場合、エラーは System.OutOfMemoryException にマップされ、UI スレッドに表示される例外になります。 レンダリング スレッドは、いくつかの場所でのみ UI スレッドと同期するため、上記の呼び出し履歴は通常、問題が発生した場所ではなく、問題に気付いた場所に表示されます。 通常、ウィンドウの設定が更新される場所 (サイズ、位置など) や、UI スレッドがレンダリング スレッドからの "チャネル" メッセージを処理する場所で同期します。

仕様上、UI スレッドの例外と呼び出し履歴は、問題を診断するのに役立ちません。 これは、例外がスローされる時点までに、レンダリング スレッドが既に障害点を通過しているためです。 レンダリング スレッドの重大な状態は、エラーが発生した場所と理由を理解するのに役立ちますが、既に失われています。 これにより、WPF アプリケーションを記述しているユーザーが、エラーが発生した理由や回避方法を知ることは事実上不可能になります。 Microsoft では、事後分析のユーザー ダンプ ファイルでこれをデバッグする方が若干優れています。 レンダー スレッドは、失敗した呼び出し履歴の循環バッファーを保持します。これは、独自のデバッガー拡張機能とプライベート デバッグ シンボルを使用して内部的に再構築して、おおよその初期障害点を示すことができます。 ただし、障害発生時にローカル、スタック変数、ヒープ オブジェクトなどの重大な状態にアクセスすることはできません。 通常、アプリケーションをもう一度実行して、関係していると思われる呼び出しでエラーを探します。

エラーの一般的な原因

WPF レンダリング スレッドエラーの最も一般的なバケットは、ビデオ ハードウェアまたはドライバーの問題に関連しています。 WPF が DirectX を介してビデオ ドライバーに機能を照会すると、ドライバーがその機能を誤って報告し、WPF がコード パスを取得し、DirectX/D3D エラーが発生する可能性があります。 ドライバーが機能を誤って報告しないが、正しく実装されていない場合があります。 レンダリング スレッドエラーの大部分は、WPF がドライバーの欠陥を公開する方法でハードウェア レンダリング パイプラインを利用しようとしたことが原因で発生します。 これは、最新のグラフィックス デバイスとドライバーを備えた最新バージョンの Windows で発生する可能性がありますが、WPF の初期ほど一般的ではありません。 これが、レンダリング スレッドの障害をテストまたは回避するための最初の提案の 1 つが、WPF でハードウェア アクセラレーションを無効にすることです。

また、ドライバー (または DirectX) が処理するには複雑すぎるシーンのレンダリングを要求するアプリによってエラーが発生する可能性もあります。 これは最新のドライバーでは一般的ではありませんが、すべてのデバイスに制限があり、それを超えるのは不可能ではありません。

レンダリング スレッドエラーのもう 1 つの履歴ソースは、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エラーとリソース割り当てエラーには、2 つの注釈が適用されます。

  • 根本原因は、エラーが発生したコードに存在しない可能性があります。 代わりに、リソースを過剰に消費する他のコードがプロセス内に存在する可能性があり、通常成功した要求には何も残っていない可能性があります。

  • 要求が異常に大きい場合は、リソースが豊富に表示されているにもかかわらず、エラーが発生する可能性があります。 大量の (連続した) メモリの要求がある場合、システムに十分なメモリがある場合でも、 System.OutOfMemoryException が発生する可能性があります。 実際の例を次に示します。Visual Studio プラグインは、前のセッションで保存された状態からウィンドウを復元する準備をしていました。 前のモニターと現在のモニターの間の DPI の違いを誤って調整しました。これは、WPF、WindowsForms、VS ウィンドウ ホスティング コンポーネントの複数のレイヤーからの調整と組み合わせて、ウィンドウのサイズを必要なサイズの 16 倍に設定しました。 レンダー スレッドは、必要な 256 倍のサイズのバック バッファーを割り当てようとしましたが、予想される割り当てに十分なメモリが十分に存在していても失敗しました。

一般的な推奨事項

  1. DisableHWAcceleration レジストリ値を使用してハードウェア レンダリングを無効にしますDisable Hardware Acceleration Optionで説明します。 これは、コンピューター上のすべての WPF アプリケーションに影響します。これは、問題がグラフィックス ハードウェアまたはドライバーに関連しているかどうかをテストする方法としてのみ行います。 その場合は、より細かいレベルでハードウェア アクセラレーションをプログラムで無効にすることで、問題を回避できます。 これは、HwndTarget.RenderMode プロパティを使用して per-window ベースで行うか RenderOptions.ProcessRenderMode プロパティを使用して、per-process ごとに実行できます。

  2. ビデオ ドライバーを更新したり、問題のコンピューターで別のビデオ ハードウェアを試したりします。

  3. ターゲット プラットフォームで使用できる .NET の最新バージョンとサービス パック レベルにアップグレードします。

  4. 最新のオペレーティング システムにアップグレードします。

  5. アプリケーションでの Windows.AllowsTransparencyPopup.AllowsTransparency の使用を無効にします。

  6. System.OutOfMemoryExceptions報告されている場合は、パフォーマンス モニターでプロセスのメモリ使用量 (特に、すべてのヒープ カウンターの Process\Virtual Bytes、Process\Private Bytes、.NET CLR Memory\# Bytes) を監視します。 Windows タスク マネージャーでプロセスのユーザー オブジェクトと GDI オブジェクトも監視します。 特定のリソースが使い果たされていると判断した場合は、アプリケーションのトラブルシューティングを行って、過剰なリソース消費を修正します。 リソース割り当ての問題に関する上記の 2 つの注釈に注意してください。

  7. プラットフォーム間で、またはさまざまなビデオ ハードウェアとドライバーの組み合わせで再現可能なシナリオがある場合は、WPF のバグが発生する可能性があります。 問題を Microsoft に報告する前に、調査に使用できる十分な情報を収集してください。 呼び出し履歴では不十分です。 Microsoft には、次のような詳細情報が必要です。

    • OS、.NET、グラフィックスなどの環境の説明など、問題を再現する手順を含む完全な VS ソリューション。
    • 問題の Time-Travel デバッグ トレース
    • 完全なクラッシュ ダンプ。