1

I tried several solutions to find a process locking a file and kill this process. I found a solution who works fine but sometimes crashes my application and I don't know why. Look at the example and please if someone could help me, it would be appreciated.

try
{
    file.CopyTo(destAbsolutePath, true);
}
catch (IOException ioException)
{
    foreach (var process in Process.GetProcessesByName(file.Name.Replace(Path.GetExtension(file.Name), "")))
    {
        process.Kill();
        process.WaitForExit();
    }
    foreach (Process p in Win32Processes.GetProcessesLockingFile(file.FullName))
    {
        p.Kill();
        process.WaitForExit();
    }
    file.CopyTo(destAbsolutePath, true);
}

And the code I used base on this post: Using C#, how does one figure out what process locked a file?

I modified the GetProcessesLockingFile() method to work for me. I explained the reason in the last comment of the post (link just above). Maybe I didn't use it well.

public static class Win32Processes
{
    /// <summary>
    /// Return a list of processes that hold on the given file.
    /// </summary>
    public static List<Process> GetProcessesLockingFile(string filePath)
    {
        var procs = new List<Process>();

        var processListSnapshot = Process.GetProcesses();
        foreach (var process in processListSnapshot)
        {
            if (process.Id <= 4) { continue; } // system processes
            List<string> paths = GetFilesLockedBy(process);
            foreach (string path in paths)
            {
                string pathDirectory = path;
                if (!pathDirectory.EndsWith(Constants.DOUBLE_BACKSLASH))
                {
                    pathDirectory = pathDirectory + Constants.DOUBLE_BACKSLASH;
                }
                string lastFolderName = Path.GetFileName(Path.GetDirectoryName(pathDirectory));

                if (filePath.Contains(lastFolderName))
                {
                    procs.Add(process);
                }

            }
        }
        return procs;
    }

    /// <summary>
    /// Return a list of file locks held by the process.
    /// </summary>
    public static List<string> GetFilesLockedBy(Process process)
    {
        var outp = new List<string>();

        ThreadStart ts = delegate
        {
            try
            {
                outp = UnsafeGetFilesLockedBy(process);
            }
            catch { Ignore(); }
        };


        try
        {
            var t = new Thread(ts);
            t.IsBackground = true;
            t.Start();
            if (!t.Join(250))
            {
                try
                {
                    t.Interrupt();
                    t.Abort();
                }
                catch { Ignore(); }
            }
        }
        catch { Ignore(); }

        return outp;
    }


    #region Inner Workings
    private static void Ignore() { }
    private static List<string> UnsafeGetFilesLockedBy(Process process)
    {
        try
        {
            var handles = GetHandles(process);
            var files = new List<string>();

            foreach (var handle in handles)
            {
                var file = GetFilePath(handle, process);
                if (file != null) files.Add(file);
            }

            return files;
        }
        catch
        {
            return new List<string>();
        }
    }

    const int CNST_SYSTEM_HANDLE_INFORMATION = 16;
    private static string GetFilePath(Win32API.SYSTEM_HANDLE_INFORMATION systemHandleInformation, Process process)
    {
        var ipProcessHwnd = Win32API.OpenProcess(Win32API.ProcessAccessFlags.All, false, process.Id);
        var objBasic = new Win32API.OBJECT_BASIC_INFORMATION();
        var objObjectType = new Win32API.OBJECT_TYPE_INFORMATION();
        var objObjectName = new Win32API.OBJECT_NAME_INFORMATION();
        var strObjectName = "";
        var nLength = 0;
        IntPtr ipTemp, ipHandle;

        if (!Win32API.DuplicateHandle(ipProcessHwnd, systemHandleInformation.Handle, Win32API.GetCurrentProcess(), out ipHandle, 0, false, Win32API.DUPLICATE_SAME_ACCESS))
            return null;

        IntPtr ipBasic = Marshal.AllocHGlobal(Marshal.SizeOf(objBasic));
        Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectBasicInformation, ipBasic, Marshal.SizeOf(objBasic), ref nLength);
        objBasic = (Win32API.OBJECT_BASIC_INFORMATION)Marshal.PtrToStructure(ipBasic, objBasic.GetType());
        Marshal.FreeHGlobal(ipBasic);

        IntPtr ipObjectType = Marshal.AllocHGlobal(objBasic.TypeInformationLength);
        nLength = objBasic.TypeInformationLength;
        // this one never locks...
        while ((uint)(Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectTypeInformation, ipObjectType, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
        {
            if (nLength == 0)
            {
                Console.WriteLine("nLength returned at zero! ");
                return null;
            }
            Marshal.FreeHGlobal(ipObjectType);
            ipObjectType = Marshal.AllocHGlobal(nLength);
        }

        objObjectType = (Win32API.OBJECT_TYPE_INFORMATION)Marshal.PtrToStructure(ipObjectType, objObjectType.GetType());
        if (Is64Bits())
        {
            ipTemp = new IntPtr(Convert.ToInt64(objObjectType.Name.Buffer.ToString(), 10) >> 32);
        }
        else
        {
            ipTemp = objObjectType.Name.Buffer;
        }


        var strObjectTypeName = Marshal.PtrToStringUni(ipTemp, objObjectType.Name.Length >> 1);
        Marshal.FreeHGlobal(ipObjectType);
        if (strObjectTypeName != "File")
            return null;

        nLength = objBasic.NameInformationLength;

        var ipObjectName = Marshal.AllocHGlobal(nLength);

        // ...this call sometimes hangs. Is a Windows error.
        while ((uint)(Win32API.NtQueryObject(ipHandle, (int)Win32API.ObjectInformationClass.ObjectNameInformation, ipObjectName, nLength, ref nLength)) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
        {
            Marshal.FreeHGlobal(ipObjectName);
            if (nLength == 0)
            {
                Console.WriteLine("nLength returned at zero! " + strObjectTypeName);
                return null;
            }
            ipObjectName = Marshal.AllocHGlobal(nLength);
        }
        objObjectName = (Win32API.OBJECT_NAME_INFORMATION)Marshal.PtrToStructure(ipObjectName, objObjectName.GetType());

        if (Is64Bits())
        {
            ipTemp = new IntPtr(Convert.ToInt64(objObjectName.Name.Buffer.ToString(), 10) >> 32);
        }
        else
        {
            ipTemp = objObjectName.Name.Buffer;
        }

        if (ipTemp != IntPtr.Zero)
        {

            var baTemp = new byte[nLength];
            try
            {
                Marshal.Copy(ipTemp, baTemp, 0, nLength);

                strObjectName = Marshal.PtrToStringUni(Is64Bits() ? new IntPtr(ipTemp.ToInt64()) : new IntPtr(ipTemp.ToInt32()));
            }
            catch (AccessViolationException)
            {
                return null;
            }
            finally
            {
                Marshal.FreeHGlobal(ipObjectName);
                Win32API.CloseHandle(ipHandle);
            }
        }

        string path = GetRegularFileNameFromDevice(strObjectName);
        try
        {
            return path;
        }
        catch
        {
            return null;
        }
    }

    private static string GetRegularFileNameFromDevice(string strRawName)
    {
        string strFileName = strRawName;
        foreach (string strDrivePath in Environment.GetLogicalDrives())
        {
            var sbTargetPath = new StringBuilder(Win32API.MAX_PATH);
            if (Win32API.QueryDosDevice(strDrivePath.Substring(0, 2), sbTargetPath, Win32API.MAX_PATH) == 0)
            {
                return strRawName;
            }
            string strTargetPath = sbTargetPath.ToString();
            if (strFileName.StartsWith(strTargetPath))
            {
                strFileName = strFileName.Replace(strTargetPath, strDrivePath.Substring(0, 2));
                break;
            }
        }
        return strFileName;
    }

    private static IEnumerable<Win32API.SYSTEM_HANDLE_INFORMATION> GetHandles(Process process)
    {
        var nHandleInfoSize = 0x10000;
        var ipHandlePointer = Marshal.AllocHGlobal(nHandleInfoSize);
        var nLength = 0;
        IntPtr ipHandle;

        while (Win32API.NtQuerySystemInformation(CNST_SYSTEM_HANDLE_INFORMATION, ipHandlePointer, nHandleInfoSize, ref nLength) == Win32API.STATUS_INFO_LENGTH_MISMATCH)
        {
            nHandleInfoSize = nLength;
            Marshal.FreeHGlobal(ipHandlePointer);
            ipHandlePointer = Marshal.AllocHGlobal(nLength);
        }

        var baTemp = new byte[nLength];
        Marshal.Copy(ipHandlePointer, baTemp, 0, nLength);

        long lHandleCount;
        if (Is64Bits())
        {
            lHandleCount = Marshal.ReadInt64(ipHandlePointer);
            ipHandle = new IntPtr(ipHandlePointer.ToInt64() + 8);
        }
        else
        {
            lHandleCount = Marshal.ReadInt32(ipHandlePointer);
            ipHandle = new IntPtr(ipHandlePointer.ToInt32() + 4);
        }

        var lstHandles = new List<Win32API.SYSTEM_HANDLE_INFORMATION>();

        for (long lIndex = 0; lIndex < lHandleCount; lIndex++)
        {
            var shHandle = new Win32API.SYSTEM_HANDLE_INFORMATION();
            if (Is64Bits())
            {
                shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
                ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle) + 8);
            }
            else
            {
                ipHandle = new IntPtr(ipHandle.ToInt64() + Marshal.SizeOf(shHandle));
                shHandle = (Win32API.SYSTEM_HANDLE_INFORMATION)Marshal.PtrToStructure(ipHandle, shHandle.GetType());
            }
            if (shHandle.ProcessID != process.Id) continue;
            lstHandles.Add(shHandle);
        }
        return lstHandles;

    }

    private static bool Is64Bits()
    {
        return Marshal.SizeOf(typeof(IntPtr)) == 8;
    }

    internal class Win32API
    {
        [DllImport("ntdll.dll")]
        public static extern int NtQueryObject(IntPtr ObjectHandle, int
            ObjectInformationClass, IntPtr ObjectInformation, int ObjectInformationLength,
            ref int returnLength);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern uint QueryDosDevice(string lpDeviceName, StringBuilder lpTargetPath, int ucchMax);

        [DllImport("ntdll.dll")]
        public static extern uint NtQuerySystemInformation(int
            SystemInformationClass, IntPtr SystemInformation, int SystemInformationLength,
            ref int returnLength);

        [DllImport("kernel32.dll")]
        public static extern IntPtr OpenProcess(ProcessAccessFlags dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
        [DllImport("kernel32.dll")]
        public static extern int CloseHandle(IntPtr hObject);
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
           ushort hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
           uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
        [DllImport("kernel32.dll")]
        public static extern IntPtr GetCurrentProcess();

        public enum ObjectInformationClass
        {
            ObjectBasicInformation = 0,
            ObjectNameInformation = 1,
            ObjectTypeInformation = 2,
            ObjectAllTypesInformation = 3,
            ObjectHandleInformation = 4
        }

        [Flags]
        public enum ProcessAccessFlags : uint
        {
            All = 0x001F0FFF,
            Terminate = 0x00000001,
            CreateThread = 0x00000002,
            VMOperation = 0x00000008,
            VMRead = 0x00000010,
            VMWrite = 0x00000020,
            DupHandle = 0x00000040,
            SetInformation = 0x00000200,
            QueryInformation = 0x00000400,
            Synchronize = 0x00100000
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct OBJECT_BASIC_INFORMATION
        { // Information Class 0
            public int Attributes;
            public int GrantedAccess;
            public int HandleCount;
            public int PointerCount;
            public int PagedPoolUsage;
            public int NonPagedPoolUsage;
            public int Reserved1;
            public int Reserved2;
            public int Reserved3;
            public int NameInformationLength;
            public int TypeInformationLength;
            public int SecurityDescriptorLength;
            public System.Runtime.InteropServices.ComTypes.FILETIME CreateTime;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct OBJECT_TYPE_INFORMATION
        { // Information Class 2
            public UNICODE_STRING Name;
            public int ObjectCount;
            public int HandleCount;
            public int Reserved1;
            public int Reserved2;
            public int Reserved3;
            public int Reserved4;
            public int PeakObjectCount;
            public int PeakHandleCount;
            public int Reserved5;
            public int Reserved6;
            public int Reserved7;
            public int Reserved8;
            public int InvalidAttributes;
            public GENERIC_MAPPING GenericMapping;
            public int ValidAccess;
            public byte Unknown;
            public byte MaintainHandleDatabase;
            public int PoolType;
            public int PagedPoolUsage;
            public int NonPagedPoolUsage;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct OBJECT_NAME_INFORMATION
        { // Information Class 1
            public UNICODE_STRING Name;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct UNICODE_STRING
        {
            public ushort Length;
            public ushort MaximumLength;
            public IntPtr Buffer;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct GENERIC_MAPPING
        {
            public int GenericRead;
            public int GenericWrite;
            public int GenericExecute;
            public int GenericAll;
        }

        [StructLayout(LayoutKind.Sequential, Pack = 1)]
        public struct SYSTEM_HANDLE_INFORMATION
        { // Information Class 16
            public int ProcessID;
            public byte ObjectTypeNumber;
            public byte Flags; // 0x01 = PROTECT_FROM_CLOSE, 0x02 = INHERIT
            public ushort Handle;
            public int Object_Pointer;
            public UInt32 GrantedAccess;
        }

        public const int MAX_PATH = 260;
        public const uint STATUS_INFO_LENGTH_MISMATCH = 0xC0000004;
        public const int DUPLICATE_SAME_ACCESS = 0x2;
        public const uint FILE_SEQUENTIAL_ONLY = 0x00000004;
    }
    #endregion
}
Community
  • 1
  • 1
  • Very bad idea. You should never kill other applications. – Phil1970 Aug 01 '16 at 17:53
  • It's for work I know what i'm doing I only kill application and files made by the company who cause error when I try to replace them during an update with my update software. – Émile Pettersen-Coulombe Aug 01 '16 at 18:14
  • If it is your own application, then why not implement a way to close you application gracefully or to tell the application to close files for a moment. Or why your application keep the file opened to start with. If the application keep file opened just for a small amount of time, then you might simply retry the copy a few seconds later if it fails one or 2 times and log an error somewhere if it does not work after a few retries. Or the application might always open temporary copies of the original files so that it is always possible to replace original file. – Phil1970 Aug 02 '16 at 19:39
  • It's just in case if we forget to close a process or a file who could cause an error – Émile Pettersen-Coulombe Aug 03 '16 at 12:14
  • The log the problem and fix it manually on next run... If it is very urgent, then send an email and retry later hoping that the process/file was closed in between. Or if the other process is your own software, implement it that software the logic necessary to ensure that file are not opened for an extended period of time. – Phil1970 Aug 04 '16 at 13:23
  • I can't fix the problem manually bacause my software will run at client place and I will not be there. It's like when you update your windows, you need to avoid errors and manage all exceptions because Bill Gates would not be there when your windows update will crash ;) – Émile Pettersen-Coulombe Aug 04 '16 at 17:30
  • It is even worst. How do you know you won't kill a critical process running at client? – Phil1970 Aug 06 '16 at 19:26
  • I dont know if this line completly avoid this :/ `if (process.Id <= 4) { continue; } // system processes` – Émile Pettersen-Coulombe Aug 09 '16 at 17:35
  • When you kill an application, you never know what could happen. If that application is modifying that file, you might end up with a corrupted file which might be far worst that one file missing from the backup. – Phil1970 Aug 10 '16 at 21:40
  • 1
    When you kill an application, you never know what could happen. If that application is modifying that file, you might end up with a corrupted file which might be far worst that one file missing from the backup. Most files will probably not be modified so you can use the previous copy until next try. Some programs like Microsoft Outlook keep their data files opened but you should neither close them as it might be important for an user to receive its emails... By the way, it should not be possible to kill an application without having admin rights. – Phil1970 Aug 10 '16 at 21:48

0 Answers0