public static IEnumerable <ProcessInfo> GetLockingProcessInfos(params string[] paths) { if (paths == null) { throw new ArgumentNullException("paths"); } const int maxRetries = 6; // See http://blogs.msdn.com/b/oldnewthing/archive/2012/02/17/10268840.aspx. var key = new StringBuilder(new string('\0', NativeMethods.CCH_RM_SESSION_KEY + 1)); uint handle; int res = NativeMethods.RmStartSession(out handle, 0, key); if (res != 0) { throw GetException(res, "RmStartSession", "Failed to begin restart manager session."); } try { string[] resources = paths; res = NativeMethods.RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null); if (res != 0) { throw GetException(res, "RmRegisterResources", "Could not register resources."); } // // Obtain the list of affected applications/services. // // NOTE: Restart Manager returns the results into the buffer allocated by the caller. The first call to // RmGetList() will return the size of the buffer (i.e. nProcInfoNeeded) the caller needs to allocate. // The caller then needs to allocate the buffer (i.e. rgAffectedApps) and make another RmGetList() // call to ask Restart Manager to write the results into the buffer. However, since Restart Manager // refreshes the list every time RmGetList()is called, it is possible that the size returned by the first // RmGetList()call is not sufficient to hold the results discovered by the second RmGetList() call. Therefore, // it is recommended that the caller follows the following practice to handle this race condition: // // Use a loop to call RmGetList() in case the buffer allocated according to the size returned in previous // call is not enough. // uint pnProcInfo = 0; NativeMethods.RM_PROCESS_INFO[] rgAffectedApps = null; int retry = 0; do { uint lpdwRebootReasons = (uint)NativeMethods.RM_REBOOT_REASON.RmRebootReasonNone; uint pnProcInfoNeeded; res = NativeMethods.RmGetList(handle, out pnProcInfoNeeded, ref pnProcInfo, rgAffectedApps, ref lpdwRebootReasons); if (res == 0) { // If pnProcInfo == 0, then there is simply no locking process (found), in this case rgAffectedApps is "null". if (pnProcInfo == 0) { return(Enumerable.Empty <ProcessInfo>()); } Debug.Assert(rgAffectedApps != null, "rgAffectedApps != null"); var lockInfos = new List <ProcessInfo>((int)pnProcInfo); for (int i = 0; i < pnProcInfo; i++) { lockInfos.Add(ProcessInfoWindows.Create(rgAffectedApps[i])); } return(lockInfos); } if (res != NativeMethods.ERROR_MORE_DATA) { throw GetException(res, "RmGetList", string.Format("Failed to get entries (retry {0}).", retry)); } pnProcInfo = pnProcInfoNeeded; rgAffectedApps = new NativeMethods.RM_PROCESS_INFO[pnProcInfo]; } while ((res == NativeMethods.ERROR_MORE_DATA) && (retry++ < maxRetries)); } finally { res = NativeMethods.RmEndSession(handle); if (res != 0) { throw GetException(res, "RmEndSession", "Failed to end the restart manager session."); } } return(Enumerable.Empty <ProcessInfo>()); }
private static void GetLockingProcessInfo(string path, List <ProcessInfo> result) { if (path == null) { throw new ArgumentNullException(nameof(path)); } var bufferPtr = IntPtr.Zero; var statusBlock = new NativeMethods.IO_STATUS_BLOCK(); try { using (var handle = NativeMethods.GetFileHandle(path)) { if (handle.IsInvalid) { // The file does not exist or is gone already. Could be a race condition. There is nothing we can contribute. // Doing this, exhibits the same behavior as the RestartManager implementation. return; } uint bufferSize = 0x4000; bufferPtr = Marshal.AllocHGlobal((int)bufferSize); uint status; while ((status = NativeMethods.NtQueryInformationFile(handle, ref statusBlock, bufferPtr, bufferSize, NativeMethods.FILE_INFORMATION_CLASS.FileProcessIdsUsingFileInformation)) == NativeMethods.STATUS_INFO_LENGTH_MISMATCH) { Marshal.FreeHGlobal(bufferPtr); bufferPtr = IntPtr.Zero; bufferSize *= 2; bufferPtr = Marshal.AllocHGlobal((int)bufferSize); } if (status != NativeMethods.STATUS_SUCCESS) { throw GetException(status, "NtQueryInformationFile", "Failed to get file process IDs"); } // Buffer contains: // struct FILE_PROCESS_IDS_USING_FILE_INFORMATION // { // ULONG NumberOfProcessIdsInList; // ULONG_PTR ProcessIdList[1]; // } var readBuffer = bufferPtr; int numEntries = Marshal.ReadInt32(readBuffer); // NumberOfProcessIdsInList readBuffer += IntPtr.Size; for (int i = 0; i < numEntries; i++) { int processId = Marshal.ReadIntPtr(readBuffer).ToInt32(); // A single ProcessIdList[] element result.Add(ProcessInfoWindows.Create(processId)); readBuffer += IntPtr.Size; } } } finally { if (bufferPtr != IntPtr.Zero) { Marshal.FreeHGlobal(bufferPtr); } } }