Dateien kopieren mit Fortschritt und verbleibender Zeit

    • VB.NET

    Es gibt 11 Antworten in diesem Thema. Der letzte Beitrag () ist von nikeee13.

      Dateien kopieren mit Fortschritt und verbleibender Zeit

      Hallo Comm,
      ich hatte gerade ein wenig langeweile und habe mich mal an ein kleines Programm gesetzt was Datein kopieren kann mit Fortschritt und verbleibender Zeit. Der Code ist eigentlich in C# geschrieben aber hier bekommt ihr ihn natürlich in VB, ist ja klar. Wer dennoch den C# Code haben möchte kann mich gerne per PN fragen ;).
      Die Form:
      2x Textbox (txtFrom und txtTo) - txtFrom für die Datei die Kopiert werden soll und txtTo whin sie kopiert werden soll
      1x ProgressBar (pbStatus) - Ist wohl klar wofür :)
      1x Label (lblSec) - Für die verbleibende Zeit. So jetzt der Code:
      1x Button (btnCopy) - Sollte klar sein

      VB.NET-Quellcode

      1. Imports System.IO
      2. Public Class Form1
      3. Public cancel As Boolean = False
      4. Private Sub button2_Click(ByVal sender As Object, ByVal e As EventArgs) Handles btnCopy.Click
      5. 'Kopieren
      6. If File.Exists(txtTo.Text) Then
      7. File.Delete(txtTo.Text)
      8. End If
      9. If File.Exists(txtFrom.Text) Then
      10. 'Streams
      11. Dim fromStream As FileStream = New FileStream(txtFrom.Text, FileMode.Open)
      12. Dim toStream As FileStream = New FileStream(txtTo.Text, FileMode.CreateNew)
      13. 'Variablen
      14. Dim total As Long = fromStream.Length
      15. Dim current As Long = 0
      16. Dim buffer(1024) As Byte
      17. Dim kbRemaining As Integer = 0
      18. Dim secRemaining As Integer = 0
      19. Dim kbs As Double = 0.0
      20. Dim eta As TimeSpan
      21. Dim startTime As DateTime
      22. 'Schleife
      23. cancel = False
      24. startTime = DateTime.Now
      25. Do
      26. 'Prüfen ob abgebrochen wurde
      27. If cancel Then
      28. fromStream.Close()
      29. toStream.Close()
      30. Exit Sub
      31. End If
      32. 'Bytes(lesen)
      33. Dim read As Integer = fromStream.Read(buffer, 0, buffer.Length)
      34. 'Bytes in die Datei schreiben
      35. toStream.Write(buffer, 0, read)
      36. 'current erhöhen
      37. current += read
      38. 'Prozente errechen
      39. Dim Prozent As Integer = current * 100 / total
      40. pbStatus.Value = Prozent
      41. 'Eta
      42. eta = startTime.Subtract(DateTime.Now)
      43. 'KB und Zeit verbleibend
      44. kbRemaining = (current - total) / 1024
      45. If eta.Seconds <> 0 Then
      46. 'KB pro Sekunde
      47. kbs = Math.Round((current / 1024) / (eta.Seconds), 2)
      48. secRemaining = kbRemaining / kbs
      49. End If
      50. lblSec.Text = "Verbleibend: " + secRemaining.ToString() + " Sekunden"
      51. Application.DoEvents()
      52. Loop While (total <> current)
      53. cancel = False
      54. fromStream.Close()
      55. toStream.Close()
      56. End If
      57. End Sub
      58. End Class

      Die Zeit berechnung ist ein bissl holperig, funktioniert aber :). Wenn ihr den Vorgang abbrechen wollt müsst ihr cancel auf True setzen.
      Ich habe das ganze jetzt nicht in einen Backgroundworker gemacht, wäre für eine größere Nutzung im Programm aber denkbar ;)

      progglord

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

      Hast du genügend Rechte mit dem Benutzer? (Denke ja)
      Versuch mal in den Projekteinstellungen (Projekt->"Projektname"-Einstellungen) auf "Windows-Einstellungen anzeigen" (Im Reiter "Anwendung"). Dort folgendes ändern:

      XML-Quellcode

      1. <requestedExecutionLevel level="asInvoker" uiAccess="false" />


      abändern in

      XML-Quellcode

      1. <requestedExecutionLevel level="highestAvailable" uiAccess="false" />


      Damit versucht das Programm mit den höhsten vorhandenen Rechten zu arbeiten (z.B. als Admin wenn du mit'm Adminkonto angemeldet bist)!


      So long, Darkshadow9
      Hi,

      es besteht auch die Möglichkeit, einen normalen Kopieren/Verschieben-Dialog über die Windows-API anzuzeigen. Ich habe dafür mal eine kleine Klasse geschrieben:

      Spoiler anzeigen

      VB.NET-Quellcode

      1. NotInheritable Class FileShellEx
      2. ' Könnte/sollte man in eine NativeMethods-Klasse auslagern
      3. <DllImport("shell32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
      4. Private Shared Function SHFileOperation(<MarshalAs(UnmanagedType.Struct)> ByRef lpFileOp As ShFileOp) As Integer
      5. End Function
      6. ' Könnte/sollte man in eine NativeTypes-Klasse auslagern
      7. <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
      8. Private Structure ShFileOp
      9. Dim hwnd As IntPtr
      10. Dim wFunc As FileOperation
      11. <MarshalAs(UnmanagedType.LPWStr)>
      12. Dim pFrom As String
      13. <MarshalAs(UnmanagedType.LPWStr)>
      14. Dim pTo As String
      15. Dim fFlags As Short
      16. <MarshalAs(UnmanagedType.Bool)>
      17. Dim fAnyOperationsAborted As Boolean
      18. Dim hNameMappings As IntPtr
      19. <MarshalAs(UnmanagedType.LPWStr)>
      20. Dim lpszProgressTitle As String
      21. End Structure
      22. Private Enum FileOperation As UInteger
      23. Move = 1
      24. Copy = 2
      25. Delete = 3
      26. Rename = 4
      27. End Enum
      28. Private Shared Function ShellOperation(ByVal operation As FileOperation, ByVal sourceFile As String, ByVal destinationFile As String, ByVal owner As IWin32Window) As Boolean
      29. Dim sf As New ShFileOp()
      30. sf.hwnd = If(owner Is Nothing, IntPtr.Zero, owner.Handle)
      31. sf.wFunc = operation
      32. sf.pFrom = sourceFile & Chr(0) & Chr(0)
      33. sf.pTo = destinationFile & Chr(0) & Chr(0)
      34. SHFileOperation(sf)
      35. Return Not sf.fAnyOperationsAborted
      36. End Function
      37. Public Shared Function Copy(ByVal sourceFile As String, ByVal destinationFile As String) As Boolean
      38. Return Copy(sourceFile, destinationFile, Nothing)
      39. End Function
      40. Public Shared Function Copy(ByVal sourceFile As String, ByVal destinationFile As String, ByVal owner As IWin32Window) As Boolean
      41. Return ShellOperation(FileOperation.Copy, sourceFile, destinationFile, ownerWindow)
      42. End Function
      43. Public Shared Function Move(ByVal sourceFile As String, ByVal destinationFile As String) As Boolean
      44. Return Move(sourceFile, destinationFile, Nothing)
      45. End Function
      46. Public Shared Function Move(ByVal sourceFile As String, ByVal destinationFile As String, ByVal owner As IWin32Window) As Boolean
      47. Return ShellOperation(FileOperation.Move, sourceFile, destinationFile, ownerWindow)
      48. End Function
      49. End Class


      Verwenden kann man sie so:

      VB.NET-Quellcode

      1. Dim wasAborted1 = FileShellEx.Copy("D:\test.txt", "C:\test2.txt") ' Die Funktionen geben zurück, ob der Vorgang
      2. Dim wasAborted2 = FileShellEx.Move("C:\test2.txt", "D:\test3.txt") ' abgebrochen wurde (Grund egal)


      Bietet den Vorteil, dass es der Standard-Widnowsdialog ist, welcher auf beim normalen Kopieren/Verschieben im Explorer verwendet wird.

      Edit:
      Übrigens meine ich mich zu erinnern, dass es auch in irgendwelchen .NET-Funktionen zum Kopieren/Verschieben einen Boolean-Parameter gibt, der angibt, ob der Windows-Dialog angezeigt werden soll. Ich konnte das aber gerade nicht finden, deshalb hab ich es hier mal schnell implementiert.
      Von meinem iPhone gesendet

      Dieser Beitrag wurde bereits 3 mal editiert, zuletzt von „nikeee13“ ()

      War glaub' ich Microsoft.VisualBasic.FileIO.FileSystem.CopyFile("src", "dest", FileIO.UIOption.AllDialogs, FileIO.UICancelOption.ThrowException). Bei deinem wird dann auch schon vorhandene Dateien reagiert oder?
      Wenn du's übrigens "dialogkonform" machen willst, kannst du ownerWindow als IWin32Window deklarieren und dann Handle abfragen. ;)

      Gruß
      ~blaze~
      @~blaze~: Ja, ich glaub das war es.

      ~blaze~ schrieb:

      Bei deinem wird dann auch schon vorhandene Dateien reagiert oder?
      Ja, und wenn zum Beispiel das Zielverzeichnis nich vorhanden ist ("Möchten Sie das Verzeichnis erstellen?"). Man kann das Verhalten auch sicherlich über die Flags des Structs abändern. Müsste man mal schauen, ich hab das jetzt nur so gemacht.

      ~blaze~ schrieb:

      Wenn du's übrigens "dialogkonform" machen willst, kannst du ownerWindow als IWin32Window deklarieren und dann Handle abfragen. ;)
      Daran habe ich so "from Scratch" nicht dran gedacht. Habe meinen post editiert.

      Die 2 0-Bytes am Ende stören mich noch. Man kann das sicherlich auch über ein Marshalling-Attribut "ausbessern", sodass man die nicht mehr braucht. Ich habe es jetzt aber auch nicht ohne die versucht.
      Von meinem iPhone gesendet

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

      Spoiler anzeigen

      VB.NET-Quellcode

      1. NotInheritable Class FileShellEx
      2. ' Könnte/sollte man in eine NativeMethods-Klasse auslagern
      3. <DllImport("shell32.dll", SetLastError:=True, CharSet:=CharSet.Auto)> _
      4. Private Shared Function SHFileOperation(<MarshalAs(UnmanagedType.Struct)> ByRef lpFileOp As ShFileOp) As Integer
      5. End Function
      6. ' Könnte/sollte man in eine NativeTypes-Klasse auslagern
      7. <StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)>
      8. Private Structure ShFileOp
      9. Public hwnd As IntPtr
      10. Public wFunc As FileOperation
      11. <MarshalAs(UnmanagedType.ByValArray)>
      12. Public pFrom() As String
      13. <MarshalAs(UnmanagedType.ByValArray)>
      14. Public pTo() As String
      15. Public fFlags As Short
      16. <MarshalAs(UnmanagedType.Bool)>
      17. Public fAnyOperationsAborted As Boolean
      18. Public hNameMappings As IntPtr
      19. <MarshalAs(UnmanagedType.LPWStr)>
      20. Public lpszProgressTitle As String
      21. End Structure
      22. Private Enum FileOperation As UInteger
      23. Move = 1
      24. Copy = 2
      25. Delete = 3
      26. Rename = 4
      27. End Enum
      28. Private Shared Function ShellOperation(ByVal operation As FileOperation, ByVal sourceFile() As String, ByVal destinationFile() As String, ByVal owner As IWin32Window) As Boolean
      29. Dim sf As New ShFileOp()
      30. sf.hwnd = If(owner Is Nothing, IntPtr.Zero, owner.Handle)
      31. sf.wFunc = operation
      32. sf.pFrom = sourceFile
      33. sf.pTo = destinationFile
      34. SHFileOperation(sf)
      35. Return Not sf.fAnyOperationsAborted
      36. End Function
      37. Public Shared Function Copy(ByVal sourceFile As String, ByVal destinationFile As String) As Boolean
      38. Return Copy(sourceFile, destinationFile, Nothing)
      39. End Function
      40. Public Shared Function Copy(ByVal sourceFile As String, ByVal destinationFile As String, ByVal owner As IWin32Window) As Boolean
      41. Return ShellOperation(FileOperation.Copy, New String() {sourceFile}, New String() {destinationFile}, owner)
      42. End Function
      43. Public Shared Function Move(ByVal sourceFile As String, ByVal destinationFile As String) As Boolean
      44. Return Move(sourceFile, destinationFile, Nothing)
      45. End Function
      46. Public Shared Function Move(ByVal sourceFile As String, ByVal destinationFile As String, ByVal owner As IWin32Window) As Boolean
      47. Return ShellOperation(FileOperation.Move, New String() {sourceFile}, New String() {destinationFile}, owner)
      48. End Function
      49. End Class

      so könnte es gehen. Hab' auch mal die Fehler, die dann durch das IWin32Window entstanden sind, ausgebessert.
      Die Nullterminierung gibt halt das String-Ende an und bei so einem String-Array folgt halt auf den letzten String dann noch einmal eine Null. Da's Unicode ist, muss die halt 2 Bytes haben. Ist aber wahrscheinlich nur eine Art, das String-Array abzuspeichern, also nicht immer so.

      Gruß
      ~blaze~

      ~blaze~ schrieb:

      Hab' auch mal die Fehler, die dann durch das IWin32Window entstanden sind, ausgebessert.
      Ups, das ist ja peinlich. Nächstes mal sollte ich es wenigstens mal in VS packen, bevor ich es so abschicke. :whistling:

      Laut der Dokumentation des Structs sind die Strings vom Typ PCZZTSTR. Das P steht wohl wie überlicher weise für "Pointer". Der Rest steht hier: peterbudai.eu/blog/2012/07/18/windows_api_at_its_best.html
      Ich habe gerade im Intenet auch nichts anders gefunden als Leute, die einfach ein oder 2 0-Bytes dranhängen, um zu terminieren. Ich hab deine Version mit ByValArray noch nicht probiert, werde ich jetzt mal machen.
      Von meinem iPhone gesendet