IOException: процесс не может получить доступ к файлу "путь к файлу", поскольку он используется другим процессом

У меня есть некоторый код, и когда он выполняется, он бросает IOException, говоря это

Процесс не может получить доступ к файлу "имя файла", потому что он используется другим процессом

Что это значит, и что я могу с этим поделать?

14 ответов

Решение

В чем причина?

Сообщение об ошибке довольно ясно: вы пытаетесь получить доступ к файлу, и он недоступен, потому что другой процесс (или даже тот же процесс) что-то делает с ним (и он не разрешает какой-либо обмен).

отладка

Это может быть довольно легко решить (или довольно сложно понять), в зависимости от вашего конкретного сценария. Давайте посмотрим некоторые.

Ваш процесс является единственным, чтобы получить доступ к этому файлу
Вы уверены, что другой процесс - это ваш собственный процесс. Если вы знаете, что открываете этот файл в другой части вашей программы, то прежде всего вы должны проверить, правильно ли вы закрываете дескриптор файла после каждого использования. Вот пример кода с этой ошибкой:

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

к счастью FileStream инвентарь IDisposableтак что легко обернуть весь ваш код в using заявление:

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

Этот шаблон также гарантирует, что файл не останется открытым в случае исключений (это может быть причиной того, что файл используется: что-то пошло не так, и никто не закрыл его; см. Этот пост для примера).

Если все выглядит нормально (вы уверены, что всегда закрываете каждый файл, который открываете, даже в случае исключений) и у вас есть несколько рабочих потоков, тогда у вас есть два варианта: переработать код для сериализации доступа к файлу (не всегда выполнимо и не всегда хотел) или применить шаблон повторов. Это довольно распространенный шаблон для операций ввода-вывода: вы пытаетесь что-то сделать, а в случае ошибки вы ждете и пытаетесь снова (вы спрашивали себя, например, почему Windows Shell требуется некоторое время, чтобы сообщить вам, что файл используется и нельзя удалить?). В C# это довольно просто реализовать (см. Также лучшие примеры дискового ввода-вывода, работы в сети и доступа к базе данных).

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);
    }
}

Обратите внимание на распространенную ошибку, которую мы очень часто видим в Stackru:

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

В этом случае ReadAllText() потерпит неудачу, потому что файл используется (File.Open() в строке раньше). Открывать файл заранее не только не нужно, но и неправильно. То же самое относится ко всем File функции, которые не возвращают дескриптор файла, с которым вы работаете: File.ReadAllText(), File.WriteAllText(), File.ReadAllLines(), File.WriteAllLines() и другие (как File.AppendAllXyz() функции) будут все открывать и закрывать файл самостоятельно.

Ваш процесс не единственный, чтобы получить доступ к этому файлу
Если ваш процесс не единственный, кто получает доступ к этому файлу, взаимодействие может быть более сложным. Шаблон повторных попыток поможет (если файл не должен быть открыт кем-либо еще, но это так, тогда вам нужна утилита, такая как Process Explorer, чтобы проверить, кто что делает).

Способов избежать

Когда это применимо, всегда используйте операторы для открытия файлов. Как было сказано в предыдущем абзаце, это поможет вам избежать многих распространенных ошибок (см. Этот пост, где показано, как его не использовать).

Если возможно, попытайтесь решить, кому принадлежит доступ к определенному файлу, и централизовать доступ несколькими известными способами. Если, например, у вас есть файл данных, в котором ваша программа читает и пишет, вы должны поместить весь код ввода / вывода в один класс. Это облегчит отладку (потому что вы всегда можете поставить точку останова и посмотреть, кто что делает), а также будет точкой синхронизации (если требуется) для множественного доступа.

Не забывайте, что операции ввода-вывода всегда могут быть неудачными, общий пример:

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

Если кто-то удаляет файл после File.Exists() но прежде File.Delete()потом бросит IOException в месте, где вы можете ошибочно чувствовать себя в безопасности.

Когда это возможно, примените шаблон повторов, и если вы используете FileSystemWatcherрассмотрите возможность отложить действие (потому что вы получите уведомление, но приложение может работать только с этим файлом).

Расширенные сценарии
Это не всегда так просто, поэтому вам может понадобиться поделиться доступом с кем-то еще. Если, например, вы читаете от начала и пишете до конца, у вас есть как минимум два варианта.

1) делить то же самое FileStream с правильными функциями синхронизации (потому что это не потокобезопасно). Смотрите этот и этот пост для примера.

2) использовать FileShare перечисление, чтобы указать ОС разрешить другим процессам (или другим частям вашего собственного процесса) одновременно обращаться к одному и тому же файлу.

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

В этом примере я показал, как открыть файл для записи и поделиться для чтения; обратите внимание, что при чтении и записи перекрытий это приводит к неопределенным или недействительным данным. Это ситуация, которая должна быть обработана при чтении. Также обратите внимание, что это не делает доступ к stream Потокобезопасен, поэтому этот объект не может использоваться несколькими потоками, если доступ не синхронизирован каким-либо образом (см. предыдущие ссылки). Другие варианты обмена доступны, и они открывают более сложные сценарии. Пожалуйста, обратитесь к MSDN для более подробной информации.

В общем случае N процессов могут читать из одного и того же файла все вместе, но писать должен только один, в контролируемом сценарии вы можете даже разрешить одновременные записи, но это не может быть обобщено в нескольких текстовых параграфах внутри этого ответа.

Можно ли разблокировать файл, используемый другим процессом? Это не всегда безопасно и не так просто, но да, это возможно.

Использование FileShare исправило мою проблему открытия файла, даже если он был открыт другим процессом.

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

Я использую FileStream и имею ту же проблему.. Когда когда-либо два запроса пытаются прочитать один и тот же файл, он выдает это исключение.

решение использовать FileShare

using FileStream fs = System.IO.File.Open(filePath, FileMode.Open, FileAccess.Read, FileShare.Read);

Я просто читаю файл одновременно FileShare.Read решить мою проблему.

Возникла проблема при загрузке изображения, и он не мог удалить его и нашел решение. гл хф

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

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

//later...

File.Delete(filePath); //now works

Как указывали другие ответы в этой теме, чтобы устранить эту ошибку, вам нужно тщательно проверить код, чтобы понять, где файл блокируется.

В моем случае я отправлял файл как вложение электронной почты перед выполнением операции перемещения.

Таким образом, файл был заблокирован на пару секунд, пока SMTP-клиент не завершил отправку электронного письма.

Я принял решение сначала переместить файл, а затем отправить электронное письмо. Это решило проблему для меня.

Другое возможное решение, как указывалось ранее Хадсоном, заключалось в удалении объекта после использования.

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
            }
} 

Ошибка указывает, что другой процесс пытается получить доступ к файлу. Может быть, вы или кто-то еще открыли его, когда вы пытаетесь писать в него. "Чтение" или "Копирование" обычно не вызывает этого, но запись в него или вызов удаления его приведет к этому.

Есть несколько основных вещей, чтобы избежать этого, как уже упоминалось в других ответах:

  1. В FileStream операции, поместите его в using блок с FileShare.ReadWrite режим доступа.

Ex.

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

Обратите внимание, что FileAccess.ReadWrite невозможно, если вы используете FileMode.Append,

  1. Я столкнулся с этой проблемой, когда я использовал поток ввода, чтобы сделать File.SaveAs когда файл использовался. В моем случае я обнаружил, что мне вообще не нужно было сохранять его обратно в файловую систему, поэтому я просто удалил его, но, возможно, я мог бы попытаться создать FileStream в using заявление с FileAccess.ReadWrite, очень похоже на код выше.

  2. Сохраните ваши данные в другом файле и вернитесь, чтобы удалить старый, если он больше не используется, а затем переименовать тот, который успешно сохранен, в имя исходного. Как вы проверяете файл, который используется, осуществляется с помощью List<Process> lstProcs = ProcessHandler.WhoIsLocking(file); строка в моем коде ниже, и может быть сделано в службе Windows, в цикле, если у вас есть определенный файл, который вы хотите регулярно просматривать и удалять, когда вы хотите заменить его. Если у вас не всегда один и тот же файл, можно обновить текстовый файл или таблицу базы данных, чтобы служба всегда проверяла имена файлов, а затем выполняла эту проверку для процессов и впоследствии выполняла уничтожение и удаление процессов, как я описываю в следующем варианте. Обратите внимание, что вам понадобится имя пользователя и пароль учетной записи, которые имеют права администратора на данном компьютере, конечно, для выполнения удаления и завершения процессов.

    4а. Если вы не знаете, будет ли файл использоваться при попытке его сохранить, вы можете закрыть все процессы, которые могут его использовать, например, Word, если это документ Word, до сохранения.

Если это локально, вы можете сделать это:

ProcessHandler.localProcessKill("winword.exe");

Если он удаленный, вы можете сделать это:

ProcessHandler.remoteProcessKill(computerName, txtUserName, txtPassword, "winword.exe");

где txtUserName в форме DOMAIN\user,

4b. Допустим, вы не знаете имя процесса, который блокирует файл... вы можете сделать это:

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);
}

Обратите внимание, что file должен быть путь UNC: \\computer\share\yourdoc.docx для того, чтобы Process выяснить, на каком компьютере он находится и p.MachineName быть действительным. Ниже приведен класс, используемый этими функциями, который требует добавления ссылки на System.Management и поступает с /questions/20085731/kak-uznat-kakoj-protsess-blokiruet-fajl-s-pomoschyunet/20085744#20085744:

    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;

        }
    }

У меня была эта проблема, и она была решена с помощью кода ниже

var _path=MyFile.FileName;
using (var stream = new FileStream
    (_path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
  { 
    // Your Code! ;
  }

Я получил эту ошибку, потому что я делал File.Move к пути к файлу без имени файла, необходимо указать полный путь в месте назначения.

У меня была очень специфическая ситуация, когда я получал «IOException: процесс не может получить доступ к файлу« путь к файлу »» в строке

      File.Delete(fileName);

Внутри теста NUnit, который выглядел так:

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

Оказывается, NUnit 3 использует то, что они называют «изолированным контекстом» для утверждений исключений. Вероятно, это выполняется в отдельном потоке.

Мое исправление состояло в том, чтобы поставить File.Delete в том же контексте.

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

У меня был следующий сценарий, который вызывал ту же ошибку:

  • Загрузить файлы на сервер
  • Затем избавьтесь от старых файлов после их загрузки

Большинство файлов были небольшими по размеру, однако некоторые были большими, поэтому попытка удалить их привела к ошибке доступа к файлу.

Было нелегко найти решение, однако, решение было таким же простым, как ожидание "завершения задачи":

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

В моем случае эта проблема была решена путем открытия файла для совместной записи/чтения . Ниже приведены примеры кодов для совместного чтения и записи: Stream Writer.

      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())
}

У меня была такая же проблема, я запускал свой проект на локальном хосте. После того, как я завершил выполнение проекта на локальном хосте, он сработал.

Перейдите к пути к файлу и удалите его, а затем снова запустите Android Studio. Возможно, конфликт между двумя файлами

Мой приведенный ниже код решает эту проблему, но я предлагаю Прежде всего вам нужно понять, что вызывает эту проблему, и попробовать решение, которое вы можете найти, изменив код.

Я могу предложить другой способ решения этой проблемы, но лучшим решением является проверка вашей структуры кодирования и попытка проанализировать, почему это происходит. Если вы не найдете никакого решения, вы можете использовать этот код ниже.

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;
    }
 }
Другие вопросы по тегам