两个不同的 GCHandle 引用内存中的同一数组



这可能不是一个很好的问题,因为我不确定发生了什么,所以我不知道如何指出它。我正在尝试学习,希望我能为此方向一些方向。您对新手的耐心表示赞赏。

我有一个我正在修改的代码。它显示图像。我想修改图像,并在其他窗口中显示。我复制显示图像,进行修改的代码,并为原始图像和修改的图像显示修改后的图像。

似乎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

您所做的绝对不是安全的。你为什么做这个?您是否有理由离开安全,管理的环境?

围绕该byte[]创建位图。只要您不介意在托管内存中固定byte[](可以待一会儿,不是在应用程序的时间等)中,这是可以的。但是,在下一行,您发布了指针!

然后,您使用相同的byte[],对其进行修改,然后将其用于另一个位图。繁荣,它仍然是相同的字节数组。这两个位图都具有相同的内容不足为奇 - 您要求。

有时会起作用的原因,有时不是因为GC没有移动句柄,则两个位图都是正确的。但是,如果GC移动字节数组,则位图无法调整 - 它们仍将指向内存中的同一位置(现在是错误的)。

您必须了解的是,Gchandle不会创建一个新对象。只要GCHandle存在,它只是指示GC不要弄乱物理位置(嗯,在虚拟内存中,但...)。如果要创建一个新对象,请执行类似byte[].Clone()的操作。但是,您仍然必须将手柄固定在位图的所有一生中,这通常是一个坏主意。取而代之的是,尝试以通常的方式创建位图,然后执行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-indroduction上查看我的博客 - 管理和。

最新更新