我有一些代码,当它执行时,它抛出一个IOException,说

进程无法访问文件'filename',因为它正在被 另一个进程

这意味着什么?我能做些什么?


当前回答

正如本文中的其他回答所指出的,要解决这个错误,您需要仔细检查代码,以了解文件被锁定的位置。

在我的例子中,我在执行移动操作之前将文件作为电子邮件附件发送出去。

所以文件被锁定了几秒钟,直到SMTP客户端完成发送电子邮件。

我采取的解决方案是先移动文件,然后再发送电子邮件。这为我解决了问题。

另一个可能的解决方案,正如Hudson之前指出的,应该是在使用后处理对象。

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
} 

其他回答

正如本文中的其他回答所指出的,要解决这个错误,您需要仔细检查代码,以了解文件被锁定的位置。

在我的例子中,我在执行移动操作之前将文件作为电子邮件附件发送出去。

所以文件被锁定了几秒钟,直到SMTP客户端完成发送电子邮件。

我采取的解决方案是先移动文件,然后再发送电子邮件。这为我解决了问题。

另一个可能的解决方案,正如Hudson之前指出的,应该是在使用后处理对象。

public static SendEmail()
{
           MailMessage mMailMessage = new MailMessage();
           //setup other email stuff

            if (File.Exists(attachmentPath))
            {
                Attachment attachment = new Attachment(attachmentPath);
                mMailMessage.Attachments.Add(attachment);
                attachment.Dispose(); //disposing the Attachment object
            }
} 

该错误表示另一个进程正在试图访问该文件。可能您或其他人在尝试向其写入时打开了它。“Read”或“Copy”通常不会导致这种情况,但对其写入或调用delete则会。

正如其他回答所提到的,有一些基本的事情可以避免这种情况:

In FileStream operations, place it in a using block with a FileShare.ReadWrite mode of access. For example: using (FileStream stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite)) { } Note that FileAccess.ReadWrite is not possible if you use FileMode.Append. I ran across this issue when I was using an input stream to do a File.SaveAs when the file was in use. In my case I found, I didn't actually need to save it back to the file system at all, so I ended up just removing that, but I probably could've tried creating a FileStream in a using statement with FileAccess.ReadWrite, much like the code above. Saving your data as a different file and going back to delete the old one when it is found to be no longer in use, then renaming the one that saved successfully to the name of the original one is an option. How you test for the file being in use is accomplished through the List<Process> lstProcs = ProcessHandler.WhoIsLocking(file); line in my code below, and could be done in a Windows service, on a loop, if you have a particular file you want to watch and delete regularly when you want to replace it. If you don't always have the same file, a text file or database table could be updated that the service always checks for file names, and then performs that check for processes & subsequently performs the process kills and deletion on it, as I describe in the next option. Note that you'll need an account user name and password that has Admin privileges on the given computer, of course, to perform the deletion and ending of processes. When you don't know if a file will be in use when you are trying to save it, you can close all processes that could be using it, like Word, if it's a Word document, ahead of the save. If it is local, you can do this: ProcessHandler.localProcessKill("winword.exe"); If it is remote, you can do this: ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe"); where txtUserName is in the form of DOMAIN\user. Let's say you don't know the process name that is locking the file. Then, you can do this: List<Process> lstProcs = new List<Process>(); lstProcs = ProcessHandler.WhoIsLocking(file); foreach (Process p in lstProcs) { if (p.MachineName == ".") ProcessHandler.localProcessKill(p.ProcessName); else ProcessHandler.remoteProcessKill(p.MachineName, txtUserName, txtPassword, p.ProcessName); } Note that file must be the UNC path: \\computer\share\yourdoc.docx in order for the Process to figure out what computer it's on and p.MachineName to be valid. Below is the class these functions use, which requires adding a reference to System.Management. The code was originally written by Eric J.: using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Runtime.InteropServices; using System.Diagnostics; using System.Management; namespace MyProject { public static class ProcessHandler { [StructLayout(LayoutKind.Sequential)] struct RM_UNIQUE_PROCESS { public int dwProcessId; public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; } const int RmRebootReasonNone = 0; const int CCH_RM_MAX_APP_NAME = 255; const int CCH_RM_MAX_SVC_NAME = 63; enum RM_APP_TYPE { RmUnknownApp = 0, RmMainWindow = 1, RmOtherWindow = 2, RmService = 3, RmExplorer = 4, RmConsole = 5, RmCritical = 1000 } [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] struct RM_PROCESS_INFO { public RM_UNIQUE_PROCESS Process; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] public string strAppName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] public string strServiceShortName; public RM_APP_TYPE ApplicationType; public uint AppStatus; public uint TSSessionId; [MarshalAs(UnmanagedType.Bool)] public bool bRestartable; } [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] static extern int RmRegisterResources(uint pSessionHandle, UInt32 nFiles, string[] rgsFilenames, UInt32 nApplications, [In] RM_UNIQUE_PROCESS[] rgApplications, UInt32 nServices, string[] rgsServiceNames); [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)] static extern int RmStartSession(out uint pSessionHandle, int dwSessionFlags, string strSessionKey); [DllImport("rstrtmgr.dll")] static extern int RmEndSession(uint pSessionHandle); [DllImport("rstrtmgr.dll")] static extern int RmGetList(uint dwSessionHandle, out uint pnProcInfoNeeded, ref uint pnProcInfo, [In, Out] RM_PROCESS_INFO[] rgAffectedApps, ref uint lpdwRebootReasons); /// <summary> /// Find out what process(es) have a lock on the specified file. /// </summary> /// <param name="path">Path of the file.</param> /// <returns>Processes locking the file</returns> /// <remarks>See also: /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa373661(v=vs.85).aspx /// http://wyupdate.googlecode.com/svn-history/r401/trunk/frmFilesInUse.cs (no copyright in code at time of viewing) /// /// </remarks> static public List<Process> WhoIsLocking(string path) { uint handle; string key = Guid.NewGuid().ToString(); List<Process> processes = new List<Process>(); int res = RmStartSession(out handle, 0, key); if (res != 0) throw new Exception("Could not begin restart session. Unable to determine file locker."); try { const int ERROR_MORE_DATA = 234; uint pnProcInfoNeeded = 0, pnProcInfo = 0, lpdwRebootReasons = RmRebootReasonNone; string[] resources = new string[] { path }; // Just checking on one resource. res = RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null); if (res != 0) throw new Exception("Could not register resource."); //Note: there's a race condition here -- the first call to RmGetList() returns // the total number of process. However, when we call RmGetList() again to get // the actual processes this number may have increased. res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, null, ref lpdwRebootReasons); if (res == ERROR_MORE_DATA) { // Create an array to store the process results RM_PROCESS_INFO[] processInfo = new RM_PROCESS_INFO[pnProcInfoNeeded]; pnProcInfo = pnProcInfoNeeded; // Get the list res = RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, processInfo, ref lpdwRebootReasons); if (res == 0) { processes = new List<Process>((int)pnProcInfo); // Enumerate all of the results and add them to the // list to be returned for (int i = 0; i < pnProcInfo; i++) { try { processes.Add(Process.GetProcessById(processInfo[i].Process.dwProcessId)); } // catch the error -- in case the process is no longer running catch (ArgumentException) { } } } else throw new Exception("Could not list processes locking resource."); } else if (res != 0) throw new Exception("Could not list processes locking resource. Failed to get size of result."); } finally { RmEndSession(handle); } return processes; } public static void remoteProcessKill(string computerName, string userName, string pword, string processName) { var connectoptions = new ConnectionOptions(); connectoptions.Username = userName; connectoptions.Password = pword; ManagementScope scope = new ManagementScope(@"\\" + computerName + @"\root\cimv2", connectoptions); // WMI query var query = new SelectQuery("select * from Win32_process where name = '" + processName + "'"); using (var searcher = new ManagementObjectSearcher(scope, query)) { foreach (ManagementObject process in searcher.Get()) { process.InvokeMethod("Terminate", null); process.Dispose(); } } } public static void localProcessKill(string processName) { foreach (Process p in Process.GetProcessesByName(processName)) { p.Kill(); } } [DllImport("kernel32.dll")] public static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, int dwFlags); public const int MOVEFILE_DELAY_UNTIL_REBOOT = 0x4; } }

我有一个非常具体的情况,我得到了一个“IOException:进程不能访问文件'文件路径'”的行

File.Delete(fileName);

在一个NUnit测试中,看起来像这样:

Assert.Throws<IOException>(() =>
{
    using (var sr = File.OpenText(fileName) {
        var line = sr.ReadLine();
    }
});
File.Delete(fileName);

NUnit 3使用了所谓的“隔离上下文”来进行异常断言。这可能运行在一个单独的线程上。

我的解决办法是把文件。在同一上下文中删除。

Assert.Throws<IOException>(() =>
{
    try
    {
        using (var sr = File.OpenText(fileName) {
            var line = sr.ReadLine();
        }
    }
    catch
    {
        File.Delete(fileName);
        throw;
    }
});

使用FileShare修复了我打开文件的问题,即使它被另一个进程打开。

using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
{
}

在上传图像时出现问题,无法删除并找到解决方案。gl高频

//C# .NET
var image = Image.FromFile(filePath);

image.Dispose(); // this removes all resources

//later...

File.Delete(filePath); //now works