0

我有一个类,它具有 DriveInfo 类型的属性,您可能已经知道它具有 IsReady 的布尔属性。这是一个表示驱动器何时“准备就绪”的值——对我来说,这意味着驱动器中有一张 CD,因为我只选择了 CDRom 驱动器。

我想做的是在属性更新时调用一个事件——目前我正在实例化对象,然后执行一个 while 循环以等待该值为真。

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

        return false;
    }

我更喜欢一种方法或类似的方法。谢谢你。

4

3 回答 3

3

如果您正在等待设备状态的变化,例如 CD 插入/移除,则收听WM_DEVICECHANGE消息将是更好的方法。

WM_DEVICECHANGE当新设备或媒体(如 CD 或 DVD)添加并可用时,以及现有设备或媒体被移除时,Windows 会向所有顶级窗口发送一组默认消息...阅读更多

尝试使用以下帮助程序类来监听媒体插入/删除:

驾驶助手

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), @":\");
  }
}

使用示例 (适用于Windows Forms应用程序)

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);
  }      
}
于 2014-02-04T06:01:48.660 回答
0

如果我正确理解您的问题,您希望在插入 CD/DVD 时收到通知。所以这可能会有所帮助:检测 CD-ROM 插入

我认为这可以解决您的 WMI 问题。

于 2014-02-03T14:54:35.633 回答
0

如果你想做一个每隔 x timeunins 发生一次的调用,你可以在任务和 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);

然后,您可以使用cts.Cancel();(从外部)中止您的任务。如果你想确保它已经完成,你可以调用yourTask.Wait().

一般来说,在大多数情况下,您不应该使用类似while(true) { permanently-polling }.

于 2014-02-03T15:01:23.557 回答