You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C# WinForm如何获取CD/DVD驱动器文件变更的系统通知?

监控CD/DVD驱动器文件变更的高效方案

嘿,恭喜你已经搞定了CD/DVD驱动器的设备插入通知!针对你不想用轮询、想高效监控光盘上文件创建/修改/删除的需求,我整理了几个实用方案,咱们来逐一拆解:

1. 优先尝试:FileSystemWatcher(适配可读写光盘)

虽然FileSystemWatcher通常用于本地硬盘,但对于UDF格式的可刻录CD/DVD(支持写入的那种),它是可以稳定工作的。你只需要把监控路径指向光盘的根目录,配置对应的事件监听即可。

代码示例:

private FileSystemWatcher _discWatcher;

private void InitializeDiscWatcher(string discRootPath)
{
    _discWatcher = new FileSystemWatcher(discRootPath)
    {
        Filter = "*.*", // 监听所有文件类型
        IncludeSubdirectories = true, // 包含子目录
        EnableRaisingEvents = true // 开启事件触发
    };

    // 绑定文件变更事件
    _discWatcher.Created += OnDiscFileCreated;
    _discWatcher.Changed += OnDiscFileChanged;
    _discWatcher.Deleted += OnDiscFileDeleted;
    // 处理异常情况(比如光盘突然弹出)
    _discWatcher.Error += OnDiscWatcherError;
}

private void OnDiscFileCreated(object sender, FileSystemEventArgs e)
{
    MessageBox.Show($"新文件创建:{e.FullPath}");
}

private void OnDiscFileChanged(object sender, FileSystemEventArgs e)
{
    MessageBox.Show($"文件已修改:{e.FullPath}");
}

private void OnDiscFileDeleted(object sender, FileSystemEventArgs e)
{
    MessageBox.Show($"文件已删除:{e.FullPath}");
}

private void OnDiscWatcherError(object sender, ErrorEventArgs e)
{
    MessageBox.Show($"监控出错:{e.GetException().Message}");
    // 停止监控,等待光盘重新插入后再初始化
    _discWatcher.EnableRaisingEvents = false;
}

注意事项:

  • 仅适用于可写入的UDF格式光盘,普通只读CD/DVD无法监听变更(本身不支持写入)
  • 当光盘弹出时,FileSystemWatcher会触发错误事件,记得及时停止监控,避免无效资源占用

2. 针对IMAPI写入场景:挂钩IMAPI的写入事件

既然你提到了IMAPI,如果你主要监控的是通过IMAPI进行的光盘写入操作,那可以直接挂钩IMAPI的相关事件,这样能精准捕获写入过程中的文件变更:

代码示例(简化版):

using IMAPI2;

private void SetupIMAPINotifications()
{
    var discMaster = new DiscMaster2();
    // 监听设备变更(和你现有WM_DEVICECHANGE逻辑互补)
    discMaster.NotifyDeviceChange += DiscMaster_NotifyDeviceChange;

    // 初始化目标驱动器(替换为你的驱动器路径)
    var discRecorder = new DiscRecorder2();
    discRecorder.InitializeDiscRecorder(@"D:\");
    
    // 监听写入会话的进度与完成事件
    var burnEngine = new BurnVerification();
    burnEngine.BurnProgress += BurnEngine_BurnProgress;
    burnEngine.BurnCompleted += BurnEngine_BurnCompleted;
}

private void DiscMaster_NotifyDeviceChange(object sender, EventArgs e)
{
    // 处理设备状态变更,比如新光盘插入
}

private void BurnEngine_BurnProgress(object sender, IBurnVerificationEvents_BurnProgressEventArgs e)
{
    // 跟踪写入进度,也可以预判文件即将写入完成
    Console.WriteLine($"写入进度:{e.currentProgress}%");
}

private void BurnEngine_BurnCompleted(object sender, IBurnVerificationEvents_BurnCompletedEventArgs e)
{
    if (e.succeeded)
    {
        MessageBox.Show("光盘写入完成,可检查文件变更");
        // 这里可以主动扫描光盘文件,对比之前的快照
    }
}

这个方案的优势是:完全针对IMAPI写入流程,不需要额外的文件系统监控,适合你只关注IMAPI写入场景的需求。

3. 更底层的方案:调用Win32 API ReadDirectoryChangesW

如果FileSystemWatcher在你的场景下不稳定(比如某些特殊格式的光盘),可以直接用它底层依赖的ReadDirectoryChangesW Win32 API,通过P/Invoke调用,能更直接地获取文件系统的变更通知:

P/Invoke声明及简化示例:

using System.Runtime.InteropServices;

[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern bool ReadDirectoryChangesW(
    IntPtr hDirectory,
    IntPtr lpBuffer,
    uint nBufferLength,
    bool bWatchSubtree,
    uint dwNotifyFilter,
    out uint lpBytesReturned,
    IntPtr lpOverlapped,
    IntPtr lpCompletionRoutine);

// 通知过滤器:包含创建、修改、删除等事件
private const uint FILE_NOTIFY_CHANGE_FILE_NAME = 0x00000001;
private const uint FILE_NOTIFY_CHANGE_LAST_WRITE = 0x00000010;
private const uint FILE_NOTIFY_CHANGE_SIZE = 0x00000008;

private void StartDirectoryMonitoring(string discPath)
{
    // 获取目录句柄
    IntPtr hDir = CreateFile(
        discPath,
        FILE_LIST_DIRECTORY,
        FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
        IntPtr.Zero,
        OPEN_EXISTING,
        FILE_FLAG_BACKUP_SEMANTICS,
        IntPtr.Zero);

    if (hDir == IntPtr.Zero)
    {
        MessageBox.Show("无法打开目录句柄");
        return;
    }

    // 分配缓冲区
    IntPtr buffer = Marshal.AllocHGlobal(1024);
    uint bytesReturned;

    // 同步读取变更通知(实际可改为异步回调)
    while (ReadDirectoryChangesW(
        hDir,
        buffer,
        1024,
        true,
        FILE_NOTIFY_CHANGE_FILE_NAME | FILE_NOTIFY_CHANGE_LAST_WRITE | FILE_NOTIFY_CHANGE_SIZE,
        out bytesReturned,
        IntPtr.Zero,
        IntPtr.Zero))
    {
        // 解析变更信息
        FILE_NOTIFY_INFORMATION fni = (FILE_NOTIFY_INFORMATION)Marshal.PtrToStructure(buffer, typeof(FILE_NOTIFY_INFORMATION));
        string fileName = Marshal.PtrToStringUni(fni.FileName, (int)fni.FileNameLength / 2);
        MessageBox.Show($"文件变更:{fileName},类型:{(FILE_ACTION)fni.Action}");
    }

    // 释放资源
    Marshal.FreeHGlobal(buffer);
    CloseHandle(hDir);
}

// 补充所需的其他P/Invoke声明
[DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
private static extern IntPtr CreateFile(
    string lpFileName,
    uint dwDesiredAccess,
    uint dwShareMode,
    IntPtr lpSecurityAttributes,
    uint dwCreationDisposition,
    uint dwFlagsAndAttributes,
    IntPtr hTemplateFile);

[DllImport("kernel32.dll", SetLastError = true)]
private static extern bool CloseHandle(IntPtr hObject);

private const uint FILE_LIST_DIRECTORY = 0x0001;
private const uint FILE_SHARE_READ = 0x00000001;
private const uint FILE_SHARE_WRITE = 0x00000002;
private const uint FILE_SHARE_DELETE = 0x00000004;
private const uint OPEN_EXISTING = 3;
private const uint FILE_FLAG_BACKUP_SEMANTICS = 0x02000000;

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
private struct FILE_NOTIFY_INFORMATION
{
    public uint NextEntryOffset;
    public uint Action;
    public uint FileNameLength;
    public char FileName;
}

private enum FILE_ACTION : uint
{
    FILE_ACTION_ADDED = 1,
    FILE_ACTION_REMOVED = 2,
    FILE_ACTION_MODIFIED = 3,
    FILE_ACTION_RENAMED_OLD_NAME = 4,
    FILE_ACTION_RENAMED_NEW_NAME = 5
}

这个方案灵活性最高,但需要处理更多Win32 API细节,适合FileSystemWatcher无法满足需求的场景。

关于轮询的优化方案

如果以上方案都不适用(比如监控只读光盘的内容更新,但只读光盘本身无法修改,除非重新刻录),你可以优化轮询逻辑:

  • 只在驱动器插入并挂载后启动轮询
  • 用System.Timers.Timer设置合理的间隔(比如1-5秒,根据需求调整)
  • 轮询时记录文件的哈希值或最后修改时间,只在变化时触发通知,减少不必要的计算

内容的提问来源于stack exchange,提问作者Kunal Jha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 03:17:08