programing

IO 예외:파일 '파일 경로'가 다른 프로세스에서 사용되고 있기 때문에 프로세스에서 액세스할 수 없습니다.

magicmemo 2023. 5. 1. 20:48
반응형

IO 예외:파일 '파일 경로'가 다른 프로세스에서 사용되고 있기 때문에 프로세스에서 액세스할 수 없습니다.

▁a▁and▁throws다▁i니▁it표시됩,▁code▁when▁it.IOException그렇다고는 해도

다른 프로세스에서 '파일 이름' 파일을 사용 중이므로 프로세스에서 액세스할 수 없습니다.

이것은 무엇을 의미하며, 무엇을 할 수 있습니까?

원인이 무엇입니까?

오류 메시지는 매우 명확합니다. 파일에 액세스하려고 하면 다른 프로세스(또는 동일한 프로세스)가 파일을 사용하여 작업을 수행하고 있기 때문에 파일에 액세스할 수 없습니다(공유를 허용하지 않았습니다).

디버깅

특정 시나리오에 따라 해결하기가 매우 쉬울 수 있습니다(또는 이해하기가 상당히 어려울 수도 있습니다).어디 보자꾸나.

파일에 수 한 프로세스입니다.
당신은 다른 공정이 당신 자신의 공정이라고 확신합니다.프로그램의 다른 부분에서 해당 파일을 연 경우에는 먼저 사용할 때마다 파일 핸들을 제대로 닫아야 합니다.다음은 이 버그가 있는 코드의 예입니다.

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

도 다히도행.FileStreamIDisposable그래서 당신의 모든 코드를 안에 포장하는 것은 쉽습니다.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

이 패턴을 사용하면 예외가 발생한 경우에도 파일이 열려 있지 않습니다. 파일이 사용 중인 이유일 수 있습니다. 문제가 발생하여 아무도 닫지 않았습니다. 예를 들어 이 게시물을 참조하십시오.

모든 것이 정상인 것처럼 보이고(예외의 경우에도 열려 있는 모든 파일을 항상 닫는 것이 확실함) 여러 개의 작업 스레드가 있는 경우 코드를 재작업하여 파일 액세스를 직렬화하거나(항상 실행할 수 있는 것은 아님) 재시도 패턴을 적용할 수 있습니다.이는 I/O 작업에서 매우 일반적인 패턴입니다. 오류가 발생하면 기다렸다가 다시 시도합니다(예: Windows Shell에서 파일이 사용 중이며 삭제할 수 없음을 알리는 데 시간이 좀 걸리는 이유가 무엇인지 자문해 보셨습니까?).C#에서는 구현이 매우 쉽습니다(디스크 I/O, 네트워킹데이터베이스 액세스에 대한 더 나은 예 참조).

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

StackOverflow에서 자주 볼 수 있는 일반적인 오류:

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()functions)를 선택하면 파일이 자동으로 열리고 닫힙니다.

만이 그 할 수 한 방법은 .
프로세스가 해당 파일에 액세스하는 유일한 프로세스가 아닌 경우 상호 작용이 더 어려워질 수 있습니다.재시도 패턴이 도움이 됩니다(파일을 다른 사용자가 열어서는 안 되며 열리면 프로세스 탐색기와 같은 유틸리티를 사용하여 누가 무엇을 하고 있는지 확인해야 합니다).

피하는 방법

해당되는 경우 항상 문을 사용하여 파일을 엽니다.이전 단락에서 언급했듯이, 일반적인 오류를 방지하는 데 적극적으로 도움이 될 것입니다(사용하지 않는 방법에 대한 예는 이 게시물을 참조하십시오).

가능하면 특정 파일에 대한 액세스 권한을 소유한 사용자를 결정하고 잘 알려진 몇 가지 방법을 통해 액세스를 중앙 집중화합니다.예를 들어 프로그램이 읽고 쓰는 데이터 파일이 있는 경우 단일 클래스 내의 모든 I/O 코드를 상자에 넣어야 합니다.그러면 디버그가 더 쉬워지고(언제든지 중단점을 두고 누가 무엇을 하고 있는지 확인할 수 있기 때문에) 다중 액세스를 위한 동기화 지점이 될 수 있습니다.

I/O 작업은 항상 실패할 수 있습니다. 일반적인 예는 다음과 같습니다.

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

파일을 삭제한 후File.Exists() 그러그전에 앞에File.Delete()그러면 그것은 던져질 것입니다.IOException당신이 잘못해서 안전하다고 느낄 수 있는 장소에서.

가능할 때마다 재시도 패턴을 적용하고 사용 중인 경우FileSystemWatcher작업을 연기하는 것이 좋습니다(알림을 받지만 응용 프로그램이 해당 파일에서만 작동할 수 있기 때문입니다).


항상 쉽지는 않으므로 다른 사용자와 액세스를 공유해야 할 수 있습니다.예를 들어 처음부터 끝까지 읽고 쓰는 경우 두 가지 이상의 옵션이 있습니다.

것을 하는 것FileStream적절한 동기화 기능을 사용합니다(스레드 세이프가 아니기 때문에).예를 들어 이 게시물과 이 게시물을 참조하십시오.

임쓰를 합니다.FileShare열거형을 사용하여 OS에 다른 프로세스(또는 사용자 프로세스의 다른 부분)가 동일한 파일에 동시에 액세스할 수 있도록 합니다.

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

이 예에서는 쓰기 위해 파일을 열고 읽기 위해 공유하는 방법을 보여주었습니다. 읽기와 쓰기가 겹치면 정의되지 않았거나 잘못된 데이터가 생성됩니다.그것은 읽을 때 반드시 처리해야 하는 상황입니다.또한이액수없다니습할세스것은에 수 .stream스레드 세이프(thread-safe)이므로 액세스가 동기화되지 않으면 이 개체를 여러 스레드와 공유할 수 없습니다(이전 링크 참조).다른 공유 옵션을 사용할 수 있으며 더 복잡한 시나리오가 열립니다.자세한 내용은 MSDN을 참조하십시오.

일반적으로 N개의 프로세스는 동일한 파일에서 모두 읽을 수 있지만 하나만 써야 합니다. 제어된 시나리오에서는 동시 쓰기를 활성화할 수도 있지만 이는 이 답변 내의 몇 개의 텍스트 단락에서 일반화할 수 없습니다.

다른 프로세스에서 사용하는 파일의 잠금을 해제할 수 있습니까?항상 안전하지도 않고 쉽지도 않지만 가능합니다.

다른 프로세스에서 파일을 열어도 FileShare를 사용하여 파일을 여는 문제를 해결했습니다.

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

문제

는 파일을 입니다.System.IO.File.Open(path, FileMode)에 대한 공유 를 원하지만 에 대한 공유 액세스를 원합니다.

시스템의 설명서를 읽는 경우.IO.파일.Open(경로, FileMode) 공유를 허용하지 않는다고 명시적으로 말하고 있습니다.

여기에 이미지 설명 입력

해결책

를 사용하여 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
            }
} 

제가 File을 하고 있어서 이 오류가 발생했습니다.파일 이름 없이 파일 경로로 이동합니다. 대상에서 전체 경로를 지정해야 합니다.

저는 이 문제를 겪었고 아래 코드에 따라 해결되었습니다.

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

이 오류는 다른 프로세스가 파일에 액세스하려고 시도하고 있음을 나타냅니다.사용자 또는 다른 사용자가 파일에 쓰려고 시도하는 동안 파일이 열려 있을 수 있습니다.일반적으로 "읽기" 또는 "복사"는 이러한 문제를 일으키지 않지만, 여기에 글을 쓰거나 삭제를 호출합니다.

다른 답변에서 언급했듯이 이 문제를 방지하기 위한 몇 가지 기본 사항이 있습니다.

  1. FileStream 작업, 에치에 합니다.using로막다로 FileShare.ReadWrite접근 방식

    예:

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

    :FileAccess.ReadWrite▁you다▁is니▁use를 하면 불가능합니다.FileMode.Append.

  2. 입력 스트림을 사용하여 작업을 수행할 때 이 문제가 발생했습니다.File.SaveAs 시스템에 다시 했지만 파일 스트림을 만들입니다.using 문장.FileAccess.ReadWrite위의 코드와 매우 유사합니다.

  3. 데이터를 다른 파일로 저장한 후 더 이상 사용하지 않는 것으로 확인되면 이전 파일을 삭제한 후 성공적으로 저장한 파일의 이름을 원래 파일의 이름으로 변경하는 것도 옵션입니다.사용 중인 파일을 테스트하는 방법은 다음을 통해 수행됩니다.

    List<Process> lstProcs = ProcessHandler.WhoIsLocking(file);
    

    보고 싶은 특정 파일이 있으면 Windows 서비스에서 루프를 통해 수행할 수 있으며, 교체할 때 정기적으로 삭제할 수 있습니다.항상 동일한 파일이 없는 경우, 서비스에서 항상 파일 이름을 확인한 다음 프로세스를 확인하고 다음 옵션에서 설명한 대로 프로세스 제거 및 삭제를 수행하는 텍스트 파일 또는 데이터베이스 테이블이 업데이트될 수 있습니다.프로세스를 삭제하고 종료하려면 해당 컴퓨터에 대한 관리자 권한이 있는 계정 사용자 이름과 암호가 필요합니다.

  4. 파일을 저장하려고 할 때 파일이 사용 중인지 모를 경우, 파일을 사용할 수 있는 모든 프로세스(예: Word 문서인 경우)를 저장하기 전에 닫을 수 있습니다.

    로컬인 경우 다음 작업을 수행할 수 있습니다.

    ProcessHandler.localProcessKill("winword.exe");
    

    원격인 경우 다음 작업을 수행할 수 있습니다.

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

    txtUserName는 의형입다니의 입니다.DOMAIN\user.

  5. 파일을 잠그는 프로세스 이름을 모른다고 가정해 보겠습니다.그러면 다음 작업을 수행할 수 있습니다.

    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이 코드는 원래 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;
    
        }
    }
    

"IO Exception:"이라는 특별한 상황이 발생했습니다. 프로세스가 라인의 '파일 경로' 파일에 액세스할없습니다.

File.Delete(fileName);

다음과 같이 보이는 NUnit 테스트 내부:

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

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

이 문제는 공유 쓰기/읽기용 파일을 열어서 해결되었습니다.다음은 공유 읽기 및 쓰기를 위한 샘플 코드입니다. - 스트림 라이터

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

나의 아래 코드는 이 문제를 해결하지만, 우선 이 문제의 원인을 이해하고 코드를 변경하여 찾을 수 있는 해결책을 시도해야 한다고 제안합니다.

이 문제를 해결하기 위한 다른 방법을 제공할 수 있지만 더 나은 해결책은 코딩 구조를 확인하고 무엇이 이 문제를 발생시키는지 분석하는 것입니다. 해결책을 찾을 수 없다면 아래 코드를 사용할 수 있습니다.

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

언급URL : https://stackoverflow.com/questions/26741191/ioexception-the-process-cannot-access-the-file-file-path-because-it-is-being

반응형