0

何が起こっているのかわからないので、これはおそらく適切に表現された質問ではありません。私は学ぼうとしていますが、これについて何らかの方向性が得られることを願っています。初心者に対するあなたの忍耐に感謝します。

変更中のコードがあります。画像を表示します。画像を変更して、別のウィンドウに表示したい。画像を表示するコードをコピーして変更を行うと、元の画像と変更された画像の両方に対して変更された画像が表示されます。

GCHandle が同じメモリを参照し続けているようですか? ハンドル名を変更して、本当に新しいハンドルを作成していませんか? 長いコードで申し訳ありませんが、私は迷っています。

何がうまくいかないのですか?

最も困惑しているのは、それが機能していたということです。その後、何かを変更しましたが、現在は機能するバージョンに戻ることができません。私のコードは機能していたものと同じだと思います。いくつかの設定はどこですか?

 System.Runtime.InteropServices.GCHandle gch3 = System.Runtime.InteropServices.GCHandle.Alloc(scaled, System.Runtime.InteropServices.GCHandleType.Pinned);

 int pitch = mImageWidth;
 if (pitch % 4 != 0)
     pitch = ((pitch / 4) + 1) * 4;

 System.Drawing.Bitmap bitmap = new Bitmap(mImageWidth, mImageHeight, pitch, System.Drawing.Imaging.PixelFormat.Format8bppIndexed, gch3.AddrOfPinnedObject());

 gch3.Free();

 if (pal == null)
     {
         System.Drawing.Imaging.ColorPalette cp = bitmap.Palette;
         for (i = 0; i < cp.Entries.Length; ++i)
             {
                  cp.Entries[i] = Color.FromArgb(i, i, i);
             }

         pal = cp;
      }

  bitmap.Palette = pal;

  FirstImageDisplay.Image = bitmap;

  //second image here
  for (i = 0; i < frame.Length; ++i)
      scaled[i] = (byte)(.5 * scaled[i]);

  System.Runtime.InteropServices.GCHandle gch4 = System.Runtime.InteropServices.GCHandle.Alloc(scaled, System.Runtime.InteropServices.GCHandleType.Pinned);

  int pitch1 = mImageWidth;

  if (pitch1 % 4 != 0)
      pitch1 = ((pitch1 / 4) + 1) * 4;

  System.Drawing.Bitmap bitmap2 = new Bitmap(mImageWidth, mImageHeight, pitch, System.Drawing.Imaging.PixelFormat.Format8bppIndexed, gch4.AddrOfPinnedObject());

  gch4.Free();

  if (pal == null)
      {
          System.Drawing.Imaging.ColorPalette cp = bitmap.Palette;

          for (i = 0; i < cp.Entries.Length; ++i)
              {
                 cp.Entries[i] = Color.FromArgb(i, i, i);
              }

           pal = cp;
      }

  bitmap.Palette = pal;
  SecondImageDisplay.Image = bitmap;
  //end second image code
4

1 に答える 1

2

あなたがやっていることは間違いなく安全ではありません。なぜあなたはこれをやっている?安全で管理された環境から離れても安心できる理由はありますか?

その周りにビットマップが作成されbyte[]ます。これは、マネージ メモリに固定されていてもかまわない限り問題ありませんbyte[](実際にはアプリケーションの実行中などではなく、しばらくの間は問題ありません)。ただし、次の行でポインターを離します。

次に、同じ を使用してbyte[]変更し、別のビットマップに使用します。ブーム、それはまだ同じバイト配列です。両方のビットマップの内容が同じであることは驚くべきことではありません。

動作する場合と動作しない場合がある理由は、ハンドルが GC によって移動されない場合、両方のビットマップが正しいためです。ただし、GC がバイト配列を移動する場合、ビットマップは調整する方法がありません。ビットマップはメモリ内の同じ場所を指します (現在は間違っています)。

理解しなければならないことは、GCHandle は新しいオブジェクトを作成しないということです。GCHandle が存在する限り、物理的な場所 (まあ、仮想メモリ内ですが...) をいじらないように GC に指示するだけです。新しいオブジェクトを作成する場合は、次のようにしbyte[].Clone()ます。ただし、Bitmap の存続期間全体にわたってハンドルを固定しておく必要がありますが、これは通常は悪い考えです。代わりに、通常の方法でビットマップを作成してから を実行してBitmap.LockBitsから、 を使用Marshal.Copyしてビットマップ配列をビットマップのアンマネージ メモリにコピーします。

概念全体を示すコード スニペットを次に示します。

byte[] data = new byte[320 * 200 * 1];

Bitmap bmp1 = new Bitmap(320, 200, 
       System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
Bitmap bmp2 = new Bitmap(320, 200, 
       System.Drawing.Imaging.PixelFormat.Format8bppIndexed);

var bdata = bmp1.LockBits(new Rectangle(new Point(0, 0), bmp1.Size), 
                 ImageLockMode.WriteOnly, bmp1.PixelFormat);
try
{
    Marshal.Copy(data, 0, bdata.Scan0, data.Length);
}
finally
{
    bmp1.UnlockBits(bdata);
}

// Do your modifications

bdata = bmp2.LockBits(new Rectangle(new Point(0, 0), bmp2.Size), 
             ImageLockMode.WriteOnly, bmp2.PixelFormat);
try
{
    Marshal.Copy(data, 0, bdata.Scan0, data.Length);
}
finally
{
    bmp2.UnlockBits(bdata);
}

これはパフォーマンスの点で最良のコードではありません (コピーが必要です) が、唯一の本当の代替手段はunsafeコードを使用することです。適切に使用しないと、厄介な問題が発生する可能性があります。いずれにせよ、パフォーマンスの向上はごくわずかかもしれません。安全でない方法に進む前に、実際に気にするかどうかを調べてください。

マネージ メモリとアンマネージ メモリを扱う際の問題と複雑さの詳細については、http://www.luaan.cz/2014/07/a-quick-introduction-to-managed- の私のブログをご覧ください。 and.htmlまだかなり大まかな内容ですが、この回答よりも多くのことを説明しています。

于 2014-02-03T08:52:42.983 に答える