导读:本期聚焦于小伙伴创作的《C#检查文件是否被占用 C#如何判断文件正在被其他进程使用》,敬请观看详情,探索知识的价值。以下视频、文章将为您系统阐述其核心内容与价值。如果您觉得《C#检查文件是否被占用 C#如何判断文件正在被其他进程使用》有用,将其分享出去将是对创作者最好的鼓励。

在C#开发中,文件操作是非常常见的需求,但是很多时候我们会遇到文件被其他进程占用的情况,直接进行读写、删除、移动等操作会抛出IOException,导致程序运行异常。因此学会判断文件是否被占用是很有必要的。

C#检查文件是否被占用 C#如何判断文件正在被其他进程使用

C#判断文件占用的常见方式

方式一:尝试打开文件捕获异常

这是最常用的一种实现方式,核心逻辑是尝试以独占模式打开目标文件,如果打开失败并且异常类型为文件被占用的相关异常,就说明文件正在被其他进程使用。这种方式的实现逻辑比较直观,不需要依赖额外的系统API。

下面是具体的代码实现:

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

public class FileOccupyChecker
{
    /// <summary>
    /// 判断文件是否被其他进程占用
    /// </summary>
    /// <param name="filePath">目标文件路径</param>
    /// <returns>true表示被占用,false表示未被占用</returns>
    public static bool IsFileOccupied(string filePath)
    {
        // 判断文件是否存在,不存在则直接返回false
        if (!File.Exists(filePath))
        {
            return false;
        }
        FileStream stream = null;
        try
        {
            // 尝试以独占模式打开文件,读写权限都申请,不共享给其他进程
            stream = new FileStream(filePath, FileMode.Open, FileAccess.ReadWrite, FileShare.None);
            return false;
        }
        catch (IOException ex)
        {
            // 判断是否是文件被占用的异常,不同系统下错误码不同
            var errorCode = Marshal.GetHRForException(ex) & 0xFFFF;
            // 32表示文件被其他进程使用,33表示文件被其他进程锁定
            return errorCode == 32 || errorCode == 33;
        }
        finally
        {
            // 确保文件流被释放
            if (stream != null)
            {
                stream.Close();
                stream.Dispose();
            }
        }
    }
}

使用这个方法的时候,只需要传入目标文件的完整路径即可,示例如下:

string testFilePath = @"D:test.txt";
bool isOccupied = FileOccupyChecker.IsFileOccupied(testFilePath);
if (isOccupied)
{
    Console.WriteLine("文件正在被其他进程占用");
}
else
{
    Console.WriteLine("文件未被占用,可以正常操作");
}

方式二:使用系统API查询文件句柄

除了通过尝试打开文件判断,还可以使用Windows的API函数查询当前系统所有打开的文件句柄,匹配目标文件路径来判断是否被占用。这种方式的准确性更高,但是实现相对复杂,需要调用系统底层接口。

首先定义需要用到的API结构和函数:

using System;
using System.Collections.Generic;
using System.Runtime.InteropServices;

public class FileHandleChecker
{
    [StructLayout(LayoutKind.Sequential)]
    public struct RM_UNIQUE_PROCESS
    {
        public int dwProcessId;
        public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime;
    }

    [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)]
    public static extern int RmRegisterResources(
        IntPtr sessionHandle,
        uint nFiles,
        string[] rgsFilenames,
        uint nApplications,
        [MarshalAs(UnmanagedType.LPArray)] RM_UNIQUE_PROCESS[] rgApplications,
        uint nServices,
        string[] rgsServiceNames
    );

    [DllImport("rstrtmgr.dll")]
    public static extern int RmStartSession(out IntPtr sessionHandle, int sessionFlags, string strSessionKey);

    [DllImport("rstrtmgr.dll")]
    public static extern int RmEndSession(IntPtr sessionHandle);

    [DllImport("rstrtmgr.dll")]
    public static extern int RmGetList(
        IntPtr sessionHandle,
        out uint nProcInfo,
        uint nProcInfoNeeded,
        [MarshalAs(UnmanagedType.LPArray)] RM_PROCESS_INFO[] rgAffectedApps,
        out uint lpdwRebootReasons
    );

    [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
    public struct RM_PROCESS_INFO
    {
        public RM_UNIQUE_PROCESS Process;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
        public string strAppName;
        [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)]
        public string strServiceShortName;
        public RM_APP_TYPE ApplicationType;
        public uint AppStatus;
        public uint TSSessionId;
        [MarshalAs(UnmanagedType.Bool)]
        public bool bRestartable;
    }

    public enum RM_APP_TYPE
    {
        RmUnknownApp = 0,
        RmMainWindow = 1,
        RmOtherWindow = 2,
        RmService = 3,
        RmExplorer = 4,
        RmConsole = 5,
        RmCritical = 1000
    }
}

然后实现判断逻辑:

public static List<int> GetFileOccupiedProcesses(string filePath)
{
    List<int> processIds = new List<int>();
    IntPtr sessionHandle = IntPtr.Zero;
    try
    {
        // 启动重启管理器会话
        int result = RmStartSession(out sessionHandle, 0, Guid.NewGuid().ToString());
        if (result != 0)
        {
            throw new Exception("启动会话失败,错误码:" + result);
        }
        // 注册要检查的文件
        string[] files = new string[] { filePath };
        result = RmRegisterResources(sessionHandle, (uint)files.Length, files, 0, null, 0, null);
        if (result != 0)
        {
            throw new Exception("注册文件资源失败,错误码:" + result);
        }
        // 获取占用文件的进程列表
        uint nProcInfoNeeded = 0;
        uint nProcInfo = 0;
        uint lpdwRebootReasons = 0;
        // 先获取需要的缓冲区大小
        result = RmGetList(sessionHandle, out nProcInfo, nProcInfoNeeded, null, out lpdwRebootReasons);
        if (result == 0)
        {
            // 没有进程占用文件
            return processIds;
        }
        else if (result == 234) // ERROR_MORE_DATA
        {
            nProcInfoNeeded = nProcInfo;
            RM_PROCESS_INFO[] processInfos = new RM_PROCESS_INFO[nProcInfoNeeded];
            nProcInfo = nProcInfoNeeded;
            result = RmGetList(sessionHandle, out nProcInfo, nProcInfoNeeded, processInfos, out lpdwRebootReasons);
            if (result == 0)
            {
                for (int i = 0; i < nProcInfo; i++)
                {
                    processIds.Add(processInfos[i].Process.dwProcessId);
                }
            }
        }
    }
    finally
    {
        // 结束会话释放资源
        if (sessionHandle != IntPtr.Zero)
        {
            RmEndSession(sessionHandle);
        }
    }
    return processIds;
}

两种方式的对比

我们可以通过下面的表格对比两种实现方式的优缺点:

实现方式优点缺点适用场景
尝试打开文件捕获异常实现简单,不需要调用系统底层API,兼容性好只能判断文件是否被占用,无法获取占用文件的进程信息只需要判断文件是否可操作,不需要知道占用进程的场景
系统API查询句柄可以获取占用文件的所有进程ID,准确性更高实现复杂,需要调用Windows API,仅支持Windows系统需要知道具体是哪个进程占用了文件,需要结束占用进程的场景

注意事项

  • 第一种方式判断文件是否存在的时候,如果文件不存在会直接返回false,不会抛出异常,符合实际使用逻辑。
  • 使用第一种方式的时候,FileShare.None参数表示不允许其他进程对文件进行任何读写操作,这样才能准确判断文件是否被独占占用。
  • 第二种方式依赖Windows的rstrtmgr.dll,所以只能在Windows系统下运行,跨平台场景不适用。
  • 不管使用哪种方式,判断文件占用都只能作为操作前的预检查,因为判断完成后到实际操作的这段时间内,文件状态可能发生变化,所以实际操作时还是要做好异常处理。

总结

如果是简单的判断文件是否可以操作,优先使用尝试打开文件捕获异常的方式,实现简单且兼容性更好。如果需要获取占用文件的进程信息,再考虑使用系统API的方式。在实际开发中,可以根据具体的需求选择合适的实现方案,同时做好文件操作的异常处理,避免程序因为文件占用问题崩溃。

C#文件占用检测FileStream异常处理进程占用判断修改时间:2026-07-20 11:33:56

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。