2009-03-24 22 views
5

Tôi đang làm việc trên ứng dụng C# có thể hiển thị hình ảnh trực tiếp từ máy ảnh. Vấn đề tôi đang gặp phải với đoạn mã sau là, tôi nhận được AccessViolationException trong Marshal.Copy khi chạy hàm này được thực thi liên tục trong một luồng. Nhưng, điều này chạy thành công khi chạy một lần (tôi nhận được một hình ảnh tĩnh đơn). Tôi đoán nó đã làm với một số vấn đề tham nhũng bộ nhớ. Bất kỳ ý tưởng/đề xuất về cách đối phó với vấn đề này?Phương thức Marshal.Copy ném AccessViolationException trong C# .NET

private Image ByteArrayToImage(byte[] myByteArray) 
    { 
     if (myByteArray != null) 
     { 
      MemoryStream ms = new MemoryStream(myByteArray); 
      int Height = 504; 
      int Width = 664; 
      Bitmap bmp = new Bitmap(Width, Height, PixelFormat.Format24bppRgb); 
      BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); 
      Marshal.Copy(myByteArray, 0, bmpData.Scan0, myByteArray.Length); 
      bmp.UnlockBits(bmpData); 

      return bmp; 
     } 
     return null; 
    } 
+0

Không chắc, nhưng bạn có thể thử đặt một khóa xung quanh 'LockBits' và 'UnlockBits'. Khi tuyên bố này khóa bitmap trên bộ nhớ hệ thống. Và bạn ngoại lệ cho phép truy cập không hợp lệ vào bộ nhớ được bảo vệ. – Savaratkar

Trả lời

9

Có vẻ với tôi như bạn luôn cố gắng để sao chép số byte myByteArray.Length vào bộ đệm bitmap.

Bạn không kiểm tra xem bộ đệm bitmap có thực sự lớn như vậy không - vì vậy có thể viết tắt phần cuối của bộ đệm bitmap.

Hãy thử kiểm tra nếu myByteArray.Length là bao giờ lớn hơn bmpData.Stride x bmp.Height

Nếu đây là trường hợp bạn sẽ cần phải relook tại các giả định bạn đã thực hiện với đĩa cứng của bạn các giá trị được mã hóa cho định dạng chiều rộng, chiều cao và pixel.

6

Bạn không nên sao chép toàn bộ hình ảnh cùng một lúc. Việc cấp phát bộ nhớ của đối tượng bitmap có thể không phải là những gì bạn mong đợi. Ví dụ, dòng quét đầu tiên có thể được lưu trữ cuối cùng trong bộ nhớ, điều đó có nghĩa là dữ liệu cho dòng quét thứ hai sẽ kết thúc bên ngoài vùng bộ nhớ được cấp phát cho đối tượng bitmap. Ngoài ra có thể có đệm giữa các dòng quét để đặt chúng trên một địa chỉ thậm chí.

Sao chép một dòng tại một thời điểm, sử dụng bmpData.Stride để tìm dòng quét tiếp theo:

int offset = 0; 
long ptr = bmpData.Scan0.ToInt64(); 
for (int i = 0; i < Height; i++) { 
    Marshal.Copy(myByteArray, offset, new IntPtr(ptr), Width * 3); 
    offset += Width * 3; 
    ptr += bmpData.Stride; 
} 
0

trả lời cho tôi: quên ->

 // Unlock the bits right after Marshal.Copy 
     bmp.UnlockBits(bmpData); 

Có ai figured này ra? Đây là trang thứ tư không có câu trả lời. Sử dụng mã chính xác từ MSDN: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx đó là:

     Bitmap bmp = new Bitmap("c:\\picture.jpg"); 

         // Lock the bitmap's bits. 
         Rectangle rect = new Rectangle(0, 0, bmp.Width, bmp.Height); 
         System.Drawing.Imaging.BitmapData bmpData = 
          bmp.LockBits(rect, System.Drawing.Imaging.ImageLockMode.ReadWrite, 
          bmp.PixelFormat); 

         // Get the address of the first line. 
         IntPtr ptr = bmpData.Scan0; 

         // Declare an array to hold the bytes of the bitmap. 
         int bytes = bmpData.Stride * bmp.Height; 
         byte[] rgbValues = new byte[bytes]; 

         // Copy the RGB values into the array. 
         //This causes read or write protected memory 
         System.Runtime.InteropServices.Marshal.Copy(ptr, rgbValues, 0, bytes); 

này không hoạt động trong chế độ tối ưu hóa và chạy như exe không trong IDE. Bất kỳ ý tưởng nào Tôi đã cố gắng đặt đây là một dự án mới và nếu tôi đính kèm quy trình khi tôi nhấn nút và nhấn nút này nhiều lần lỗi xảy ra, nhưng trong mã của tôi, tôi chỉ gọi một lần, một trong hai cách không chắc chắn lý do lỗi.

+0

Bạn đang bỏ qua thực tế là một pixel nhiều hơn một byte tùy thuộc vào định dạng pixel trong 'int bytes = bmpData.Stride * bmp.Height;' – VVS

0

lẽ

BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat);

có lý lẽ không hợp lệ cho ghi chỉ - thử ReadWrite, hoặc ReadOnly trong ImageLockMode? Có thể điều đó sẽ hữu ích.

0

Tôi đã tìm kiếm một chút và nếu chúng ta bỏ qua khả năng rằng các mảng không phải là kích thước thích hợp, chúng tôi kết thúc trong Remarks for BitmapData.Stride Property:

Các sải chân là chiều rộng của một hàng duy nhất của pixel (quét dòng), được làm tròn thành ranh giới bốn byte. Nếu sải chân là dương, thì bitmap là từ trên xuống. Nếu sải chân là âm, bitmap là từ dưới lên.

Vì vậy, có lẽ chúng ta nên làm điều đó theo cách này:

BitmapData bmpData = bmp.LockBits(new Rectangle(0, 0, bmp.Width, bmp.Height), ImageLockMode.WriteOnly, bmp.PixelFormat); 
Marshal.Copy(myByteArray, 0, bmpData.Scan0 + 
(bmpData.Stride >= 0 ? 0 : bmpData.Stride*(bmp.Height-1)), 
myByteArray.Length); 

Nhưng tôi tự hỏi: Chúng tôi đã tạo bitmap: new Bitmap(Width, Height, PixelFormat.Format24bppRgb); ... như vậy, thế nào có thể nó có thể được tiêu cực?
Thời ILSpy:

public Bitmap(int width, int height, PixelFormat format) 
{ 
    IntPtr zero = IntPtr.Zero; 
    int num = SafeNativeMethods.Gdip.GdipCreateBitmapFromScan0(width, height, 0, (int)format, NativeMethods.NullHandleRef, out zero); 
    if (num != 0) 
    { 
      throw SafeNativeMethods.Gdip.StatusException(num); 
    } 
    base.SetNativeImage(zero); 
} 

// System.Drawing.SafeNativeMethods.Gdip 
[DllImport("gdiplus.dll", CharSet = CharSet.Unicode, ExactSpelling = true, SetLastError = true)] 
internal static extern int GdipCreateBitmapFromScan0(int width, int height, int stride, int format, HandleRef scan0, out IntPtr bitmap); 

Và nó chỉ cho tôi herehere:

Bitmap(
    [in] INT width, 
    [in] INT height, 
    [in] INT stride, 
    [in] PixelFormat format, 
    [in] BYTE *scan0 
); 

sải chân [trong]
Loại: INT
Integer chỉ định byte bù đắp giữa bắt đầu của một dòng quét và tiếp theo. Đây thường là (nhưng không nhất thiết) số lượng byte ở định dạng pixel (ví dụ: 2 cho 16 bit trên mỗi pixel) nhân với chiều rộng của bitmap. Giá trị được chuyển đến thông số này phải là bội số của bốn.

Ý nghĩa của việc vượt qua 0 là gì? Không biết, không thể tìm thấy. Có ai không? Bitmap có thể được tạo ra với bước tiến tiêu cực không? (bởi .NET new Bitmap(Width, Height, PixelFormat.Format24bppRgb)). Trong mọi trường hợp, , chúng tôi cần ít nhất kiểm tra BitmapData.Stride.

0

Tôi đã tìm thấy công thức rawStride trong ví dụ mã cho số BitmapSource Class. Có vẻ như đáng để thử tạo một mảng bằng cách sử dụng mã bên dưới và cố gắng thực thi phương thức sao chép của bạn nhiều lần mà không bị đánh bom. Nếu bạn có thể có một cơ hội công bằng rằng đây là một vấn đề kích thước mảng. Nếu dữ liệu từ máy ảnh của bạn không khớp với kích thước của bitmap trong bộ nhớ, có thể bạn sẽ phải sao chép dòng dữ liệu theo dòng.

private byte[] CreateImageByteArray(int width, int height, PixelFormat pixelFormat) 
{ 
    int rawStride = (width * pixelFormat.BitsPerPixel + 7)/8; 
    byte[] rawImage = new byte[rawStride * height]; 

    return rawImage; 
} 

Điều khác bạn nên làm là đảm bảo rằng đối tượng Bitmap đang được xử lý đúng khi bạn kết thúc. Đôi khi tôi thấy kết quả lạ với các đối tượng đã được vận hành với mã không được quản lý và không được dọn dẹp sau đó.

Ngoài ra, việc chuyển tiếp các đối tượng qua các chuỗi có thể đôi khi khó khăn. Xem How to: Make Thread-Safe Calls to Windows Forms ControlsThread-Safe Calls Using Windows Form Controls in C# bài viết.

0

Cho phép thử thay đổi ThreadApartmentState thành Single Threaded.

Ngoài ra, hãy kiểm tra các hoạt động của chuỗi chéo gây ra lỗi này.

+0

Xin chào. Điều này thực sự phải là một bình luận không phải là một câu trả lời (Tôi biết rằng bạn không có đủ đại diện để đăng bình luận). – GHC

0

Cá nhân tôi đã nhìn thấy một số lỗi heap (gỡ lỗi các bãi đổ vỡ) vì .Length được sử dụng.

Như trong:

IntPtr ptr = bitmapdata.Scan0; 
Marshal.Copy(pixeldata, 0, ptr, pixeldata.Length); 

Giải pháp cho tham nhũng đống là để làm tính toán .Length cách khác nhau:

IntPtr ptr = bitmapdata.Scan0; 
int bytes = Math.Abs(bitmapdata.Stride) * bmp.Height; 
Marshal.Copy(pixeldata, 0, ptr, bytes); 

byte và .Length có 1 sự khác biệt byte, dẫn đến tham nhũng đống .

Math.Abs ​​ được lấy trực tiếp từ ví dụ về Microsoft. Vì Stride có thể âm cho bitmap từ dưới lên.

Ví dụ microsoft: https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.scan0%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396#Examples

(. + Đừng quên các .Unlock và thêm nó vào một thử-cuối cùng tuyên bố)

Các vấn đề liên quan