OutOfMemoryException - bei Bitmaps kopieren

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 28 Antworten in diesem Thema. Der letzte Beitrag () ist von MichaHo.

    OutOfMemoryException - bei Bitmaps kopieren

    Hi,
    ich weis nicht woran es liegt, aber nachfolgender Code erzeugt eine OutOfMemoryException sobald das erste Bild kopiert wurde:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Environment
    3. Public Class frmMain
    4. Private Destination As String = GetFolderPath(SpecialFolder.MyPictures) & "\winSpotlight\"
    5. Private Source() As FileInfo = New DirectoryInfo(GetFolderPath(SpecialFolder.LocalApplicationData) & "\Packages\Microsoft.Windows.ContentDeliveryManager_cw5n1h2txyewy\LocalState\Assets").GetFiles
    6. Private bmpCounter As Integer = 0
    7. Private Sub btnCopyImage_Click(sender As Object, e As EventArgs) Handles btnCopyImage.Click
    8. For Each f As FileInfo In Source
    9. Using bmp = New Bitmap(Bitmap.FromFile(f.FullName))
    10. If bmp.Width < 1900 Then
    11. bmp.Dispose()
    12. Continue For
    13. End If
    14. bmp.Save(Destination & "Wallpaper" & bmpCounter & ".bmp")
    15. bmpCounter += 1
    16. End Using
    17. Next
    18. End Sub
    19. End Class

    Kann mir jemand sagen was da falsch ist?
    "Hier könnte Ihre Werbung stehen..."
    Vielleicht macht die Länge der Pfade in Source() Schwierigkeiten. Da gibt's ne Zeichenbegrenzung.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    Hmm, hab mir mal die länge des Pfades angeschaut, komme auf 179 Zeichen... die Begrenzung ist doch 255, richtig?

    Kann ich das irgendwie umgehen? denn der Pfad ist ja nun auch schon etwas länger... (also der Pfad zum Ordner wo ich hin muss)
    "Hier könnte Ihre Werbung stehen..."
    Ja die Begrenzung liegt bei 255/256. Umgehen war hier neulich mal Thema, war aber nicht so einfach.

    Warum nutzt du nicht die File.Copy Methode?
    msdn.microsoft.com/de-de/library/9706cfs5(v=vs.110).aspx
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @MichaHo Dann mach es einfach so (ggf. stimmen dann die Extrensionen nicht, der IrfanView korrigiert dass aber):

    VB.NET-Quellcode

    1. For Each f As FileInfo In Source
    2. f.CopyTo(Destination & "Wallpaper" & bmpCounter & ".jpg")
    3. bmpCounter += 1
    4. 'Using bmp = New Bitmap(Bitmap.FromFile(f.FullName))
    5. ' If bmp.Width < 1900 Then
    6. ' 'bmp.Dispose()
    7. ' Continue For
    8. ' End If
    9. ' bmp.Save(Destination & "Wallpaper" & bmpCounter & ".png", Imaging.ImageFormat.Png)
    10. ' bmpCounter += 1
    11. 'End Using
    12. Next
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hab grad nachgeschaut. Die Exception fliegt auch wenn das Dateiformat ungültig ist. Das Format wird von dir nicht geprüft.

    -> msdn.microsoft.com/de-de/library/4sahykhd(v=vs.110).aspx
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @RodFromGermany, @mrMo Danke für Eure Antworten...

    das f.CopyTo funktioniert natürlich, das war meine erste Variante. Das Problem daran ist aber, das ich dann nochmal in den Ordner muss und die Bitmaps, die nichts sind (zu klein, zu schmal, nicht für Desktop geeignet) händisch löschen muss...
    Daher hatte ich die Variante mit dem Bitmap, damit ich schauen kann, wie die Width eines Bitmaps ist und dadurch raus filtern kann...
    gibt es vieleicht eine Möglichkeit das zu umgehen?
    Im Grunde möchte ich folgendes erreichen:
    Anwendung läuft im Hintergrund und überwacht den Source Ordner mit einem Filesystemwatcher. Kommt ein neues Bild hinzu, wird es in den Wallpaper Ordner kopiert.
    Der Wallpaper Ordner dient als Bildschirmschoner Ordner...

    EDIT: @Fakiz, gerade erst deinen Post gesehen. das schaue ich mir an, danke
    @mrMo: die Dateien haben scheinbar kein Dateiformat was ich prüfen könnte:
    "Hier könnte Ihre Werbung stehen..."
    @MichaHo Ich hab mal identifiziert, bei welcher Datei das knallt:

    Das ist so ein Online-Format.
    Da machst Du um Deine Schleife ein Try / Catch mit dieser Exception und feddich.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Habs bei mir am PC mal getestet. Die Dateien haben kein prüfbares Dateiformat sondern sind irgendwelche komischen Dinger aus dem Internet. Wohl irgendwelches gecachtes Zeug (bei mir zumindest). Das kann mal also nicht prüfen. Würde wohl die Brechstange nehmen und, wie Rod empfohlen hat, die Fehler mit nem Try/Catch verschlucken.
    "Gib einem Mann einen Fisch und du ernährst ihn für einen Tag. Lehre einen Mann zu fischen und du ernährst ihn für sein Leben."

    Wie debugge ich richtig? => Debuggen, Fehler finden und beseitigen
    Wie man VisualStudio nutzt? => VisualStudio richtig nutzen
    @RodFromGermany, @mrMo, @Bluespide.
    Bei den Dateien handelt es sich um die dynamischen Bilder von Windows 10´s Windows Spotlight.
    Immer wenn der PC gesperrt wird, erscheint ein Bild, im Hintergrund werden diese in den besagten Ordner (Source) abgelegt.
    Da da wirklich recht ansehnliche Bilder dabei sind, speichere ich mir die, die eine minimum breite vonb 1900 Pixel haben in einen anderen Ordner und lasse dann diesen Ordner als Bildschirmschoner laufen...
    Das zum Hintergrund...
    Ich werd einen Try/Catch drum rum bauen...
    Das bmp.dispose mache ich ja, wenn das bild kleiner als die geforderte breite ist, damit es halt ausm Speicher fliegt und dann soll das nächste Bild abgearbeitet werden. Das das Using am Ende der Verarbeitung ein Dispose auslöst weis ich, deswegen nutze ich ja using :D... Da ich aber ja mit Continue For die schleife an der Stelle verlasse, muss ich das Bild doch selbst disposen oder sehe ich das falsch?
    Ich sag Euch Bescheid wie es klappt und schreib dann auch den entsprechenden Code hier rein...
    Jetzt muss ich erstmal nen Kleiderschrank aufbauen... mein Kleiner nörgelt schon seit ner Stunde... :D
    Danke Euch allen....
    "Hier könnte Ihre Werbung stehen..."

    MichaHo schrieb:

    muss ich das Bild doch selbst disposen oder sehe ich das falsch?
    Nein, das musst Du nicht selbst disposen, genau dafür gibt es das Using.
    =======
    Alle rüberkopieren mit einer Extension und dann alle im IrfanView ansehen, die falschen und kleinen löschen und feddich.
    Übriggeblieben sind bei mir 9 Bilder. :thumbsup:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „RodFromGermany“ ()

    Hi @RodFromGermany
    bei meinem Try/Catch kracht es immernoch, sobald ich mit Using arbeite...

    VB.NET-Quellcode

    1. Private Sub btnCopyImage_Click(sender As Object, e As EventArgs) Handles btnCopyImage.Click
    2. Try
    3. For Each f As FileInfo In Source
    4. Using img = Image.FromFile(f.FullName)
    5. If img.Width < 1900 Then Continue For
    6. img.Save(Destination & "Wallpaper" & imgCounter & ".jpg")
    7. imgCounter += 1
    8. End Using
    9. Next
    10. Catch ex As OutOfMemoryException
    11. MessageBox.Show(ex.Message)
    12. End Try
    13. End Sub
    "Hier könnte Ihre Werbung stehen..."

    ErfinderDesRades schrieb:

    und bei anderen nicht?
    Siehe Post #9, habs probiert.
    @MichaHo Mach mal das Try / Catch in die For Each-Schleife rein und lass mal testweise alle Exceptions zu.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @ErfinderDesRades, @RodFromGermany ich bins mal im Einzelschritt durch gegangen...
    Solange die Image Width < 1900 ist, läuft der Code durch -> Continue For
    Sobald das erste File die geforderte mindesbreite von 1900 hat, fliegt die Exception:
    er scheint auch garnicht in den Catch rein zu laufen, denn die Messagbox wird nicht gezeigt...
    Kommentiere ich die Prüfung nach der Width aus, kopiert er mir Files rüber und wandelt sie in .jpg um:
    Somit kann es eigentlich auch nicht daran liegen, das er das Dateiformat nicht erfassen kann, oder?
    Ich setz mich mal an einen Workaround...
    Erst alle Files rüber kopieren und umbenennen, dann im Zielordner die Files checken und alle zu kleinen löschen....
    Mal schauen ob das geht...
    "Hier könnte Ihre Werbung stehen..."
    @RodFromGermany, @ErfinderDesRades, @mrMo, @Bluespide, @Fakiz
    Ich hab jetzt eine für mich funktionierende Lösung gefunden.
    Die Dateien, die nicht konvertiert werden konnten, waren meist kleine Dateien (kleienr 20 KB) oder sogar 0 Byte Dateien.
    Da die Wallpapers, die ich haben möchte meist größer 500 KB sind, frage ich nun einfach die Länge der Datei ab und kopiere nur die, die größer 500 KB sind.
    Im Ergebnis bekomme ich dann genau die 10 Dateien, die es tatsächlich sind.

    Hier nun der Code, der, wie gesagt, soweit funktioniert...

    VB.NET-Quellcode

    1. Private Sub btnCopyImage_Click(sender As Object, e As EventArgs) Handles btnCopyImage.Click
    2. For Each f As FileInfo In Source
    3. If f.Length > 512000 Then
    4. Using img As Image = Image.FromFile(f.FullName)
    5. If img.Width >= 1900 Then
    6. img.Save(Destination & "Wallpaper" & imgCounter & ".jpg")
    7. imgCounter += 1
    8. End If
    9. End Using
    10. End If
    11. Next
    12. End Sub

    Danke Euch allen für die Denkanstöße...
    "Hier könnte Ihre Werbung stehen..."

    MichaHo schrieb:

    Sobald das erste File die geforderte mindesbreite von 1900 hat, fliegt die Exception

    tja, das war wohl eine Fehl-Information. Die bösen Dateien sind nicht die mit .Width>1900, sondern es sind meist oder immer die kleinen Dateien, die sich nicht einlesen lassen. Und naja - wenn eine Datei sich nicht einlesen lässt, dann kann man natürlich erst garnet abprüfen, ob das Image.Width >= 1900.
    Wie gesagt: Eine sichere und logische Lösung wäre, mit TryCatch abzufangen, ob die Datei einlesbar ist.
    Und diesen Trycatch innerhalb der ForEach, damit nicht gleich die ganze Methode beendet, wenn eine Niete gezogen wird.
    Aber zusätzlich alle Winz-Dateien von vornherein aussondern ist auch sinnvoll, weil spart glaub erheblich Resourcen.