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

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

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


当前回答

在我的案例中,这个问题通过打开文件进行共享写/读解决了。共享读写的代码示例如下:— 流的作家

using(FileStream fs = new FileStream("D:\\test.txt", 
FileMode.Append, FileAccess.Write, FileShare.ReadWrite))
using (StreamWriter sw = new StreamWriter(fs))
{
    sw.WriteLine("any thing which you want to write");
}

流的读者

using (FileStream fs = new FileStream("D:\\test.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
using (StreamReader rr=new StreamReader(fs))
{
    rr.ReadLine())
}

其他回答

该错误表示另一个进程正在试图访问该文件。可能您或其他人在尝试向其写入时打开了它。“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; } }

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

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

我下面的代码解决这个问题,但我建议 首先,你需要了解是什么导致了这个问题,并尝试通过修改代码来找到解决方案

我可以给出另一种方法来解决这个问题,但更好的解决方案是检查你的编码结构,并尝试分析是什么导致这种情况发生,如果你没有找到任何解决方案,那么你可以使用下面的代码

try{
Start:
///Put your file access code here


}catch (Exception ex)
 {
//by anyway you need to handle this error with below code
   if (ex.Message.StartsWith("The process cannot access the file"))
    {
         //Wait for 5 seconds to free that file and then start execution again
         Thread.Sleep(5000);
         goto Start;
    }
 }

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

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

所以文件被锁定了几秒钟,直到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
            }
} 

我有下面的场景会导致同样的错误:

上传文件到服务器 然后在旧文件上传后删除它们

大多数文件都很小,但也有少数文件很大,因此试图删除这些文件会导致无法访问文件的错误。

然而,要找到这个问题并不容易,解决方法很简单,就是“等待任务完成执行”:

using (var wc = new WebClient())
{
   var tskResult = wc.UploadFileTaskAsync(_address, _fileName);
   tskResult.Wait(); 
}