カテゴリー別

お絵描き、デザイン

写真、動画関連ソフト

アメーバピグ専用ソフト

ホームページ関連

画像処理

スキャナー用

SEO 関連

お楽しみ

その他

過去ログ

2011年07月04日(月)

c# BitmapDecoder を別スレッドで作成するとリソースリークする

別スレッドで、 BitmapDecoderCreate メソッドを呼び出すと、
リソースリークが発生します。

2012/5/7 追記
よりよい解決法を発見したので、ホームページに掲載しました。
c# BitmapDecoder を別スレッドで作成するとリソースリークする
をご覧ください。

どうやら、BitmapDecoder.Create 内で、
RegisterClassEx を使用してウィンドウクラスが登録されるようですが、
(少なくともすぐには) 開放されないみたいです。

.NET フレームワーク 3.5 で見られた現象ですが、
別のバージョンについては調べてません。

ウィンドウクラスの解法が行われず、リソースが足りない状態に陥ると、
IE などのほとんどのアプリケーションが起動できなくなります。

また、発生するエラーは、
「このコマンドを実行するのに十分な記憶域がありません」
ですが、メモリーがいくらあっても発生します。

↓がテスト用のコードです。

using System;
using System.Threading;
using System.Windows.Media.Imaging;
using System.Windows.Threading;

namespace UnitTest
{
  class TestBitmapDecoder
  {
    const string path = @"C:\Windows\Web\Wallpaper\img1.jpg";
    const BitmapCreateOptions createOption = BitmapCreateOptions.None;
    const BitmapCacheOption cacheOption = BitmapCacheOption.Default;

    public void Test()
    {
      int count = 0;
      bool error = false;

      while (!error)
      {
        Thread thread = new Thread(new ThreadStart(delegate
          {
            try
            {
              BitmapDecoder decoder = BitmapDecoder.Create(
                new Uri(path), createOption, cacheOption
              );
              ++ count;
              if (count % 100 == 0)
                Console.WriteLine(count);
            }
            catch (Exception e)
            {
              Console.WriteLine(count);
              Console.WriteLine(e.ToString());
              error = true;
            }
#if false
            finally
            {
              Dispatcher.CurrentDispatcher.InvokeShutdown();
            }
#endif
          }));

        thread.SetApartmentState(ApartmentState.STA);
        thread.Start();
        thread.Join();
      }
    }
  }
}

テスト用のクラスを呼び出すコード↓。

using System;

namespace UnitTest
{
  class Program
  {
    static void Main(string[] args)
    {
      new TestBitmapDecoder().Test();
    }
  }
}

System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command を参考にしています。

参考のページのコードでは、ロック不要なのにロックしてたり、
反面、catch 内で、はロックしてなかったりして意味不明なのですがw
参考になりました。

Windows Vista 32 ビットバージョンでの出力は、↓のようになります。

100
.
.
.
15600
15674
System.ComponentModel.Win32Exception: このコマンドを実行するのに十分な記憶域がありません。
   場所 MS.Win32.UnsafeNativeMethods.RegisterClassEx(WNDCLASSEX_D wc_d)
   場所 MS.Win32.HwndWrapper..ctor(Int32 classStyle, Int32 style, Int32 exStyle, Int32 x, Int32 y, Int32 width, Int32 height, String name, IntPtr parent, HwndWrapperHook[] hooks)
   場所 MS.Win32.MessageOnlyHwndWrapper..ctor()
   場所 System.Windows.Threading.Dispatcher..ctor()
   場所 System.Windows.Threading.Dispatcher.get_CurrentDispatcher()
   場所 System.Windows.Threading.DispatcherObject..ctor()
   場所 System.Windows.Media.Imaging.BitmapDecoder..ctor(SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Boolean insertInDecoderCache, Boolean isOriginalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle)
   場所 System.Windows.Media.Imaging.JpegBitmapDecoder..ctor(SafeMILHandle decoderHandle, BitmapDecoder decoder, Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, Boolean insertInDecoderCache, Boolean originalWritable, Stream uriStream, UnmanagedMemoryStream unmanagedMemoryStream, SafeFileHandle safeFilehandle)
   場所 System.Windows.Media.Imaging.BitmapDecoder.CreateFromUriOrStream(Uri baseUri, Uri uri, Stream stream, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy, Boolean insertInDecoderCache)
   場所 System.Windows.Media.Imaging.BitmapDecoder.Create(Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption, RequestCachePolicy uriCachePolicy)
   場所 System.Windows.Media.Imaging.BitmapDecoder.Create(Uri bitmapUri, BitmapCreateOptions createOptions, BitmapCacheOption cacheOption)
   場所 UnitTest.TestBitmapDecoder.<>c__DisplayClass2.b__0() 場所 X:\Original\cs\Common\UnitTest\TestBitmapDecoder.cs:行 25

回避の方法は、スレッド終了前に、
Dispatcher.CurrentDispatcherInvokeShutdown() を呼ぶことです。
(コード中で #if false によりコメントアウトされている部分)

やはり、32 ビットバージョンの、ウィンドウズでは、
ウィンドウクラスの登録は、16000 くらいが限界みたいですね。

ちなみに、Windows XP の 64 ビットバージョンでは、62685 くらいで
同様のエラーが起こりましたが、プログラムを終了させても、
他のプログラムはほとんどまともに動かなくなりました。

Vista では、プログラムを終了させれば他のプログラム、
少なくとも終了後に起動したプログラムはまともに動作したので、
そこら辺は進化してるみたいですね。

System.ComponentModel.Win32Exception (0x80004005): Not enough storage is available to process this command と似てますけど、
この問題と解法を公開するのは世界で最初っぽいですねー。

ミルノ PC フォトフレーム では、
バージョン 1.2.2.2 でこの不具合を修正済みです。

2つ以上の CPU を持つパソコンを最大限活かすには、
ミルノ PC フォトフレームのようなマルチスレッド対応のソフトが最適ですよ。

試用は無料なので、是非、使ってみてくださいね。

画像ビューア: ミルノ PC フォトフレームのダウンロード
画像ビューア: ミルノ PC フォトフレームの更新情報
ご意見・ご要望連絡窓口


コメント
コメントする