Solved

Detect if a usb device was inserted.

Posted on 2007-04-02
12
2,578 Views
Last Modified: 2013-11-27
Hi,

Is there any other way to detect when a USB drives was inserted or deleted without the use of wmi or systeminfo of vb6?

If there is an alternative, I will raise the points to 500.

I am using visual basic 2005.

Thanks,
Jack.
0
Comment
Question by:JackOfPH
  • 7
  • 5
12 Comments
 
LVL 5

Expert Comment

by:jef06
ID: 18841637
This one use  Win32, it is in C#, but the file DriveDetector.cs can be easily converted to Visual basic The developer say that it only works with .Net 2.0 but should work with 1.1
here the link
http://www.codeproject.com/cs/system/DriveDetector.asp
0
 
LVL 15

Author Comment

by:JackOfPH
ID: 18841755
Hi,
Please have it converted?
I really do not know how to convert it in vb. I do not know how to use c#...

thanks.

Jack.

PS.
Sorry for the inconvinience. I can't find away to convert it. Jack


0
 
LVL 5

Expert Comment

by:jef06
ID: 18841757
0
 
LVL 15

Author Comment

by:JackOfPH
ID: 18841808
Will try...
0
 
LVL 15

Author Comment

by:JackOfPH
ID: 18841904
Classes can inherit only from other classes.

'Public Event DeviceArrived(sender As Object, e As DriveDetectorEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

'Public Event QueryRemove(sender As Object, e As DriveDetectorEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

'Public Event DeviceRemoved(sender As Object, e As DriveDetectorEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

Can't make it run. having this error.
0
 
LVL 5

Expert Comment

by:jef06
ID: 18841911
do like this

RaiseEvent New DeviceArrived(this, new DriveDetectorEventArgs(etc ...
0
Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

 
LVL 15

Author Comment

by:JackOfPH
ID: 18841932
Is this in vb?
0
 
LVL 5

Expert Comment

by:jef06
ID: 18841934
copy the code converted you got here i will modify it
0
 
LVL 15

Author Comment

by:JackOfPH
ID: 18841936
Dim tempDeviceArrived As DriveDetectorEventHandler = DeviceArrived

This is the line that gives the error...
0
 
LVL 15

Author Comment

by:JackOfPH
ID: 18841939
This is the generated code from the link you had given...

Have a look...

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms
' required for Message
Imports System.Runtime.InteropServices
' required for Marshal
Imports System.IO
Imports Microsoft.Win32.SafeHandles
Namespace Dolinay
   


    ' Delegate for event handler to handle the device events
    Public Delegate Sub DriveDetectorEventHandler(ByVal sender As Object, ByVal e As DriveDetectorEventArgs)

    ''' <summary>
    ''' Our class for passing in custom arguments to our event handlers
    '''
    ''' </summary>
    Public Class DriveDetectorEventArgs
        Inherits EventArgs

        ''' <summary>
        ''' Get/Set the value indicating that the event should be cancelled
        ''' Only in QueryRemove handler.
        ''' </summary>
        Public Cancel As Boolean

        ''' <summary>
        ''' Drive letter for the device which caused this event
        ''' </summary>
        Public Drive As String

        ''' <summary>
        ''' Set to true in your DeviceArrived event handler if you wish to receive the
        ''' QueryRemove event for this drive.
        ''' </summary>
        Public HookQueryRemove As Boolean

        Public Sub New()
            MyBase.New()
            Cancel = False
            Drive = ""
            HookQueryRemove = False
        End Sub
    End Class

    ''' <summary>
    ''' Detects insertion or removal of removable drives.
    ''' Use it in 2 steps:
    ''' 1) Create instance of this class in your project and add handlers for the
    ''' DeviceArrived, DeviceRemoved and QueryRemove events.
    ''' 2) Override WndProc in your form and call DriveDetector's WndProc from there.
    ''' </summary>
    Class DriveDetector
        Inherits IDisposable

        ''' <summary>
        ''' Class which contains also handle to the file opened on the flash drive
        ''' </summary>
        Private mFileOnFlash As FileStream = Nothing

        ''' <summary>
        ''' Name of the file to try to open on the removable drive for query remove registration
        ''' </summary>
        Private mFileToOpen As String

        ''' <summary>
        ''' Handle to file which we keep opened on the drive if query remove message is required by the client
        ''' </summary>      
        Private mDeviceNotifyHandle As IntPtr

        ''' <summary>
        ''' Handle of the window which receives messages from Windows. This will be a form.
        ''' </summary>
        Private mRecipientHandle As IntPtr

        ''' <summary>
        ''' Drive which is currently hooked for query remove
        ''' </summary>
        Private mCurrentDrive As String

        ' Win32 constants
        Private Const DBT_DEVTYP_DEVICEINTERFACE As Integer = 5

        Private Const DBT_DEVTYP_HANDLE As Integer = 6

        Private Const BROADCAST_QUERY_DENY As Integer = 1112363332

        Private Const WM_DEVICECHANGE As Integer = 537

        Private Const DBT_DEVICEARRIVAL As Integer = 32768

        ' system detected a new device
        Private Const DBT_DEVICEQUERYREMOVE As Integer = 32769

        ' Preparing to remove (any program can disable the removal)
        Private Const DBT_DEVICEREMOVECOMPLETE As Integer = 32772

        ' removed
        Private Const DBT_DEVTYP_VOLUME As Integer = 2

        ''' <summary>
        ''' Default constructor.
        ''' </summary>
        ''' <param name="control">object which will receive Windows messages.
        ''' Pass "this" as this argument from your form class.</param>
        Public Sub New(ByVal control As Control)
            MyBase.New()
            mFileToOpen = Nothing
            mDeviceNotifyHandle = IntPtr.Zero
            mRecipientHandle = control.Handle
            mCurrentDrive = ""
        End Sub

        ''' <summary>
        ''' Consructs DriveDetector object setting also path to file which should be opened
        ''' when registering for query remove.  
        ''' </summary>
        '''<param name="control">object which will receive Windows messages.
        ''' Pass "this" as this argument from your form class.</param>
        ''' <param name="FileToOpen">Optional. Name of a file on the removable drive which should be opened.
        ''' If null, any file on the drive will be opened. Opening a file is needed for us
        ''' to be able to register for the query remove message. TIP: Use relative path without drive letter.
        ''' e.g. "SomeFolder\file_on_flash.txt"</param>
        Public Sub New(ByVal control As Control, ByVal FileToOpen As String)
            MyBase.New()
            mFileToOpen = FileToOpen
            mDeviceNotifyHandle = IntPtr.Zero
            mRecipientHandle = control.Handle
            mCurrentDrive = ""
        End Sub

        ''' <summary>
        ''' Gets the value indicating whether the query remove event will be fired.
        ''' </summary>
        Public ReadOnly Property IsQueryHooked() As Boolean
            Get
                If (mDeviceNotifyHandle = IntPtr.Zero) Then
                    Return False
                Else
                    Return True
                End If
            End Get
        End Property

        ''' <summary>
        ''' Gets letter of drive which is currently hooked. Empty string if none.
        ''' See also IsQueryHooked.
        ''' </summary>
        Public ReadOnly Property HookedDrive() As String
            Get
                Return mCurrentDrive
            End Get
        End Property

        ''' <summary>
        ''' Gets the file stream for file which this class opened on a drive to be notified
        ''' about it's removal.
        ''' </summary>
        Public ReadOnly Property OpenedFile() As FileStream
            Get
                Return mFileOnFlash
            End Get
        End Property

        ''' <summary>
        ''' Events signalized to the client app.
        ''' Add handlers for these events in your form to be notified of removable device events
        ''' </summary>
        Public Event DeviceArrived As DriveDetectorEventHandler

        Public Event DeviceRemoved As DriveDetectorEventHandler

        Public Event QueryRemove As DriveDetectorEventHandler

        ''' <summary>
        ''' Hooks specified drive to receive a message when it is being removed.  
        ''' This can be achieved also by setting e.HookQueryRemove to true in your
        ''' DeviceArrived event handler. In that case mFileToOpen (which can be set using second constructor)
        ''' is used as the file to open.
        ''' </summary>
        ''' <param name="fileOnDrive">drive letter or relative path to a file on the drive which should be
        ''' used to get a handle - required for registering to receive query remove messages.
        ''' If only drive letter is specified (e.g. "D:\\", any file found on the flash drive can be used.</param>
        ''' <returns>true if hooked ok, false otherwise</returns>
        Public Function EnableQueryRemove(ByVal fileOnDrive As String) As Boolean
            If ((fileOnDrive = Nothing) _
                        OrElse (fileOnDrive.Length = 0)) Then
                Throw New ArgumentException("Drive path must be supplied to register for Query remove.")
            End If
            If ((fileOnDrive.Length = 2) _
                        AndAlso (fileOnDrive(1) = Microsoft.VisualBasic.ChrW(58))) Then
                fileOnDrive = (fileOnDrive + Microsoft.VisualBasic.ChrW(92))
            End If
            ' append "\\" if only drive letter with ":" was passed in.
            If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                RegisterForDeviceChange(False, Nothing)
            End If
            If Not File.Exists(fileOnDrive) Then
                mFileToOpen = Nothing
            End If
            ' use any file
            mFileToOpen = fileOnDrive
            RegisterQuery(Path.GetPathRoot(fileOnDrive))
            If (mDeviceNotifyHandle = IntPtr.Zero) Then
                Return False
            End If
            ' failed to register
            Return True
        End Function

        ''' <summary>
        ''' Unhooks any currently hooked drive so that the query remove
        ''' message is not generated for it.
        ''' </summary>
        Public Sub DisableQueryRemove()
            If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                RegisterForDeviceChange(False, Nothing)
            End If
        End Sub

        ''' <summary>
        ''' Unregister and close the file we may have opened on the removable drive.
        ''' Garbage collector will call this method.
        ''' </summary>
        Public Sub Dispose()
            RegisterForDeviceChange(False, Nothing)
        End Sub

        ''' <summary>
        ''' Message handler which must be called from client form.
        ''' Processes Windows messages and calls event handlers.
        ''' </summary>
        ''' <param name="m"></param>
        Public Sub WndProc(ByRef m As Message)
            Dim devType As Integer
            Dim c As Char
            If (m.Msg = WM_DEVICECHANGE) Then
                ' WM_DEVICECHANGE can have several meanings depending on the WParam value...
                Select Case (m.WParam.ToInt32)
                    Case DBT_DEVICEARRIVAL
                        devType = Marshal.ReadInt32(m.LParam, 4)
                        If (devType = DBT_DEVTYP_VOLUME) Then
                            Dim vol As DEV_BROADCAST_VOLUME
                            vol = CType(Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
                            ' Get the drive letter
                            c = DriveMaskToLetter(vol.dbcv_unitmask)
                            '
                            ' Call the client event handler
                            '
                            ' We should create copy of the event before testing it and
                            ' calling the delegate - if any
                            Dim tempDeviceArrived As DriveDetectorEventHandler = DeviceArrived
                            If (Not (tempDeviceArrived) Is Nothing) Then
                                Dim e As DriveDetectorEventArgs = New DriveDetectorEventArgs
                                e.Drive = (c + ":\\")
                                tempDeviceArrived(Me, e)
                                ' Register for query remove if requested
                                If e.HookQueryRemove Then
                                    ' If something is already hooked, unhook it now
                                    If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                                        RegisterForDeviceChange(False, Nothing)
                                    End If
                                    RegisterQuery((c + ":\\"))
                                End If
                            End If
                            ' if  has event handler
                        End If
                    Case DBT_DEVICEQUERYREMOVE
                        devType = Marshal.ReadInt32(m.LParam, 4)
                        If (devType = DBT_DEVTYP_HANDLE) Then
                            ' TODO: we could get the handle for which this message is sent
                            ' from vol.dbch_handle and compare it agains a list of handles for
                            ' which we have registered the query remove message (?)                                                
                            'DEV_BROADCAST_HANDLE vol;
                            'vol = (DEV_BROADCAST_HANDLE)
                            '   Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HANDLE));
                            ' if ( vol.dbch_handle ....
                            '
                            ' Call the event handler in client
                            '
                            Dim tempQuery As DriveDetectorEventHandler = QueryRemove
                            If (Not (tempQuery) Is Nothing) Then
                                Dim e As DriveDetectorEventArgs = New DriveDetectorEventArgs
                                e.Drive = mCurrentDrive
                                ' drive which is hooked
                                tempQuery(Me, e)
                                ' If the client wants to cancel, let Windows know
                                If e.Cancel Then
                                    m.Result = CType(BROADCAST_QUERY_DENY, IntPtr)
                                Else
                                    ' Close the handle so that the drive can be unmounted
                                    If (Not (mFileOnFlash) Is Nothing) Then
                                        mFileOnFlash.Close()
                                        mFileOnFlash = Nothing
                                    End If
                                End If
                            End If
                        End If
                    Case DBT_DEVICEREMOVECOMPLETE
                        devType = Marshal.ReadInt32(m.LParam, 4)
                        If (devType = DBT_DEVTYP_VOLUME) Then
                            devType = Marshal.ReadInt32(m.LParam, 4)
                            If (devType = DBT_DEVTYP_VOLUME) Then
                                Dim vol As DEV_BROADCAST_VOLUME
                                vol = CType(Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
                                c = DriveMaskToLetter(vol.dbcv_unitmask)
                                '
                                ' Call the client event handler
                                '
                                Dim tempDeviceRemoved As DriveDetectorEventHandler = DeviceRemoved
                                If (Not (tempDeviceRemoved) Is Nothing) Then
                                    Dim e As DriveDetectorEventArgs = New DriveDetectorEventArgs
                                    e.Drive = (c + ":\\")
                                    tempDeviceRemoved(Me, e)
                                End If
                                ' TODO: we could unregister the notify handle here if we knew it is the
                                ' right drive which has been just removed
                                'RegisterForDeviceChange(false, null);
                            End If
                        End If
                End Select
            End If
        End Sub

        ' drive type is logical volume
        ''' <summary>
        ''' Registers for receiving the query remove message for a given drive.
        ''' We need to open a handle on that drive and register with this handle.
        ''' CLient can specify this file in mFileToOpen or we will use any file we fing on the drive
        ''' </summary>
        ''' <param name="drive">drive for which to register. </param>
        Private Sub RegisterQuery(ByVal drive As String)
            Dim register As Boolean = True
            If (mFileToOpen = Nothing) Then
                ' If client gave us no file, let's pick one on the drive...
                mFileToOpen = GetAnyFile(drive)
                If (mFileToOpen.Length = 0) Then
                    Return
                End If
                ' no file found on the flash drive
            Else
                ' Make sure the path in mFileToOpen contains valid drive
                ' If there is a drive letter in the path, it may be different from the  actual
                ' letter assigned to the drive now. We will cut it off and merge the actual drive
                ' with the rest of the path.
                If mFileToOpen.Contains(":") Then
                    Dim tmp As String = mFileToOpen.Substring(3)
                    Dim root As String = Path.GetPathRoot(drive)
                    mFileToOpen = Path.Combine(root, tmp)
                Else
                    mFileToOpen = Path.Combine(drive, mFileToOpen)
                End If
            End If
            Try
                mFileOnFlash = New FileStream(mFileToOpen, FileMode.Open)
            Catch ex As Exception
                ' just do not register if the file could not be opened
                register = False
            End Try
            If register Then
                RegisterForDeviceChange(True, mFileOnFlash.SafeFileHandle)
                mCurrentDrive = drive
            End If
        End Sub

        ''' <summary>
        ''' Registers to be notified when the volume is about to be removed
        ''' This is requierd if you want to get the QUERY REMOVE messages
        ''' </summary>
        ''' <param name="register">true to register, false to unregister</param>
        ''' <param name="fileHandle">handle of a file opened on the removable drive</param>
        Private Sub RegisterForDeviceChange(ByVal register As Boolean, ByVal fileHandle As SafeFileHandle)
            If register Then
                ' Register for handle
                Dim data As DEV_BROADCAST_HANDLE = New DEV_BROADCAST_HANDLE
                data.dbch_devicetype = DBT_DEVTYP_HANDLE
                data.dbch_reserved = 0
                data.dbch_nameoffset = 0
                'data.dbch_data = null;
                'data.dbch_eventguid = 0;
                data.dbch_handle = fileHandle.DangerousGetHandle
                'Marshal. fileHandle;
                data.dbch_hdevnotify = CType(0, IntPtr)
                Dim size As Integer = Marshal.SizeOf(data)
                data.dbch_size = size
                Dim buffer As IntPtr = Marshal.AllocHGlobal(size)
                Marshal.StructureToPtr(data, buffer, True)
                mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0)
            Else
                ' unregister
                If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                    Native.UnregisterDeviceNotification(mDeviceNotifyHandle)
                End If
                mDeviceNotifyHandle = IntPtr.Zero
                mCurrentDrive = ""
                If (Not (mFileOnFlash) Is Nothing) Then
                    mFileOnFlash.Close()
                    mFileOnFlash = Nothing
                End If
            End If
        End Sub

        ''' <summary>
        ''' Gets drive letter from a bit mask where bit 0 = A, bit 1 = B etc.
        ''' There can actually be more than one drive in the mask but we
        ''' just use the last one in this case.
        ''' </summary>
        ''' <param name="mask"></param>
        ''' <returns></returns>
        Private Shared Function DriveMaskToLetter(ByVal mask As Integer) As Char
            Dim letter As Char
            Dim drives As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            ' 1 = A
            ' 2 = B
            ' 4 = C...
            Dim cnt As Integer = 0
            Dim pom As Integer = (mask / 2)

            While (pom <> 0)
                ' while there is any bit set in the mask
                ' shift it to the righ...                
                pom = (pom / 2)
                cnt = (cnt + 1)

            End While
            If (cnt < drives.Length) Then
                letter = drives(cnt)
            Else
                letter = Microsoft.VisualBasic.ChrW(63)
            End If
            Return letter
        End Function

        ''' <summary>
        ''' Searches for any file in a given path and returns its full path
        ''' </summary>
        ''' <param name="drive">drive to search</param>
        ''' <returns>path of the file or empty string</returns>
        Private Function GetAnyFile(ByVal drive As String) As String
            Dim file As String = ""
            ' First try files in the root
            Dim files() As String = Directory.GetFiles(drive)
            If (files.Length = 0) Then
                ' if no file in the root, search whole drive
                files = Directory.GetFiles(drive, "*.*", SearchOption.AllDirectories)
            End If
            If (files.Length > 0) Then
                file = files(0)
            End If
            ' get the first file
            ' return empty string if no file found
            Return file
        End Function
        ''' <summary>
        ''' WinAPI functions
        ''' </summary>        
        Private Class Native

            '   HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
            Public Declare Function RegisterDeviceNotification Lib "user32.dll" (ByVal hRecipient As IntPtr, ByVal NotificationFilter As IntPtr, ByVal Flags As UInteger) As IntPtr

            Public Declare Function UnregisterDeviceNotification Lib "user32.dll" (ByVal hHandle As IntPtr) As UInteger
        End Class

        ' Structure with information for RegisterDeviceNotification.
        <StructLayout(LayoutKind.Sequential)> _
        Public Structure DEV_BROADCAST_HANDLE

            Public dbch_size As Integer

            Public dbch_devicetype As Integer

            Public dbch_reserved As Integer

            Public dbch_handle As IntPtr

            Public dbch_hdevnotify As IntPtr

            Public dbch_eventguid As Guid

            Public dbch_nameoffset As Long

            'public byte[] dbch_data[1]; // = new byte[1];
            Public dbch_data As Byte

            Public dbch_data1 As Byte
        End Structure

        ' Struct for parameters of the WM_DEVICECHANGE message
        <StructLayout(LayoutKind.Sequential)> _
        Public Structure DEV_BROADCAST_VOLUME

            Public dbcv_size As Integer

            Public dbcv_devicetype As Integer

            Public dbcv_reserved As Integer

            Public dbcv_unitmask As Integer
        End Structure
    End Class

End Namespace
0
 
LVL 15

Author Comment

by:JackOfPH
ID: 18841941
This are the errors:

1) Classes can inherit only from other classes.

2) Public Event DeviceArrived(sender As Object, e As DriveDetectorEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

3) Public Event QueryRemove(sender As Object, e As DriveDetectorEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.

4) Public Event DeviceRemoved(sender As Object, e As DriveDetectorEventArgs)' is an event, and cannot be called directly. Use a 'RaiseEvent' statement to raise an event.
0
 
LVL 5

Accepted Solution

by:
jef06 earned 500 total points
ID: 18841975
compile now

Imports System
Imports System.Collections.Generic
Imports System.Text
Imports System.Windows.Forms
' required for Message
Imports System.Runtime.InteropServices
' required for Marshal
Imports System.IO
Imports Microsoft.Win32.SafeHandles

Namespace Dolinay


    ' Delegate for event handler to handle the device events
    Public Delegate Sub DriveDetectorEventHandler(ByVal sender As Object, ByVal e As DriveDetectorEventArgs)

    ''' <summary>
    ''' Our class for passing in custom arguments to our event handlers
    '''
    ''' </summary>
    Public Class DriveDetectorEventArgs
        Inherits EventArgs

        ''' <summary>
        ''' Get/Set the value indicating that the event should be cancelled
        ''' Only in QueryRemove handler.
        ''' </summary>
        Public Cancel As Boolean

        ''' <summary>
        ''' Drive letter for the device which caused this event
        ''' </summary>
        Public Drive As String

        ''' <summary>
        ''' Set to true in your DeviceArrived event handler if you wish to receive the
        ''' QueryRemove event for this drive.
        ''' </summary>
        Public HookQueryRemove As Boolean

        Public Sub New()
            MyBase.New()
            Cancel = False
            Drive = ""
            HookQueryRemove = False
        End Sub
    End Class

    ''' <summary>
    ''' Detects insertion or removal of removable drives.
    ''' Use it in 2 steps:
    ''' 1) Create instance of this class in your project and add handlers for the
    ''' DeviceArrived, DeviceRemoved and QueryRemove events.
    ''' 2) Override WndProc in your form and call DriveDetector's WndProc from there.
    ''' </summary>
    Class DriveDetector
        Implements IDisposable

        ''' <summary>
        ''' Class which contains also handle to the file opened on the flash drive
        ''' </summary>
        Private mFileOnFlash As FileStream = Nothing

        ''' <summary>
        ''' Name of the file to try to open on the removable drive for query remove registration
        ''' </summary>
        Private mFileToOpen As String

        ''' <summary>
        ''' Handle to file which we keep opened on the drive if query remove message is required by the client
        ''' </summary>      
        Private mDeviceNotifyHandle As IntPtr

        ''' <summary>
        ''' Handle of the window which receives messages from Windows. This will be a form.
        ''' </summary>
        Private mRecipientHandle As IntPtr

        ''' <summary>
        ''' Drive which is currently hooked for query remove
        ''' </summary>
        Private mCurrentDrive As String

        ' Win32 constants
        Private Const DBT_DEVTYP_DEVICEINTERFACE As Integer = 5

        Private Const DBT_DEVTYP_HANDLE As Integer = 6

        Private Const BROADCAST_QUERY_DENY As Integer = 1112363332

        Private Const WM_DEVICECHANGE As Integer = 537

        Private Const DBT_DEVICEARRIVAL As Integer = 32768

        ' system detected a new device
        Private Const DBT_DEVICEQUERYREMOVE As Integer = 32769

        ' Preparing to remove (any program can disable the removal)
        Private Const DBT_DEVICEREMOVECOMPLETE As Integer = 32772

        ' removed
        Private Const DBT_DEVTYP_VOLUME As Integer = 2

        ''' <summary>
        ''' Default constructor.
        ''' </summary>
        ''' <param name="control">object which will receive Windows messages.
        ''' Pass "this" as this argument from your form class.</param>
        Public Sub New(ByVal control As Control)
            MyBase.New()
            mFileToOpen = Nothing
            mDeviceNotifyHandle = IntPtr.Zero
            mRecipientHandle = control.Handle
            mCurrentDrive = ""
        End Sub

        ''' <summary>
        ''' Consructs DriveDetector object setting also path to file which should be opened
        ''' when registering for query remove.  
        ''' </summary>
        '''<param name="control">object which will receive Windows messages.
        ''' Pass "this" as this argument from your form class.</param>
        ''' <param name="FileToOpen">Optional. Name of a file on the removable drive which should be opened.
        ''' If null, any file on the drive will be opened. Opening a file is needed for us
        ''' to be able to register for the query remove message. TIP: Use relative path without drive letter.
        ''' e.g. "SomeFolder\file_on_flash.txt"</param>
        Public Sub New(ByVal control As Control, ByVal FileToOpen As String)
            MyBase.New()
            mFileToOpen = FileToOpen
            mDeviceNotifyHandle = IntPtr.Zero
            mRecipientHandle = control.Handle
            mCurrentDrive = ""
        End Sub

        ''' <summary>
        ''' Gets the value indicating whether the query remove event will be fired.
        ''' </summary>
        Public ReadOnly Property IsQueryHooked() As Boolean
            Get
                If (mDeviceNotifyHandle = IntPtr.Zero) Then
                    Return False
                Else
                    Return True
                End If
            End Get
        End Property

        ''' <summary>
        ''' Gets letter of drive which is currently hooked. Empty string if none.
        ''' See also IsQueryHooked.
        ''' </summary>
        Public ReadOnly Property HookedDrive() As String
            Get
                Return mCurrentDrive
            End Get
        End Property

        ''' <summary>
        ''' Gets the file stream for file which this class opened on a drive to be notified
        ''' about it's removal.
        ''' </summary>
        Public ReadOnly Property OpenedFile() As FileStream
            Get
                Return mFileOnFlash
            End Get
        End Property

        ''' <summary>
        ''' Events signalized to the client app.
        ''' Add handlers for these events in your form to be notified of removable device events
        ''' </summary>
        Public Event DeviceArrived As DriveDetectorEventHandler

        Public Event DeviceRemoved As DriveDetectorEventHandler

        Public Event QueryRemove As DriveDetectorEventHandler

        ''' <summary>
        ''' Hooks specified drive to receive a message when it is being removed.  
        ''' This can be achieved also by setting e.HookQueryRemove to true in your
        ''' DeviceArrived event handler. In that case mFileToOpen (which can be set using second constructor)
        ''' is used as the file to open.
        ''' </summary>
        ''' <param name="fileOnDrive">drive letter or relative path to a file on the drive which should be
        ''' used to get a handle - required for registering to receive query remove messages.
        ''' If only drive letter is specified (e.g. "D:\\", any file found on the flash drive can be used.</param>
        ''' <returns>true if hooked ok, false otherwise</returns>
        Public Function EnableQueryRemove(ByVal fileOnDrive As String) As Boolean
            If ((fileOnDrive = Nothing) _
                        OrElse (fileOnDrive.Length = 0)) Then
                Throw New ArgumentException("Drive path must be supplied to register for Query remove.")
            End If
            If ((fileOnDrive.Length = 2) _
                        AndAlso (fileOnDrive(1) = Microsoft.VisualBasic.ChrW(58))) Then
                fileOnDrive = (fileOnDrive + Microsoft.VisualBasic.ChrW(92))
            End If
            ' append "\\" if only drive letter with ":" was passed in.
            If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                RegisterForDeviceChange(False, Nothing)
            End If
            If Not File.Exists(fileOnDrive) Then
                mFileToOpen = Nothing
            End If
            ' use any file
            mFileToOpen = fileOnDrive
            RegisterQuery(Path.GetPathRoot(fileOnDrive))
            If (mDeviceNotifyHandle = IntPtr.Zero) Then
                Return False
            End If
            ' failed to register
            Return True
        End Function

        ''' <summary>
        ''' Unhooks any currently hooked drive so that the query remove
        ''' message is not generated for it.
        ''' </summary>
        Public Sub DisableQueryRemove()
            If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                RegisterForDeviceChange(False, Nothing)
            End If
        End Sub

        ''' <summary>
        ''' Unregister and close the file we may have opened on the removable drive.
        ''' Garbage collector will call this method.
        ''' </summary>
        ' Implement IDisposable.
        ' Do not make this method virtual.
        ' A derived class should not be able to override this method.
        Public Sub Dispose() Implements IDisposable.Dispose
            RegisterForDeviceChange(False, Nothing)
        End Sub


        ''' <summary>
        ''' Message handler which must be called from client form.
        ''' Processes Windows messages and calls event handlers.
        ''' </summary>
        ''' <param name="m"></param>
        Public Sub WndProc(ByRef m As Message)
            Dim devType As Integer
            Dim c As Char
            If (m.Msg = WM_DEVICECHANGE) Then
                ' WM_DEVICECHANGE can have several meanings depending on the WParam value...
                Select Case (m.WParam.ToInt32)
                    Case DBT_DEVICEARRIVAL
                        devType = Marshal.ReadInt32(m.LParam, 4)
                        If (devType = DBT_DEVTYP_VOLUME) Then
                            Dim vol As DEV_BROADCAST_VOLUME
                            vol = CType(Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
                            ' Get the drive letter
                            c = DriveMaskToLetter(vol.dbcv_unitmask)
                            '
                            ' Call the client event handler
                            '
                            ' We should create copy of the event before testing it and
                            ' calling the delegate - if any
                            If (Not (DeviceArrivedEvent) Is Nothing) Then
                                Dim e As DriveDetectorEventArgs = New DriveDetectorEventArgs
                                e.Drive = (c + ":\\")
                                RaiseEvent DeviceArrived(Me, e)
                                ' Register for query remove if requested
                                If e.HookQueryRemove Then
                                    ' If something is already hooked, unhook it now
                                    If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                                        RegisterForDeviceChange(False, Nothing)
                                    End If
                                    RegisterQuery((c + ":\\"))
                                End If
                            End If
                            ' if  has event handler
                        End If
                    Case DBT_DEVICEQUERYREMOVE
                        devType = Marshal.ReadInt32(m.LParam, 4)
                        If (devType = DBT_DEVTYP_HANDLE) Then
                            ' TODO: we could get the handle for which this message is sent
                            ' from vol.dbch_handle and compare it agains a list of handles for
                            ' which we have registered the query remove message (?)                                                
                            'DEV_BROADCAST_HANDLE vol;
                            'vol = (DEV_BROADCAST_HANDLE)
                            '   Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_HANDLE));
                            ' if ( vol.dbch_handle ....
                            '
                            ' Call the event handler in client
                            '
                            If (Not (QueryRemoveEvent) Is Nothing) Then
                                Dim e As DriveDetectorEventArgs = New DriveDetectorEventArgs
                                e.Drive = mCurrentDrive
                                ' drive which is hooked
                                RaiseEvent QueryRemove(Me, e)
                                ' If the client wants to cancel, let Windows know
                                If e.Cancel Then
                                    m.Result = CType(BROADCAST_QUERY_DENY, IntPtr)
                                Else
                                    ' Close the handle so that the drive can be unmounted
                                    If (Not (mFileOnFlash) Is Nothing) Then
                                        mFileOnFlash.Close()
                                        mFileOnFlash = Nothing
                                    End If
                                End If
                            End If
                        End If
                    Case DBT_DEVICEREMOVECOMPLETE
                        devType = Marshal.ReadInt32(m.LParam, 4)
                        If (devType = DBT_DEVTYP_VOLUME) Then
                            devType = Marshal.ReadInt32(m.LParam, 4)
                            If (devType = DBT_DEVTYP_VOLUME) Then
                                Dim vol As DEV_BROADCAST_VOLUME
                                vol = CType(Marshal.PtrToStructure(m.LParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
                                c = DriveMaskToLetter(vol.dbcv_unitmask)
                                '
                                ' Call the client event handler
                                '
                                If (Not (DeviceRemovedEvent) Is Nothing) Then
                                    Dim e As DriveDetectorEventArgs = New DriveDetectorEventArgs
                                    e.Drive = (c + ":\\")
                                    RaiseEvent DeviceRemoved(Me, e)
                                End If
                                ' TODO: we could unregister the notify handle here if we knew it is the
                                ' right drive which has been just removed
                                'RegisterForDeviceChange(false, null);
                            End If
                        End If
                End Select
            End If
        End Sub

        ' drive type is logical volume
        ''' <summary>
        ''' Registers for receiving the query remove message for a given drive.
        ''' We need to open a handle on that drive and register with this handle.
        ''' CLient can specify this file in mFileToOpen or we will use any file we fing on the drive
        ''' </summary>
        ''' <param name="drive">drive for which to register. </param>
        Private Sub RegisterQuery(ByVal drive As String)
            Dim register As Boolean = True
            If (mFileToOpen = Nothing) Then
                ' If client gave us no file, let's pick one on the drive...
                mFileToOpen = GetAnyFile(drive)
                If (mFileToOpen.Length = 0) Then
                    Return
                End If
                ' no file found on the flash drive
            Else
                ' Make sure the path in mFileToOpen contains valid drive
                ' If there is a drive letter in the path, it may be different from the  actual
                ' letter assigned to the drive now. We will cut it off and merge the actual drive
                ' with the rest of the path.
                If mFileToOpen.Contains(":") Then
                    Dim tmp As String = mFileToOpen.Substring(3)
                    Dim root As String = Path.GetPathRoot(drive)
                    mFileToOpen = Path.Combine(root, tmp)
                Else
                    mFileToOpen = Path.Combine(drive, mFileToOpen)
                End If
            End If
            Try
                mFileOnFlash = New FileStream(mFileToOpen, FileMode.Open)
            Catch ex As Exception
                ' just do not register if the file could not be opened
                register = False
            End Try
            If register Then
                RegisterForDeviceChange(True, mFileOnFlash.SafeFileHandle)
                mCurrentDrive = drive
            End If
        End Sub

        ''' <summary>
        ''' Registers to be notified when the volume is about to be removed
        ''' This is requierd if you want to get the QUERY REMOVE messages
        ''' </summary>
        ''' <param name="register">true to register, false to unregister</param>
        ''' <param name="fileHandle">handle of a file opened on the removable drive</param>
        Private Sub RegisterForDeviceChange(ByVal register As Boolean, ByVal fileHandle As SafeFileHandle)
            If register Then
                ' Register for handle
                Dim data As DEV_BROADCAST_HANDLE = New DEV_BROADCAST_HANDLE
                data.dbch_devicetype = DBT_DEVTYP_HANDLE
                data.dbch_reserved = 0
                data.dbch_nameoffset = 0
                'data.dbch_data = null;
                'data.dbch_eventguid = 0;
                data.dbch_handle = fileHandle.DangerousGetHandle
                'Marshal. fileHandle;
                data.dbch_hdevnotify = CType(0, IntPtr)
                Dim size As Integer = Marshal.SizeOf(data)
                data.dbch_size = size
                Dim buffer As IntPtr = Marshal.AllocHGlobal(size)
                Marshal.StructureToPtr(data, buffer, True)
                mDeviceNotifyHandle = Native.RegisterDeviceNotification(mRecipientHandle, buffer, 0)
            Else
                ' unregister
                If (mDeviceNotifyHandle <> IntPtr.Zero) Then
                    Native.UnregisterDeviceNotification(mDeviceNotifyHandle)
                End If
                mDeviceNotifyHandle = IntPtr.Zero
                mCurrentDrive = ""
                If (Not (mFileOnFlash) Is Nothing) Then
                    mFileOnFlash.Close()
                    mFileOnFlash = Nothing
                End If
            End If
        End Sub

        ''' <summary>
        ''' Gets drive letter from a bit mask where bit 0 = A, bit 1 = B etc.
        ''' There can actually be more than one drive in the mask but we
        ''' just use the last one in this case.
        ''' </summary>
        ''' <param name="mask"></param>
        ''' <returns></returns>
        Private Shared Function DriveMaskToLetter(ByVal mask As Integer) As Char
            Dim letter As Char
            Dim drives As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
            ' 1 = A
            ' 2 = B
            ' 4 = C...
            Dim cnt As Integer = 0
            Dim pom As Integer = (mask / 2)

            While (pom <> 0)
                ' while there is any bit set in the mask
                ' shift it to the righ...                
                pom = (pom / 2)
                cnt = (cnt + 1)

            End While
            If (cnt < drives.Length) Then
                letter = drives(cnt)
            Else
                letter = Microsoft.VisualBasic.ChrW(63)
            End If
            Return letter
        End Function

        ''' <summary>
        ''' Searches for any file in a given path and returns its full path
        ''' </summary>
        ''' <param name="drive">drive to search</param>
        ''' <returns>path of the file or empty string</returns>
        Private Function GetAnyFile(ByVal drive As String) As String
            Dim file As String = ""
            ' First try files in the root
            Dim files() As String = Directory.GetFiles(drive)
            If (files.Length = 0) Then
                ' if no file in the root, search whole drive
                files = Directory.GetFiles(drive, "*.*", SearchOption.AllDirectories)
            End If
            If (files.Length > 0) Then
                file = files(0)
            End If
            ' get the first file
            ' return empty string if no file found
            Return file
        End Function
        ''' <summary>
        ''' WinAPI functions
        ''' </summary>        
        Private Class Native

            '   HDEVNOTIFY RegisterDeviceNotification(HANDLE hRecipient,LPVOID NotificationFilter,DWORD Flags);
            Public Declare Function RegisterDeviceNotification Lib "user32.dll" (ByVal hRecipient As IntPtr, ByVal NotificationFilter As IntPtr, ByVal Flags As UInteger) As IntPtr

            Public Declare Function UnregisterDeviceNotification Lib "user32.dll" (ByVal hHandle As IntPtr) As UInteger
        End Class

        ' Structure with information for RegisterDeviceNotification.
        <StructLayout(LayoutKind.Sequential)> _
        Public Structure DEV_BROADCAST_HANDLE

            Public dbch_size As Integer

            Public dbch_devicetype As Integer

            Public dbch_reserved As Integer

            Public dbch_handle As IntPtr

            Public dbch_hdevnotify As IntPtr

            Public dbch_eventguid As Guid

            Public dbch_nameoffset As Long

            'public byte[] dbch_data[1]; // = new byte[1];
            Public dbch_data As Byte

            Public dbch_data1 As Byte
        End Structure

        ' Struct for parameters of the WM_DEVICECHANGE message
        <StructLayout(LayoutKind.Sequential)> _
        Public Structure DEV_BROADCAST_VOLUME

            Public dbcv_size As Integer

            Public dbcv_devicetype As Integer

            Public dbcv_reserved As Integer

            Public dbcv_unitmask As Integer
        End Structure
    End Class

End Namespace
0

Featured Post

Enabling OSINT in Activity Based Intelligence

Activity based intelligence (ABI) requires access to all available sources of data. Recorded Future allows analysts to observe structured data on the open, deep, and dark web.

Join & Write a Comment

It’s quite interesting for me as I worked with Excel using vb.net for some time. Here are some topics which I know want to share with others whom this might help. First of all if you are working with Excel then you need to Download the Following …
A long time ago (May 2011), I have written an article showing you how to create a DLL using Visual Studio 2005 to be hosted in SQL Server 2005. That was valid at that time and it is still valid if you are still using these versions. You can still re…
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…

743 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

12 Experts available now in Live!

Get 1:1 Help Now