.NET中检测文件是否被其他进程占用
阅读原文时间:2023年07月11日阅读:1

更新记录

本文迁移自Panda666原博客,原发布时间:2021年7月2日。

一、检测文件是否被进程占用的几种方式

在.NET中主要有以下方式进行检测文件是否被进程占用的几种方式:

  1. 通过直接打开文件等操作,根据是否弹出异常来判断是否被其他进程占用。
  2. 使用互操作去检测文件是否被其他进程占用。

二、使用异常来测试是否被其他进程占用

直接打开文件进行读,因为文件不完整,所以会抛出异常。通过goto实现一直监听文件是否复制完成。因为异常的性能相比正常代码性能非常低,并且使用异常来实现代码逻辑也非常不合适,所以建议使用另一种方法进行实现同样的功能。

代码实例:

using System;
using System.IO;
using System.Linq;
using System.Threading;

namespace PandaTestClass
{
    class Program
    {
        static void Main(string[] args)
        {
            //新建文件监听器
            FileSystemWatcher watcher = new FileSystemWatcher();

            //====配置文件监听器=====
            //监听的目录
            string monitoredPath = @"E:/";
            //监听的文件类型
            string monitoredFileType = "*.txt|*.cs";
            //监听的修改的具体操作类型
            NotifyFilters notifyFilters = NotifyFilters.FileName
                                            | NotifyFilters.Size
                                            | NotifyFilters.LastWrite;
            //设置参数
            watcher.Path = monitoredPath;
            monitoredFileType.Split("|")
                                .ToList()
                                .ForEach(elem => watcher.Filters.Add(elem));

            watcher.NotifyFilter = notifyFilters;
            //====配置文件监听器=====

            //====绑定事件处理函数====
            //绑定文件修改事件处理函数
            watcher.Changed += (object sender, FileSystemEventArgs args) => {
                Console.WriteLine("文件修改啦");
                Console.WriteLine($"被修改的文件是{args.Name}");
                Console.WriteLine($"被修改的文件文件路径是{args.FullPath}");
                Console.WriteLine($"修改的类型{args.ChangeType.ToString()}");

                //====检测创建后是否可用(比如:是否复制完成)====
                //先检测文件是否存在
                FileInfo fileInfo = new(args.FullPath);
                if (!fileInfo.Exists)
                {
                    return;
                }

                //检测文件是否复制完成
                NotComplate: try
                {
                    File.OpenRead(fileInfo.FullName);
                }
                catch(Exception e)
                {
                    Console.WriteLine("文件还未复制完成");
                    Thread.Sleep(TimeSpan.FromSeconds(3));
                    goto NotComplate;
                }

                //可以对文件进行操作
                Console.WriteLine("可以对文件进行操作了");
                //====检测创建后是否可用(比如:是否复制完成)====
                //对文件具体操作的代码
            };
            //====绑定事件处理函数====

            //开启监听
            watcher.EnableRaisingEvents = true;

            //等待用户关闭监听
            while (true)
            {
                Console.WriteLine("如需关闭监听,请按Y:");
                if ((Console.ReadKey()).Key == ConsoleKey.Y)
                {
                    //释放监听器
                    watcher.Dispose();
                    return;
                }
            }
        }
    }
}

三、通过互操作检测文件是否被其他进程占用

首先,需要在代码中引入互操作函数和常量。

打开文件用于测试文件是否可用。

[DllImport("kernel32.dll")]
public static extern IntPtr _lopen(string lpPathName, int iReadWrite);

关闭句柄,用于关闭已打开的文件句柄。

[DllImport("kernel32.dll")]
public static extern bool CloseHandle(IntPtr hObject);

文件打开的模式-读写

public const int OF_READWRITE = 2;

文件打开的模式-共享读写

public const int OF_SHARE_DENY_NONE = 0x40;

文件打开错误标志位,用于判断文件打开后的状态判断

public static readonly IntPtr HFILE_ERROR = new IntPtr(-1);

在每次进行文件操作前,对文件进行测试是否可以访问

//检测文件是被其他进程占用
IntPtr vHandle = _lopen(args.FullPath, OF_READWRITE | OF_SHARE_DENY_NONE);
if (vHandle == HFILE_ERROR)
{
    Console.WriteLine("文件被其他进程占用,不可以操作");
    return;
}

//释放句柄
CloseHandle(vHandle);

//文件可以操作了

代码实例:监听文件的操作,如果文件复制完成了再进行操作

using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;

namespace PandaTestClass
{
    class Program
    {
        /// <summary>
        /// 打开文件,用于测试文件是否可用
        /// </summary>
        /// <param name="lpPathName"></param>
        /// <param name="iReadWrite"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        public static extern IntPtr _lopen(string lpPathName, int iReadWrite);

        /// <summary>
        /// 关闭句柄
        /// </summary>
        /// <param name="hObject"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll")]
        public static extern bool CloseHandle(IntPtr hObject);

        /// <summary>
        /// 文件打开的模式,读写权限
        /// </summary>
        public const int OF_READWRITE = 2;

        /// <summary>
        /// 文件打开的模式,进程间共享读写
        /// </summary>
        public const int OF_SHARE_DENY_NONE = 0x40;

        /// <summary>
        /// 文件打开错误标志位
        /// </summary>
        public static readonly IntPtr HFILE_ERROR = new IntPtr(-1);

        static void Main(string[] args)
        {
            //新建文件监听器
            FileSystemWatcher watcher = new FileSystemWatcher();

            //====配置文件监听器=====
            //监听的目录
            string monitoredPath = @"E:/";
            //监听的文件类型
            string monitoredFileType = "*.txt|*.cs";
            //监听的修改的具体操作类型
            NotifyFilters notifyFilters = NotifyFilters.FileName
                                            | NotifyFilters.Size
                                            | NotifyFilters.LastWrite;
            //设置参数
            watcher.Path = monitoredPath;
            monitoredFileType.Split("|")
                                .ToList()
                                .ForEach(elem => watcher.Filters.Add(elem));

            watcher.NotifyFilter = notifyFilters;
            //====配置文件监听器=====

            //====绑定事件处理函数====
            //绑定文件修改事件处理函数
            watcher.Changed += (object sender, FileSystemEventArgs args) => {
                Console.WriteLine("文件修改啦");
                Console.WriteLine($"被修改的文件是{args.Name}");
                Console.WriteLine($"被修改的文件文件路径是{args.FullPath}");
                Console.WriteLine($"修改的类型{args.ChangeType.ToString()}");

                //====检测创建后是否可用(比如:是否复制完成)====
                //先检测文件是否存在
                FileInfo fileInfo = new(args.FullPath);
                if (!fileInfo.Exists)
                {
                    return;
                }

                //检测文件是否复制完成
                IntPtr vHandle = _lopen(args.FullPath, OF_READWRITE | OF_SHARE_DENY_NONE);
                if (vHandle == HFILE_ERROR)
                {
                    Console.WriteLine("文件还未复制完成");
                    return;
                }

                //释放句柄
                CloseHandle(vHandle);

                //可以对文件进行操作
                Console.WriteLine("可以对文件进行操作了");
                //====检测创建后是否可用(比如:是否复制完成)====
                //对文件具体操作的代码
            };
            //====绑定事件处理函数====

            //开启监听
            watcher.EnableRaisingEvents = true;

            //等待用户关闭监听
            while (true)
            {
                Console.WriteLine("如需关闭监听,请按Y:");
                if ((Console.ReadKey()).Key == ConsoleKey.Y)
                {
                    //释放监听器
                    watcher.Dispose();
                    return;
                }
            }
        }
    }
}