Freigeben von Ressourcen

  • VB.NET

Es gibt 36 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Freigeben von Ressourcen

    Ich habe eine Routine zum Ver- und Entschlüsseln von Dateien. Da ganze Verzeichnisse dateiweise verschlüsselt werden, sind dabei gelegentlich recht große Datenmengen zu verarbeiten.

    Wenn ich die Dateien einzeln verarbeite klappt das auch problemlos. Wenn ich das aber in einer Schleife für ein Verzeichnis mache, kriege ich nach 4 - 5 Dateien eine "OutOfMemory" Exception. Irgendwie werden Ressourcen also nicht richtig freigegeben.

    Mein Rahmencode sieht ungefähr so aus:

    VB.NET-Quellcode

    1. Private Function ProcFile(ByVal strObject As String) As Integer
    2. Dim bytFileBuffer As Byte()
    3. Try
    4. bytFileBuffer = File.ReadAllBytes(strObject)
    5. ...
    6. End Try
    7. Dim bytFileBufferEncrypted As Byte()
    8. Dim wrapper As New Simple3Des(txtPwrd.Text)
    9. Try
    10. bytFileBufferEncrypted = wrapper.EncryptData(bytFileBuffer)
    11. ...
    12. End Try
    13. Try
    14. File.WriteAllBytes(strNewObject, bytFileBufferEncrypted)
    15. ...
    16. End try
    17. Return vbOK
    18. End Function


    bytFileBuffer und bytFileBufferEncrypted können recht groß werden (4 - 10 MB). Macht es Sinn, die am Ende der Funktion zu löschen? Und wie würde man das machen?

    Im Task Manager sehe ich eigentlich nix besonderes ... der belegte Speicher geht auf bis zu 900 KB hoch ... sinkt dann aber wieder auf 200-300KB ... bis dann irgendwann die Memory Exception zuschlägt!

    Wo sollte ich ansetzen? Irgendwie wird da wohl eine "Garbage Collection" empfohlen. GC.irgendwas? Aber so richtig klar ist mir das nicht geworden.

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

    Peter329 schrieb:

    Wo sollte ich ansetzen?
    Sorge nach jeder Datei dafür, dass der Speicher wieder so aussieht wie vorher.
    Mach Dir z.B. eine Klasse, die das ganze macht, nach jedem Vorgang machst Du eine neue Instanz dieser Klasse.
    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!
    Ich habe die Routinen "lesen File - verschlüsseln/entschlüsseln File - schreiben File" in eine eigene Klasse ausgelagert.

    Die rufende Routine sieht jetzt wie folgt aus:

    VB.NET-Quellcode

    1. 'ENCRYPT file
    2. Dim wrapper1 As New EncryptDecryptFile
    3. lblMessage.Text = wrapper1.EncryptFile(strObject, txtPwrd.Text, strNewObject)
    4. ...
    5. 'DECRYPT file
    6. Dim wrapper1 As New EncryptDecryptFile
    7. lblMessage.Text = wrapper1.DecryptFile(strObject, txtPwrd.Text, strNewObject)


    Bisher ist das Programm nach ca. 4 Files abgebrochen ... nun schaffe ich immerhin 8 Files bis die OutOfMemory Exception zuschlägt.

    Außerhalb der Klasse "EncryptDecryptFile" laufen aber nur noch harmlose Anweisungen.

    Irgendwie klappt das also mit dem Aufräumen noch nicht so richtig. Muss ich die Instanzierung der Klasse "EncryptDecryptFile" nicht irgendwie beenden?

    [edit]

    Ich hab mal

    VB.NET-Quellcode

    1. wrapper1 = Nothing


    versucht. Aber das Ergebnis bleibt das Gleiche. Nach 8 Files ist Ende im Gelände.

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

    Mir ist dein CryptoWrapper sehr suspekt. Der scheint die ganze Datei in ein Array einzulesen und ein verschlüsseltes Array draus zu generieren, was wegzuschreiben ist. Evtl. fahren intern da noch MemoryStreams herum (die du vmtl. nicht korrekt aufräumst)

    Egal: Nutze besser die bereits gegebenen Stream-Konzepte: Streams kann man beliebig zusammenstöpseln, also setze einen CryptoStream auf einen lesenden Filestream, und kopiere den CryptoStream mit Stream.WriteTo in einen schreibenden Filestream und feddich.
    Jau, ich glaube du hast meinen Crypto Wrapper wohl richtig erraten.

    VB.NET-Quellcode

    1. Public NotInheritable Class Simple3Des
    2. Private TripleDes As New TripleDESCryptoServiceProvider
    3. Private Function TruncateHash(ByVal key As String, ByVal length As Integer) As Byte()
    4. Dim sha1 As New SHA1CryptoServiceProvider
    5. 'Hash the key.
    6. Dim keyBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(key)
    7. Dim hash() As Byte = sha1.ComputeHash(keyBytes)
    8. 'Truncate or pad the hash.
    9. ReDim Preserve hash(length - 1)
    10. Return hash
    11. End Function
    12. Sub New(ByVal key As String)
    13. 'Initialize the crypto provider.
    14. TripleDes.Key = TruncateHash(key, TripleDes.KeySize \ 8)
    15. TripleDes.IV = TruncateHash("", TripleDes.BlockSize \ 8)
    16. End Sub
    17. Public Function EncryptData(ByRef plainTextBytes As Byte()) As Byte()
    18. ' Create the stream.
    19. Dim ms As New System.IO.MemoryStream
    20. ' Create the encoder to write to the stream.
    21. Dim encStream As New CryptoStream(ms,
    22. TripleDes.CreateEncryptor(),
    23. System.Security.Cryptography.CryptoStreamMode.Write)
    24. ' Use the crypto stream to write the byte array to the stream.
    25. encStream.Write(plainTextBytes, 0, plainTextBytes.Length)
    26. encStream.FlushFinalBlock()
    27. encStream.Close()
    28. Return ms.ToArray
    29. End Function
    30. Public Function DecryptData(ByRef encryptedBytes As Byte()) As Byte()
    31. ' Create the stream.
    32. Dim ms As New System.IO.MemoryStream
    33. ' Create the decoder to write to the stream.
    34. Dim decStream As New CryptoStream(ms,
    35. TripleDes.CreateDecryptor(),
    36. System.Security.Cryptography.CryptoStreamMode.Write)
    37. ' Use the crypto stream to write the byte array to the stream.
    38. decStream.Write(encryptedBytes, 0, encryptedBytes.Length)
    39. decStream.FlushFinalBlock()
    40. decStream.Close()
    41. Return ms.ToArray
    42. End Function
    43. End Class

    ErfinderDesRades schrieb:

    also setze einen CryptoStream auf einen lesenden Filestream, und kopiere den CryptoStream mit Stream.WriteTo in einen schreibenden Filestream und feddich.


    Das klingt sehr überzeugend. Nur stellst du mich da erst mal vor unlösbare Probleme.

    Wie setze ich einen CryptoStream auf einen lesenden Filestream?

    Wie kopiere ich den CryptoStream mit Stream.WriteTo in einen schreibenden Filestream?

    hmm ... ich sehe mir fehlt schon wieder Basiswissen ... :(

    Peter329 schrieb:

    Und wie geht's nun weiter?
    Probier mal, solche Instanzen, deren Klasse IDisposable implementieren, per Using aufzurufen.

    VB.NET-Quellcode

    1. Dim strAllText As String
    2. Using sr As New StreamReader("...mypath...")
    3. strAllText = sr.ReadToEnd
    4. End Using

    VB.NET-Quellcode

    1. Using TripleDes As New TripleDESCryptoServiceProvider
    2. ' TripleDes verwenden
    3. End Using
    usw.
    Wenn eine Klasse das nicht kann, meckert der Compiler, da geht dann das Using nicht.
    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!

    VB.NET-Quellcode

    1. Using wrapper2 As New Simple3Des(strPassword)


    Fehler 1 Ein Using-Operand vom Typ "EncryptDecrypt.Simple3Des" muss System.IDisposable implementieren.

    Ich nehme an, das ist die Fehlermeldung, die du in Aussicht gestellt hast. Schade, dann geht das wohl nicht ...

    Peter329 schrieb:

    dann geht das wohl nicht
    mit der Klasse Simple3Des, wohl aber mit anderen Klassen. :D
    Übe gleichzeitig einen ordentlichen Programmierstil.
    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!
    Ok ... ich hab nach deinem ersten Beitrag eh schon versucht das auf andere Klassen anzuwenden:

    VB.NET-Quellcode

    1. Using ms As New System.IO.MemoryStream
    2. 'Create the encoder to write to the stream.
    3. Using encStream As New CryptoStream(ms,
    4. TripleDes.CreateEncryptor(),
    5. System.Security.Cryptography.CryptoStreamMode.Write)
    6. 'Use the crypto stream to write the byte array to the stream.
    7. encStream.Write(plainTextBytes, 0, plainTextBytes.Length)
    8. encStream.FlushFinalBlock()
    9. encStream.Close()
    10. End Using
    11. Return ms.ToArray
    12. End Using


    Das Ergebnis ist unverändert. Nach dem 8. File gibts eine OutOfMemory Exception. Der Fehler ist reproduzierbar. Das ist das einzige Positive bisher an der Sache! :(
    Implementiere doch einfach IDisposable, schreibe dazu einfach eine Zeile unter Public Class wrapper2 "Implents IDisposable" und drücke ENTER, die IDE generiert dir dan folgenden Code den du etwas bearbeiten solltest. Dann klappt das auch mit der using anweisung.

    VB.NET-Quellcode

    1. #Region "IDisposable Support"
    2. Private disposedValue As Boolean ' So ermitteln Sie überflüssige Aufrufe
    3. ' IDisposable
    4. Protected Overridable Sub Dispose(disposing As Boolean)
    5. If Not Me.disposedValue Then
    6. If disposing Then
    7. ' TODO: Verwalteten Zustand löschen (verwaltete Objekte).
    8. End If
    9. ' TODO: Nicht verwaltete Ressourcen (nicht verwaltete Objekte) freigeben und Finalize() unten überschreiben.
    10. ' TODO: Große Felder auf NULL festlegen.
    11. End If
    12. Me.disposedValue = True
    13. End Sub
    14. ' TODO: Finalize() nur überschreiben, wenn Dispose(ByVal disposing As Boolean) oben über Code zum Freigeben von nicht verwalteten Ressourcen verfügt.
    15. 'Protected Overrides Sub Finalize()
    16. ' ' Ändern Sie diesen Code nicht. Fügen Sie oben in Dispose(ByVal disposing As Boolean) Bereinigungscode ein.
    17. ' Dispose(False)
    18. ' MyBase.Finalize()
    19. 'End Sub
    20. ' Dieser Code wird von Visual Basic hinzugefügt, um das Dispose-Muster richtig zu implementieren.
    21. Public Sub Dispose() Implements IDisposable.Dispose
    22. ' Ändern Sie diesen Code nicht. Fügen Sie oben in Dispose(disposing As Boolean) Bereinigungscode ein.
    23. Dispose(True)
    24. GC.SuppressFinalize(Me)
    25. End Sub
    26. #End Region

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Peter329 schrieb:

    Das ist das einzige Positive bisher an der Sache!
    Kannst Du mal das Projekt anhängen und auch (Test-) Daten, um den Fehler zu reproduzieren?
    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!
    Ok, ich hab mal versucht, das Ganze so aufzubereiten, dass man es nachspielen kann.

    Dabei ist mir aufgefallen, dass das Problem dann auftritt, sobald ein File eine bestimmte Größe überschreitet. Hier ist der Code, mit dem man das Problem nachstellen kann:

    VB.NET-Quellcode

    1. Imports System.IO
    2. Imports System.Security.Cryptography
    3. Imports System.Text
    4. Public Class Form1
    5. Private Sub Button1_Click(sender As System.Object, e As System.EventArgs) Handles Button1.Click
    6. Dim strObject = "...eingabe..."
    7. Dim strNewObject = "...ausgabe..."
    8. 'ENCRYPT file
    9. Dim bytFileBuffer As Byte() = File.ReadAllBytes(strObject)
    10. Dim wrapper2 As New Simple3Des("Test") '<-- das ist das Passwort
    11. Dim bytFileBufferEncrypted As Byte() = wrapper2.EncryptData(bytFileBuffer)
    12. 'Write encrypted file onto disk
    13. File.WriteAllBytes(strNewObject, bytFileBufferEncrypted)
    14. End Sub
    15. End Class
    16. Public NotInheritable Class Simple3Des
    17. Private TripleDes As New TripleDESCryptoServiceProvider
    18. Private Function TruncateHash(ByVal key As String, ByVal length As Integer) As Byte()
    19. Dim sha1 As New SHA1CryptoServiceProvider
    20. 'Hash the key.
    21. Dim keyBytes() As Byte = System.Text.Encoding.Unicode.GetBytes(key)
    22. Dim hash() As Byte = sha1.ComputeHash(keyBytes)
    23. 'Truncate or pad the hash.
    24. ReDim Preserve hash(length - 1)
    25. Return hash
    26. End Function
    27. Sub New(ByVal key As String)
    28. 'Initialize the crypto provider.
    29. TripleDes.Key = TruncateHash(key, TripleDes.KeySize \ 8)
    30. TripleDes.IV = TruncateHash("", TripleDes.BlockSize \ 8)
    31. End Sub
    32. Public Function EncryptData(ByRef plainTextBytes As Byte()) As Byte()
    33. 'Create the stream.
    34. Using ms As New System.IO.MemoryStream
    35. 'Create the encoder to write to the stream.
    36. Using encStream As New CryptoStream(ms,
    37. TripleDes.CreateEncryptor(),
    38. System.Security.Cryptography.CryptoStreamMode.Write)
    39. 'Use the crypto stream to write the byte array to the stream.
    40. encStream.Write(plainTextBytes, 0, plainTextBytes.Length)
    41. encStream.FlushFinalBlock() '<--- hier tritt die OutOfMemory Exception auf
    42. encStream.Close()
    43. End Using
    44. Return ms.ToArray
    45. End Using
    46. End Function
    47. End Class


    Mein Rechner hat 12GB Realspeicher. Ich fahre Windows 7. Und der File den ich verschlüsseln will hat rund 322 MB. (Bis 100 MB scheint die Sache problemlos zu funktionieren). Im Task Manager sehe ich, dass das Programm 654 MB Speicher belegt.

    Also irgendwie geht die Sache wohl schief, weil der File zu groß ist, um ihn auf einen Rutsch zu verarbeiten. Kann der Byte Array nicht so viele Bytes aufnehmen? Kann man das irgendwie puffern - also auf Blöcke etwa von 32KB verteilen?

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

    @Peter329:: Meinst Du diese (unabhängig von der Arraygröße):
    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!
    Bei mir sieht die Exception wie angehängt aus.

    Im Feld plainTextBytes.Length ist der Wert 329xxxxxx enhalten, also irgendwas um die 329 Mio Bytes. Das dürfte das Problem sein!
    Bilder
    • MemoryException.jpg

      241,16 kB, 1.276×618, 157 mal angesehen

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

    Peter329 schrieb:

    Das dürfte das Problem sein!
    Versuch mal, das ganze Speixcherhandling auf Streams umzustellen, da werden die Bytes geholt, wie sie gebraucht werden.
    Dann hat CryptoStream eine asynchrone BeginWrite-Prozedur. Das wäre vieleicht was in Deiner Richtung.
    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!

    RodFromGermany schrieb:

    Versuch mal, das ganze Speixcherhandling auf Streams umzustellen


    Womit wir dann wieder am Anfang des Threads gelandet wären! :D

    Wenn ich wüsste wie das geht! Mit dem Konzept der Streams, vor allem der Memory-Streams muss ich mich wohl auseinandersetzen. Ich muss auf irgend eine Weise dafür sorgen, dass diese riesige Bytewurst in handhabbaren Portionen verspeist wird.

    Danke für dein Geduld!

    LG
    Peter

    edr schrieb:

    Kannst du einen lesenden FileStream erstellen?

    Peter329 schrieb:

    Ich denke schon ...

    VB.NET-Quellcode

    1. Dim sr As New StreamReader("...mypath...")
    2. Dim strAllText As String = sr.ReadToEnd
    3. sr.Close()


    War's recht so? Und wie geht's nun weiter?
    Dassis falsch, und deshalb gehts schon hier nicht weiter.
    StreamReader<>FileStream

    also nocheinmal: Kannst du einen lesenden FileStream erstellen?
    Ok ... dann versuch ich es noch einmal:

    VB.NET-Quellcode

    1. Dim fs As New IO.FileStream("... mypath ...", FileMode.Open)


    Ich hab jetzt keine Ahnung, was das macht .... aber das scheint mir jetzt am ehesten zu passen. War es das, was du von mir erwartest? Wir setzen also einen lesenden Stream auf den Pfad und öffnen den. Und nun?