Search code examples
c#eventsdriveinfo

Events and the DriveInfo class


I have a class that has a property of type DriveInfo, which has a boolean property of IsReady as you might already know. This is a value representing when the drive is "ready" - for me, that means that there's a CD in the drive, as I've chosen only CDRom drives.

What I would like to do is call an event when the property is updated - currently I'm instantiating the object, then performing a while loop to wait for the value to be true.

    public bool WaitUntilReady()
    {
        while (!Cancelled)
        {
            if (Drive.IsReady) return true;
        }

        return false;
    }

I'd much prefer a method, or something similar. Thank you.


Solution

  • If you're waiting for changes in device state such as CD insertion/ removal, listening to WM_DEVICECHANGE message would be a better way.

    Windows sends all top-level windows a set of default WM_DEVICECHANGE messages when new devices or media (such as a CD or DVD) are added and become available, and when existing devices or media are removed... Read More

    Try using the following helper class to listen for media insertion/ removal:

    DriveHelper

    public static class DriveHelper
    {
      const int WM_DEVICECHANGE = 0x0219;
      const int DBT_DEVICEARRIVAL = 0x8000;
      const int DBT_DEVICEREMOVECOMPLETE = 0x8004;
      const int DBT_DEVTYP_VOLUME = 0x00000002;
      const ushort DBTF_MEDIA = 0x0001;
    
      [StructLayout(LayoutKind.Sequential)]
      struct DEV_BROADCAST_VOLUME
      {
        public uint dbch_Size;
        public uint dbch_Devicetype;
        public uint dbch_Reserved;
        public uint dbch_Unitmask;
        public ushort dbch_Flags;
      }
    
      public class StateChangedEventArgs : EventArgs
      {
        public StateChangedEventArgs(string drive, bool ready)
        {
          Drive = drive;
          Ready = ready;
        }
    
        public string Drive { get; private set; }
    
        public bool Ready { get; private set; }
      }
    
      public static void QueryDeviceChange(Message m, Action<StateChangedEventArgs> action)
      {
        if (action == null || m.Msg != WM_DEVICECHANGE) return;
    
        var devType = Marshal.ReadInt32(m.LParam, 4);
        if (devType != DBT_DEVTYP_VOLUME) return;
    
        var lpdbv = (DEV_BROADCAST_VOLUME)Marshal.PtrToStructure(m.LParam, typeof(DEV_BROADCAST_VOLUME));
        if (lpdbv.dbch_Flags != DBTF_MEDIA) return;
    
        var eventCode = m.WParam.ToInt32();
        var drive = GetFirstDriveFromMask(lpdbv.dbch_Unitmask);
    
        switch (eventCode)
        {
          case DBT_DEVICEARRIVAL:
            action(new StateChangedEventArgs(drive, true));
            break;
          case DBT_DEVICEREMOVECOMPLETE:
            action(new StateChangedEventArgs(drive, false));
            break;
        }
      }
    
      static string GetFirstDriveFromMask(uint mask)
      {
        int i;
    
        for (i = 0; i < 26; ++i)
        {
          if ((mask & 0x1) == 0x1)
            break;
          mask = mask >> 1;
        }
    
        return string.Concat((char)(i + 65), @":\");
      }
    }
    

    Usage Example (for Windows Forms apps)

    public partial class Form1 : Form
    {    
      public Form1()
      {
        InitializeComponent();      
      }
    
      void OnStateChanged(DriveHelper.StateChangedEventArgs e)
      {
        // do your work here
        MessageBox.Show(string.Format("Drive: {0} => e.Ready: {1}, DriveInfo.IsReady: {2}", e.Drive, e.Ready, new DriveInfo(e.Drive).IsReady));
      }    
    
      protected override void WndProc(ref Message m)
      {
        DriveHelper.QueryDeviceChange(m, OnStateChanged);
    
        base.WndProc(ref m);
      }      
    }