0

私たちのアプリケーションでは、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 の明示的なシャットダウンに関するコメントは見当たりません。

4

1 に答える 1