IOException: The process cannot access the file 'file path' because it is being used by another process

前端 未结 10 2054
陌清茗
陌清茗 2020-11-22 00:13

I have some code and when it executes, it throws a IOException, saying that

The process cannot access the file \'filename\' because it

相关标签:
10条回答
  • 2020-11-22 00:39

    Using FileShare fixed my issue of opening file even if it is opened by another process.

    using (var stream = File.Open(path, FileMode.Open, FileAccess.Write, FileShare.ReadWrite))
    {
    }
    
    0 讨论(0)
  • 2020-11-22 00:41

    The error indicates another process is trying to access the file. Maybe you or someone else has it open while you are attempting to write to it. "Read" or "Copy" usually doesn't cause this, but writing to it or calling delete on it would.

    There are some basic things to avoid this, as other answers have mentioned:

    1. 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.

    2. 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.

    3. 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.

    4. 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.

    5. 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;
      
          }
      }
      
    0 讨论(0)
  • 2020-11-22 00:45

    What is the cause?

    The error message is pretty clear: you're trying to access a file, and it's not accessible because another process (or even the same process) is doing something with it (and it didn't allow any sharing).

    Debugging

    It may be pretty easy to solve (or pretty hard to understand), depending on your specific scenario. Let's see some.

    Your process is the only one to access that file
    You're sure the other process is your own process. If you know you open that file in another part of your program, then first of all you have to check that you properly close the file handle after each use. Here is an example of code with this bug:

    var stream = new FileStream(path, FileAccess.Read);
    var reader = new StreamReader(stream);
    // Read data from this file, when I'm done I don't need it any more
    File.Delete(path); // IOException: file is in use
    

    Fortunately FileStream implements IDisposable, so it's easy to wrap all your code inside a using statement:

    using (var stream = File.Open("myfile.txt", FileMode.Open)) {
        // Use stream
    }
    
    // Here stream is not accessible and it has been closed (also if
    // an exception is thrown and stack unrolled
    

    This pattern will also ensure that the file won't be left open in case of exceptions (it may be the reason the file is in use: something went wrong, and no one closed it; see this post for an example).

    If everything seems fine (you're sure you always close every file you open, even in case of exceptions) and you have multiple working threads, then you have two options: rework your code to serialize file access (not always doable and not always wanted) or apply a retry pattern. It's a pretty common pattern for I/O operations: you try to do something and in case of error you wait and try again (did you ask yourself why, for example, Windows Shell takes some time to inform you that a file is in use and cannot be deleted?). In C# it's pretty easy to implement (see also better examples about disk I/O, networking and database access).

    private const int NumberOfRetries = 3;
    private const int DelayOnRetry = 1000;
    
    for (int i=1; i <= NumberOfRetries; ++i) {
        try {
            // Do stuff with file
            break; // When done we can break loop
        }
        catch (IOException e) when (i <= NumberOfRetries) {
            // You may check error code to filter some exceptions, not every error
            // can be recovered.
            Thread.Sleep(DelayOnRetry);
        }
    }
    

    Please note a common error we see very often on StackOverflow:

    var stream = File.Open(path, FileOpen.Read);
    var content = File.ReadAllText(path);
    

    In this case ReadAllText() will fail because the file is in use (File.Open() in the line before). To open the file beforehand is not only unnecessary but also wrong. The same applies to all File functions that don't return a handle to the file you're working with: File.ReadAllText(), File.WriteAllText(), File.ReadAllLines(), File.WriteAllLines() and others (like File.AppendAllXyz() functions) will all open and close the file by themselves.

    Your process is not the only one to access that file
    If your process is not the only one to access that file, then interaction can be harder. A retry pattern will help (if the file shouldn't be open by anyone else but it is, then you need a utility like Process Explorer to check who is doing what).

    Ways to avoid

    When applicable, always use using statements to open files. As said in previous paragraph, it'll actively help you to avoid many common errors (see this post for an example on how not to use it).

    If possible, try to decide who owns access to a specific file and centralize access through a few well-known methods. If, for example, you have a data file where your program reads and writes, then you should box all I/O code inside a single class. It'll make debug easier (because you can always put a breakpoint there and see who is doing what) and also it'll be a synchronization point (if required) for multiple access.

    Don't forget I/O operations can always fail, a common example is this:

    if (File.Exists(path))
        File.Delete(path);
    

    If someone deletes the file after File.Exists() but before File.Delete(), then it'll throw an IOException in a place where you may wrongly feel safe.

    Whenever it's possible, apply a retry pattern, and if you're using FileSystemWatcher, consider postponing action (because you'll get notified, but an application may still be working exclusively with that file).

    Advanced scenarios
    It's not always so easy, so you may need to share access with someone else. If, for example, you're reading from the beginning and writing to the end, you have at least two options.

    1) share the same FileStream with proper synchronization functions (because it is not thread-safe). See this and this posts for an example.

    2) use FileShare enumeration to instruct OS to allow other processes (or other parts of your own process) to access same file concurrently.

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

    In this example I showed how to open a file for writing and share for reading; please note that when reading and writing overlaps, it results in undefined or invalid data. It's a situation that must be handled when reading. Also note that this doesn't make access to the stream thread-safe, so this object can't be shared with multiple threads unless access is synchronized somehow (see previous links). Other sharing options are available, and they open up more complex scenarios. Please refer to MSDN for more details.

    In general N processes can read from same file all together but only one should write, in a controlled scenario you may even enable concurrent writings but this can't be generalized in few text paragraphs inside this answer.

    Is it possible to unlock a file used by another process? It's not always safe and not so easy but yes, it's possible.

    0 讨论(0)
  • 2020-11-22 00:45

    I had this problem and it was solved by following the code below

    var _path=MyFile.FileName;
    using (var stream = new FileStream
        (_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
      { 
        // Your Code! ;
      }
    
    0 讨论(0)
提交回复
热议问题