2009-03-24 3 views
5

카메라의 라이브 이미지를 표시하는 C# 응용 프로그램을 만들고 있습니다. 다음 코드 단편에 직면 한 문제는 스레드에서 계속 실행되는이 함수를 실행할 때 Marshal.Copy에서 AccessViolationException이 발생한다는 것입니다. 그러나이 작업은 한 번 실행하면 성공적으로 실행됩니다 (단일 정적 이미지를 얻음). 나는 그것이 메모리 손상 문제와 관련이 있다고 생각한다. 이 문제를 해결하는 방법에 대한 아이디어 나 제안은 없습니까? 당신은 항상 비트 맵 버퍼에 바이트 myByteArray.Length의 수를 복사하려고처럼Marshal.Copy 메서드는 C# .NET에서 AccessViolationException을 throw합니다.

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

확실하지,하지만 당신은 'LockBits'주위에 잠금을 넣어보십시오 수 있으며, 'UnlockBits'. 이 문은 시스템 메모리의 비트 맵을 잠급니다. 그리고 예외는 보호 된 메모리에 대한 무효 액세스를 알려줍니다. – Savaratkar

답변

9

그것은 나에게 보인다.

비트 맵 버퍼가 실제 크기보다 큰지 확인하지 않으므로 비트 맵 버퍼의 끝을 쓸 것입니다.

myByteArray.Length이 당신이 하드로 만든 한 가정에서 relook해야합니다 경우라면 bmpData.Stride이 bmp.Height

X 이제까지 큰 보다 경우에 체크하십시오 너비, 높이 및 픽셀 형식에 대한 코드 값.

6

전체 이미지를 한 번에 복사해서는 안됩니다. 비트 맵 객체의 메모리 할당이 예상 한 것과 다를 수 있습니다. 예를 들어, 제 1 스캔 라인은 메모리에 마지막으로 저장 될 수 있으며, 이것은 제 2 스캔 라인에 대한 데이터가 비트 맵 오브젝트에 대해 할당 된 메모리 영역 외부로 끝날 것이라는 것을 의미한다. 또한 짝수 주소에 배치하기 위해 스캔 라인 사이에 패딩이있을 수 있습니다. 나를 위해

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

대답 : 다음 스캔 라인을 찾을 bmpData.Stride를 사용하여 한 번에

복사 한 줄은 - 사람이 알아 냈나요>

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

깜빡? 이것은 대답이없는 네 번째 페이지입니다. MSDN에서 정확한 코드를 사용 : http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx 것은 어떤이는 최적화 모드에서 작동 및 IDE가 아닌 exe 인으로 실행하지 않는

     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); 

. 모든 아이디어 나는이 새로운 프로젝트를 넣으려고했는데 버튼을 눌렀을 때 프로세스에 붙이고이 버튼을 여러 번 누르면 오류가 발생하지만 내 코드에서는 한 번만 호출합니다. 오류. ImageLockMode에서 읽기 쓰기 또는 읽기 전용을 시도 -

+0

'int bytes = bmpData.Stride * bmp.Height; '의 픽셀 형식에 따라 한 픽셀이 두 개 이상의 바이트라는 사실을 무시하고 있습니다. – VVS

0

아마

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

쓰기에 대한 잘못된 인수에만있다? 아마도 도움이됩니다.

0

내가 약간 검색하고, 우리가 배열되지 적절한 크기인지 가능성을 이동하는 경우, 우리는 Remarks for BitmapData.Stride Property 끝나는 :

스트라이드 픽셀의 단일 행의 폭 (스캔 인 라인), 은 4 바이트 경계로 반올림됩니다. 스트라이드가 양수이면 비트 맵이 하향식입니다. 보폭이 음수이면 비트 맵은 상향식입니다.

그래서, 어쩌면 우리는이 방법을 수행해야합니다

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); 

을하지만 난 궁금 : new Bitmap(Width, Height, PixelFormat.Format24bppRgb);이 ... 그래서, 이 어떻게 가능 음수가 될 수있다 : 우리는 비트 맵을 만들었습니다? ILSpy에 대한
시간 : INT
정수 바이트를 지정 사이의 오프셋 : 유형
[에]

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

보폭 :

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); 

는 그리고 그것은 나에게 herehere을 지적 하나의 스캔 라인과 다음 스캔 라인의 시작. 일반적으로 픽셀 형식의 바이트 수 (예 : 픽셀 당 16 비트의 2)에 비트 맵의 ​​너비를 곱한 값 (반드시 그런 것은 아닙니다)입니다. 이 매개 변수에 전달 된 값은 4의 배수 여야합니다.

0을 전달한다는 것은 무엇을 의미합니까? 모르겠다. 찾을 수 없습니다. 아무도 할 수 있니? 네거티브 스트라이드로 비트 맵을 만들 수 있습니까? (에 의해. NET new Bitmap(Width, Height, PixelFormat.Format24bppRgb)). 어떤 경우 든 적어도 BitmapData.Stride을 확인해야합니다.

0

BitmapSource Class에 대한 코드 예제에서 rawStride 수식을 찾았습니다. 아래 코드를 사용하여 배열을 만들고 폭격없이 복사 방법을 여러 번 실행하려고하면 가치가있는 것 같습니다. 가능한 경우 배열 크기 조정 문제 일 가능성이 있습니다. 카메라의 데이터가 메모리의 비트 맵 크기와 일치하지 않으면 데이터를 한 줄씩 복사해야합니다.

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

    return rawImage; 
} 

당신이해야 할 다른 일은 당신이 끝내면 비트 맵 객체가 제대로 폐기되었는지 확인하는 것입니다. 나는 관리되지 않는 코드로 조작 된 객체로 때때로 이상한 결과를 보았고 이후에 정리되지 않았습니다.

또한 스레드간에 객체를 전송하는 것은 때로는 까다로울 수 있습니다. How to: Make Thread-Safe Calls to Windows Forms ControlsThread-Safe Calls Using Windows Form Controls in C# 도움말을 참조하십시오.

0

ThreadApartmentState를 Single Threaded로 변경하십시오.

또한이 오류의 원인이되는 스레드 간 작업을 확인하십시오.

+0

Hello. 이것은 정말로 대답이 아닌 주석이어야합니다 (아직 코멘트를 게시 할 충분한 담당자가 없다는 것을 알고 있습니다). – GHC

0

.Length가 사용 되었기 때문에 개인적으로 힙 손상 (디버그 크래시 덤프)을 보았습니다.

로서 지역 :

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

힙 손상에 대한 해결책은 다르게 .Length를 산출 할이었다

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

바이트와 .Length 힙 손상의 결과로, 1 개 바이트의 차이가 있었다 .

Math.Abs ​​은 Microsoft의 예에서 직접 가져온 것입니다. 스트라이드는 상향식 비트 맵에서 음수가 될 수 있기 때문에.

예 마이크로 소프트 : https://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.scan0%28v=vs.110%29.aspx?f=255&MSPPError=-2147217396#Examples

(. +를 .Unlock을 잊고 시도 - finally 문에 추가하지 않음)

관련 문제