none
Wo wird die Datei gesperrt? RRS feed

  • Frage

  • Hallo!

    Irgendwie wird meine Datei gesperrt, und ich kann später nicht mehr auf sie zugreifen.

    Sieht jemand, wo?

        Private Sub btnImport_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btnImport.Click

            Dim f As OpenFileDialog = New OpenFileDialog()
            f.Title = "Open File Dialog"
            f.InitialDirectory = "c:\"
            f.Filter = "All files (*.*)|*.*|All files (*.*)|*.*"
            f.FilterIndex = 2
            f.RestoreDirectory = True

            If f.ShowDialog() = DialogResult.OK Then
                Try
                    Me.txtImagePath.Text = f.FileName

                    Dim n As Image = Image.FromFile(f.FileName)
                    Dim bm_source As New Bitmap(f.FileName)
                    Dim bm_dest As New Bitmap(CInt(Me.Picture1.Width), CInt(Me.Picture1.Height))
                    Dim gr_dest As Graphics = Graphics.FromImage(bm_dest)

                    ' Copy the source image into the destination bitmap.
                    gr_dest.DrawImage(bm_source, 0, 0, bm_dest.Width + 1, bm_dest.Height + 1)

                    Me.Picture1.Image = bm_dest

                Catch ex As Exception

                Finally

                End Try

            End If
        End Sub

    Irgendwie kann ich jetzt (nachdem ich den Code für 's Posting vorbereitet habe) auch kein Bild mehr darstellen, es kommt nur noch das rote Kreuz für "Ungültiges Bild".

    Sieht jemand vielleicht, warum ich die Datei mit meinem obigen Code sperre und kann mir vielleicht sogar noch sagen, was ich hier jetzt verkehrt gemacht habe?

    Danke.

     

    Mittwoch, 25. Mai 2011 17:45

Antworten

  • Hallo,

    Zum Code: Du hältst immer noch einen Verweis, weil Du die Grafik zweimal lädst:
    Dim nBitmapSrc As New Bitmap(nImage) 'Make a new bitmap from the image
    

    lädt eine Bitmap und hält wie Image.FromFile die Datei offen, bis die Bitmap disposed wird.

    Reichen tut hier:

      ' Vorheriges Bild freigeben
      If Me.Picture1.Image IsNot Nothing Then
       Me.Picture1.Image.Dispose()
      EndIf
    
     ' Bitmap Kopie aus Datei erstellen und zuweisen
     Using bitmap As Image = Image.FromFile(f.FileName)
       Me.Picture1.Image = New Bitmap(bitmap)
     End Using
    
    

    und brauchst Du das zusätzliche (transparente) Pixel an den Rändern, gibt es auch dafür einen Konstruktor.

    Wobei es im allgemeinen kein Problem ist, eine Bild-Datei solange zu sperren,
    solange sie in Gebrauch ist (gegen Änderung, lesen ist möglich).
    In PictureBox Image speichern unter - ich hatte gestern den C# Thread erwischt -
    passiert das und die Bitmap wird nur beim Wechseln freigegeben.

    Zum Using:
    Des Using vornehmste Aufgabe ist, das Dispose auch bei einer Ausnahme auszuführen.
    Ohne das - und bei VB.NET 2002/3 war das noch so - müsste man schreiben:

      Dim bitmap As Image = Nothing
      Try
        bitmap = Image.FromFile(f.FileName)
        Me.Picture1.Image = New Bitmap(bitmap)
      Finally
        If bitmap IsNot Nothing Then
          bitmap.Dispose()
        End If
      End Try
    
    
    was eindeutig langatmiger wird.

    Lies Dir bitte die Kapitel zur Ausnahmebehandlung in .NET durch, die ich Dir im anderen Thread genannt hatte -
    denn das steht in engem Zusammenhang. Wenn das gesackt ist, wird die Sache nach und nach klarer.

    Gruß Elmar

    Donnerstag, 26. Mai 2011 07:01
    Beantworter

Alle Antworten

  • Hi,

    generell sollte man bei jeder Klasse, die eine Dispose Methode anbietet, auch einen entsprechenden Aufruf durchführen, wenn man das Objekt nicht mehr benötigt.

      n.Dispose()
      bm_source.Dispose()

    und ggfs. auch:

      bm_dest.Dispose()
      gr_dest.Dispose()

    wobei das ggfs. mit der Zuweisung an Picture1.Image kollidieren könnte. (Mag ich aber grad nicht testen, das kannst Du dann bitte machen :)

     


    Gruß, Stefan
    Microsoft MVP - Visual Developer ASP/ASP.NET
    http://www.asp-solutions.de/ - Consulting, Development
    http://www.aspnetzone.de/ - ASP.NET Zone, die ASP.NET Community
    Mittwoch, 25. Mai 2011 17:55
    Moderator
  • Hallo,

    na denn, gleich weiter beim Using ;-))

    Der sperrende Böswicht ist hier Image.FromFile, wie dort auch dokumentiert.
    Dahinter steckt hier GDI+, das eine Datei nicht schließt, um ggf. weiteren Zugriff auf die Bilddaten zu haben.
    Du erzeugst mit bm_source noch eine Verknüpfung - denn dahinter steckt implizt ein Image.FromFile

    Will man diese Verbindung kappen könnt man das wie folgt machen:

      Dim targetBitmap As Image
      Using bitmap As Image = Image.FromFile(f.FileName)
       targetBitmap = New Bitmap(bitmap)
      End Using
    
    
    
    Im übrigen gehört ein Graphics-Kontext ebenso in einen Using Block,
    denn dahinter steckt mit Windows Forms auch eine unmanaged Komponente,
    die direkt nach Gebrauch freigeben sollte.

    Und auch ein OpenFileDialog ist ein unmanaged Common Dialog.
    Wobei man die Datei-Daloge für die Lebenszeit eines Formulars weiter nutzen kann
    (und dazu auf das Formular ziehen).

     

    Ein Beispiel wie man eine PictureBox mit einem Image versorgt findest Du u. a. in
    Bild von Festplatte einer Picturebox zuweisen

    (dort lasse ich den Datei-Handle jedoch bis zum Wechseln am Leben ;-)

    Gruß Elmar

     

    Mittwoch, 25. Mai 2011 18:10
    Beantworter
  • Hallo Elmar,

    also ich bin schon mal ein großer Fan von USING, das ist echt sehr cool.

    Ich hätte es aber irgendwie sinnig gefunden (glaube ich), wenn die Funktionen immer selbst-endend wären und wenn man stattdessen die Lebensdauer festlegen können möchte, dass man das dann explizit angeben muss. Sowas wie "Private" auf Klassen-Ebene... Aber vielleicht finde ich es später besser, so wie es jetzt ist, wenn ich mehr Erfahrung in .NET habe.

    Ich würde aber doch gerne nochmal was fragen, ich komme nämlich seit Stunden nicht weiter.

     

            If f.ShowDialog() = DialogResult.OK Then
                Try
                    Me.txtImagePath.Text = f.FileName

                    Using nImage As Image = Image.FromFile(f.FileName) 'Load the file to an Image
                        Dim nBitmapSrc As New Bitmap(nImage) 'Make a new bitmap from the image
                        Dim nBitmapDest As New Bitmap(CInt(Me.Picture1.Width), CInt(Me.Picture1.Height)) 'Make a new bitmap with the size of the picturebox
                        Dim nGraphics As Graphics = Graphics.FromImage(nBitmapDest) 'Make a graphics object from the new bitmap
                        nGraphics.DrawImage(nBitmapSrc, 0, 0, nBitmapDest.Width + 1, nBitmapDest.Height + 1) 'Draw the source bitmap onto the new bitmap, giving it a new size
                        Me.Picture1.Image = nBitmapDest 'Set the PictureBox' image to the new bitmap
                    End Using

                Catch ex As Exception
                    MsgBox("Could not load file.", MsgBoxStyle.OkOnly, "Image")
                Finally
                    'Keine Ahnung, was da reinkommt in diesem Fall
                End Try

            End If

     

    Bei diesem Code bekomme ich kein Bild mehr (nur ein rotes Kreuz) in die PictureBox. Siehst Du, warum? Ich nämlich nicht. Ich meine, Du hattest ja auch USING verwendet, und trotzdem wurde bei Dir ein Bild angezeigt, obwohl das Image dann gelöscht wurde (am Ende des USING-Blocks), oder?

    Noch eine Frage, falls Du Nerv hast: Wenn innerhalb des Using-Blocks eine Ausnahme erfolgt, wird dann alles im Using-Block trotzdem zerstört? Weil er springt ja bei der Ausnahme zu "Catch ex", bevor er End Using erreichen kann. Hoffentlich ist meine Frage nicht so blöd wie mein All-Time-Favorit "Wird mein Code langsamer, wenn ich lange Prozedurnamen verwende?" :-)

    Liebe Grüße.

    Donnerstag, 26. Mai 2011 06:25
  • Hallo,

    Zum Code: Du hältst immer noch einen Verweis, weil Du die Grafik zweimal lädst:
    Dim nBitmapSrc As New Bitmap(nImage) 'Make a new bitmap from the image
    

    lädt eine Bitmap und hält wie Image.FromFile die Datei offen, bis die Bitmap disposed wird.

    Reichen tut hier:

      ' Vorheriges Bild freigeben
      If Me.Picture1.Image IsNot Nothing Then
       Me.Picture1.Image.Dispose()
      EndIf
    
     ' Bitmap Kopie aus Datei erstellen und zuweisen
     Using bitmap As Image = Image.FromFile(f.FileName)
       Me.Picture1.Image = New Bitmap(bitmap)
     End Using
    
    

    und brauchst Du das zusätzliche (transparente) Pixel an den Rändern, gibt es auch dafür einen Konstruktor.

    Wobei es im allgemeinen kein Problem ist, eine Bild-Datei solange zu sperren,
    solange sie in Gebrauch ist (gegen Änderung, lesen ist möglich).
    In PictureBox Image speichern unter - ich hatte gestern den C# Thread erwischt -
    passiert das und die Bitmap wird nur beim Wechseln freigegeben.

    Zum Using:
    Des Using vornehmste Aufgabe ist, das Dispose auch bei einer Ausnahme auszuführen.
    Ohne das - und bei VB.NET 2002/3 war das noch so - müsste man schreiben:

      Dim bitmap As Image = Nothing
      Try
        bitmap = Image.FromFile(f.FileName)
        Me.Picture1.Image = New Bitmap(bitmap)
      Finally
        If bitmap IsNot Nothing Then
          bitmap.Dispose()
        End If
      End Try
    
    
    was eindeutig langatmiger wird.

    Lies Dir bitte die Kapitel zur Ausnahmebehandlung in .NET durch, die ich Dir im anderen Thread genannt hatte -
    denn das steht in engem Zusammenhang. Wenn das gesackt ist, wird die Sache nach und nach klarer.

    Gruß Elmar

    Donnerstag, 26. Mai 2011 07:01
    Beantworter
  • Hallo Elmar,

    ich werde die Ausnahmebehandlung gleich durchrackern, aber ich habe noch eine Frage zu USING.

    Kannst Du mir sagen, warum mein Using-Code nicht geht? Hier ist er einmal MIT und einmal OHNE Using.

    Nur der ohne Using geht. Bei dem mit Using bekomme ich das rote Kreuz.

    Danke nochmal.

    (ps: Das mit dem PictureBox.Image.Dispose mache ich jetzt immer, bevor ich ein neue images reinlade in die PictureBox)

     

                    Using nBitmapFromFile As New Bitmap(Image.FromFile(f.FileName))
                        Using nBitmapDest As New Bitmap(nBitmapFromFile.Width, nBitmapFromFile.Height)
                            Using nGraphicsDest As Graphics = Graphics.FromImage(nBitmapDest)
                                nGraphicsDest.DrawImage(nBitmapFromFile, 0, 0, Me.PictureBox1.Width + 1, Me.PictureBox1.Height + 1)
                                Me.PictureBox1.Image = nBitmapDest
                            End Using
                        End Using
                    End Using

     

                    Dim nBitmapFromFile As New Bitmap(Image.FromFile(f.FileName))
                    Dim nBitmapDest As New Bitmap(nBitmapFromFile.Width, nBitmapFromFile.Height)
                    Dim nGraphicsDest As Graphics = Graphics.FromImage(nBitmapDest)
                    nGraphicsDest.DrawImage(nBitmapFromFile, 0, 0, Me.PictureBox1.Width + 1, Me.PictureBox1.Height + 1)
                    Me.PictureBox1.Image = nBitmapDest



    Donnerstag, 26. Mai 2011 12:03
  • Hallo,

    jetzt hast Du es beim ersten Teil mit dem Dispose ein wenig zu weit getrieben ;-)

    Du dürftest alle andere freigeben, nur die nBitmapDist nicht.

    Dadurch dass Du nBitmapDest in einen Using Block gepackt hast, wird die Bitmap
    gleich wieder freigegeben, worüber die PictureBox nicht sehr glücklich ist und Dir die kalte Schulter zeigt ;-).

    Anstatt selbst zu skalieren, ist es im allgemein sinnvoller die SizeMode-Eigenschaft zu setzen.
    Dann funktioniert das auch, wenn die PictureBox die Größe ändert, z. B. wenn man sie der
    Formulargröße anpassen will, siehe Anordnen von Steuerelementen in Windows Forms
    (was eine neue Baustelle wäre).

    Willst Du die Bitmap fix einmalig anpassen reicht:

     ' Bitmap Kopie aus Datei erstellen und zuweisen
     Using bitmap As Image = Image.FromFile(f.FileName)
      Me.Picture1.Image = New Bitmap(bitmap, Me.PictureBox.ClientSize)
     End Using
    
    Beachte, dass ich ClientSize verwendet habe, Width und Height entstammen Size,
    und sind unter Umständen (z. B. mit Rahmen) zu viel.

     

    Gruß Elmar

    Donnerstag, 26. Mai 2011 16:03
    Beantworter
  • Danke.

    Das End Using ruft automatisch bitmap.Dispose auf, ja?

    Warum sagst Du, bevor Du in die PictureBox ein neues Image setzt, .PictureBox.Image.Dispose auf? Macht die PictureBox das nicht automatisch?

    Liebe Grüße.

    Freitag, 27. Mai 2011 06:12
  • Hallo,

    anstatt das End Using ruft auf, wäre besser formuliert, das Verlassen des Blocks / Gültigkeitsbereichs.

    Wie am Ende im Beitrag von gestern gezeigt, ist die Using Anweisung
    eine Verpackung für das Aufrufen von Dispose in einem Try Finally Block.

    Die PictureBox macht das aus gutem Grund nicht selbst,
    denn sie weiß nicht, was man mit dem Bild sonst noch vorhat.
    Sonst würde das gleiche passieren wie bei Deinem Schnipsel.
    Und jedesmal eine Kopie erstellen würde den Speicher heftig belasten,
    wenn es sich um grössere Kunstwerke handelt ;-)

    Gruß Elmar

    Freitag, 27. Mai 2011 07:00
    Beantworter