2010-10-02 2 views
12

È possibile utilizzare il metodo FromStream di System.Drawing.Image senza dover mantenere aperto lo stream per la durata dell'immagine?Caricamento di un'immagine da uno streaming senza tenere aperto lo stream

Possiedo un'applicazione che carica una serie di elementi grafici della barra degli strumenti dai file di risorse, utilizzando una combinazione di Image.FromStream e Assembly.GetManifestResourceStream.

Il problema che sto avendo è mentre questo funziona bene su Windows 7, Windows XP l'applicazione si blocca se un elemento dell'interfaccia utente collegato a una di queste immagini è disabilitata. Su Windows 7, l'immagine è resa in scala di grigi. Su XP, si blocca con un'eccezione di memoria insufficiente.

Dopo un carico di hairpulling l'ho finalmente tracciato al caricamento iniziale dell'immagine. Come una cosa ovvia, se creo qualsiasi oggetto che implementi IDisposable che è anche distrutto nello stesso metodo, avvolgo in un'istruzione using, per esempio

using (Stream resourceStream = assembly.GetManifestResourceStream(resourceName)) 
{ 
    image = Image.FromStream(resourceStream); 
} 

Se rimuovo l'istruzione using modo che il flusso isn 'disposed, quindi l'applicazione non si blocca più su XP. Ma ora ho un mucchio di flussi "orfani" che si aggirano - le immagini sono archiviate in classi di comando e queste correttamente dispongono delle immagini quando sono disposte, ma il flusso originale non lo è.

ho controllato la documentazione per FromStream e conferma il flusso deve rimanere aperta. Perché questo non è andato in crash e bruciato sul sistema di sviluppo di Windows 7 è comunque un mistero!

Io davvero non voglio che questo flusso vada in giro, e certamente non voglio dover memorizzare un riferimento a questo stream così come l'immagine in modo che io possa disporre di esso in seguito. Ho solo bisogno di quel flusso una volta così voglio liberarmene :)

È possibile creare l'immagine e quindi uccidere il flusso lì e poi?

+3

A modo, un 'OutOfMemoryException' in' System.Drawing' ususally significa * errore generico * in GDI +. Il motivo è [storico] (http://stackoverflow.com/questions/2610416/is-there-a-reason-image-fromfile-throws-an-outofmemoryexception-for-an-invalid-im) ed è dovuto alla mappatura dei codici di errore non gestiti. –

+0

Se il flusso che si passa a Image.FromStream non è ricercabile, è possibile chiudere il flusso in modo sicuro. Ma questo è un comportamento non documentato, quindi usalo a tuo rischio. – user2452157

risposta

19

La ragione il flusso deve essere aperta è la following:

GDI +, e quindi la System.Drawing namespace, può rinviare la decodifica di bit di immagine grezzi finché i bit sono richiesti dall'immagine . Inoltre, anche dopo che l'immagine è stata decodificata, GDI + potrebbe determinare che è più efficiente scartare la memoria per un Bitmap di grandi dimensioni e decodificare in un secondo momento. Pertanto, GDI + deve avere accesso ai bit di origine dell'immagine per la durata dell'oggetto Bitmap o Image.

La soluzione documentata è di creare sia un'immagine non indicizzata utilizzando Graphics.DrawImage o per creare un indicizzato Bitmap della immagine originale come descritto qui:

Bitmap and Image constructor dependencies

+0

Grazie per la risposta estremamente utile! Conoscevo i file di blocco FromFile, ma non ho mai saputo il motivo per cui fino ad ora. Ho provato a utilizzare l'approccio Creare un'immagine non indicizzata che ha funzionato correttamente e l'applicazione non si blocca più sulla mia macchina virtuale XP. Verificherò di nuovo usando l'approccio indicizzato e vedrò cosa succede - non sono chiaro sulla differenza tra i due tipi. –

+2

Fwiw: hanno risolto diversi problemi con i formati dei pixel indicizzati nella versione Vista di gdiplus.dll. Bello da parte loro farlo, ma un mal di testa quando il tuo codice deve essere eseguito su XP. In generale, state alla larga da loro per evitare questo problema. –

0

Si potrebbe salvare il flusso in un file temporaneo e utilizzare il metodo Image.FromFile. O semplicemente non incorporare l'immagine, conservarla come file e caricarla da questo file in fase di runtime.

+0

Grazie per la risposta, anche se questo è qualcosa che ho già scartato, come so per esperienza che questo blocca il file non sarebbe quindi in grado di cancellarlo fino alla fine dell'applicazione. –

2

In base alla documentazione di Image.FromStream, il flusso deve essere tenuto aperto mentre l'immagine è in uso. Pertanto, anche se la chiusura ha funzionato (e non c'è nulla da dire che non è possibile chiudere uno stream prima di essere eliminato, per quanto riguarda l'oggetto stream stesso), potrebbe non essere un approccio molto affidabile.

È possibile copiare l'immagine in un altro oggetto immagine e utilizzarla. Tuttavia, è probabile che sia più intensivo della memoria che mantenere aperto il flusso.

+0

Grazie per la risposta. Questo è ciò che ho seguito, usando l'articolo della Knowledge Base collegato sopra che ha fornito i motivi per cui. –

+0

Illustra anche i dettagli mentre potrebbe essere più efficace mantenere il flusso in alcuni casi, il che vale la pena tenere a mente. –