$vbCode = @'
' ***********************************************************************
' Author : Elektro
' Modified : 16-December-2016
' ***********************************************************************
Imports Microsoft.VisualBasic
Imports System
Imports System.Collections
Imports System.Collections.Generic
Imports System.ComponentModel
Imports System.Diagnostics
Imports System.IO
Imports System.Reflection
Imports System.Runtime.InteropServices
Imports System.Security
Imports System.Text
Imports System.Threading
Imports System.Windows.Forms
Namespace VBNamespace
#Region " Drive Watcher "
''' <summary>
''' A device insertion and removal monitor.
''' </summary>
Public Class DriveWatcher : Inherits NativeWindow : Implements IDisposable
#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 Event DriveStatusChanged As EventHandler(Of DriveStatusChangedEventArgs)
#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>
<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>
<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>
<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>
<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 Region
#Region " DriveStatusChanged EventArgs "
''' <summary>
''' Contains the event-data of a <see cref="DriveWatcher.DriveStatusChanged"/> event.
''' </summary>
Public NotInheritable Class DriveStatusChangedEventArgs : Inherits System.EventArgs
#Region " Properties "
''' <summary>
''' Gets the device event that occurred.
''' </summary>
Public ReadOnly Property DeviceEvent As DeviceEvents
Get
Return Me.deviceEventB
End Get
End Property
Private ReadOnly deviceEventB As DeviceEvents
''' <summary>
''' Gets the drive info.
''' </summary>
Public ReadOnly Property DriveInfo As DriveInfo
Get
Return Me.driveInfoB
End Get
End Property
Private ReadOnly driveInfoB As DriveInfo
#End Region
#Region " Constructors "
<DebuggerNonUserCode>
Private Sub New()
End Sub
''' <summary>
''' Initializes a new instance of the <see cref="DriveStatusChangedEventArgs"/> class.
''' </summary>
<DebuggerStepThrough>
Public Sub New(ByVal deviceEvent As DeviceEvents, ByVal driveInfo As DriveInfo)
Me.deviceEventB = deviceEvent
Me.driveInfoB = driveInfo
End Sub
#End Region
End Class
#End Region
#Region " Device Events "
''' <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 Region
#Region " DevBroadcast Volume "
''' <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 Region
Public NotInheritable Class VBClass
Friend WithEvents DriveMon As DriveWatcher
Private waitHandle As ManualResetEvent
Public Sub New()
Me.DriveMon = New DriveWatcher()
Me.waitHandle = New ManualResetEvent(initialState:=False)
End Sub
Public Sub StartMon()
Me.DriveMon.Start()
Me.waitHandle.WaitOne()
End Sub
Public Sub StopMon()
Me.DriveMon.Stop()
Me.waitHandle.Set()
End Sub
''' <summary>
''' Handles the <see cref="DriveWatcher.DriveStatusChanged"/> event of the <see cref="DriveMon"/> instance.
''' </summary>
Private Sub DriveMon_DriveStatusChanged(sender As Object, e As DriveStatusChangedEventArgs) _
Handles DriveMon.DriveStatusChanged
Select Case e.DeviceEvent
Case DeviceEvents.Arrival
' Descartar cualquier dispositivo no extraible.
If (e.DriveInfo.DriveType <> DriveType.Removable) Then
Exit Sub
End If
Dim sb As New StringBuilder
With sb
.AppendLine("New drive connected...'")
.AppendLine(String.Format("Type......: {0}", e.DriveInfo.DriveType.ToString()))
.AppendLine(String.Format("Label.....: {0}", e.DriveInfo.VolumeLabel))
.AppendLine(String.Format("Name......: {0}", e.DriveInfo.Name))
.AppendLine(String.Format("Root......: {0}", e.DriveInfo.RootDirectory))
.AppendLine(String.Format("FileSystem: {0}", e.DriveInfo.DriveFormat))
.AppendLine(String.Format("Size......: {0} GB", (e.DriveInfo.TotalSize / (1024 ^ 3)).ToString("n1")))
.AppendLine(String.Format("Free space: {0} GB", (e.DriveInfo.AvailableFreeSpace / (1024 ^ 3)).ToString("n1")))
End With
Console.WriteLine(sb.ToString())
Case DeviceEvents.RemoveComplete
Dim sb As New StringBuilder
With sb
.AppendLine("Drive disconnected...'")
.AppendLine(String.Format("Name: {0}", e.DriveInfo.Name))
.AppendLine(String.Format("Root: {0}", e.DriveInfo.RootDirectory))
End With
Console.WriteLine(sb.ToString())
End Select
End Sub
End Class
End Namespace
'@
$vbType = Add-Type -TypeDefinition $vbCode `
-CodeDomProvider (New-Object Microsoft.VisualBasic.VBCodeProvider) `
-PassThru `
-ReferencedAssemblies "Microsoft.VisualBasic.dll", `
"System.dll", `
"System.Collections.dll", `
"System.ComponentModel.dll", `
"System.IO.dll", `
"System.Reflection.dll", `
"System.Runtime.InteropServices.dll", `
"System.Security.dll", `
"System.Threading.dll", `
"System.Windows.Forms.dll" | where { $_.IsPublic }
Write-Host "USB monitoring..."
$instance = (New-Object VBNamespace.VBClass)
$instance.StartMon()
Exit(0)