Ayuda con Copiador de USB

Iniciado por Tomas1982, 10 Febrero 2017, 18:40 PM

0 Miembros y 1 Visitante están viendo este tema.

Tomas1982

Buenas ante todos: Estoy tratando de resolver un problema que no tengo idea de como hacerlo. (En cuestión)  Quiero copiar las USB que son introducida en mi PC. Tengo el código que hace esa función pero si se esta copiando al momento de insertar otro USB esa ya no me la copia, Como puedo resolver ese problema.   

Código (vbnet) [Seleccionar]


'Estructura de datos que almacena la gestión de conexiones
Public Structure dispositivo
  Public dispTamaño As Integer
  Public dispTipo As Integer
  Public dispReserv As Integer
  Public dispMask As Integer
End Structure

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)

  'Se ha producido un cambio en los dispositivos
  Const deviceChange As Integer = &H219
  'El sistema detecta un nuevo dispositivo
  Const deviceArrival As Integer = &H8000
  'Solicita retirada del dispositivo
  Const deviceQueryRemove As Integer = &H8001
  'Ha fallado la retirada del dispositivo
  Const devideQueryRemoveFailed As Integer = &H8002
  'Pendiente extracción del dispositivo
  Const deviceRemovePending As Integer = &H8003
  'Dispositivo extraído del sistema
  Const deviceRemoveComplete As Integer = &H8004
  ' Volumen lógico (Se ha insertado un disco)
  Const deviceTypeVolume As Integer = &H2

  Select Case m.Msg
    'Cambian los dispositivos del sistema
    Case deviceChange
    Select Case m.WParam.ToInt32
      'Llegada de un dispositivo
      Case deviceArrival
      Dim devType As Integer = Marshal.ReadInt32(m.LParam, 4)
      'Si es un volumen lógico..(unidad de disco)
      If devType = deviceTypeVolume Then
        Dim vol As dispositivo
        vol = CType(Marshal.PtrToStructure(m.LParam, GetType(dispositivo)), dispositivo)
        MessageBox.Show("Se insertó un dispositivo en la unidad " & LetraUnidad(vol.dispMask) & ".")
'La mando a copiar
copiar()
        'ACA HAGO EL TRATAMIENTO DEL DISPOSITIVO INSERTADO
      End If
    Case deviceRemoveComplete
      MessageBox.Show("Se retiró el dispositivo.")
    End Select
  End Select

  'Ahora se usa el manejador predeterminado
  MyBase.WndProc(m)
End Sub

Private Function LetraUnidad(ByVal unitmask As Integer) As Char
  Dim units() As Char = {"A", "B", "C", "D", "E", "F", "G",
      "H", "I", "J", "K", "L", "M", "N", "O", "P",
      "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"}
  Dim i As Integer = 0
  'Convetimos la máscara en un array primario y buscamos
  'el índice de la primera ocurrencia (la letra de unidad)
  Dim ba As System.Collections.BitArray
  ba = New System.Collections.BitArray(System.BitConverter.GetBytes(unitmask))

  For i = 0 To ba.Length
    If ba(i) = True Then
    Exit For
  End If
  Next

  Return units(i)
End Function



Serapis

#1
bla,bla,bla...

Serapis

#2
bla,bla,bla...

Eleкtro

#3
Hola.

La siguiente solución que voy a compartir es de forma gratuita pero también se puede encontrar junto a muchas otras diversas utilidades imprescindibles (must-have!) en mi framework de pago Elektrokit, el cual se encuentra a la venta por un módico precio en el mercado web de CodeCanyon, en Envato.

Bien. Primero empecemos con los miembros que compondrian el algoritmo que se suscribe a los eventos de inserción y extracción de dispositivos extraibles; la lógica de todo ello lo encapsulé en un tipo bautizado con el nombre DriveWatcher y cuyo modo de empleo es orientado a eventos, muy sencillo de manejar...

Simplemente tienes que copiar y pegar estas clases en tu proyecto:

DriveWatcher.vb
Código (vbnet) [Seleccionar]
' ***********************************************************************
' Author   : Elektro
' Modified : 16-December-2016
' ***********************************************************************

#Region " Public Members Summary "

#Region " Constructors "

' New()

#End Region

#Region " Events "

' DriveStatusChanged As EventHandler(Of DriveStatusChangedEventArgs)

#End Region

#Region " Properties "

' Drives As IEnumerable(Of DriveInfo)
' Handle As IntPtr
' IsRunning As Boolean

#End Region

#Region " Methods "

' Start()
' Stop()

#End Region

#End Region

#Region " Usage Examples "

#End Region

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

Imports WindowsApplication1.IO.Types.EventArgs
Imports WindowsApplication1.Win32.Enums
Imports WindowsApplication1.Win32.Types

' Imports Elektro.Core.Types
' Imports Elektro.Core.IO.Types.EventArgs
' Imports Elektro.Interop.Win32.Enums
' Imports Elektro.Interop.Win32.Types

#End Region

#Region " Drive Watcher "

Namespace IO.Types

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' A device insertion and removal monitor.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <example> This is a code example.
   ''' <code>
   ''' </code>
   ''' </example>
   ''' ----------------------------------------------------------------------------------------------------
   Public Class DriveWatcher : Inherits NativeWindow : Implements IDisposable ' : Inherits AestheticNativeWindow

#Region " Properties "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Gets the connected drives on this computer.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Overridable ReadOnly Property Drives As IEnumerable(Of DriveInfo)
           <DebuggerStepThrough>
           Get
               Return DriveInfo.GetDrives
           End Get
       End Property

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Gets a value that determines whether the monitor is running.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Overridable ReadOnly Property IsRunning As Boolean
           <DebuggerStepThrough>
           Get
               Return Me.isRunningB
           End Get
       End Property
       Protected isRunningB As Boolean

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Gets the handle for the <see cref="NativeWindow"/> that owns this <see cref="DriveWatcher"/> instance.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <value>
       ''' The handle.
       ''' </value>
       ''' ----------------------------------------------------------------------------------------------------
       Public Overridable Shadows ReadOnly Property Handle As IntPtr
           Get
               Return MyBase.Handle
           End Get
       End Property

#End Region

#Region " Enumerations "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Specifies a computer device type.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <remarks>
       ''' <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363246%28v=vs.85%29.aspx"/>
       ''' </remarks>
       ''' ----------------------------------------------------------------------------------------------------
       Private Enum DeviceType As Integer

           ' *****************************************************************************
           '                            WARNING!, NEED TO KNOW...
           '
           '  THIS ENUMERATION IS PARTIALLY DEFINED TO MEET THE PURPOSES OF THIS API
           ' *****************************************************************************

           ''' <summary>
           ''' Logical volume.
           ''' </summary>
           Logical = &H2

       End Enum

#End Region

#Region " Events "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' A list of event delegates.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Private ReadOnly events As EventHandlerList

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Occurs when a drive is inserted, removed, or changed.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Custom Event DriveStatusChanged As EventHandler(Of DriveStatusChangedEventArgs)

           <DebuggerNonUserCode>
           <DebuggerStepThrough>
           AddHandler(ByVal value As EventHandler(Of DriveStatusChangedEventArgs))
               Me.events.AddHandler("DriveStatusChangedEvent", value)
           End AddHandler

           <DebuggerNonUserCode>
           <DebuggerStepThrough>
           RemoveHandler(ByVal value As EventHandler(Of DriveStatusChangedEventArgs))
               Me.events.RemoveHandler("DriveStatusChangedEvent", value)
           End RemoveHandler

           <DebuggerNonUserCode>
           <DebuggerStepThrough>
           RaiseEvent(ByVal sender As Object, ByVal e As DriveStatusChangedEventArgs)
               Dim handler As EventHandler(Of DriveStatusChangedEventArgs) =
                   DirectCast(Me.events("DriveStatusChangedEvent"), EventHandler(Of DriveStatusChangedEventArgs))

               If (handler IsNot Nothing) Then
                   handler.Invoke(sender, e)
               End If
           End RaiseEvent

       End Event

#End Region

#Region " Event Invocators "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Raises <see cref="DriveStatusChanged"/> event.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="e">
       ''' The <see cref="DriveStatusChangedEventArgs"/> instance containing the event data.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Protected Overridable Sub OnDriveStatusChanged(ByVal e As DriveStatusChangedEventArgs)

           RaiseEvent DriveStatusChanged(Me, e)

       End Sub

#End Region

#Region " Constructors "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Initializes a new instance of <see cref="DriveWatcher"/> class.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Sub New()
           Me.events = New EventHandlerList
       End Sub

#End Region

#Region " Public Methods "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Starts monitoring.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <exception cref="Exception">
       ''' Monitor is already running.
       ''' </exception>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Overridable Sub Start()

           If (Me.Handle = IntPtr.Zero) Then
               MyBase.CreateHandle(New CreateParams())
               Me.isRunningB = True

           Else
               Throw New Exception(message:="Monitor is already running.")

           End If

       End Sub

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Stops monitoring.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <exception cref="Exception">
       ''' Monitor is already stopped.
       ''' </exception>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Overridable Sub [Stop]()

           If (Me.Handle <> IntPtr.Zero) Then
               Me.isRunningB = False
               MyBase.DestroyHandle()

           Else
               Throw New Exception(message:="Monitor is already stopped.")

           End If

       End Sub

#End Region

#Region " Private Methods "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Gets the drive letter stored in a <see cref="DevBroadcastVolume"/> structure.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="device">
       ''' The <see cref="DevBroadcastVolume"/> structure containing the device mask.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <returns>
       ''' The drive letter.
       ''' </returns>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Protected Overridable Function GetDriveLetter(ByVal device As DevBroadcastVolume) As Char

           Dim driveLetters As Char() = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray

           Dim deviceID As New BitArray(BitConverter.GetBytes(device.Mask))

           For i As Integer = 0 To deviceID.Length

               If deviceID(i) Then
                   Return driveLetters(i)
               End If

           Next i

           Return Nothing

       End Function

#End Region

#Region " Window Procedure (WndProc) "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Invokes the default window procedure associated with this window to process windows messages.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="m">
       ''' A <see cref="Message"/> that is associated with the current Windows message.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Protected Overrides Sub WndProc(ByRef m As Message)

           Select Case m.Msg

               Case DeviceEvents.Change ' The hardware has changed.

                   If (m.LParam = IntPtr.Zero) Then
                       Exit Select
                   End If

                   ' If it's an storage device then...
                   If Marshal.ReadInt32(m.LParam, 4) = DeviceType.Logical Then

                       ' Transform the LParam pointer into the data structure.
                       Dim currentWDrive As DevBroadcastVolume =
                           DirectCast(Marshal.PtrToStructure(m.LParam, GetType(DevBroadcastVolume)), DevBroadcastVolume)

                       Dim driveLetter As Char = Me.GetDriveLetter(currentWDrive)
                       Dim deviceEvent As DeviceEvents = DirectCast(m.WParam.ToInt32, DeviceEvents)
                       Dim driveInfo As New DriveInfo(driveLetter)

                       Me.OnDriveStatusChanged(New DriveStatusChangedEventArgs(deviceEvent, driveInfo))

                   End If

           End Select

           ' Return Message to base message handler.
           MyBase.WndProc(m)

       End Sub

#End Region

#Region " IDisposable Implementation "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Flag to detect redundant calls when disposing.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Private isDisposed As Boolean

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Releases all the resources used by this instance.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Sub Dispose() Implements IDisposable.Dispose

           Me.Dispose(isDisposing:=True)
           GC.SuppressFinalize(obj:=Me)

       End Sub

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
       ''' Releases unmanaged and, optionally, managed resources.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="isDisposing">
       ''' <see langword="True"/>  to release both managed and unmanaged resources;
       ''' <see langword="False"/> to release only unmanaged resources.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Protected Overridable Sub Dispose(ByVal isDisposing As Boolean)

           If (Not Me.isDisposed) AndAlso (isDisposing) Then
               Me.events.Dispose()
               If Me.isRunningB Then
                   Me.Stop()
               End If
           End If

           Me.isDisposed = True

       End Sub

#End Region

   End Class

End Namespace

#End Region


DriveStatusChangedEventArgs.vb
Código (vbnet) [Seleccionar]
' ***********************************************************************
' Author   : Elektro
' Modified : 10-November-2015
' ***********************************************************************

#Region " Public Members Summary "

#Region " Constructors "

' New(DeviceEvents, DriveInfo)

#End Region

#Region " Properties "

' DeviceEvent As DriveWatcher.DeviceEvents
' DriveInfo As DriveInfo

#End Region

#End Region

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

Imports WindowsApplication1.Win32.Enums

' Imports Elektro.Core.Types
' Imports Elektro.Interop.Win32.Enums

#End Region

#Region " DriveStatusChanged EventArgs "

Namespace IO.Types.EventArgs

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Contains the event-data of a <see cref="DriveWatcher.DriveStatusChanged"/> event.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   Public NotInheritable Class DriveStatusChangedEventArgs : Inherits System.EventArgs ' : Inherits AestheticEventArgs

#Region " Properties "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Gets the device event that occurred.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <value>
       ''' The drive info.
       ''' </value>
       ''' ----------------------------------------------------------------------------------------------------
       Public ReadOnly Property DeviceEvent As DeviceEvents
           <DebuggerStepThrough>
           Get
               Return Me.deviceEventsB
           End Get
       End Property
       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' ( Backing field )
       ''' The device event that occurred.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Private ReadOnly deviceEventsB As DeviceEvents

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Gets the drive info.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <value>
       ''' The drive info.
       ''' </value>
       ''' ----------------------------------------------------------------------------------------------------
       Public ReadOnly Property DriveInfo As DriveInfo
           <DebuggerStepThrough>
           Get
               Return Me.driveInfoB
           End Get
       End Property
       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' ( Backing field )
       ''' The drive info.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Private ReadOnly driveInfoB As DriveInfo

#End Region

#Region " Constructors "

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Prevents a default instance of the <see cref="DriveStatusChangedEventArgs"/> class from being created.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerNonUserCode>
       Private Sub New()
       End Sub

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Initializes a new instance of the <see cref="DriveStatusChangedEventArgs"/> class.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       ''' <param name="driveInfo">
       ''' The drive info.
       ''' </param>
       ''' ----------------------------------------------------------------------------------------------------
       <DebuggerStepThrough>
       Public Sub New(ByVal deviceEvent As DeviceEvents, ByVal driveInfo As DriveInfo)

           Me.deviceEventsB = deviceEvent
           Me.driveInfoB = driveInfo

       End Sub

#End Region

   End Class

End Namespace

#End Region


DeviceEvents.vb
Código (vbnet) [Seleccionar]
' ***********************************************************************
' Author   : Elektro
' Modified : 10-November-2015
' ***********************************************************************

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Device Events "

Namespace Win32.Enums

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Specifies a change to the hardware configuration of a device.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363480%28v=vs.85%29.aspx"/>
   ''' <para></para>
   ''' <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363232%28v=vs.85%29.aspx"/>
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   Public Enum DeviceEvents As Integer

       ' *****************************************************************************
       '                            WARNING!, NEED TO KNOW...
       '
       '  THIS ENUMERATION IS PARTIALLY DEFINED TO MEET THE PURPOSES OF THIS API
       ' *****************************************************************************

       ''' <summary>
       ''' The current configuration has changed, due to a dock or undock.
       ''' </summary>
       Change = &H219

       ''' <summary>
       ''' A device or piece of media has been inserted and becomes available.
       ''' </summary>
       Arrival = &H8000

       ''' <summary>
       ''' Request permission to remove a device or piece of media.
       ''' <para></para>
       ''' This message is the last chance for applications and drivers to prepare for this removal.
       ''' However, any application can deny this request and cancel the operation.
       ''' </summary>
       QueryRemove = &H8001

       ''' <summary>
       ''' A request to remove a device or piece of media has been canceled.
       ''' </summary>
       QueryRemoveFailed = &H8002

       ''' <summary>
       ''' A device or piece of media is being removed and is no longer available for use.
       ''' </summary>
       RemovePending = &H8003

       ''' <summary>
       ''' A device or piece of media has been removed.
       ''' </summary>
       RemoveComplete = &H8004

   End Enum

End Namespace

#End Region


DevBroadcastVolume.vb
Código (vbnet) [Seleccionar]
' ***********************************************************************
' Author   : Elektro
' Modified : 10-November-2015
' ***********************************************************************

#Region " Option Statements "

Option Strict On
Option Explicit On
Option Infer Off

#End Region

#Region " Imports "

Imports System.Runtime.InteropServices

#End Region

#Region " DevBroadcast Volume "

Namespace Win32.Types

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Contains information about a logical volume.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <remarks>
   ''' <see href="http://msdn.microsoft.com/en-us/library/windows/desktop/aa363249%28v=vs.85%29.aspx"/>
   ''' </remarks>
   ''' ----------------------------------------------------------------------------------------------------
   <DebuggerStepThrough>
   <StructLayout(LayoutKind.Sequential)>
   Public Structure DevBroadcastVolume

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' The size of this structure, in bytes.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Size As UInteger

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Set to DBT_DEVTYP_VOLUME (2).
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Type As UInteger

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' Reserved parameter; do not use this.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Reserved As UInteger

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' The logical unit mask identifying one or more logical units.
       ''' Each bit in the mask corresponds to one logical drive.
       ''' Bit 0 represents drive A, bit 1 represents drive B, and so on.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Mask As UInteger

       ''' ----------------------------------------------------------------------------------------------------
       ''' <summary>
       ''' This parameter can be one of the following values:
       ''' '0x0001': Change affects media in drive. If not set, change affects physical device or drive.
       ''' '0x0002': Indicated logical volume is a network volume.
       ''' </summary>
       ''' ----------------------------------------------------------------------------------------------------
       Public Flags As UShort

   End Structure

End Namespace

#End Region





Por último, esto de aquí abajo seria uno de los posibles algoritmos que se podría elaborar para el copiado de archivos. Para llevarlo a cabo simplemente he reciclado y adaptado el código que ya te enseñé en este otro post:
...por eso lo he dejado tan "en bruto" y con lineas comentadas, para denotar las diferencias entre uno y otro código.

Código (vbnet) [Seleccionar]
Imports System
Imports WindowsApplication1.IO.Types
Imports WindowsApplication1.IO.Types.EventArgs

Public NotInheritable Class Form1 : Inherits Form

   Friend WithEvents DriveMon As New DriveWatcher()
   Private ReadOnly outDir As String = "C:\Monitor\"

   ''' ----------------------------------------------------------------------------------------------------
   ''' <summary>
   ''' Handles the <see cref="DriveWatcher.DriveStatusChanged"/> event of the <see cref="DriveMon"/> instance.
   ''' </summary>
   ''' ----------------------------------------------------------------------------------------------------
   ''' <param name="sender">
   ''' The source of the event.
   ''' </param>
   '''
   ''' <param name="e">
   ''' The <see cref="DriveStatusChangedEventArgs"/> instance containing the event data.
   ''' </param>
   ''' ----------------------------------------------------------------------------------------------------
   Private Sub DriveMon_DriveStatusChanged(ByVal sender As Object, ByVal e As DriveStatusChangedEventArgs) Handles DriveMon.DriveStatusChanged

       Select Case e.DeviceEvent

           Case Win32.Enums.DeviceEvents.Arrival
               Dim sb As New StringBuilder
               With sb
                   .AppendLine("New drive connected...'")
                   .AppendLine(String.Format("Name: {0}", e.DriveInfo.Name))
                   .AppendLine(String.Format("Root.: {0}", e.DriveInfo.RootDirectory))
                   .AppendLine(String.Format("Label: {0}", e.DriveInfo.VolumeLabel))
                   .AppendLine(String.Format("Size.: {0} GB", (e.DriveInfo.TotalSize / (1024 ^ 3)).ToString("n1")))
               End With
               Console.WriteLine(sb.ToString())
               Console.WriteLine("Initializing copy...")

               Dim copyTask As Task(Of Integer) = Me.CopyFiles(e.DriveInfo.VolumeLabel, e.DriveInfo.RootDirectory.FullName, outDir)
               ' Await Task.WhenAll(copyTask)

           Case Win32.Enums.DeviceEvents.RemoveComplete
               Dim sb As New StringBuilder
               With sb
                   .AppendLine("Drive disconnected...'")
                   .AppendLine(String.Format("Name: {0}", e.DriveInfo.Name))
                   sb.AppendLine(String.Format("Root: {0}", e.DriveInfo.RootDirectory))
               End With
               Console.WriteLine(sb.ToString())

       End Select

   End Sub

   Private Sub StartMon_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button_StartMon.Click
       Me.DriveMon.Start()
   End Sub

   Private Sub StopMon_Click(ByVal sender As Object, ByVal e As EventArgs) Handles Button_StopMon.Click
       Me.DriveMon.Stop()
   End Sub

   Public Async Function CopyFiles(ByVal labelName As String, ByVal srcDirPath As String, ByVal dstDirPath As String) As Task(Of Integer)

       Dim filesDict As New Dictionary(Of FileInfo, FileInfo) ' {[Key: Source File], [Value: Destination File]}

       For Each file As FileInfo In New DirectoryInfo(srcDirPath).EnumerateFiles("*", SearchOption.AllDirectories)
           Dim srcRoot As String = file.Directory.Root.FullName
           Dim dstFilepath As String = file.FullName.Replace(srcRoot, Path.Combine(Me.outDir, labelName) & "\")
           filesDict.Add(file, New FileInfo(dstFilepath))
       Next file

       ' Dim maxFileCount As Integer = filesDict.Count()
       ' Dim curFileCount As Integer

       ' Dim updateLabelCallback As New SendOrPostCallback(
       '     Sub(ByVal state As Object)
       '         progressCtrl.Text = String.Format("{0} of {1} files copied...", CInt(state), maxFileCount)
       '     End Sub)

       Dim copySingleFileAction As New Action(Of KeyValuePair(Of FileInfo, FileInfo))(
           Sub(ByVal pair As KeyValuePair(Of FileInfo, FileInfo))
               Try
                   If Not (pair.Value.Directory.Exists) Then
                       pair.Value.Directory.Create()
                   End If
                   pair.Key.CopyTo(pair.Value.FullName, overwrite:=False)
                   ' SynchronizationContext.Current.Post(updateLabelCallback, Interlocked.Increment(curFileCount))
               Catch ex As Exception
               End Try
           End Sub)

       Dim copyAllFilesFunc As Func(Of Integer) =
           Function() As Integer
               ' Parallel.ForEach(Of KeyValuePair(Of FileInfo, FileInfo))(filesDict, copySingleFileAction)
               ' -- Sin paralelismo en este caso es más eficiente.
               For Each pair As KeyValuePair(Of FileInfo, FileInfo) In filesDict
                   copySingleFileAction(pair)
               Next
               ' Return curFileCount ' Return the amount of files that were copied.
           End Function

       If Not New DirectoryInfo(dstDirPath).Exists Then
           Directory.CreateDirectory(dstDirPath, Nothing)
       End If

       Dim t As New Task(Of Integer)(copyAllFilesFunc, TaskCreationOptions.LongRunning)
       t.Start()
       Await t

       Return t.Result
   End Function

End Class


De esa manera, se ejecutará de forma automática y asíncrona una tarea que iniciará el copiado de archivos por cada dispositivo extraible de almacenamiento que insertes en el PC en cuestión. Los archivos se copiarán del USB a la carpeta destino especificada, conservando la estructura de directorios de la ruta del archivo de origen a copiar. Si extraes un USB durante una operación de copiado, simplemente se disparará una excepción controlada que ignorará el copiado, y podrás reinsertar el mismo USB para seguir la copia, o insertar un nuevo USB. Puedes ocupar varias ranuras de USB de forma simultanea para copair varios dispositivos al mismo tiempo. Y creo que no me dejo nada destacable por mencionar sobre la funcionalidad del código.

Cabe mencionar que el código no está optimizado, tan solo es un ejemplo básico pero funcional para lograr lo que quieres, pues no voy a ponerme a hacerte yo todo el trabajo y escribir aquí la de Dios. Te recomiendo añadir controles de errores al menos para evaluar los permisos de usuario de la carpeta destino, además también podrías habilitar la capacidad de poder cancelar al instante una operación de copiado que esté en curso al momento de extraer un dispositivo USB, esto lo harias mediante el la creación y el uso de un token de cancelación el cual lo pasarías como argumento a una de las sobrecargas del método Task.Start(), y evaluarias una petición de cancelación en el bloque del método CopyFiles del código que te mostré. Otra cosa que se puede mejorar es el orden de iteración/copiado de archivos, podrías ordenar la colección para que se procesen los archivos de menor tamaño a mayor, dependiendo de tus necesidades.
En fin, puedes improvisar ese código de ejemplo de mil maneras distintas para perfeccionarlo.

¡Saludos!








Serapis

Curiosamente encontré que el VBTeam (el equipo de visual Basic), en su página han hablado de esto (hace apenas una semana), parece como si nos hubieran leído...

Aportan algunas soluciones más, que aunque óptimas, creo que son más complicadas para usuarios noveles. Vale la pena hecharles un vistazo:
https://blogs.msdn.microsoft.com/vbteam/2017/04/10/easy-async-and-await-for-vbs-part-1-or/

...es la parte 1, o sea que posiblemente vuelvan en otro momento a retomar el tema...

Maurice_Lupin

Buenos codigos, voy a experimentar con ellos para hacer bromas a mis amigos, sólo que en lugar de copiar será cortar :xD

Saludos.
Un error se comete al equivocarse.