私たちのアプリケーションでは、PngBitmapEncoder を使用して PNG 画像をエンコードし、別のスレッド\タスクに保存しています。アプリケーションを数日実行した後、Dispatcher を Encoder から作成できず、エラーがスローされることがわかりました
コマンドを処理するのに十分なストレージがありません
そして、以下のコールスタックを持っています
System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command
at MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
at System.Windows.Threading.Dispatcher..ctor()
at System.Windows.Threading.DispatcherObject..ctor()
at System.Windows.Media.Imaging.BitmapEncoder..ctor(Boolean isBuiltIn)
.Net はオープン ソースで利用できるため、Dispatcher コンストラクター内のどの行がエラーをスローしているのか気になりました
[SecurityCritical, SecurityTreatAsSafe]
private Dispatcher()
{
_queue = new PriorityQueue<DispatcherOperation>();
_tlsDispatcher = this; // use TLS for ownership only
_dispatcherThread = Thread.CurrentThread;
// Add ourselves to the map of dispatchers to threads.
lock(_globalLock)
{
_dispatchers.Add(new WeakReference(this));
}
_unhandledExceptionEventArgs = new DispatcherUnhandledExceptionEventArgs(this);
_exceptionFilterEventArgs = new DispatcherUnhandledExceptionFilterEventArgs(this);
_defaultDispatcherSynchronizationContext = new DispatcherSynchronizationContext(this);
// Create the message-only window we use to receive messages
// that tell us to process the queue.
MessageOnlyHwndWrapper window = new MessageOnlyHwndWrapper();
_window = new SecurityCriticalData<MessageOnlyHwndWrapper>( window );
_hook = new HwndWrapperHook(WndProcHook);
_window.Value.AddHook(_hook);
// DDVSO:447590
// Verify that the accessibility switches are set prior to any major UI code running.
AccessibilitySwitches.VerifySwitches(this);
}
アップデート
.net オープン ソースからのコンストラクター コードを更新しました。dispatcher.cs はこちらから入手できますhttps://referencesource.microsoft.com/#WindowsBase/Base/System/Windows/Threading/Dispatcher.cs,078d6b27d9837a35
調査の結果、約 15000 回の反復後に問題が発生することがわかりました (反復ごとに新しいスレッドが作成され、PngBitmapEncoder が呼び出されます)。その後、これが Global Atom Table 制限 (0x4000 または 16384) にリンクされていることがわかりました。グローバル アトム テーブルの詳細については、https://docs.microsoft.com/en-us/archive/blogs/ntdebugging/identifying-global-atom-table-leaksをご覧ください。
毎回作成されるディスパッチャはグローバル アトム テーブルにエントリを作成し、スレッドの終了時にこのエントリはクリアされません。これにより、グローバル アトム テーブルでリークが発生し、最大制限に達すると、「ストレージが不足しています....」というエラーがスローされます。これは、Microsoft の Dispatcher の取り扱いに問題があるようです。PngBitmapEncoder のドキュメントでさえ、Dispatcher の処理と Dispatcher の明示的なシャットダウンに関するコメントは見当たりません。