Search code examples
c#wmidriveinfo

Best way to detect dvd insertion in drive c#


I tried using WMI to detect new media insertion in Disk Drive using following code. But is there managed solution like using loop in background thread with DriveInfo.GetDrives? Which is best way to do this? I'm getting 'Disk is not in the drive please insert disk' dialog with abort, retry and continue button on other pc when i tried the following code? On may machine it works fine.

private void DriveWatcher()
{
    try
    {
        var wqlEventQuery = new WqlEventQuery
            {
                EventClassName = "__InstanceModificationEvent",
                WithinInterval = new TimeSpan(0, 0, 1),
                Condition =
                    @"TargetInstance ISA 'Win32_LogicalDisk' and TargetInstance.DriveType = 5"
            };

        var connectionOptions = new ConnectionOptions
            {
                EnablePrivileges = true,
                Authority = null,
                Authentication = AuthenticationLevel.Default
            };

        var managementScope = new ManagementScope("\\root\\CIMV2", connectionOptions);

        ManagementEventWatcher = new ManagementEventWatcher(managementScope, wqlEventQuery);
        ManagementEventWatcher.EventArrived += CdrEventArrived;
        ManagementEventWatcher.Start();
    }
    catch (ManagementException e)
    {
        MessageBox.Show(e.Message, e.GetType().ToString(), MessageBoxButtons.OK, MessageBoxIcon.Error);
    }
}

private void CdrEventArrived(object sender, EventArrivedEventArgs e)
{
    var wmiDevice = (ManagementBaseObject) e.NewEvent["TargetInstance"];
    if (wmiDevice.Properties["VolumeName"].Value != null)
        GetDrives();
    else
        GetDrives();
}

private void GetDrives()
{
    if (InvokeRequired)
    {
        Invoke(new GetDrivesDelegate(GetDrives));
    }
    else
    {
        toolStripComboBoxDrives.Items.Clear();
        DriveInfo[] drives = DriveInfo.GetDrives();
        _drives = new Dictionary<string, DriveInfo>();
        int selectedIndex = 0;
        foreach (DriveInfo drive in drives)
        {
            if (drive.DriveType.Equals(DriveType.CDRom))
            {
                if (drive.IsReady)
                {
                    string name = string.Format("{0} ({1})", drive.VolumeLabel, drive.Name.Substring(0, 2));
                    int selectedDrive = toolStripComboBoxDrives.Items.Add(name);
                    _drives.Add(name, drive);
                    selectedIndex = selectedDrive;
                }
                else
                {
                    toolStripComboBoxDrives.Items.Add(drive.Name);
                    _drives.Add(drive.Name, drive);
                }
            }
        }
        toolStripComboBoxDrives.SelectedIndex = selectedIndex;
    }
}

Basically what i'm doing is on form load event called Drive Watcher. So when disk is inserted ready disk will be listed first in combo box and user can eject the drive easily.


Solution

  • I'm going with following solution. It's 100% managed solution. It's not using WMI and works great.

    internal class DriveWatcher
    {
        public delegate void OpticalDiskArrivedEventHandler(Object sender, OpticalDiskArrivedEventArgs e);
    
        /// <summary>
        ///     Gets or sets the time, in seconds, before the drive watcher checks for new media insertion relative to the last occurance of check.
        /// </summary>
        public int Interval = 1;
    
        private Timer _driveTimer;
    
        private Dictionary<string, bool> _drives;
    
        private bool _haveDisk;
    
        /// <summary>
        ///     Occurs when a new optical disk is inserted or ejected.
        /// </summary>
        public event OpticalDiskArrivedEventHandler OpticalDiskArrived;
    
        private void OnOpticalDiskArrived(OpticalDiskArrivedEventArgs e)
        {
            OpticalDiskArrivedEventHandler handler = OpticalDiskArrived;
            if (handler != null) handler(this, e);
        }
    
        public void Start()
        {
            _drives = new Dictionary<string, bool>();
            foreach (
                DriveInfo drive in
                    DriveInfo.GetDrives().Where(driveInfo => driveInfo.DriveType.Equals(DriveType.CDRom)))
            {
                _drives.Add(drive.Name, drive.IsReady);
            }
            _driveTimer = new Timer {Interval = Interval*1000};
            _driveTimer.Elapsed += DriveTimerOnElapsed;
            _driveTimer.Start();
        }
    
        public void Stop()
        {
            if (_driveTimer != null)
            {
                _driveTimer.Stop();
                _driveTimer.Dispose();
            }
        }
    
        private void DriveTimerOnElapsed(object sender, ElapsedEventArgs elapsedEventArgs)
        {
            if (!_haveDisk)
            {
                try
                {
                    _haveDisk = true;
                    foreach (DriveInfo drive in from drive in DriveInfo.GetDrives()
                                                where drive.DriveType.Equals(DriveType.CDRom)
                                                where _drives.ContainsKey(drive.Name)
                                                where !_drives[drive.Name].Equals(drive.IsReady)
                                                select drive)
                    {
                        _drives[drive.Name] = drive.IsReady;
                        OnOpticalDiskArrived(new OpticalDiskArrivedEventArgs {Drive = drive});
                    }
                }
                catch (Exception exception)
                {
                    Debug.Write(exception.Message);
                }
                finally
                {
                    _haveDisk = false;
                }
            }
        }
    }
    
    internal class OpticalDiskArrivedEventArgs : EventArgs
    {
        public DriveInfo Drive;
    }
    

    You can use this as follows.

    var driveWatcher = new DriveWatcher();
    driveWatcher.OpticalDiskArrived += DriveWatcherOnOpticalDiskArrived;
    driveWatcher.Start();
    
    private void DriveWatcherOnOpticalDiskArrived(object sender, OpticalDiskArrivedEventArgs e)
    {
        MessageBox.Show(e.Drive.Name);
    }