' ***********************************************************************
' 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