web-dev-qa-db-ja.com

Image / BitmapおよびPictureBoxを破棄する正しい方法

Windows Mobile 6(WF/C#)アプリケーションを開発しようとしています。フォームは1つだけで、フォームにはPictureBoxオブジェクトのみがあります。その上で、すべての必要なコントロールまたは必要なものを何でも描画します。

私がしていることは2つあります。カスタム形状の描画と.pngファイルからのビットマップの読み込み。

次の行は、ロード時にファイルをロックします(これは望ましくないシナリオです)。

Bitmap bmp = new Bitmap("file.png");

だから私はビットマップをロードする別の方法を使用しています。

public static Bitmap LoadBitmap(string path) {
    using (Bitmap original = new Bitmap(path))
    {
        return new Bitmap(original);
    }
}

これはかなり遅いと思いますが、ファイルのロックをすばやく解除しながら画像をロードするためのより良い方法はわかりません。

今、画像を描くとき、​​私が使う方法があります:

public void Draw() {
    Bitmap bmp = new Bitmap(240,320);
    Graphics g = Graphics.FromImage(bmp);

    // draw something with Graphics here.
    g.Clear(Color.Black);
    g.DrawImage(Images.CloseIcon, 16, 48);
    g.DrawImage(Images.RefreshIcon, 46, 48);
    g.FillRectangle(new SolidBrush(Color.Black), 0, 100, 240, 103);

    pictureBox.Image = bmp; 
}

ただし、これは何らかのメモリリークのようです。そして、それをあまりにも長く続けていると、アプリケーションは最終的にクラッシュします。

したがって、3つの質問があります。

1。)ファイルをロックせずにファイルからビットマップをロードするためのより良い方法は何ですか?

2。)メモリリークやObjectDisposedExceptionのスローが発生しないように、Draw()関数で手動で破棄する必要のあるオブジェクト(およびその順序)を教えてください。

。) pictureBox.Imageがコードの最終行のようにbmpに設定されている場合、pictureBox.Image.Dispose()は、pictureBox.Imageまたは基になるビットマップの維持に関連するリソースのみを破棄しますそれに設定?

23
Kornelije Petak

1:Windows Mobileで動作するかどうかはわかりませんが、これを試してください。

_FileStream bitmapFile = new FileStream("mybitmap.bmp", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
Image loaded = new Bitmap(bitmapFile);
_

2:SolidBrushは破棄する必要があります。処分には一般的なルールがあります。 -> "disposeを実装するすべてのオブジェクト(インスタンス化されたオブジェクト)は、オブジェクトis戻り値/参照値/出力値の場合を除いて、手動で破棄する必要があります"

この場合、usingステートメントを使用することをお勧めします

_using (new objecttodispose){ ..... } 
_

usingステートメントは、どのような場合でも(例を除いて)Dispose()の呼び出しを保証します。

3:Dispose()は、ビットマップリソースを解放します。

7
Jack

実際のメモリリークはないと思います。問題は、古いビットマップを破棄しないことです。データをクリーンアップするのはGCです。しかし、決定的な方法はありませんwhenこれが発生します。

したがって、多くの画像をループ処理する場合は、メモリが増加し、別の時点で、1つの位置で落ちるか、抵抗することになると思います。

私はそれをテストしませんでしたが、おそらくこれはそれをより確定的にするのに少し役立つでしょう:

_public void Draw() {
    Bitmap bmp = new Bitmap(240,320);
    using(var g = Graphics.FromImage(bmp))
    using(var solidBrush = SolidBrush(Color.Black))
    {
        // draw something with Graphics here.
        g.Clear(Color.Black);
        g.DrawImage(Images.CloseIcon, 16, 48);
        g.DrawImage(Images.RefreshIcon, 46, 48);
        g.FillRectangle(solidBrush, 0, 100, 240, 103);

        //Backup old image in pictureBox
        var oldImage = pictureBox.Image;
        pictureBox.Image = bmp; 
        //Release resources from old image
        if(oldImage != null)
            ((IDisposable)oldImage).Dispose();            
    }
}
_

更新

そして、jack30lenaに触発された別のアイデア:

_public static Bitmap LoadBitmap(string path)
{
    //Open file in read only mode
    using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read))
    //Get a binary reader for the file stream
    using (BinaryReader reader = new BinaryReader(stream))
    {
        //copy the content of the file into a memory stream
        var memoryStream = new MemoryStream(reader.ReadBytes((int)stream.Length));
        //make a new Bitmap object the owner of the MemoryStream
        return new Bitmap(memoryStream);
    }
}
_

2番目のコードサンプルの背後にあるアイデアは、ファイルハンドルを削除して、ファイルの内容をメモリにコピーすることです。その後、ビットマップはoldImage.Dispose()を呼び出すことにより、最初のサンプル内に配置されるMemoryStreamの所有権を取得します。

このアプローチを使用することで、メモリに2つを超える画像が存在することはなくなり、実際には、大きな画像または少量のRAMによってOutOfMemoryExceptionsのみが発生します。

13
Oliver