문제

이 질문에는 이미 답변이 있습니다.

try/catch 블록을 사용하지 않고 파일이 잠겨 있는지 확인할 수 있는 방법이 있습니까?

지금 내가 아는 유일한 방법은 파일을 열고 파일을 잡는 것뿐입니다. System.IO.IOException.

도움이 되었습니까?

해결책

아니요, 안타깝게도 생각해 보면 파일이 바로 다음 순간에 잠길 수 있으므로 해당 정보는 어차피 쓸모가 없을 것입니다(읽기:짧은 시간).

어쨌든 파일이 잠겨 있는지 구체적으로 알아야 하는 이유는 무엇입니까?이를 알면 우리가 당신에게 좋은 조언을 줄 수 있는 다른 방법을 얻을 수 있을 것입니다.

코드가 다음과 같은 경우:

if not locked then
    open and update file

그런 다음 두 줄 사이에서 다른 프로세스가 쉽게 파일을 잠글 수 있으며, 처음부터 피하려고 했던 것과 동일한 문제가 발생합니다.예외.

다른 팁

비슷한 문제에 직면했을 때 다음 코드로 마무리했습니다.

public bool IsFileLocked(string filePath)
{
    try
    {
        using (File.Open(filePath, FileMode.Open)){}
    }
    catch (IOException e)
    {
        var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);

        return errorCode == 32 || errorCode == 33;
    }

    return false;
}

다른 답변은 오래된 정보에 의존합니다.이것은 더 나은 솔루션을 제공합니다.

오래 전에는 Windows가 해당 정보를 추적하지 않았기 때문에 파일을 잠그는 프로세스 목록을 안정적으로 얻는 것이 불가능했습니다.지원하기 위해 관리자 API 다시 시작, 이제 해당 정보가 추적됩니다.Restart Manager API는 Windows Vista 및 Windows Server 2008(관리자 다시 시작:런타임 요구 사항).

나는 파일의 경로를 취하고 다음을 반환하는 코드를 작성했습니다. List<Process> 해당 파일을 잠그고 있는 모든 프로세스의

static public class FileUtil
{
    [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;
    }
}

업데이트

여기 또 다른 것이 있습니다 샘플 코드에 대한 토론 Restart Manager API를 사용하는 방법에 대해 알아보세요.

또한 이 파일을 사용하는 프로세스가 있는지 확인하고 설치 프로그램처럼 계속하려면 닫아야 하는 프로그램 목록을 표시할 수도 있습니다.

public static string GetFileProcessName(string filePath)
{
    Process[] procs = Process.GetProcesses();
    string fileName = Path.GetFileName(filePath);

    foreach (Process proc in procs)
    {
        if (proc.MainWindowHandle != new IntPtr(0) && !proc.HasExited)
        {
            ProcessModule[] arr = new ProcessModule[proc.Modules.Count];

            foreach (ProcessModule pm in proc.Modules)
            {
                if (pm.ModuleName == fileName)
                    return proc.ProcessName;
            }
        }
    }

    return null;
}

Interop을 사용하는 대신 .NET FileStream 클래스 메서드 Lock 및 Unlock을 사용할 수 있습니다.

파일스트림.잠금http://msdn.microsoft.com/en-us/library/system.io.filestream.lock.aspx

파일스트림.잠금해제http://msdn.microsoft.com/en-us/library/system.io.filestream.unlock.aspx

당신은 전화할 수 있습니다 잠금파일 관심 있는 파일 영역의 interop을 통해이것은 예외를 발생시키지 않습니다. 성공하면 파일의 해당 부분(프로세스에 의해 유지됨)에 대한 잠금이 설정되고 해당 잠금은 호출할 때까지 유지됩니다. 파일 잠금해제 아니면 프로세스가 종료됩니다.

DixonD의 탁월한 답변(위)의 변형입니다.

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           TimeSpan timeout,
                           out Stream stream)
{
    var endTime = DateTime.Now + timeout;

    while (DateTime.Now < endTime)
    {
        if (TryOpen(path, fileMode, fileAccess, fileShare, out stream))
            return true;
    }

    stream = null;
    return false;
}

public static bool TryOpen(string path,
                           FileMode fileMode,
                           FileAccess fileAccess,
                           FileShare fileShare,
                           out Stream stream)
{
    try
    {
        stream = File.Open(path, fileMode, fileAccess, fileShare);
        return true;
    }
    catch (IOException e)
    {
        if (!FileIsLocked(e))
            throw;

        stream = null;
        return false;
    }
}

private const uint HRFileLocked = 0x80070020;
private const uint HRPortionOfFileLocked = 0x80070021;

private static bool FileIsLocked(IOException ioException)
{
    var errorCode = (uint)Marshal.GetHRForException(ioException);
    return errorCode == HRFileLocked || errorCode == HRPortionOfFileLocked;
}

용법:

private void Sample(string filePath)
{
    Stream stream = null;

    try
    {
        var timeOut = TimeSpan.FromSeconds(1);

        if (!TryOpen(filePath,
                     FileMode.Open,
                     FileAccess.ReadWrite,
                     FileShare.ReadWrite,
                     timeOut,
                     out stream))
            return;

        // Use stream...
    }
    finally
    {
        if (stream != null)
            stream.Close();
    }
}

다음은 파일이 잠금 해제될 때까지 기다리는 시간(초)을 추가하고 다시 시도하는 DixonD 코드의 변형입니다.

public bool IsFileLocked(string filePath, int secondsToWait)
{
    bool isLocked = true;
    int i = 0;

    while (isLocked &&  ((i < secondsToWait) || (secondsToWait == 0)))
    {
        try
        {
            using (File.Open(filePath, FileMode.Open)) { }
            return false;
        }
        catch (IOException e)
        {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            i++;

            if (secondsToWait !=0)
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
        }
    }

    return isLocked;
}


if (!IsFileLocked(file, 10))
{
    ...
}
else
{
    throw new Exception(...);
}

그런 다음 두 줄 사이에서 다른 프로세스가 쉽게 파일을 잠글 수 있으며, 처음부터 피하려고 했던 것과 동일한 문제가 발생합니다.예외.

그러나 이렇게 하면 문제가 일시적이라는 것을 알 수 있으므로 나중에 다시 시도해야 합니다.(예를 들어 쓰기를 시도하는 동안 잠금이 발생하면 잠금이 사라질 때까지 계속 재시도하는 스레드를 작성할 수 있습니다.)

반면에 IOException은 잠금이 IO 실패의 원인일 만큼 충분히 구체적이지 않습니다.일시적이지 않은 이유가 있을 수 있습니다.

먼저 파일을 읽거나 잠그면 파일이 잠겨 있는지 확인할 수 있습니다.

자세한 내용은 여기에서 내 답변을 참조하세요..

같은 일이지만 Powershell에서

function Test-FileOpen
{
    Param
    ([string]$FileToOpen)
    try
    {
        $openFile =([system.io.file]::Open($FileToOpen,[system.io.filemode]::Open))
        $open =$true
        $openFile.close()
    }
    catch
    {
        $open = $false
    }
    $open
}

내가 한 일은 다음과 같습니다.

internal void LoadExternalData() {
    FileStream file;

    if (TryOpenRead("filepath/filename", 5, out file)) {
        using (file)
        using (StreamReader reader = new StreamReader(file)) {
         // do something 
        }
    }
}


internal bool TryOpenRead(string path, int timeout, out FileStream file) {
    bool isLocked = true;
    bool condition = true;

    do {
        try {
            file = File.OpenRead(path);
            return true;
        }
        catch (IOException e) {
            var errorCode = Marshal.GetHRForException(e) & ((1 << 16) - 1);
            isLocked = errorCode == 32 || errorCode == 33;
            condition = (isLocked && timeout > 0);

            if (condition) {
                // we only wait if the file is locked. If the exception is of any other type, there's no point on keep trying. just return false and null;
                timeout--;
                new System.Threading.ManualResetEvent(false).WaitOne(1000);
            }
        }
    }
    while (condition);

    file = null;
    return false;
}
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top