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.asp

    Dasselbe Bitmap speichgere ich dann mit Image.Save()
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpref/html/frlrfsystemdrawingimageclasssavetopic.asp

    Danach 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. 🙂


Anmelden zum Antworten