Вопрос

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.

Это было полезно?

Решение

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);
  }      
}

Другие советы

If I understand your question correctly, you want to be informed when a CD/DVD is inserted. So this maybe could help: Detect CD-ROM Insertion

I think this solves your problem with WMI.

If you want to do make a call that happens every x timeunins you could do something like this with the help of tasks and async/await:

    var cts = new CancellationTokenSource();
    var token = cts.Token;

    Task yourTask = Task.Factory.StartNew(async () =>
    {
        while (true)
        {               
            // run in a loop until aborted
            if (token.IsCancellationRequested)
                break;

            // do your request here ...

            // wait
            await Task.Delay(theTimeIntervalToWait, token);
        }
    }, token);

Then you can use cts.Cancel(); to abort your task (from outside). If you want to make sure it has completed you might then call yourTask.Wait().

Generally speaking you should in most cases not use something like while(true) { permanently-polling }.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top