2017-11-10 4 views
0

カメラとインターフェイスするフォームを含むアプリケーションがあります。ユーザーはフォームからカメラを制御し、写真を撮ったり、WinForms PictureBoxに表示したり、別のフォームを開いて表示されている画像を編集したり、その画像をディスクに保存することができます。私たちの顧客は、断続的なAccessViolationExceptionsについて不平を言っています。午後になってから、PictureBox.ImageMemoryStreamに保存する前に、GC.Collectを強制して私の開発環境で問題を再現するための信頼できる方法を見つけました。ガベージコレクションの直後にPictureBox.Imageを保存しようとすると、AccessViolationExceptionが発生する

:ユーザーが写真を編集したい場合は、我々はJPEG(私はなぜ知らない)としてフォーマットのMemoryStreamとしてビットマップを引き出す

int w = videoInfoHeader.BmiHeader.Width; 
int h = videoInfoHeader.BmiHeader.Height; 
int stride = w * 3; 

GCHandle handle = GCHandle.Alloc(savedArray, GCHandleType.Pinned); 
long scan0 = (long)handle.AddrOfPinnedObject(); 
scan0 += (h - 1) * stride; //image is upside down, so start at the bottom (I guess bottom-up bitmaps still scan left-to-right?) 
Bitmap b = new Bitmap(w, h, -stride, PixelFormat.Format24bppRgb, (IntPtr)scan0); 
handle.Free(); 
Image old = pictureBog.Image; 
pictureBox.Image = b; 
if (old != null) 
    old.Dispose(); 
//Show picturebox, and let user use the form 

私たちは、このような私たちのPictureBoxコントロールを作成します

//GC.Collect() 
using (MemoryStream stream = new MemoryStream()) 
{ 
    pictureBox.Image.Save(stream, ImageFormat.Jpeg); //AccessViolationException here if GC.Collect() is uncommented. 
    byte[] pic = stream.ToArray(); 
    //Do stuff with pic and open editor form 
} 

私はRandomly occurring AccessViolationException in GDI+からGCの移動は、アンマネージコードの下から出て詰め込むので、あなたが起きてからこれを防ぐために「ピン」の管理対象オブジェクトに持っている問題があると指摘しました。 PictureBoxにデータを入力したときにこれを行っていることがわかりますが、画像をPictureBoxから引き出しているときは表示されません。バイト配列は単なる配列であるため、ソースバイト配列を「ピン」する必要があることを理解しています。しかし、イメージを保存するときに私が "ピン"しようとするもの、つまりMemoryStreamを知りませんか? Bitmap.Save(Stream)は、必要に応じて自動的に処理できるほどスマートであると思います。さらに、バッファは、それがどんどんいっぱいになると再割り当てされる必要があるので、ピン設定は私にはあまり意味がありません。誰でも何がここに悪いメモリアクセスを引き起こしているか考えている?

イメージデータを取り出して元のバイト配列を保持し、それからBitmapを作成して新鮮なコピーから保存するときは、PictureBoxを使用しないでください。これはうまくいくと思われますが、これはただの偶然であり、実際の問題がまだ残っているかどうかはわかりません。

+2

BitmapコンストラクタのMSDNのドキュメントをご覧ください。 「呼び出し元は、scan0パラメータで指定されたメモリブロックの割り当てと解放を担当しますが、メモリは関連するビットマップが解放されるまで解放されるべきではありません。コード内で固定されたメモリを解放していますが、ビットマップはもう使用しません。すぐにガベージコレクションを実行すると、すぐに対応する例外が発生します。それ以外の場合は、GCが実行を決定したときにいつでも例外が発生する可能性があります。 – KBO

答えて

0

Aバイト配列からイメージを作るためのより安全な方法は、まずLockBitsとのバッキング・データを開いて、それが公開さScan0ポインタにデータをコピーし、簡単なnew Bitmap(width, height, pixelformat)コンストラクタで画像自体を作ることです、走査線スキャンラインごとに、Marshal.Copyを使用します。そうすれば、問題を引き起こす可能性のあるアンマネージポインタを決して混乱させることはありません。関係する唯一のポインタは、LockBitsによって予約された特別にロックされたメモリにあり、unlockBitsが呼び出された後、再び安全に内部イメージデータの一部となります。

コードはここで見つけることができます:

A: Why must "stride" in the System.Drawing.Bitmap constructor be a multiple of 4?

(答えは同じですが、質問は大幅に異なっているので、私は多くの使用が重複としてこれをマーキングしてないと思う)

逆さまのストライドの処理がメソッドに含まれていることに注意してください。

関連する問題