Question Is there a good way to call a function on CD change?

ikantspelwurdz

Well-known member
Joined
Dec 8, 2009
Messages
49
Programming Experience
1-3
I've got a program that has a text box with a list of files that are on any removable media. Suppose you launch the program and there's nothing in your CD-ROM drive. Right now, you'd get nothing, and you'd have to insert a disc and click a "refresh" button, which triggers a function that repopulates the list.

Is there any good way to call this function on the event that the user inserts/removes a CD-ROM from any CD-ROM drive? Also, inserting/removing external disks and flash drives? Without having to constantly poll the drives. Floppy disks aren't important to me.
 
With WM_DEVICECHANGE Message (Windows) you can detect DBT_DEVICEARRIVAL/DBT_DEVICEREMOVECOMPLETE for DBT_DEVTYP_VOLUME, the retrieved DEV_BROADCAST_VOLUME structure will identify the affected drives (dbcv_unitmask) and if change affects Device/Media/Network (dbcv_flags). These two events are broadcast to all toplevel windows so the WndProc override in code sample below is meant for the form code.

For fixed drives like cd/dvd you typically get Media change notification, for USB typically device notification, for smartcard readers it depends on if the drive is fixed or like in my case a new drive is added when a card is inserted. One of my USB sticks is a special case, it also has a fixed partition that identifies as "cd", so for when I insert this I actually get two drives which result in two device arrival events and one media arrival event, when it is removed I get a single device removal event identifying both drive letters.
VB.NET:
'windows message
Public Const WM_DEVICECHANGE As Int32 = &H219
'wparams
Public Const DBT_DEVICEARRIVAL As Int32 = &H8000
Public Const DBT_DEVICEREMOVECOMPLETE As Int32 = &H8004
'notification filter types
Public Const DBT_DEVTYP_VOLUME As Int32 = &H2
'DEV_BROADCAST structures    
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
    Public dbcv_flags As UShort
End Structure
'volume flags
Public Const DBTF_MEDIA As Short = &H1
Public Const DBTF_NET As Short = &H2

Protected Overrides Sub WndProc(ByRef m As System.Windows.Forms.Message)
    MyBase.WndProc(m)
    If m.Msg = WM_DEVICECHANGE Then            
        Select Case m.WParam.ToInt32
            Case DBT_DEVICEARRIVAL
                Debug.Write("DEVICEARRIVAL")
                CheckVolume(m.LParam)  
            Case DBT_DEVICEREMOVECOMPLETE
                Debug.Write("DEVICEREMOVECOMPLETE")
                CheckVolume(m.LParam)
        End Select
    End If
End Sub

Private Sub CheckVolume(ByVal lParam As IntPtr)
    Dim dbch_devicetype As Integer = Marshal.ReadInt32(lParam, 4) 'from DEV_BROADCAST_HDR
    If dbch_devicetype <> DBT_DEVTYP_VOLUME Then Return
    Dim vol As DEV_BROADCAST_VOLUME = CType(Marshal.PtrToStructure(lParam, GetType(DEV_BROADCAST_VOLUME)), DEV_BROADCAST_VOLUME)
    Select Case vol.dbcv_flags
        Case 0
            Debug.WriteLine(" - DEVICE - drives " & New String(getDriveLetters(vol.dbcv_unitmask)))
        Case DBTF_MEDIA
            Debug.WriteLine(" - MEDIA - drives " & New String(getDriveLetters(vol.dbcv_unitmask)))
        Case DBTF_NET
            Debug.WriteLine(" - NET - drives " & New String(getDriveLetters(vol.dbcv_unitmask)))
    End Select
End Sub

Public Shared Function getDriveLetters(ByVal mask As Integer) As Char()
    Dim drives As New List(Of Char)
    Dim driveletters As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    Dim b As New BitArray(New Integer() {mask})
    For x As Integer = 0 To driveletters.Length - 1
        If b(x) Then drives.Add(driveletters(x))
    Next
    Return drives.ToArray
End Function
 
Back
Top