.NET을 사용하여 파일을 잠그는 프로세스를 어떻게 알 수 있습니까?


154

Handle 또는 Process Monitor 사용에 대한 몇 가지 답변을 보았지만 파일을 잠그는 프로세스가 내 코드 (C #)에서 찾을 수 있기를 원합니다.

나는 win32 API에서 뛰어 들어야한다고 불쾌감을 느낍니다. 그러나 이미이 작업을 수행하고 올바른 길로 나를 인도 할 수 있다면 정말 도움을 주셔서 감사합니다.

최신 정보

비슷한 질문에 대한 링크


답변:


37

좋은 점 중 하나는 handle.exe하위 프로세스로 실행하여 출력을 구문 분석 할 수 있다는 것입니다.

배포 스크립트에서이 작업을 수행합니다. 매력처럼 작동합니다.


21
그러나 소프트웨어와 함께 handle.exe를 배포 할 수 없습니다
torpederos

1
좋은 지적. 내부적으로 사용되는 배포 스크립트에는 문제가 없었지만 다른 시나리오에있을 것입니다.
orip

2
C #의 전체 소스 코드 샘플? get 프로세스에도 유효합니다. FOLDER?
Kiquenet

3
handle.exe가 필요없는 솔루션에 대한 내 대답을 확인하십시오. stackoverflow.com/a/20623311/141172
Eric J.

"Handle을 실행하려면 관리자 권한이 있어야합니다."
Uwe Keim

135

오래 전에 Windows는 단순히 해당 정보를 추적하지 않았기 때문에 파일을 잠그는 프로세스 목록을 안정적으로 얻는 것이 불가능했습니다. Restart Manager API 를 지원하기 위해 해당 정보가 추적됩니다.

파일의 경로를 가져 와서 List<Process>해당 파일을 잠그는 모든 프로세스 중 하나 를 반환하는 코드 를 작성했습니다.

using System.Runtime.InteropServices;
using System.Diagnostics;
using System;
using System.Collections.Generic;

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

제한된 권한에서 사용 (예 : IIS)

이 호출은 레지스트리에 액세스합니다. 프로세스에 권한이 없으면을 의미하는 ERROR_WRITE_FAULT가 표시됩니다 An operation was unable to read or write to the registry. 당신은 할 수 선택적으로 레지스트리의 필요한 부분에 제한된 계정에 권한을 부여합니다. 제한된 액세스 프로세스가 플래그를 설정 (예 : 데이터베이스 또는 파일 시스템에서 또는 큐 또는 명명 된 파이프와 같은 프로세스 간 통신 메커니즘 사용)하는 것이 더 안전하고 두 번째 프로세스가 Restart Manager API를 호출하도록하는 것이 더 안전합니다.

IIS 사용자에게 최소 이외의 권한을 부여하면 보안 위험이 있습니다.


누구든지 그것을 시도해 보았지만, 실제로 작동 할 수있는 것 같습니다 (Vista 및 srv 2008 이상의 창)
Daniel Mošmondor

1
@Blagoh : Windows XP에서 다시 시작 관리자를 사용할 수 있다고 생각하지 않습니다. 여기에 게시 된 덜 정확한 다른 방법 중 하나에 의지해야합니다.
Eric J.

4
@Blagoh : 특정 DLL을 잠그는 사람을 알고 싶다면 tasklist /m YourDllName.dll출력을 사용 하고 구문 분석 할 수 있습니다 . 참조 stackoverflow.com/questions/152506/...
에릭 J.

19
타사 도구 나 문서화되지 않은 API 호출이 필요없는 솔루션 만 받아 들여질만한 대답이되어야합니다.
IInspectable

4
나는 이것을 Windows 2008R2, Windows 2012R2, Windows 7 및 Windows 10에서 시도해 보았습니다. 많은 상황에서 높은 권한으로 실행해야한다는 것을 알았습니다. 그렇지 않으면 목록을 가져올 때 실패합니다. 파일 잠금 프로세스.
Jay

60

C #에서 Win32를 호출하는 것은 매우 복잡합니다.

Handle.exe 도구를 사용해야합니다 .

그런 다음 C # 코드는 다음과 같아야합니다.

string fileName = @"c:\aaa.doc";//Path to locked file

Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = fileName+" /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();           
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();

string matchPattern = @"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach(Match match in Regex.Matches(outputTool, matchPattern))
{
    Process.GetProcessById(int.Parse(match.Value)).Kill();
}

1
좋은 예,하지만 내 지식, handle.exe 지금 당신이 처음으로 클라이언트 시스템에서 실행할 때 일부 조건에 동의 불쾌한 메시지를 표시하는 내 의견, 자격을 상실에
아르센 Zahray

13
@Arsen Zahray :의 명령 행 옵션을 전달하여 eula를 자동으로 수락 할 수 있습니다 /accepteula. Gennady의 답변을 변경 사항으로 업데이트했습니다.
Jon Cage

어떤 버전의 Handle.exe를 사용 했습니까? 최신 V4는 깨진 방식으로 변경되는 것 같습니다. / accepteula 및 파일 이름은 더 이상 지원되지 않습니다
Venson

3
재배포 할 수 없음handle.exe
기본

4
C #에서 win32 API를 호출 할 때 복잡성이 없습니다.
Idan

10

스테판의 솔루션에 문제가있었습니다 . 아래는 잘 작동하는 수정 된 버전입니다.

using System;
using System.Collections;
using System.Diagnostics;
using System.Management;
using System.IO;

static class Module1
{
    static internal ArrayList myProcessArray = new ArrayList();
    private static Process myProcess;

    public static void Main()
    {
        string strFile = "c:\\windows\\system32\\msi.dll";
        ArrayList a = getFileProcesses(strFile);
        foreach (Process p in a)
        {
            Debug.Print(p.ProcessName);
        }
    }

    private static ArrayList getFileProcesses(string strFile)
    {
        myProcessArray.Clear();
        Process[] processes = Process.GetProcesses();
        int i = 0;
        for (i = 0; i <= processes.GetUpperBound(0) - 1; i++)
        {
            myProcess = processes[i];
            //if (!myProcess.HasExited) //This will cause an "Access is denied" error
            if (myProcess.Threads.Count > 0)
            {
                try
                {
                    ProcessModuleCollection modules = myProcess.Modules;
                    int j = 0;
                    for (j = 0; j <= modules.Count - 1; j++)
                    {
                        if ((modules[j].FileName.ToLower().CompareTo(strFile.ToLower()) == 0))
                        {
                            myProcessArray.Add(myProcess);
                            break;
                            // TODO: might not be correct. Was : Exit For
                        }
                    }
                }
                catch (Exception exception)
                {
                    //MsgBox(("Error : " & exception.Message)) 
                }
            }
        }

        return myProcessArray;
    }
}

최신 정보

특정 DLL을 잠그는 프로세스를 알고 싶다면의 출력을 실행하고 구문 분석 할 수 있습니다 tasklist /m YourDllName.dll. Windows XP 이상에서 작동합니다. 보다

이것은 무엇을 하는가? 작업 목록 / m "mscor *"


내가 그렇게 많이 이유를 실패 myProcessArray? 클래스 멤버입니다 (물론 실제로 getFileProcesses (에서 반환) 동일은 간다 myProcess.
오스카 Berggren

7

이것은 다른 프로세스에 의해 잠긴 DLL에서 작동합니다. 이 루틴은 예를 들어 텍스트 파일이 워드 프로세스에 의해 잠겨 있음을 알 수 없습니다.

씨#:

using System.Management; 
using System.IO;   

static class Module1 
{ 
static internal ArrayList myProcessArray = new ArrayList(); 
private static Process myProcess; 

public static void Main() 
{ 

    string strFile = "c:\\windows\\system32\\msi.dll"; 
    ArrayList a = getFileProcesses(strFile); 
    foreach (Process p in a) { 
        Debug.Print(p.ProcessName); 
    } 
} 


private static ArrayList getFileProcesses(string strFile) 
{ 
    myProcessArray.Clear(); 
    Process[] processes = Process.GetProcesses; 
    int i = 0; 
    for (i = 0; i <= processes.GetUpperBound(0) - 1; i++) { 
        myProcess = processes(i); 
        if (!myProcess.HasExited) { 
            try { 
                ProcessModuleCollection modules = myProcess.Modules; 
                int j = 0; 
                for (j = 0; j <= modules.Count - 1; j++) { 
                    if ((modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) == 0)) { 
                        myProcessArray.Add(myProcess); 
                        break; // TODO: might not be correct. Was : Exit For 
                    } 
                } 
            } 
            catch (Exception exception) { 
            } 
            //MsgBox(("Error : " & exception.Message)) 
        } 
    } 
    return myProcessArray; 
} 
} 

VB.Net :

Imports System.Management
Imports System.IO

Module Module1
Friend myProcessArray As New ArrayList
Private myProcess As Process

Sub Main()

    Dim strFile As String = "c:\windows\system32\msi.dll"
    Dim a As ArrayList = getFileProcesses(strFile)
    For Each p As Process In a
        Debug.Print(p.ProcessName)
    Next
End Sub


Private Function getFileProcesses(ByVal strFile As String) As ArrayList
    myProcessArray.Clear()
    Dim processes As Process() = Process.GetProcesses
    Dim i As Integer
    For i = 0 To processes.GetUpperBound(0) - 1
        myProcess = processes(i)
        If Not myProcess.HasExited Then
            Try
                Dim modules As ProcessModuleCollection = myProcess.Modules
                Dim j As Integer
                For j = 0 To modules.Count - 1
                    If (modules.Item(j).FileName.ToLower.CompareTo(strFile.ToLower) = 0) Then
                        myProcessArray.Add(myProcess)
                        Exit For
                    End If
                Next j
            Catch exception As Exception
                'MsgBox(("Error : " & exception.Message))
            End Try
        End If
    Next i
    Return myProcessArray
End Function
End Module

내 예에서는 msi.dll을 사용합니다. wich는 .Net DLL이 아닙니다.
Stefan

0

linq로 더 간단하게 :

public void KillProcessesAssociatedToFile(string file)
    {
        GetProcessesAssociatedToFile(file).ForEach(x =>
        {
            x.Kill();
            x.WaitForExit(10000);
        });
    }

    public List<Process> GetProcessesAssociatedToFile(string file)
    {
        return Process.GetProcesses()
            .Where(x => !x.HasExited
                && x.Modules.Cast<ProcessModule>().ToList()
                    .Exists(y => y.FileName.ToLowerInvariant() == file.ToLowerInvariant())
                ).ToList();
    }

같은 예외를 다시 던지는 것 같습니다
Sinaesthetic

오류가 발생했습니다. 32 비트 프로세스는 64 비트 프로세스의 모듈에 액세스 할 수 없습니다.
ajinkya
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.