2009-03-24 9 views
5

Sto lavorando a un'applicazione C# che consente di visualizzare immagini in diretta da una videocamera. Il problema che sto affrontando con il seguente frammento di codice è che, ottengo AccessViolationException in Marshal.Copy quando si esegue questa funzione eseguita continuamente in un thread. Ma, questo funziona con successo una volta eseguito (ottengo una singola immagine statica). Immagino che abbia a che fare con qualche problema di corruzione della memoria. Qualche idea/suggerimenti su come affrontare questo problema?Il metodo Marshal.Copy genera AccessViolationException in 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

Non sono sicuro, ma si potrebbe provare a mettere un lucchetto intorno 'LockBits' e 'UnLockBits'. Poiché questa istruzione blocca la bitmap sulla memoria di sistema. E l'eccezione dice l'accesso non valido alla memoria protetta. – Savaratkar

risposta

9

Sembra a me come si sta sempre cercando di copiare il numero di byte myByteArray.Length al buffer bitmap.

Non si sta verificando che il buffer bitmap sia effettivamente così grande, quindi probabilmente si sta scrivendo alla fine del buffer bitmap.

Prova a controllare se myByteArray.Length è mai superiore a bmpData.Stride x bmp.Height

Se questo è il caso avrete bisogno di relook le ipotesi che hai fatto con il vostro disco valori codificati per larghezza, altezza e formato pixel.

6

Non è necessario copiare l'intera immagine in una sola volta. L'allocazione della memoria dell'oggetto bitmap potrebbe non essere quello che ti aspetti. Ad esempio, la prima riga di scansione può essere archiviata per ultima nella memoria, il che significherebbe che i dati per la seconda riga di scansione finirebbero fuori dall'area di memoria allocata per l'oggetto bitmap. Inoltre potrebbe esserci una spaziatura tra le linee di scansione per posizionarle su un indirizzo pari.

Copia una riga alla volta, utilizzando bmpData.Stride per trovare la prossima linea di scansione:

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

risposta per me: ha dimenticato di ->

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

Qualcuno ha capito questo? Si tratta della quarta pagina senza una risposta. Utilizzando il codice esatto da MSDN: http://msdn.microsoft.com/en-us/library/system.drawing.imaging.bitmapdata.aspx che è:

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

Questo non funziona nella modalità di ottimizzare e funzionante come exe non in IDE. Qualche idea Ho provato a mettere questo è un nuovo progetto e se ho collegato per elaborare quando preme un pulsante, e premere questo pulsante più volte l'errore si verifica, ma nel mio codice sto solo chiamando una volta, in entrambi i casi non so perché l'errore.

+0

Stai ignorando il fatto che un pixel è più di un byte a seconda del formato pixel in 'int bytes = bmpData.Stride * bmp.Height;' – VVS

0

Forse

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

ha argomento non valido per la scrittura solo - provare ReadWrite, o di sola lettura in ImageLockMode? Forse questo aiuta.

0

Cercavo un po 'e se si salta la possibilità che la matrice non di dimensioni appropriate è, si finisce in Remarks for BitmapData.Stride Property:

Il passo è la larghezza di una singola fila di pixel (una scansione linea), arrotondato a un limite di quattro byte. Se la falcata è positiva, la bitmap è top-down. Se il passo è negativo, la bitmap è dal basso verso l'alto.

Così, forse dovremmo fare in questo modo:

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

Ma mi sono chiesto: Abbiamo creato la bitmap: new Bitmap(Width, Height, PixelFormat.Format24bppRgb); ... così, come potrebbe eventualmente essere negativo?
Time for 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); 

E mi indicò here e here:

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

passo [in]
Tipo: INT
intero che specifica il byte offset tra l' inizio di una linea di scansione e la successiva. Questo è solitamente (ma non necessariamente) il numero di byte nel formato pixel (ad esempio, 2 per 16 bit per pixel ) moltiplicato per la larghezza della bitmap. Il valore passato a questo parametro deve essere un multiplo di quattro.

Cosa significa passare 0? Non lo so, non sono riuscito a trovarlo. Qualcuno? La bitmap può essere creata con andatura negativa? (tramite .NET new Bitmap(Width, Height, PixelFormat.Format24bppRgb)). In ogni caso, dobbiamo almeno controllare BitmapData.Stride.

0

Ho trovato la formula rawStride nell'esempio di codice per BitmapSource Class. Sembra che valga la pena provare a creare un array usando il codice sottostante e tentando di eseguire il tuo metodo di copia più volte senza bombardarlo. Se puoi, c'è una buona possibilità che questo sia un problema di dimensionamento della matrice. Se i dati della tua fotocamera non corrispondono alla dimensione della bitmap in memoria, probabilmente dovrai copiare i dati riga per riga.

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

    return rawImage; 
} 

L'altra cosa che dovresti fare è assicurarti che l'oggetto Bitmap sia Smaltito correttamente quando lo finisci. Occasionalmente ho visto risultati strani con oggetti che sono stati utilizzati con codice non gestito e non ripuliti successivamente.

Inoltre, il passaggio di oggetti attraverso i thread può essere difficile a volte. Vedi articolo How to: Make Thread-Safe Calls to Windows Forms Controls e Thread-Safe Calls Using Windows Form Controls in C#.

0

Consente di provare ThreadApartmentState su Single Threaded.

Inoltre, controllare le operazioni tra thread che causano questi errori.

+0

Ciao. Questo dovrebbe essere davvero un commento, non una risposta (so che non hai abbastanza rep per postare ancora commenti). – GHC

0

Ho visto alcune corruzioni di heap (eseguire il debug dei dump di arresto anomalo) perché è stato utilizzato .Length.

Come in:

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

La soluzione al danneggiamento dell'heap era fare calcolare il .Length diversamente:

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

byte e .Length aveva 1 differenza byte, con conseguente danneggiamento di heap .

Math.Abs ​​ è stato preso direttamente dall'esempio di Microsoft. Poiché Stride può essere negativo per una bitmap bottom-up.

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

(. + Non dimenticare il .Unlock e aggiungerlo in un try-finally)