Access denied wegen System.Drawing.Bitmap
-
Hallo Leute!
Ich bin gerade dabei ein kleines Fotoalbum in C# zu schreiben.
Bei der Aktion "Bild Drehen" bin ich jedoch auf ein kleines Problem gestossen:
Um ein Bild zu drehen erstelle ich zuerst eine Kopie des Originals im InternetCache Ordner, öffne diese dann, drehe sie und speichere sie dann üeber das Original im Albumordner. Das Problem ist, wenn ich ein Bild ein zweites Mal drehen möchte, bekomme ich einen Generich Error in GDI+, der damit zusammenhängt, dass die Originaldatei nicht überschrieben werden kann - und das obwohl ich bei jedem Bitmap, dass ich zu Drehen verwende immer Dispose() aufrufe.Vielleicht kann mir einer von euch ja helfen - ich jedenfalls verstehe nicht, wieso eine Datei noch immer benutzt wird, obwohl die dazugehörige Bitmap bereits Disposed wurde.
-
Post mal deinen Code und die MSDN-Links zu den verwendeten Funktionen.
-
Zum Drehen verwende ich die Methoe Image.RotateFlip()
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawingimageclassrotatefliptopic.aspDasselbe Bitmap speichgere ich dann mit Image.Save()
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawingimageclasssavetopic.aspDanach kommt nur noch Bitmap.Dispose()
Beim ersten Mal funktioniert es auch, nur wenn ich es ein zeites Mal versuche bekommt ich diesen Fehler.
P.S: Hier ist der Link zum (bis jetzt) ziehmlich Buggygen Code: http://www.tommazzo.com/vpo/VirtualPhotoOrganizer.zip
-
Der Mehrfache Aufruf dieser Methode schlägt fehl:
public void Rotate(string path, RotateFlipType flipType, int quality) { // copy the photo to a temp path string tempPath = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache) + "\\VPO_temp" + DateTime.Now.Millisecond.ToString(); System.IO.File.Copy(path, tempPath, true); Bitmap b = new Bitmap(Bitmap.FromFile(tempPath)); // create the bitmap // rotate and save the bitmap b.RotateFlip(flipType); JpegSaver js = new JpegSaver(); js.SaveJpeg(path, b, quality); // dispose the bitmap b.Dispose(); }
Das liegt daran, dass du das temporäre und namenlose Image nicht aufräumst.
Wenn wir das jetzt elegant abändern, funktioniert es:public void Rotate(string path, RotateFlipType flipType, int quality) { // copy the photo to a temp path string tempPath = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache) + "\\VPO_temp" + DateTime.Now.Millisecond.ToString(); System.IO.File.Copy(path, tempPath, true); Image image = Bitmap.FromFile(tempPath); Bitmap b = new Bitmap(image); // create the bitmap // rotate and save the bitmap b.RotateFlip(flipType); JpegSaver js = new JpegSaver(); js.SaveJpeg(path, b, quality); // dispose the bitmap b.Dispose(); image.Dispose(); }
-
Danke für die prompte Antwort, es geht jedoch immer noch nicht - ich bekomme immer noch einen Fehler in der Methode SaveJpeg
public void SaveJpeg(string path, Bitmap image, int quality) { // if we were unable to find the JPEG codec, we'll use the standard .NET quality setting of 75 if(Codec == null) image.Save(path, System.Drawing.Imaging.ImageFormat.Jpeg); else // use the specified quality { EncoderParameters ep = new EncoderParameters(); ep.Param[0] = new EncoderParameter(Encoder.Quality, quality); image.Save(path, Codec, ep); // hier kann beim zweiten Anlauf nicht gespeichert werden!!!! ep.Dispose(); } }
-
Geht es um die Rotate-Methode, die ich oben angegeben habe? Ich kann nach dem Fix folgenden Code ausführen:
/// <summary> /// Application's main entry point /// </summary> [STAThread] static void Main() { Resizer test = new Resizer(); for( int i = 0; i < 1000; ++i ) test.Rotate("test.bmp", RotateFlipType.Rotate90FlipX, 100); Application.Run(new MainForm()); }
was auch beinhaltet, SaveJpeg() 1000mal aufzurufen. Ich kann das also nicht reproduzieren.
-
Die Rotate() Methode ruft die oben genannte SaveJpeg Methode auf.
In deinem Testcode funktioniert das ganze auch, wenn ich jedoch ein Bild im Album 2X drehe geht es nicht.Versuche mal ein Foto in ein Album testweise zu importieren (Rechtsklick auf den AlbumsPane) und es dann zu drehen - in diesem Fall geht es nicht!
-
Ich habe das Problem soeben gelöst: Ich hatte in der Methode CreateThumbnail() noch ein namenloses Image herumschwirren:
public void CreateThumbnail(string srcPath, string destPath) { // initialize the tempPath var System.IO.FileInfo fi = new System.IO.FileInfo(destPath); string tempPath = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache) + '\\' + fi.Name + "temp" + fi.Name.GetHashCode(); // the defined constants for the max thumbnail dimensions const int cthWidth = 150; const int cthHeight = 113; // the aspect ratio and the newly calculated thumbnail dimensions int thWidth = 0; int thHeight = 0; Bitmap srcImage = new Bitmap(Bitmap.FromFile(srcPath)); // load the source image // calculate the new tumbnail dimensions, based on the longer side of the image double ratio = srcImage.Height / srcImage.Width; if(ratio < cthHeight / cthWidth) { // Width is constant thWidth = cthWidth; thHeight = (srcImage.Height * cthWidth) / srcImage.Width; } else { // Height is constant thHeight = cthHeight; thWidth = (srcImage.Width * cthHeight) / srcImage.Height; } /*if(srcImage.Width > srcImage.Height) { thWidth = cthWidth; thHeight = srcImage.Height / (srcImage.Width / cthWidth); } else { thHeight = cthHeight; thWidth = srcImage.Width / (srcImage.Height / cthHeight); }*/ // create the thumbnail and save it Image thumb = srcImage.GetThumbnailImage(thWidth, thHeight, null, IntPtr.Zero); thumb.Save(tempPath); // dispose the gdi+ objects srcImage.Dispose(); thumb.Dispose(); // draw the border around the thumb CreateThumbBorder(tempPath, destPath, cthWidth, cthHeight); }
So ist's besser:
public void CreateThumbnail(string srcPath, string destPath) { // initialize the tempPath var System.IO.FileInfo fi = new System.IO.FileInfo(destPath); string tempPath = Environment.GetFolderPath(Environment.SpecialFolder.InternetCache) + '\\' + fi.Name + "temp" + fi.Name.GetHashCode(); // the defined constants for the max thumbnail dimensions const int cthWidth = 150; const int cthHeight = 113; // the aspect ratio and the newly calculated thumbnail dimensions int thWidth = 0; int thHeight = 0; // load the source image Image img = Bitmap.FromFile(srcPath); // jetzt hat das namenlose ding einen namen! Bitmap srcImage = new Bitmap(img); // calculate the new tumbnail dimensions, based on the longer side of the image double ratio = srcImage.Height / srcImage.Width; if(ratio < cthHeight / cthWidth) { // Width is constant thWidth = cthWidth; thHeight = (srcImage.Height * cthWidth) / srcImage.Width; } else { // Height is constant thHeight = cthHeight; thWidth = (srcImage.Width * cthHeight) / srcImage.Height; } /*if(srcImage.Width > srcImage.Height) { thWidth = cthWidth; thHeight = srcImage.Height / (srcImage.Width / cthWidth); } else { thHeight = cthHeight; thWidth = srcImage.Width / (srcImage.Height / cthHeight); }*/ // create the thumbnail and save it Image thumb = srcImage.GetThumbnailImage(thWidth, thHeight, null, IntPtr.Zero); thumb.Save(tempPath); // dispose the gdi+ objects img.Dispose(); // und natürlich disposen srcImage.Dispose(); thumb.Dispose(); // draw the border around the thumb CreateThumbBorder(tempPath, destPath, cthWidth, cthHeight); }
Nochmals vielen Dank für deine Hilfe, ohne dich hätte ich wahrscheinlich noch ein Monat im Code suchen müssen!
-
Ah, cool. Ich hab da gestern nicht mehr so viel Zeit gehabt und wollte heut noch ein bisschen rumsuchen. Schön, dass ich mir das jetzt sparen kann.
Falls du es noch nicht kennst, könnte das using-Statement für dich interessant sein: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/csref/html/vclrfusingstatement.asp
-
Danke für den Tipp - das könnte mir in Zukunft vielleicht einige dieser Probleme ersparen.