├── .gitignore ├── GetAllHandles.cs ├── GetProcessHandles.cs ├── LICENSE.md ├── README.md ├── RestartManager.cs ├── WalkmanLib.GetFileLocks.GetAllHandles.cs ├── WalkmanLib.GetFileLocks.GetProcessHandles.cs ├── WalkmanLib.RestartManager.cs └── WalkmanLib.SystemHandles.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | [Ll]ogs/ 33 | 34 | # Visual Studio 2015/2017 cache/options directory 35 | .vs/ 36 | # Uncomment if you have tasks that create the project's static files in wwwroot 37 | #wwwroot/ 38 | 39 | # Visual Studio 2017 auto generated files 40 | Generated\ Files/ 41 | 42 | # MSTest test Results 43 | [Tt]est[Rr]esult*/ 44 | [Bb]uild[Ll]og.* 45 | 46 | # NUnit 47 | *.VisualState.xml 48 | TestResult.xml 49 | nunit-*.xml 50 | 51 | # Build Results of an ATL Project 52 | [Dd]ebugPS/ 53 | [Rr]eleasePS/ 54 | dlldata.c 55 | 56 | # Benchmark Results 57 | BenchmarkDotNet.Artifacts/ 58 | 59 | # .NET Core 60 | project.lock.json 61 | project.fragment.lock.json 62 | artifacts/ 63 | 64 | # StyleCop 65 | StyleCopReport.xml 66 | 67 | # Files built by Visual Studio 68 | *_i.c 69 | *_p.c 70 | *_h.h 71 | *.ilk 72 | *.meta 73 | *.obj 74 | *.iobj 75 | *.pch 76 | *.pdb 77 | *.ipdb 78 | *.pgc 79 | *.pgd 80 | *.rsp 81 | *.sbr 82 | *.tlb 83 | *.tli 84 | *.tlh 85 | *.tmp 86 | *.tmp_proj 87 | *_wpftmp.csproj 88 | *.log 89 | *.vspscc 90 | *.vssscc 91 | .builds 92 | *.pidb 93 | *.svclog 94 | *.scc 95 | 96 | # Chutzpah Test files 97 | _Chutzpah* 98 | 99 | # Visual C++ cache files 100 | ipch/ 101 | *.aps 102 | *.ncb 103 | *.opendb 104 | *.opensdf 105 | *.sdf 106 | *.cachefile 107 | *.VC.db 108 | *.VC.VC.opendb 109 | 110 | # Visual Studio profiler 111 | *.psess 112 | *.vsp 113 | *.vspx 114 | *.sap 115 | 116 | # Visual Studio Trace Files 117 | *.e2e 118 | 119 | # TFS 2012 Local Workspace 120 | $tf/ 121 | 122 | # Guidance Automation Toolkit 123 | *.gpState 124 | 125 | # ReSharper is a .NET coding add-in 126 | _ReSharper*/ 127 | *.[Rr]e[Ss]harper 128 | *.DotSettings.user 129 | 130 | # TeamCity is a build add-in 131 | _TeamCity* 132 | 133 | # DotCover is a Code Coverage Tool 134 | *.dotCover 135 | 136 | # AxoCover is a Code Coverage Tool 137 | .axoCover/* 138 | !.axoCover/settings.json 139 | 140 | # Visual Studio code coverage results 141 | *.coverage 142 | *.coveragexml 143 | 144 | # NCrunch 145 | _NCrunch_* 146 | .*crunch*.local.xml 147 | nCrunchTemp_* 148 | 149 | # MightyMoose 150 | *.mm.* 151 | AutoTest.Net/ 152 | 153 | # Web workbench (sass) 154 | .sass-cache/ 155 | 156 | # Installshield output folder 157 | [Ee]xpress/ 158 | 159 | # DocProject is a documentation generator add-in 160 | DocProject/buildhelp/ 161 | DocProject/Help/*.HxT 162 | DocProject/Help/*.HxC 163 | DocProject/Help/*.hhc 164 | DocProject/Help/*.hhk 165 | DocProject/Help/*.hhp 166 | DocProject/Help/Html2 167 | DocProject/Help/html 168 | 169 | # Click-Once directory 170 | publish/ 171 | 172 | # Publish Web Output 173 | *.[Pp]ublish.xml 174 | *.azurePubxml 175 | # Note: Comment the next line if you want to checkin your web deploy settings, 176 | # but database connection strings (with potential passwords) will be unencrypted 177 | *.pubxml 178 | *.publishproj 179 | 180 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 181 | # checkin your Azure Web App publish settings, but sensitive information contained 182 | # in these scripts will be unencrypted 183 | PublishScripts/ 184 | 185 | # NuGet Packages 186 | *.nupkg 187 | # NuGet Symbol Packages 188 | *.snupkg 189 | # The packages folder can be ignored because of Package Restore 190 | **/[Pp]ackages/* 191 | # except build/, which is used as an MSBuild target. 192 | !**/[Pp]ackages/build/ 193 | # Uncomment if necessary however generally it will be regenerated when needed 194 | #!**/[Pp]ackages/repositories.config 195 | # NuGet v3's project.json files produces more ignorable files 196 | *.nuget.props 197 | *.nuget.targets 198 | 199 | # Microsoft Azure Build Output 200 | csx/ 201 | *.build.csdef 202 | 203 | # Microsoft Azure Emulator 204 | ecf/ 205 | rcf/ 206 | 207 | # Windows Store app package directories and files 208 | AppPackages/ 209 | BundleArtifacts/ 210 | Package.StoreAssociation.xml 211 | _pkginfo.txt 212 | *.appx 213 | *.appxbundle 214 | *.appxupload 215 | 216 | # Visual Studio cache files 217 | # files ending in .cache can be ignored 218 | *.[Cc]ache 219 | # but keep track of directories ending in .cache 220 | !?*.[Cc]ache/ 221 | 222 | # Others 223 | ClientBin/ 224 | ~$* 225 | *~ 226 | *.dbmdl 227 | *.dbproj.schemaview 228 | *.jfm 229 | *.pfx 230 | *.publishsettings 231 | orleans.codegen.cs 232 | 233 | # Including strong name files can present a security risk 234 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 235 | #*.snk 236 | 237 | # Since there are multiple workflows, uncomment next line to ignore bower_components 238 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 239 | #bower_components/ 240 | 241 | # RIA/Silverlight projects 242 | Generated_Code/ 243 | 244 | # Backup & report files from converting an old project file 245 | # to a newer Visual Studio version. Backup files are not needed, 246 | # because we have git ;-) 247 | _UpgradeReport_Files/ 248 | Backup*/ 249 | UpgradeLog*.XML 250 | UpgradeLog*.htm 251 | ServiceFabricBackup/ 252 | *.rptproj.bak 253 | 254 | # SQL Server files 255 | *.mdf 256 | *.ldf 257 | *.ndf 258 | 259 | # Business Intelligence projects 260 | *.rdl.data 261 | *.bim.layout 262 | *.bim_*.settings 263 | *.rptproj.rsuser 264 | *- [Bb]ackup.rdl 265 | *- [Bb]ackup ([0-9]).rdl 266 | *- [Bb]ackup ([0-9][0-9]).rdl 267 | 268 | # Microsoft Fakes 269 | FakesAssemblies/ 270 | 271 | # GhostDoc plugin setting file 272 | *.GhostDoc.xml 273 | 274 | # Node.js Tools for Visual Studio 275 | .ntvs_analysis.dat 276 | node_modules/ 277 | 278 | # Visual Studio 6 build log 279 | *.plg 280 | 281 | # Visual Studio 6 workspace options file 282 | *.opt 283 | 284 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 285 | *.vbw 286 | 287 | # Visual Studio LightSwitch build output 288 | **/*.HTMLClient/GeneratedArtifacts 289 | **/*.DesktopClient/GeneratedArtifacts 290 | **/*.DesktopClient/ModelManifest.xml 291 | **/*.Server/GeneratedArtifacts 292 | **/*.Server/ModelManifest.xml 293 | _Pvt_Extensions 294 | 295 | # Paket dependency manager 296 | .paket/paket.exe 297 | paket-files/ 298 | 299 | # FAKE - F# Make 300 | .fake/ 301 | 302 | # CodeRush personal settings 303 | .cr/personal 304 | 305 | # Python Tools for Visual Studio (PTVS) 306 | __pycache__/ 307 | *.pyc 308 | 309 | # Cake - Uncomment if you are using it 310 | # tools/** 311 | # !tools/packages.config 312 | 313 | # Tabs Studio 314 | *.tss 315 | 316 | # Telerik's JustMock configuration file 317 | *.jmconfig 318 | 319 | # BizTalk build output 320 | *.btp.cs 321 | *.btm.cs 322 | *.odx.cs 323 | *.xsd.cs 324 | 325 | # OpenCover UI analysis results 326 | OpenCover/ 327 | 328 | # Azure Stream Analytics local run output 329 | ASALocalRun/ 330 | 331 | # MSBuild Binary and Structured Log 332 | *.binlog 333 | 334 | # NVidia Nsight GPU debugger configuration file 335 | *.nvuser 336 | 337 | # MFractors (Xamarin productivity tool) working folder 338 | .mfractor/ 339 | 340 | # Local History for Visual Studio 341 | .localhistory/ 342 | 343 | # BeatPulse healthcheck temp database 344 | healthchecksdb 345 | 346 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 347 | MigrationBackup/ 348 | 349 | # Ionide (cross platform F# VS Code tools) working folder 350 | .ionide/ 351 | -------------------------------------------------------------------------------- /GetAllHandles.cs: -------------------------------------------------------------------------------- 1 | // Get all system open handles method - uses NTQuerySystemInformation and NTQueryObject 2 | //https://gist.github.com/i-e-b/2290426 3 | //https://stackoverflow.com/a/13735033/2999220 4 | 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Runtime.ConstrainedExecution; 9 | using System.Runtime.InteropServices; 10 | 11 | namespace GetAllHandles 12 | { 13 | public static class HandleUtil 14 | { 15 | public enum HandleType 16 | { 17 | Unknown, 18 | Other, 19 | File, Directory, SymbolicLink, Key, 20 | Process, Thread, Job, Session, WindowStation, 21 | Timer, Desktop, Semaphore, Token, 22 | Mutant, Section, Event, KeyedEvent, IoCompletion, IoCompletionReserve, 23 | TpWorkerFactory, AlpcPort, WmiGuid, UserApcReserve, 24 | } 25 | 26 | public enum NT_STATUS 27 | { 28 | STATUS_SUCCESS = 0x00000000, 29 | STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L), 30 | STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L) 31 | } 32 | 33 | public enum SYSTEM_INFORMATION_CLASS 34 | { 35 | SystemBasicInformation = 0, 36 | SystemPerformanceInformation = 2, 37 | SystemTimeOfDayInformation = 3, 38 | SystemProcessInformation = 5, 39 | SystemProcessorPerformanceInformation = 8, 40 | SystemHandleInformation = 16, 41 | SystemInterruptInformation = 23, 42 | SystemExceptionInformation = 33, 43 | SystemRegistryQuotaInformation = 37, 44 | SystemLookasideInformation = 45 45 | } 46 | 47 | public enum OBJECT_INFORMATION_CLASS 48 | { 49 | ObjectBasicInformation = 0, 50 | ObjectNameInformation = 1, 51 | ObjectTypeInformation = 2, 52 | ObjectAllTypesInformation = 3, 53 | ObjectHandleInformation = 4 54 | } 55 | 56 | [StructLayout(LayoutKind.Sequential)] 57 | private struct SystemHandleEntry 58 | { 59 | public int OwnerProcessId; 60 | public byte ObjectTypeNumber; 61 | public byte Flags; 62 | public ushort Handle; 63 | public IntPtr Object; 64 | public int GrantedAccess; 65 | } 66 | 67 | [DllImport("ntdll.dll")] 68 | internal static extern NT_STATUS NtQuerySystemInformation( 69 | [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, 70 | [In] IntPtr SystemInformation, 71 | [In] int SystemInformationLength, 72 | [Out] out int ReturnLength); 73 | 74 | [DllImport("ntdll.dll")] 75 | internal static extern NT_STATUS NtQueryObject( 76 | [In] IntPtr Handle, 77 | [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, 78 | [In] IntPtr ObjectInformation, 79 | [In] int ObjectInformationLength, 80 | [Out] out int ReturnLength); 81 | 82 | [DllImport("kernel32.dll")] 83 | internal static extern IntPtr GetCurrentProcess(); 84 | 85 | [DllImport("kernel32.dll", SetLastError = true)] 86 | public static extern IntPtr OpenProcess( 87 | [In] int dwDesiredAccess, 88 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 89 | [In] int dwProcessId); 90 | 91 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 92 | [DllImport("kernel32.dll", SetLastError = true)] 93 | [return: MarshalAs(UnmanagedType.Bool)] 94 | internal static extern bool CloseHandle( 95 | [In] IntPtr hObject); 96 | 97 | [DllImport("kernel32.dll", SetLastError = true)] 98 | [return: MarshalAs(UnmanagedType.Bool)] 99 | public static extern bool DuplicateHandle( 100 | [In] IntPtr hSourceProcessHandle, 101 | [In] IntPtr hSourceHandle, 102 | [In] IntPtr hTargetProcessHandle, 103 | [Out] out IntPtr lpTargetHandle, 104 | [In] int dwDesiredAccess, 105 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 106 | [In] int dwOptions); 107 | 108 | public class HandleInfo 109 | { 110 | public int ProcessId { get; private set; } 111 | public ushort Handle { get; private set; } 112 | public int GrantedAccess { get; private set; } 113 | public byte RawType { get; private set; } 114 | 115 | public HandleInfo(int processId, ushort handle, int grantedAccess, byte rawType) 116 | { 117 | ProcessId = processId; 118 | Handle = handle; 119 | GrantedAccess = grantedAccess; 120 | RawType = rawType; 121 | } 122 | 123 | private static Dictionary _rawTypeMap = new Dictionary(); 124 | 125 | private string _name, _typeStr; 126 | private HandleType _type; 127 | 128 | public string Name { get { if (_name == null) initTypeAndName(); return _name; } } 129 | public string TypeString { get { if (_typeStr == null) initType(); return _typeStr; } } 130 | public HandleType Type { get { if (_typeStr == null) initType(); return _type; } } 131 | 132 | private void initType() 133 | { 134 | if (_rawTypeMap.ContainsKey(RawType)) 135 | { 136 | _typeStr = _rawTypeMap[RawType]; 137 | _type = HandleTypeFromString(_typeStr); 138 | } 139 | else 140 | initTypeAndName(); 141 | } 142 | 143 | bool _typeAndNameAttempted = false; 144 | 145 | private void initTypeAndName() 146 | { 147 | if (_typeAndNameAttempted) 148 | return; 149 | _typeAndNameAttempted = true; 150 | 151 | IntPtr sourceProcessHandle = IntPtr.Zero; 152 | IntPtr handleDuplicate = IntPtr.Zero; 153 | try 154 | { 155 | sourceProcessHandle = OpenProcess(0x40 /* dup_handle */, true, ProcessId); 156 | 157 | // To read info about a handle owned by another process we must duplicate it into ours 158 | // For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality 159 | if (!DuplicateHandle(sourceProcessHandle, (IntPtr)Handle, GetCurrentProcess(), out handleDuplicate, 0, false, 2 /* same_access */)) 160 | return; 161 | 162 | // Query the object type 163 | if (_rawTypeMap.ContainsKey(RawType)) 164 | _typeStr = _rawTypeMap[RawType]; 165 | else 166 | { 167 | int length; 168 | NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); 169 | IntPtr ptr = IntPtr.Zero; 170 | try 171 | { 172 | ptr = Marshal.AllocHGlobal(length); 173 | if (NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS) 174 | return; 175 | _typeStr = Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x58 + 2 * IntPtr.Size)); 176 | _rawTypeMap[RawType] = _typeStr; 177 | } 178 | finally 179 | { 180 | Marshal.FreeHGlobal(ptr); 181 | } 182 | } 183 | _type = HandleTypeFromString(_typeStr); 184 | 185 | // Query the object name 186 | if (_typeStr != null && GrantedAccess != 0x0012019f && GrantedAccess != 0x00120189 && GrantedAccess != 0x120089) // don't query some objects that could get stuck 187 | { 188 | int length; 189 | NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length); 190 | IntPtr ptr = IntPtr.Zero; 191 | try 192 | { 193 | ptr = Marshal.AllocHGlobal(length); 194 | if (NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS) 195 | return; 196 | _name = Marshal.PtrToStringUni((IntPtr)((int)ptr + 2 * IntPtr.Size)); 197 | } 198 | finally 199 | { 200 | Marshal.FreeHGlobal(ptr); 201 | } 202 | } 203 | } 204 | finally 205 | { 206 | CloseHandle(sourceProcessHandle); 207 | if (handleDuplicate != IntPtr.Zero) 208 | CloseHandle(handleDuplicate); 209 | } 210 | } 211 | 212 | public static HandleType HandleTypeFromString(string typeStr) 213 | { 214 | switch (typeStr) 215 | { 216 | case null: 217 | return HandleType.Unknown; 218 | case "File": 219 | return HandleType.File; 220 | case "IoCompletion": 221 | return HandleType.IoCompletion; 222 | case "TpWorkerFactory": 223 | return HandleType.TpWorkerFactory; 224 | case "ALPC Port": 225 | return HandleType.AlpcPort; 226 | case "Event": 227 | return HandleType.Event; 228 | case "Section": 229 | return HandleType.Section; 230 | case "Directory": 231 | return HandleType.Directory; 232 | case "KeyedEvent": 233 | return HandleType.KeyedEvent; 234 | case "Process": 235 | return HandleType.Process; 236 | case "Key": 237 | return HandleType.Key; 238 | case "SymbolicLink": 239 | return HandleType.SymbolicLink; 240 | case "Thread": 241 | return HandleType.Thread; 242 | case "Mutant": 243 | return HandleType.Mutant; 244 | case "WindowStation": 245 | return HandleType.WindowStation; 246 | case "Timer": 247 | return HandleType.Timer; 248 | case "Semaphore": 249 | return HandleType.Semaphore; 250 | case "Desktop": 251 | return HandleType.Desktop; 252 | case "Token": 253 | return HandleType.Token; 254 | case "Job": 255 | return HandleType.Job; 256 | case "Session": 257 | return HandleType.Session; 258 | case "IoCompletionReserve": 259 | return HandleType.IoCompletionReserve; 260 | case "WmiGuid": 261 | return HandleType.WmiGuid; 262 | case "UserApcReserve": 263 | return HandleType.UserApcReserve; 264 | default: 265 | return HandleType.Other; 266 | } 267 | } 268 | } 269 | 270 | public static IEnumerable GetHandles() 271 | { 272 | // Attempt to retrieve the handle information 273 | int length = 0x10000; 274 | IntPtr ptr = IntPtr.Zero; 275 | try 276 | { 277 | while (true) 278 | { 279 | ptr = Marshal.AllocHGlobal(length); 280 | int wantedLength; 281 | var result = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out wantedLength); 282 | if (result == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) 283 | { 284 | length = Math.Max(length, wantedLength); 285 | Marshal.FreeHGlobal(ptr); 286 | ptr = IntPtr.Zero; 287 | } 288 | else if (result == NT_STATUS.STATUS_SUCCESS) 289 | break; 290 | else 291 | throw new Exception("Failed to retrieve system handle information."); 292 | } 293 | 294 | int handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : (int)Marshal.ReadInt64(ptr); 295 | int offset = IntPtr.Size; 296 | int size = Marshal.SizeOf(typeof(SystemHandleEntry)); 297 | for (int i = 0; i < handleCount; i++) 298 | { 299 | var struc = (SystemHandleEntry)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SystemHandleEntry)); 300 | 301 | // see https://gist.github.com/i-e-b/2290426#gistcomment-3234676 302 | if (!(struc.GrantedAccess == 0x001a019f && struc.Flags == 2)) 303 | yield return new HandleInfo(struc.OwnerProcessId, struc.Handle, struc.GrantedAccess, struc.ObjectTypeNumber); 304 | offset += size; 305 | } 306 | } 307 | finally 308 | { 309 | if (ptr != IntPtr.Zero) 310 | Marshal.FreeHGlobal(ptr); 311 | } 312 | } 313 | } 314 | } -------------------------------------------------------------------------------- /GetProcessHandles.cs: -------------------------------------------------------------------------------- 1 | // Get a process's open handles method - uses NTQuerySystemInformation and NTQueryObject 2 | //https://stackoverflow.com/a/6351168/2999220 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Runtime.CompilerServices; 9 | using System.Runtime.ConstrainedExecution; 10 | using System.Runtime.InteropServices; 11 | using System.Security.Permissions; 12 | using System.Text; 13 | using System.Threading; 14 | using Microsoft.Win32.SafeHandles; 15 | using System.Diagnostics; 16 | using System.Linq; 17 | 18 | namespace GetProcessHandles 19 | { 20 | public static class DetectOpenFiles 21 | { 22 | private static Dictionary deviceMap; 23 | private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\"; 24 | private const int MAX_PATH = 260; 25 | private const int handleTypeTokenCount = 27; 26 | private static readonly string[] handleTypeTokens = new string[] { 27 | "", "", "Directory", "SymbolicLink", "Token", 28 | "Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant", 29 | "Unknown11", "Semaphore", "Timer", "Profile", "WindowStation", 30 | "Desktop", "Section", "Key", "Port", "WaitablePort", 31 | "Unknown21", "Unknown22", "Unknown23", "Unknown24", 32 | "IoCompletion", "File" 33 | }; 34 | 35 | internal enum NT_STATUS 36 | { 37 | STATUS_SUCCESS = 0x00000000, 38 | STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L), 39 | STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L) 40 | } 41 | 42 | internal enum SYSTEM_INFORMATION_CLASS 43 | { 44 | SystemBasicInformation = 0, 45 | SystemPerformanceInformation = 2, 46 | SystemTimeOfDayInformation = 3, 47 | SystemProcessInformation = 5, 48 | SystemProcessorPerformanceInformation = 8, 49 | SystemHandleInformation = 16, 50 | SystemInterruptInformation = 23, 51 | SystemExceptionInformation = 33, 52 | SystemRegistryQuotaInformation = 37, 53 | SystemLookasideInformation = 45 54 | } 55 | 56 | internal enum OBJECT_INFORMATION_CLASS 57 | { 58 | ObjectBasicInformation = 0, 59 | ObjectNameInformation = 1, 60 | ObjectTypeInformation = 2, 61 | ObjectAllTypesInformation = 3, 62 | ObjectHandleInformation = 4 63 | } 64 | 65 | [Flags] 66 | internal enum ProcessAccessRights 67 | { 68 | PROCESS_DUP_HANDLE = 0x00000040 69 | } 70 | 71 | [Flags] 72 | internal enum DuplicateHandleOptions 73 | { 74 | DUPLICATE_CLOSE_SOURCE = 0x1, 75 | DUPLICATE_SAME_ACCESS = 0x2 76 | } 77 | 78 | private enum SystemHandleType 79 | { 80 | OB_TYPE_UNKNOWN = 0, 81 | OB_TYPE_TYPE = 1, 82 | OB_TYPE_DIRECTORY, 83 | OB_TYPE_SYMBOLIC_LINK, 84 | OB_TYPE_TOKEN, 85 | OB_TYPE_PROCESS, 86 | OB_TYPE_THREAD, 87 | OB_TYPE_UNKNOWN_7, 88 | OB_TYPE_EVENT, 89 | OB_TYPE_EVENT_PAIR, 90 | OB_TYPE_MUTANT, 91 | OB_TYPE_UNKNOWN_11, 92 | OB_TYPE_SEMAPHORE, 93 | OB_TYPE_TIMER, 94 | OB_TYPE_PROFILE, 95 | OB_TYPE_WINDOW_STATION, 96 | OB_TYPE_DESKTOP, 97 | OB_TYPE_SECTION, 98 | OB_TYPE_KEY, 99 | OB_TYPE_PORT, 100 | OB_TYPE_WAITABLE_PORT, 101 | OB_TYPE_UNKNOWN_21, 102 | OB_TYPE_UNKNOWN_22, 103 | OB_TYPE_UNKNOWN_23, 104 | OB_TYPE_UNKNOWN_24, 105 | //OB_TYPE_CONTROLLER, 106 | //OB_TYPE_DEVICE, 107 | //OB_TYPE_DRIVER, 108 | OB_TYPE_IO_COMPLETION, 109 | OB_TYPE_FILE 110 | }; 111 | 112 | [StructLayout(LayoutKind.Sequential)] 113 | private struct SYSTEM_HANDLE_ENTRY 114 | { 115 | public int OwnerPid; 116 | public byte ObjectType; 117 | public byte HandleFlags; 118 | public short HandleValue; 119 | public int ObjectPointer; 120 | public int AccessMask; 121 | } 122 | 123 | [DllImport("ntdll.dll")] 124 | internal static extern NT_STATUS NtQuerySystemInformation( 125 | [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, 126 | [In] IntPtr SystemInformation, 127 | [In] int SystemInformationLength, 128 | [Out] out int ReturnLength); 129 | 130 | [DllImport("ntdll.dll")] 131 | internal static extern NT_STATUS NtQueryObject( 132 | [In] IntPtr Handle, 133 | [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, 134 | [In] IntPtr ObjectInformation, 135 | [In] int ObjectInformationLength, 136 | [Out] out int ReturnLength); 137 | 138 | [DllImport("kernel32.dll", SetLastError = true)] 139 | internal static extern SafeProcessHandle OpenProcess( 140 | [In] ProcessAccessRights dwDesiredAccess, 141 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 142 | [In] int dwProcessId); 143 | 144 | [DllImport("kernel32.dll", SetLastError = true)] 145 | [return: MarshalAs(UnmanagedType.Bool)] 146 | internal static extern bool DuplicateHandle( 147 | [In] IntPtr hSourceProcessHandle, 148 | [In] IntPtr hSourceHandle, 149 | [In] IntPtr hTargetProcessHandle, 150 | [Out] out SafeObjectHandle lpTargetHandle, 151 | [In] int dwDesiredAccess, 152 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 153 | [In] DuplicateHandleOptions dwOptions); 154 | 155 | [DllImport("kernel32.dll")] 156 | internal static extern IntPtr GetCurrentProcess(); 157 | 158 | [DllImport("kernel32.dll", SetLastError = true)] 159 | internal static extern int GetProcessId( 160 | [In] IntPtr Process); 161 | 162 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 163 | [DllImport("kernel32.dll", SetLastError = true)] 164 | [return: MarshalAs(UnmanagedType.Bool)] 165 | internal static extern bool CloseHandle( 166 | [In] IntPtr hObject); 167 | 168 | [DllImport("kernel32.dll", SetLastError = true)] 169 | internal static extern int QueryDosDevice( 170 | [In] string lpDeviceName, 171 | [Out] StringBuilder lpTargetPath, 172 | [In] int ucchMax); 173 | 174 | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] 175 | internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid 176 | { 177 | private SafeObjectHandle() 178 | : base(true) 179 | { } 180 | 181 | internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle) 182 | : base(ownsHandle) 183 | { 184 | base.SetHandle(preexistingHandle); 185 | } 186 | 187 | protected override bool ReleaseHandle() 188 | { 189 | return CloseHandle(base.handle); 190 | } 191 | } 192 | 193 | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] 194 | internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid 195 | { 196 | private SafeProcessHandle() 197 | : base(true) 198 | { } 199 | 200 | internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle) 201 | : base(ownsHandle) 202 | { 203 | base.SetHandle(preexistingHandle); 204 | } 205 | 206 | protected override bool ReleaseHandle() 207 | { 208 | return CloseHandle(base.handle); 209 | } 210 | } 211 | 212 | private sealed class OpenFiles : IEnumerable 213 | { 214 | private readonly int processId; 215 | 216 | internal OpenFiles(int processId) 217 | { 218 | this.processId = processId; 219 | } 220 | 221 | public IEnumerator GetEnumerator() 222 | { 223 | NT_STATUS ret; 224 | int length = 0x10000; 225 | // Loop, probing for required memory. 226 | 227 | do 228 | { 229 | IntPtr ptr = IntPtr.Zero; 230 | RuntimeHelpers.PrepareConstrainedRegions(); 231 | try 232 | { 233 | RuntimeHelpers.PrepareConstrainedRegions(); 234 | try 235 | { } 236 | finally 237 | { 238 | // CER guarantees that the address of the allocated 239 | // memory is actually assigned to ptr if an 240 | // asynchronous exception occurs. 241 | ptr = Marshal.AllocHGlobal(length); 242 | } 243 | int returnLength; 244 | ret = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out returnLength); 245 | if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) 246 | { 247 | // Round required memory up to the nearest 64KB boundary. 248 | length = (returnLength + 0xffff) & ~0xffff; 249 | } 250 | else if (ret == NT_STATUS.STATUS_SUCCESS) 251 | { 252 | int handleCount = Marshal.ReadInt32(ptr); 253 | int offset = sizeof(int); 254 | int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY)); 255 | for (int i = 0; i < handleCount; i++) 256 | { 257 | SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY)); 258 | if (handleEntry.OwnerPid == processId) 259 | { 260 | IntPtr handle = (IntPtr)handleEntry.HandleValue; 261 | SystemHandleType handleType; 262 | 263 | if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE) 264 | { 265 | string devicePath; 266 | if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out devicePath)) 267 | { 268 | string dosPath; 269 | if (ConvertDevicePathToDosPath(devicePath, out dosPath)) 270 | { 271 | if (File.Exists(dosPath)) 272 | yield return dosPath; // return new FileInfo(dosPath); 273 | else if (Directory.Exists(dosPath)) 274 | yield return dosPath; // new DirectoryInfo(dosPath); 275 | } 276 | } 277 | } 278 | } 279 | offset += size; 280 | } 281 | } 282 | } 283 | finally 284 | { 285 | // CER guarantees that the allocated memory is freed, 286 | // if an asynchronous exception occurs. 287 | Marshal.FreeHGlobal(ptr); 288 | //sw.Flush(); 289 | //sw.Close(); 290 | } 291 | } 292 | while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH); 293 | } 294 | 295 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 296 | { 297 | return GetEnumerator(); 298 | } 299 | } 300 | 301 | private class FileNameFromHandleState : IDisposable 302 | { 303 | private ManualResetEvent _mr; 304 | public IntPtr Handle { get; } 305 | public string FileName { get; set; } 306 | public bool RetValue { get; set; } 307 | 308 | public FileNameFromHandleState(IntPtr handle) 309 | { 310 | _mr = new ManualResetEvent(false); 311 | this.Handle = handle; 312 | } 313 | 314 | public bool WaitOne(int wait) 315 | { 316 | return _mr.WaitOne(wait, false); 317 | } 318 | 319 | public void Set() 320 | { 321 | try 322 | { 323 | _mr.Set(); 324 | } 325 | catch { } 326 | } 327 | 328 | public void Dispose() 329 | { 330 | if (_mr != null) 331 | _mr.Close(); 332 | } 333 | } 334 | 335 | private static bool GetFileNameFromHandle(IntPtr handle, out string fileName) 336 | { 337 | IntPtr ptr = IntPtr.Zero; 338 | RuntimeHelpers.PrepareConstrainedRegions(); 339 | try 340 | { 341 | int length = 0x200; // 512 bytes 342 | RuntimeHelpers.PrepareConstrainedRegions(); 343 | try 344 | { } 345 | finally 346 | { 347 | // CER guarantees the assignment of the allocated 348 | // memory address to ptr, if an ansynchronous exception 349 | // occurs. 350 | ptr = Marshal.AllocHGlobal(length); 351 | } 352 | NT_STATUS ret = NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); 353 | if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW) 354 | { 355 | RuntimeHelpers.PrepareConstrainedRegions(); 356 | try 357 | { } 358 | finally 359 | { 360 | // CER guarantees that the previous allocation is freed, 361 | // and that the newly allocated memory address is 362 | // assigned to ptr if an asynchronous exception occurs. 363 | Marshal.FreeHGlobal(ptr); 364 | ptr = Marshal.AllocHGlobal(length); 365 | } 366 | ret = NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); 367 | } 368 | if (ret == NT_STATUS.STATUS_SUCCESS) 369 | { 370 | fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2); 371 | return fileName.Length != 0; 372 | } 373 | } 374 | finally 375 | { 376 | // CER guarantees that the allocated memory is freed, 377 | // if an asynchronous exception occurs. 378 | Marshal.FreeHGlobal(ptr); 379 | } 380 | 381 | fileName = string.Empty; 382 | return false; 383 | } 384 | private static void GetFileNameFromHandle(object state) 385 | { 386 | FileNameFromHandleState s = (FileNameFromHandleState)state; 387 | string fileName; 388 | s.RetValue = GetFileNameFromHandle(s.Handle, out fileName); 389 | s.FileName = fileName; 390 | s.Set(); 391 | } 392 | private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait) 393 | { 394 | using (FileNameFromHandleState f = new FileNameFromHandleState(handle)) 395 | { 396 | ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f); 397 | if (f.WaitOne(wait)) 398 | { 399 | fileName = f.FileName; 400 | return f.RetValue; 401 | } 402 | else 403 | { 404 | fileName = string.Empty; 405 | return false; 406 | } 407 | } 408 | } 409 | private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName) 410 | { 411 | IntPtr currentProcess = GetCurrentProcess(); 412 | bool remote = processId != GetProcessId(currentProcess); 413 | SafeProcessHandle processHandle = null; 414 | SafeObjectHandle objectHandle = null; 415 | try 416 | { 417 | if (remote) 418 | { 419 | processHandle = OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId); 420 | if (DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) 421 | handle = objectHandle.DangerousGetHandle(); 422 | } 423 | return GetFileNameFromHandle(handle, out fileName, 200); 424 | } 425 | finally 426 | { 427 | if (remote) 428 | { 429 | if (processHandle != null) 430 | processHandle.Close(); 431 | if (objectHandle != null) 432 | objectHandle.Close(); 433 | } 434 | } 435 | } 436 | 437 | private static string GetHandleTypeToken(IntPtr handle) 438 | { 439 | int length; 440 | NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); 441 | IntPtr ptr = IntPtr.Zero; 442 | RuntimeHelpers.PrepareConstrainedRegions(); 443 | try 444 | { 445 | RuntimeHelpers.PrepareConstrainedRegions(); 446 | try 447 | { } 448 | finally 449 | { 450 | if (length >= 0) 451 | ptr = Marshal.AllocHGlobal(length); 452 | } 453 | if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS) 454 | { 455 | return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60)); 456 | } 457 | } 458 | finally 459 | { 460 | Marshal.FreeHGlobal(ptr); 461 | } 462 | return string.Empty; 463 | } 464 | private static string GetHandleTypeToken(IntPtr handle, int processId) 465 | { 466 | IntPtr currentProcess = GetCurrentProcess(); 467 | bool remote = processId != GetProcessId(currentProcess); 468 | SafeProcessHandle processHandle = null; 469 | SafeObjectHandle objectHandle = null; 470 | try 471 | { 472 | if (remote) 473 | { 474 | processHandle = OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId); 475 | if (DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) 476 | { 477 | handle = objectHandle.DangerousGetHandle(); 478 | } 479 | } 480 | return GetHandleTypeToken(handle); 481 | } 482 | finally 483 | { 484 | if (remote) 485 | { 486 | if (processHandle != null) 487 | { 488 | processHandle.Close(); 489 | } 490 | if (objectHandle != null) 491 | { 492 | objectHandle.Close(); 493 | } 494 | } 495 | } 496 | } 497 | private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType) 498 | { 499 | for (int i = 1; i < handleTypeTokenCount; i++) 500 | { 501 | if (handleTypeTokens[i] == token) 502 | { 503 | handleType = (SystemHandleType)i; 504 | return true; 505 | } 506 | } 507 | handleType = SystemHandleType.OB_TYPE_UNKNOWN; 508 | return false; 509 | } 510 | private static bool GetHandleType(IntPtr handle, out SystemHandleType handleType) 511 | { 512 | string token = GetHandleTypeToken(handle); 513 | return GetHandleTypeFromToken(token, out handleType); 514 | } 515 | private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType) 516 | { 517 | string token = GetHandleTypeToken(handle, processId); 518 | return GetHandleTypeFromToken(token, out handleType); 519 | } 520 | 521 | private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath) 522 | { 523 | EnsureDeviceMap(); 524 | int i = devicePath.Length; 525 | while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1) 526 | { 527 | string drive; 528 | if (deviceMap.TryGetValue(devicePath.Substring(0, i), out drive)) 529 | { 530 | dosPath = string.Concat(drive, devicePath.Substring(i)); 531 | return dosPath.Length != 0; 532 | } 533 | } 534 | dosPath = string.Empty; 535 | return false; 536 | } 537 | 538 | private static void EnsureDeviceMap() 539 | { 540 | if (deviceMap == null) 541 | { 542 | Dictionary localDeviceMap = BuildDeviceMap(); 543 | Interlocked.CompareExchange(ref deviceMap, localDeviceMap, null); 544 | } 545 | } 546 | 547 | private static Dictionary BuildDeviceMap() 548 | { 549 | string[] logicalDrives = Environment.GetLogicalDrives(); 550 | Dictionary localDeviceMap = new Dictionary(logicalDrives.Length); 551 | StringBuilder lpTargetPath = new StringBuilder(MAX_PATH); 552 | foreach (string drive in logicalDrives) 553 | { 554 | string lpDeviceName = drive.Substring(0, 2); 555 | QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH); 556 | localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName); 557 | } 558 | localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\"); 559 | return localDeviceMap; 560 | } 561 | 562 | private static string NormalizeDeviceName(string deviceName) 563 | { 564 | if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0) 565 | { 566 | string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1); 567 | return string.Concat(networkDevicePrefix, shareName); 568 | } 569 | return deviceName; 570 | } 571 | 572 | /// 573 | /// Gets the open files enumerator. 574 | /// 575 | /// The process id. 576 | /// 577 | public static IEnumerable GetOpenFilesEnumerator(int processId) 578 | { 579 | return new OpenFiles(processId); 580 | } 581 | 582 | public static List GetProcessesUsingFile(string fName) 583 | { 584 | List result = new List(); 585 | foreach (Process p in Process.GetProcesses()) 586 | { 587 | try 588 | { 589 | if (GetOpenFilesEnumerator(p.Id).Contains(fName)) 590 | { 591 | result.Add(p); 592 | } 593 | } 594 | catch { }//some processes will fail 595 | } 596 | return result; 597 | } 598 | 599 | } 600 | } 601 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 ░▒▓█│【Walkman】│█▓▒░ 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FileLocks 2 | Research on determining a File's locks in C#.Net 3 | -------------------------------------------------------------------------------- /RestartManager.cs: -------------------------------------------------------------------------------- 1 | // RestartManager method 2 | //https://stackoverflow.com/a/3504251/2999220 3 | //https://stackoverflow.com/a/20623311/2999220 4 | //https://stackoverflow.com/a/20623302/2999220 5 | //https://gist.github.com/mlaily/9423f1855bb176d52a327f5874915a97 6 | //https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/april/net-matters-restart-manager-and-generic-method-compilation 7 | //https://devblogs.microsoft.com/oldnewthing/?p=8283 8 | 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Runtime.InteropServices; 13 | using System.Diagnostics; 14 | 15 | namespace RestartManager 16 | { 17 | public static class FileUtil 18 | { 19 | /// 20 | /// Uniquely identifies a process by its PID and the time the process began. 21 | /// An array of RM_UNIQUE_PROCESS structures can be passed to the RmRegisterResources function. 22 | /// 23 | [StructLayout(LayoutKind.Sequential)] 24 | struct RM_UNIQUE_PROCESS 25 | { 26 | public int dwProcessId; 27 | public System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; 28 | } 29 | 30 | enum RM_APP_TYPE 31 | { 32 | /// 33 | /// The application cannot be classified as any other type. 34 | /// An application of this type can only be shut down by a forced shutdown. 35 | /// 36 | RmUnknownApp = 0, 37 | /// 38 | /// A Windows application run as a stand-alone process that displays a top-level window. 39 | /// 40 | RmMainWindow = 1, 41 | /// 42 | /// A Windows application that does not run as a stand-alone process and does not display a top-level window. 43 | /// 44 | RmOtherWindow = 2, 45 | /// 46 | /// The application is a Windows service. 47 | /// 48 | RmService = 3, 49 | /// 50 | /// The application is Windows Explorer. 51 | /// 52 | RmExplorer = 4, 53 | /// 54 | /// The application is a stand-alone console application. 55 | /// 56 | RmConsole = 5, 57 | /// 58 | /// A system restart is required to complete the installation because a process cannot be shut down. 59 | /// The process cannot be shut down because of the following reasons. 60 | /// The process may be a critical process. 61 | /// The current user may not have permission to shut down the process. 62 | /// The process may belong to the primary installer that started the Restart Manager. 63 | /// 64 | RmCritical = 1000 65 | } 66 | 67 | [Flags] 68 | public enum RM_APP_STATUS 69 | { 70 | /// 71 | /// The application is in a state that is not described by any other enumerated state. 72 | /// 73 | RmStatusUnknown = 0x0, 74 | /// 75 | /// The application is currently running. 76 | /// 77 | RmStatusRunning = 0x1, 78 | /// 79 | /// The Restart Manager has stopped the application. 80 | /// 81 | RmStatusStopped = 0x2, 82 | /// 83 | /// An action outside the Restart Manager has stopped the application. 84 | /// 85 | RmStatusStoppedOther = 0x4, 86 | /// 87 | /// The Restart Manager has restarted the application. 88 | /// 89 | RmStatusRestarted = 0x8, 90 | /// 91 | /// The Restart Manager encountered an error when stopping the application. 92 | /// 93 | RmStatusErrorOnStop = 0x10, 94 | /// 95 | /// The Restart Manager encountered an error when restarting the application. 96 | /// 97 | RmStatusErrorOnRestart = 0x20, 98 | /// 99 | /// Shutdown is masked by a filter. 100 | /// 101 | RmStatusShutdownMasked = 0x40, 102 | /// 103 | /// Restart is masked by a filter. 104 | /// 105 | RmStatusRestartMasked = 0x80, 106 | } 107 | 108 | [Flags] 109 | public enum RM_REBOOT_REASON 110 | { 111 | /// 112 | /// A system restart is not required. 113 | /// 114 | RmRebootReasonNone = 0x0, 115 | /// 116 | /// The current user does not have sufficient privileges to shut down one or more processes. 117 | /// 118 | RmRebootReasonPermissionDenied = 0x1, 119 | /// 120 | /// One or more processes are running in another Terminal Services session. 121 | /// 122 | RmRebootReasonSessionMismatch = 0x2, 123 | /// 124 | /// A system restart is needed because one or more processes to be shut down are critical processes. 125 | /// 126 | RmRebootReasonCriticalProcess = 0x4, 127 | /// 128 | /// A system restart is needed because one or more services to be shut down are critical services. 129 | /// 130 | RmRebootReasonCriticalService = 0x8, 131 | /// 132 | /// A system restart is needed because the current process must be shut down. 133 | /// 134 | RmRebootReasonDetectedSelf = 0x10, 135 | } 136 | 137 | public enum RmResult 138 | { 139 | /// 140 | /// The resources specified have been registered. 141 | /// 142 | ERROR_SUCCESS = 0, 143 | /// 144 | /// A Restart Manager function could not obtain a Registry write mutex in the allotted time. 145 | /// A system restart is recommended because further use of the Restart Manager is likely to fail. 146 | /// 147 | ERROR_SEM_TIMEOUT = 121, 148 | /// 149 | /// One or more arguments are not correct. 150 | /// This error value is returned by Restart Manager function if a NULL pointer or 0 is passed in a parameter that requires a non-null and non-zero value. 151 | /// 152 | ERROR_BAD_ARGUMENTS = 160, 153 | /// 154 | /// An operation was unable to read or write to the registry. 155 | /// 156 | ERROR_WRITE_FAULT = 29, 157 | /// 158 | /// A Restart Manager operation could not complete because not enough memory was available. 159 | /// 160 | ERROR_OUTOFMEMORY = 14, 161 | /// 162 | /// An invalid handle was passed to the function. 163 | /// No Restart Manager session exists for the handle supplied. 164 | /// 165 | ERROR_INVALID_HANDLE = 6, 166 | /// 167 | /// The maximum number of sessions has been reached. 168 | /// 169 | ERROR_MAX_SESSIONS_REACHED = 353, 170 | /// 171 | /// This error value is returned by the RmGetList function if the rgAffectedApps buffer is too small to hold all application information in the list. 172 | /// 173 | ERROR_MORE_DATA = 234, 174 | /// 175 | /// The current operation is canceled by user. 176 | /// 177 | ERROR_CANCELLED = 1223 178 | } 179 | 180 | /// 181 | /// Describes an application that is to be registered with the Restart Manager. 182 | /// 183 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 184 | struct RM_PROCESS_INFO 185 | { 186 | public const int CCH_RM_MAX_APP_NAME = 255; 187 | public const int CCH_RM_MAX_SVC_NAME = 63; 188 | public const int RM_INVALID_SESSION = -1; 189 | 190 | /// 191 | /// Contains an RM_UNIQUE_PROCESS structure that uniquely identifies the application by its PID and the time the process began. 192 | /// 193 | public RM_UNIQUE_PROCESS Process; 194 | 195 | /// 196 | /// If the process is a service, this parameter returns the long name for the service. 197 | /// If the process is not a service, this parameter returns the user-friendly name for the application. 198 | /// If the process is a critical process, and the installer is run with elevated privileges, 199 | /// this parameter returns the name of the executable file of the critical process. 200 | /// If the process is a critical process, and the installer is run as a service, this parameter returns the long name of the critical process. 201 | /// 202 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] 203 | public string strAppName; 204 | 205 | /// 206 | /// If the process is a service, this is the short name for the service. 207 | /// This member is not used if the process is not a service. 208 | /// 209 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] 210 | public string strServiceShortName; 211 | 212 | /// 213 | /// Contains an RM_APP_TYPE enumeration value that specifies the type of application as RmUnknownApp, RmMainWindow, RmOtherWindow, RmService, RmExplorer or RmCritical. 214 | /// 215 | public RM_APP_TYPE ApplicationType; 216 | 217 | /// 218 | /// Contains a bit mask that describes the current status of the application. 219 | /// See the RM_APP_STATUS enumeration. 220 | /// 221 | public RM_APP_STATUS AppStatus; 222 | 223 | /// 224 | /// Contains the Terminal Services session ID of the process. 225 | /// If the terminal session of the process cannot be determined, the value of this member is set to RM_INVALID_SESSION (-1). 226 | /// This member is not used if the process is a service or a system critical process. 227 | /// 228 | public int TSSessionId; 229 | 230 | /// 231 | /// TRUE if the application can be restarted by the Restart Manager; otherwise, FALSE. 232 | /// This member is always TRUE if the process is a service. 233 | /// This member is always FALSE if the process is a critical system process. 234 | /// 235 | [MarshalAs(UnmanagedType.Bool)] 236 | public bool bRestartable; 237 | } 238 | 239 | /// 240 | /// Registers resources to a Restart Manager session. 241 | /// The Restart Manager uses the list of resources registered with the session to determine which applications 242 | /// and services must be shut down and restarted. Resources can be identified by filenames, service short names, 243 | /// or RM_UNIQUE_PROCESS structures that describe running applications. 244 | /// The RmRegisterResources function can be used by a primary or secondary installer. 245 | /// 246 | /// A handle to an existing Restart Manager session. 247 | /// The number of files being registered. 248 | /// An array of null-terminated strings of full filename paths. This parameter can be NULL if nFiles is 0. 249 | /// The number of processes being registered. 250 | /// An array of RM_UNIQUE_PROCESS structures. This parameter can be NULL if nApplications is 0. 251 | /// The number of services to be registered. 252 | /// An array of null-terminated strings of service short names. This parameter can be NULL if nServices is 0. 253 | /// This is the most recent error received. The function can return one of the system error codes that are defined in Winerror.h. 254 | [DllImport("rstrtmgr.dll", CharSet = CharSet.Unicode)] 255 | static extern RmResult RmRegisterResources(int dwSessionHandle, 256 | uint nFiles, 257 | string[] rgsFilenames, 258 | uint nApplications, 259 | [In] RM_UNIQUE_PROCESS[] rgApplications, 260 | uint nServices, 261 | string[] rgsServiceNames); 262 | 263 | /// 264 | /// Starts a new Restart Manager session. 265 | /// A maximum of 64 Restart Manager sessions per user session can be open on the system at the same time. 266 | /// When this function starts a session, it returns a session handle and session key that can be used in subsequent calls to the Restart Manager API. 267 | /// 268 | /// 269 | /// A pointer to the handle of a Restart Manager session. 270 | /// The session handle can be passed in subsequent calls to the Restart Manager API. 271 | /// 272 | /// Reserved. This parameter should be 0. 273 | /// 274 | /// A null-terminated string that contains the session key to the new session. 275 | /// The string must be allocated before calling the RmStartSession function. 276 | /// 277 | /// This is the most recent error received. The function can return one of the system error codes that are defined in Winerror.h. 278 | [DllImport("rstrtmgr.dll", CharSet = CharSet.Auto)] 279 | static extern RmResult RmStartSession(out int pSessionHandle, 280 | int dwSessionFlags, 281 | string strSessionKey); 282 | 283 | /// 284 | /// Ends the Restart Manager session. 285 | /// This function should be called by the primary installer that has previously started the session by calling the RmStartSession function. 286 | /// The RmEndSession function can be called by a secondary installer that is joined to the session once no more resources need to be registered 287 | /// by the secondary installer. 288 | /// 289 | /// A handle to an existing Restart Manager session. 290 | /// This is the most recent error received. The function can return one of the system error codes that are defined in Winerror.h. 291 | [DllImport("rstrtmgr.dll")] 292 | static extern RmResult RmEndSession(int dwSessionHandle); 293 | 294 | /// 295 | /// Gets a list of all applications and services that are currently using resources that have been registered with the Restart Manager session. 296 | /// 297 | /// A handle to an existing Restart Manager session. 298 | /// 299 | /// A pointer to an array size necessary to receive RM_PROCESS_INFO structures required to return information for all affected applications and services. 300 | /// 301 | /// A pointer to the total number of RM_PROCESS_INFO structures in an array and number of structures filled. 302 | /// 303 | /// An array of RM_PROCESS_INFO structures that list the applications and services using resources that have been registered with the session. 304 | /// 305 | /// 306 | /// Pointer to location that receives a value of the RM_REBOOT_REASON enumeration that describes the reason a system restart is needed. 307 | /// 308 | /// This is the most recent error received. The function can return one of the system error codes that are defined in Winerror.h. 309 | [DllImport("rstrtmgr.dll")] 310 | static extern RmResult RmGetList(int dwSessionHandle, 311 | out uint pnProcInfoNeeded, 312 | ref uint pnProcInfo, 313 | [In, Out] RM_PROCESS_INFO[] rgAffectedApps, 314 | out RM_REBOOT_REASON lpdwRebootReasons); 315 | 316 | public static List GetProcessesLockingFile(string path) 317 | { 318 | int handle; 319 | if (RmStartSession(out handle, 0, Guid.NewGuid().ToString()) != RmResult.ERROR_SUCCESS) 320 | throw new Exception("Could not begin session. Unable to determine file lockers."); 321 | 322 | try 323 | { 324 | string[] resources = { path }; // Just checking on one resource. 325 | if (RmRegisterResources(handle, (uint)resources.LongLength, resources, 0, null, 0, null) != RmResult.ERROR_SUCCESS) 326 | throw new Exception("Could not register resource."); 327 | 328 | // The first try is done expecting at most ten processes to lock the file. 329 | uint arraySize = 10; 330 | RmResult result; 331 | do 332 | { 333 | RM_PROCESS_INFO[] array = new RM_PROCESS_INFO[arraySize]; 334 | uint requiredArraySize; 335 | RM_REBOOT_REASON lpdwRebootReasons; 336 | result = RmGetList(handle, out requiredArraySize, ref arraySize, array, out lpdwRebootReasons); 337 | 338 | if (result == RmResult.ERROR_SUCCESS) 339 | { 340 | // Adjust the array length to fit the actual count. 341 | Array.Resize(ref array, (int)requiredArraySize); 342 | 343 | List processes = new List((int)arraySize); 344 | // Enumerate all of the results and add them to the 345 | // list to be returned 346 | for (int i = 0; i < arraySize; i++) 347 | { 348 | try 349 | { 350 | processes.Add(Process.GetProcessById(array[i].Process.dwProcessId)); 351 | } 352 | // catch the error -- in case the process is no longer running 353 | catch (ArgumentException) { } 354 | } 355 | return processes; 356 | } 357 | else if (result == RmResult.ERROR_MORE_DATA) 358 | { 359 | // We need to initialize a bigger array. We only set the size, and do another iteration. 360 | // (the out parameter requiredArraySize contains the expected value for the next try) 361 | arraySize = requiredArraySize; 362 | } 363 | else 364 | throw new Exception("Could not list processes locking resource. Failed to get size of result."); 365 | } while (result != RmResult.ERROR_SUCCESS); 366 | } 367 | finally 368 | { 369 | RmEndSession(handle); 370 | } 371 | 372 | return new List(); 373 | } 374 | } 375 | } 376 | -------------------------------------------------------------------------------- /WalkmanLib.GetFileLocks.GetAllHandles.cs: -------------------------------------------------------------------------------- 1 | // Get all system open handles method - uses NTQuerySystemInformation and NTQueryObject 2 | //https://gist.github.com/i-e-b/2290426 3 | //https://stackoverflow.com/a/13735033/2999220 4 | 5 | 6 | using System; 7 | using System.Collections.Generic; 8 | using System.Runtime.ConstrainedExecution; 9 | using System.Runtime.InteropServices; 10 | 11 | namespace WalkmanLib.GetFileLocks 12 | { 13 | public static class GetAllHandles 14 | { 15 | public enum HandleType 16 | { 17 | Unknown, 18 | Other, 19 | File, Directory, SymbolicLink, Key, 20 | Process, Thread, Job, Session, WindowStation, 21 | Timer, Desktop, Semaphore, Token, 22 | Mutant, Section, Event, KeyedEvent, IoCompletion, IoCompletionReserve, 23 | TpWorkerFactory, AlpcPort, WmiGuid, UserApcReserve, 24 | } 25 | 26 | public enum NT_STATUS 27 | { 28 | STATUS_SUCCESS = 0x00000000, 29 | STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L), 30 | STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L) 31 | } 32 | 33 | public enum SYSTEM_INFORMATION_CLASS 34 | { 35 | SystemBasicInformation = 0, 36 | SystemPerformanceInformation = 2, 37 | SystemTimeOfDayInformation = 3, 38 | SystemProcessInformation = 5, 39 | SystemProcessorPerformanceInformation = 8, 40 | SystemHandleInformation = 16, 41 | SystemInterruptInformation = 23, 42 | SystemExceptionInformation = 33, 43 | SystemRegistryQuotaInformation = 37, 44 | SystemLookasideInformation = 45 45 | } 46 | 47 | public enum OBJECT_INFORMATION_CLASS 48 | { 49 | ObjectBasicInformation = 0, 50 | ObjectNameInformation = 1, 51 | ObjectTypeInformation = 2, 52 | ObjectAllTypesInformation = 3, 53 | ObjectHandleInformation = 4 54 | } 55 | 56 | [StructLayout(LayoutKind.Sequential)] 57 | public struct SystemHandleEntry 58 | { 59 | public int OwnerProcessId; 60 | public byte ObjectTypeNumber; 61 | public byte Flags; 62 | public ushort Handle; 63 | public IntPtr Object; 64 | public int GrantedAccess; 65 | } 66 | 67 | [DllImport("ntdll.dll")] 68 | internal static extern NT_STATUS NtQuerySystemInformation( 69 | [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, 70 | [In] IntPtr SystemInformation, 71 | [In] int SystemInformationLength, 72 | [Out] out int ReturnLength); 73 | 74 | [DllImport("ntdll.dll")] 75 | internal static extern NT_STATUS NtQueryObject( 76 | [In] IntPtr Handle, 77 | [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, 78 | [In] IntPtr ObjectInformation, 79 | [In] int ObjectInformationLength, 80 | [Out] out int ReturnLength); 81 | 82 | [DllImport("kernel32.dll")] 83 | internal static extern IntPtr GetCurrentProcess(); 84 | 85 | [DllImport("kernel32.dll", SetLastError = true)] 86 | public static extern IntPtr OpenProcess( 87 | [In] int dwDesiredAccess, 88 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 89 | [In] int dwProcessId); 90 | 91 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 92 | [DllImport("kernel32.dll", SetLastError = true)] 93 | [return: MarshalAs(UnmanagedType.Bool)] 94 | internal static extern bool CloseHandle( 95 | [In] IntPtr hObject); 96 | 97 | [DllImport("kernel32.dll", SetLastError = true)] 98 | [return: MarshalAs(UnmanagedType.Bool)] 99 | public static extern bool DuplicateHandle( 100 | [In] IntPtr hSourceProcessHandle, 101 | [In] IntPtr hSourceHandle, 102 | [In] IntPtr hTargetProcessHandle, 103 | [Out] out IntPtr lpTargetHandle, 104 | [In] int dwDesiredAccess, 105 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 106 | [In] int dwOptions); 107 | 108 | public class HandleInfo 109 | { 110 | public int ProcessId { get; private set; } 111 | public ushort Handle { get; private set; } 112 | public int GrantedAccess { get; private set; } 113 | public byte RawType { get; private set; } 114 | public byte Flags { get; private set; } 115 | 116 | public HandleInfo(int processId, ushort handle, int grantedAccess, byte rawType, byte flags) 117 | { 118 | ProcessId = processId; 119 | Handle = handle; 120 | GrantedAccess = grantedAccess; 121 | RawType = rawType; 122 | Flags = flags; 123 | } 124 | 125 | private static Dictionary _rawTypeMap = new Dictionary(); 126 | 127 | private string _name, _typeStr; 128 | private HandleType _type; 129 | 130 | public string Name { get { if (_name == null) initTypeAndName(); return _name; } } 131 | public string TypeString { get { if (_typeStr == null) initType(); return _typeStr; } } 132 | public HandleType Type { get { if (_typeStr == null) initType(); return _type; } } 133 | 134 | private void initType() 135 | { 136 | if (_rawTypeMap.ContainsKey(RawType)) 137 | { 138 | _typeStr = _rawTypeMap[RawType]; 139 | _type = HandleTypeFromString(_typeStr); 140 | } 141 | else 142 | initTypeAndName(); 143 | } 144 | 145 | private bool _typeAndNameAttempted = false; 146 | 147 | private void initTypeAndName() 148 | { 149 | if (_typeAndNameAttempted) 150 | return; 151 | _typeAndNameAttempted = true; 152 | 153 | IntPtr sourceProcessHandle = IntPtr.Zero; 154 | IntPtr handleDuplicate = IntPtr.Zero; 155 | try 156 | { 157 | sourceProcessHandle = OpenProcess(0x40 /* dup_handle */, true, ProcessId); 158 | 159 | // To read info about a handle owned by another process we must duplicate it into ours 160 | // For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality 161 | if (!DuplicateHandle(sourceProcessHandle, (IntPtr)Handle, GetCurrentProcess(), out handleDuplicate, 0, false, 2 /* same_access */)) 162 | return; 163 | 164 | // Query the object type 165 | if (_rawTypeMap.ContainsKey(RawType)) 166 | _typeStr = _rawTypeMap[RawType]; 167 | else 168 | { 169 | int length; 170 | NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); 171 | IntPtr ptr = IntPtr.Zero; 172 | try 173 | { 174 | ptr = Marshal.AllocHGlobal(length); 175 | if (NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS) 176 | return; 177 | _typeStr = Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x58 + (2 * IntPtr.Size))); 178 | _rawTypeMap[RawType] = _typeStr; 179 | } 180 | finally 181 | { 182 | Marshal.FreeHGlobal(ptr); 183 | } 184 | } 185 | _type = HandleTypeFromString(_typeStr); 186 | 187 | // Query the object name 188 | if (_typeStr != null && 189 | !(GrantedAccess == 0x0012019f && Flags == 0) && 190 | !(GrantedAccess == 0x0012019f && Flags == 2) && 191 | !(GrantedAccess == 0x00120189 && Flags == 2) && 192 | !(GrantedAccess == 0x00120189 && Flags == 0) && 193 | !(GrantedAccess == 0x001a019f && Flags == 2) && 194 | !(GrantedAccess == 0x00120089 && Flags == 2) 195 | )// don't query some objects that could get stuck 196 | { 197 | int length; 198 | NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length); 199 | IntPtr ptr = IntPtr.Zero; 200 | try 201 | { 202 | ptr = Marshal.AllocHGlobal(length); 203 | if (NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NT_STATUS.STATUS_SUCCESS) 204 | return; 205 | _name = Marshal.PtrToStringUni((IntPtr)((int)ptr + (2 * IntPtr.Size))); 206 | } 207 | finally 208 | { 209 | Marshal.FreeHGlobal(ptr); 210 | } 211 | } 212 | } 213 | finally 214 | { 215 | CloseHandle(sourceProcessHandle); 216 | if (handleDuplicate != IntPtr.Zero) 217 | CloseHandle(handleDuplicate); 218 | } 219 | } 220 | 221 | public static HandleType HandleTypeFromString(string typeStr) 222 | { 223 | switch (typeStr) 224 | { 225 | case null: 226 | return HandleType.Unknown; 227 | case "File": 228 | return HandleType.File; 229 | case "IoCompletion": 230 | return HandleType.IoCompletion; 231 | case "TpWorkerFactory": 232 | return HandleType.TpWorkerFactory; 233 | case "ALPC Port": 234 | return HandleType.AlpcPort; 235 | case "Event": 236 | return HandleType.Event; 237 | case "Section": 238 | return HandleType.Section; 239 | case "Directory": 240 | return HandleType.Directory; 241 | case "KeyedEvent": 242 | return HandleType.KeyedEvent; 243 | case "Process": 244 | return HandleType.Process; 245 | case "Key": 246 | return HandleType.Key; 247 | case "SymbolicLink": 248 | return HandleType.SymbolicLink; 249 | case "Thread": 250 | return HandleType.Thread; 251 | case "Mutant": 252 | return HandleType.Mutant; 253 | case "WindowStation": 254 | return HandleType.WindowStation; 255 | case "Timer": 256 | return HandleType.Timer; 257 | case "Semaphore": 258 | return HandleType.Semaphore; 259 | case "Desktop": 260 | return HandleType.Desktop; 261 | case "Token": 262 | return HandleType.Token; 263 | case "Job": 264 | return HandleType.Job; 265 | case "Session": 266 | return HandleType.Session; 267 | case "IoCompletionReserve": 268 | return HandleType.IoCompletionReserve; 269 | case "WmiGuid": 270 | return HandleType.WmiGuid; 271 | case "UserApcReserve": 272 | return HandleType.UserApcReserve; 273 | default: 274 | return HandleType.Other; 275 | } 276 | } 277 | } 278 | 279 | public static SystemHandleEntry[] GetSystemHandles() 280 | { 281 | // Attempt to retrieve the handle information 282 | int length = 0x10000; 283 | IntPtr ptr = IntPtr.Zero; 284 | try 285 | { 286 | while (true) 287 | { 288 | ptr = Marshal.AllocHGlobal(length); 289 | int wantedLength; 290 | NT_STATUS result = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out wantedLength); 291 | if (result == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) 292 | { 293 | length = Math.Max(length, wantedLength); 294 | Marshal.FreeHGlobal(ptr); 295 | ptr = IntPtr.Zero; 296 | } 297 | else if (result == NT_STATUS.STATUS_SUCCESS) 298 | break; 299 | else 300 | throw new Exception("Failed to retrieve system handle information.", new System.ComponentModel.Win32Exception()); 301 | } 302 | 303 | int handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : (int)Marshal.ReadInt64(ptr); 304 | int offset = IntPtr.Size; 305 | int size = Marshal.SizeOf(typeof(SystemHandleEntry)); 306 | 307 | SystemHandleEntry[] systemHandleEntries = new SystemHandleEntry[handleCount]; 308 | for (int i = 0; i < handleCount; i++) 309 | { 310 | SystemHandleEntry struc = (SystemHandleEntry)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SystemHandleEntry)); 311 | systemHandleEntries[i] = struc; 312 | 313 | offset += size; 314 | } 315 | 316 | return systemHandleEntries; 317 | } 318 | finally 319 | { 320 | if (ptr != IntPtr.Zero) 321 | Marshal.FreeHGlobal(ptr); 322 | } 323 | } 324 | 325 | public static IEnumerable GetFileHandles() 326 | { 327 | SystemHandleEntry[] systemHandleEntries = GetSystemHandles(); 328 | 329 | foreach (SystemHandleEntry struc in systemHandleEntries) 330 | { 331 | HandleInfo hi = new HandleInfo(struc.OwnerProcessId, struc.Handle, struc.GrantedAccess, struc.ObjectTypeNumber, struc.Flags); 332 | if (hi.Type == HandleType.File && hi.Name != null) 333 | yield return hi; 334 | } 335 | } 336 | } 337 | } -------------------------------------------------------------------------------- /WalkmanLib.GetFileLocks.GetProcessHandles.cs: -------------------------------------------------------------------------------- 1 | // Get a process's open handles method - uses NTQuerySystemInformation and NTQueryObject 2 | //https://stackoverflow.com/a/6351168/2999220 3 | 4 | 5 | using System; 6 | using System.Collections.Generic; 7 | using System.IO; 8 | using System.Runtime.CompilerServices; 9 | using System.Runtime.ConstrainedExecution; 10 | using System.Runtime.InteropServices; 11 | using System.Security.Permissions; 12 | using System.Text; 13 | using System.Threading; 14 | using Microsoft.Win32.SafeHandles; 15 | using System.Diagnostics; 16 | using System.Linq; 17 | 18 | namespace WalkmanLib.GetFileLocks 19 | { 20 | public static class GetProcessHandles 21 | { 22 | private static Dictionary deviceMap; 23 | private const string networkDevicePrefix = "\\Device\\LanmanRedirector\\"; 24 | private const int MAX_PATH = 260; 25 | private const int handleTypeTokenCount = 27; 26 | private static readonly string[] handleTypeTokens = new string[] { 27 | "", "", "Directory", "SymbolicLink", "Token", 28 | "Process", "Thread", "Unknown7", "Event", "EventPair", "Mutant", 29 | "Unknown11", "Semaphore", "Timer", "Profile", "WindowStation", 30 | "Desktop", "Section", "Key", "Port", "WaitablePort", 31 | "Unknown21", "Unknown22", "Unknown23", "Unknown24", 32 | "IoCompletion", "File" 33 | }; 34 | 35 | internal enum NT_STATUS 36 | { 37 | STATUS_SUCCESS = 0x00000000, 38 | STATUS_BUFFER_OVERFLOW = unchecked((int)0x80000005L), 39 | STATUS_INFO_LENGTH_MISMATCH = unchecked((int)0xC0000004L) 40 | } 41 | 42 | internal enum SYSTEM_INFORMATION_CLASS 43 | { 44 | SystemBasicInformation = 0, 45 | SystemPerformanceInformation = 2, 46 | SystemTimeOfDayInformation = 3, 47 | SystemProcessInformation = 5, 48 | SystemProcessorPerformanceInformation = 8, 49 | SystemHandleInformation = 16, 50 | SystemInterruptInformation = 23, 51 | SystemExceptionInformation = 33, 52 | SystemRegistryQuotaInformation = 37, 53 | SystemLookasideInformation = 45 54 | } 55 | 56 | internal enum OBJECT_INFORMATION_CLASS 57 | { 58 | ObjectBasicInformation = 0, 59 | ObjectNameInformation = 1, 60 | ObjectTypeInformation = 2, 61 | ObjectAllTypesInformation = 3, 62 | ObjectHandleInformation = 4 63 | } 64 | 65 | [Flags] 66 | internal enum ProcessAccessRights 67 | { 68 | PROCESS_DUP_HANDLE = 0x00000040 69 | } 70 | 71 | [Flags] 72 | internal enum DuplicateHandleOptions 73 | { 74 | DUPLICATE_CLOSE_SOURCE = 0x1, 75 | DUPLICATE_SAME_ACCESS = 0x2 76 | } 77 | 78 | private enum SystemHandleType 79 | { 80 | OB_TYPE_UNKNOWN = 0, 81 | OB_TYPE_TYPE = 1, 82 | OB_TYPE_DIRECTORY, 83 | OB_TYPE_SYMBOLIC_LINK, 84 | OB_TYPE_TOKEN, 85 | OB_TYPE_PROCESS, 86 | OB_TYPE_THREAD, 87 | OB_TYPE_UNKNOWN_7, 88 | OB_TYPE_EVENT, 89 | OB_TYPE_EVENT_PAIR, 90 | OB_TYPE_MUTANT, 91 | OB_TYPE_UNKNOWN_11, 92 | OB_TYPE_SEMAPHORE, 93 | OB_TYPE_TIMER, 94 | OB_TYPE_PROFILE, 95 | OB_TYPE_WINDOW_STATION, 96 | OB_TYPE_DESKTOP, 97 | OB_TYPE_SECTION, 98 | OB_TYPE_KEY, 99 | OB_TYPE_PORT, 100 | OB_TYPE_WAITABLE_PORT, 101 | OB_TYPE_UNKNOWN_21, 102 | OB_TYPE_UNKNOWN_22, 103 | OB_TYPE_UNKNOWN_23, 104 | OB_TYPE_UNKNOWN_24, 105 | //OB_TYPE_CONTROLLER, 106 | //OB_TYPE_DEVICE, 107 | //OB_TYPE_DRIVER, 108 | OB_TYPE_IO_COMPLETION, 109 | OB_TYPE_FILE 110 | }; 111 | 112 | [StructLayout(LayoutKind.Sequential)] 113 | private struct SYSTEM_HANDLE_ENTRY 114 | { 115 | public int OwnerPid; 116 | public byte ObjectType; 117 | public byte HandleFlags; 118 | public short HandleValue; 119 | public int ObjectPointer; 120 | public int AccessMask; 121 | } 122 | 123 | [DllImport("ntdll.dll")] 124 | internal static extern NT_STATUS NtQuerySystemInformation( 125 | [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, 126 | [In] IntPtr SystemInformation, 127 | [In] int SystemInformationLength, 128 | [Out] out int ReturnLength); 129 | 130 | [DllImport("ntdll.dll")] 131 | internal static extern NT_STATUS NtQueryObject( 132 | [In] IntPtr Handle, 133 | [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, 134 | [In] IntPtr ObjectInformation, 135 | [In] int ObjectInformationLength, 136 | [Out] out int ReturnLength); 137 | 138 | [DllImport("kernel32.dll", SetLastError = true)] 139 | internal static extern SafeProcessHandle OpenProcess( 140 | [In] ProcessAccessRights dwDesiredAccess, 141 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 142 | [In] int dwProcessId); 143 | 144 | [DllImport("kernel32.dll", SetLastError = true)] 145 | [return: MarshalAs(UnmanagedType.Bool)] 146 | internal static extern bool DuplicateHandle( 147 | [In] IntPtr hSourceProcessHandle, 148 | [In] IntPtr hSourceHandle, 149 | [In] IntPtr hTargetProcessHandle, 150 | [Out] out SafeObjectHandle lpTargetHandle, 151 | [In] int dwDesiredAccess, 152 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 153 | [In] DuplicateHandleOptions dwOptions); 154 | 155 | [DllImport("kernel32.dll")] 156 | internal static extern IntPtr GetCurrentProcess(); 157 | 158 | [DllImport("kernel32.dll", SetLastError = true)] 159 | internal static extern int GetProcessId( 160 | [In] IntPtr Process); 161 | 162 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 163 | [DllImport("kernel32.dll", SetLastError = true)] 164 | [return: MarshalAs(UnmanagedType.Bool)] 165 | internal static extern bool CloseHandle( 166 | [In] IntPtr hObject); 167 | 168 | [DllImport("kernel32.dll", SetLastError = true)] 169 | internal static extern int QueryDosDevice( 170 | [In] string lpDeviceName, 171 | [Out] StringBuilder lpTargetPath, 172 | [In] int ucchMax); 173 | 174 | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] 175 | internal sealed class SafeObjectHandle : SafeHandleZeroOrMinusOneIsInvalid 176 | { 177 | private SafeObjectHandle() 178 | : base(true) 179 | { } 180 | 181 | internal SafeObjectHandle(IntPtr preexistingHandle, bool ownsHandle) 182 | : base(ownsHandle) 183 | { 184 | base.SetHandle(preexistingHandle); 185 | } 186 | 187 | protected override bool ReleaseHandle() 188 | { 189 | return CloseHandle(base.handle); 190 | } 191 | } 192 | 193 | [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] 194 | internal sealed class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid 195 | { 196 | private SafeProcessHandle() 197 | : base(true) 198 | { } 199 | 200 | internal SafeProcessHandle(IntPtr preexistingHandle, bool ownsHandle) 201 | : base(ownsHandle) 202 | { 203 | base.SetHandle(preexistingHandle); 204 | } 205 | 206 | protected override bool ReleaseHandle() 207 | { 208 | return CloseHandle(base.handle); 209 | } 210 | } 211 | 212 | private sealed class OpenFiles : IEnumerable 213 | { 214 | private readonly int processId; 215 | 216 | internal OpenFiles(int processId) 217 | { 218 | this.processId = processId; 219 | } 220 | 221 | public IEnumerator GetEnumerator() 222 | { 223 | NT_STATUS ret; 224 | int length = 0x10000; 225 | // Loop, probing for required memory. 226 | 227 | do 228 | { 229 | IntPtr ptr = IntPtr.Zero; 230 | RuntimeHelpers.PrepareConstrainedRegions(); 231 | try 232 | { 233 | RuntimeHelpers.PrepareConstrainedRegions(); 234 | try 235 | { } 236 | finally 237 | { 238 | // CER guarantees that the address of the allocated 239 | // memory is actually assigned to ptr if an 240 | // asynchronous exception occurs. 241 | ptr = Marshal.AllocHGlobal(length); 242 | } 243 | int returnLength; 244 | ret = NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS.SystemHandleInformation, ptr, length, out returnLength); 245 | if (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH) 246 | { 247 | // Round required memory up to the nearest 64KB boundary. 248 | length = (returnLength + 0xffff) & ~0xffff; 249 | } 250 | else if (ret == NT_STATUS.STATUS_SUCCESS) 251 | { 252 | int handleCount = Marshal.ReadInt32(ptr); 253 | int offset = sizeof(int); 254 | int size = Marshal.SizeOf(typeof(SYSTEM_HANDLE_ENTRY)); 255 | for (int i = 0; i < handleCount; i++) 256 | { 257 | SYSTEM_HANDLE_ENTRY handleEntry = (SYSTEM_HANDLE_ENTRY)Marshal.PtrToStructure((IntPtr)((int)ptr + offset), typeof(SYSTEM_HANDLE_ENTRY)); 258 | if (handleEntry.OwnerPid == processId) 259 | { 260 | IntPtr handle = (IntPtr)handleEntry.HandleValue; 261 | SystemHandleType handleType; 262 | 263 | if (GetHandleType(handle, handleEntry.OwnerPid, out handleType) && handleType == SystemHandleType.OB_TYPE_FILE) 264 | { 265 | string devicePath; 266 | if (GetFileNameFromHandle(handle, handleEntry.OwnerPid, out devicePath)) 267 | { 268 | string dosPath; 269 | if (ConvertDevicePathToDosPath(devicePath, out dosPath)) 270 | { 271 | if (File.Exists(dosPath)) 272 | yield return dosPath; // return new FileInfo(dosPath); 273 | else if (Directory.Exists(dosPath)) 274 | yield return dosPath; // new DirectoryInfo(dosPath); 275 | } 276 | } 277 | } 278 | } 279 | offset += size; 280 | } 281 | } 282 | } 283 | finally 284 | { 285 | // CER guarantees that the allocated memory is freed, 286 | // if an asynchronous exception occurs. 287 | Marshal.FreeHGlobal(ptr); 288 | //sw.Flush(); 289 | //sw.Close(); 290 | } 291 | } 292 | while (ret == NT_STATUS.STATUS_INFO_LENGTH_MISMATCH); 293 | } 294 | 295 | System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() 296 | { 297 | return GetEnumerator(); 298 | } 299 | } 300 | 301 | private class FileNameFromHandleState : IDisposable 302 | { 303 | private ManualResetEvent _mr; 304 | public IntPtr Handle { get; } 305 | public string FileName { get; set; } 306 | public bool RetValue { get; set; } 307 | 308 | public FileNameFromHandleState(IntPtr handle) 309 | { 310 | _mr = new ManualResetEvent(false); 311 | this.Handle = handle; 312 | } 313 | 314 | public bool WaitOne(int wait) 315 | { 316 | return _mr.WaitOne(wait, false); 317 | } 318 | 319 | public void Set() 320 | { 321 | try 322 | { 323 | _mr.Set(); 324 | } 325 | catch { } 326 | } 327 | 328 | public void Dispose() 329 | { 330 | if (_mr != null) 331 | _mr.Close(); 332 | } 333 | } 334 | 335 | private static bool GetFileNameFromHandle(IntPtr handle, out string fileName) 336 | { 337 | IntPtr ptr = IntPtr.Zero; 338 | RuntimeHelpers.PrepareConstrainedRegions(); 339 | try 340 | { 341 | int length = 0x200; // 512 bytes 342 | RuntimeHelpers.PrepareConstrainedRegions(); 343 | try 344 | { } 345 | finally 346 | { 347 | // CER guarantees the assignment of the allocated 348 | // memory address to ptr, if an ansynchronous exception 349 | // occurs. 350 | ptr = Marshal.AllocHGlobal(length); 351 | } 352 | NT_STATUS ret = NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); 353 | if (ret == NT_STATUS.STATUS_BUFFER_OVERFLOW) 354 | { 355 | RuntimeHelpers.PrepareConstrainedRegions(); 356 | try 357 | { } 358 | finally 359 | { 360 | // CER guarantees that the previous allocation is freed, 361 | // and that the newly allocated memory address is 362 | // assigned to ptr if an asynchronous exception occurs. 363 | Marshal.FreeHGlobal(ptr); 364 | ptr = Marshal.AllocHGlobal(length); 365 | } 366 | ret = NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length); 367 | } 368 | if (ret == NT_STATUS.STATUS_SUCCESS) 369 | { 370 | fileName = Marshal.PtrToStringUni((IntPtr)((int)ptr + 8), (length - 9) / 2); 371 | return fileName.Length != 0; 372 | } 373 | } 374 | finally 375 | { 376 | // CER guarantees that the allocated memory is freed, 377 | // if an asynchronous exception occurs. 378 | Marshal.FreeHGlobal(ptr); 379 | } 380 | 381 | fileName = string.Empty; 382 | return false; 383 | } 384 | private static void GetFileNameFromHandle(object state) 385 | { 386 | FileNameFromHandleState s = (FileNameFromHandleState)state; 387 | string fileName; 388 | s.RetValue = GetFileNameFromHandle(s.Handle, out fileName); 389 | s.FileName = fileName; 390 | s.Set(); 391 | } 392 | private static bool GetFileNameFromHandle(IntPtr handle, out string fileName, int wait) 393 | { 394 | using (FileNameFromHandleState f = new FileNameFromHandleState(handle)) 395 | { 396 | ThreadPool.QueueUserWorkItem(new WaitCallback(GetFileNameFromHandle), f); 397 | if (f.WaitOne(wait)) 398 | { 399 | fileName = f.FileName; 400 | return f.RetValue; 401 | } 402 | else 403 | { 404 | fileName = string.Empty; 405 | return false; 406 | } 407 | } 408 | } 409 | private static bool GetFileNameFromHandle(IntPtr handle, int processId, out string fileName) 410 | { 411 | IntPtr currentProcess = GetCurrentProcess(); 412 | bool remote = processId != GetProcessId(currentProcess); 413 | SafeProcessHandle processHandle = null; 414 | SafeObjectHandle objectHandle = null; 415 | try 416 | { 417 | if (remote) 418 | { 419 | processHandle = OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId); 420 | if (DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) 421 | handle = objectHandle.DangerousGetHandle(); 422 | } 423 | return GetFileNameFromHandle(handle, out fileName, 200); 424 | } 425 | finally 426 | { 427 | if (remote) 428 | { 429 | if (processHandle != null) 430 | processHandle.Close(); 431 | if (objectHandle != null) 432 | objectHandle.Close(); 433 | } 434 | } 435 | } 436 | 437 | private static string GetHandleTypeToken(IntPtr handle) 438 | { 439 | int length; 440 | NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); 441 | IntPtr ptr = IntPtr.Zero; 442 | RuntimeHelpers.PrepareConstrainedRegions(); 443 | try 444 | { 445 | RuntimeHelpers.PrepareConstrainedRegions(); 446 | try 447 | { } 448 | finally 449 | { 450 | if (length >= 0) 451 | ptr = Marshal.AllocHGlobal(length); 452 | } 453 | if (NtQueryObject(handle, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) == NT_STATUS.STATUS_SUCCESS) 454 | { 455 | return Marshal.PtrToStringUni((IntPtr)((int)ptr + 0x60)); 456 | } 457 | } 458 | finally 459 | { 460 | Marshal.FreeHGlobal(ptr); 461 | } 462 | return string.Empty; 463 | } 464 | private static string GetHandleTypeToken(IntPtr handle, int processId) 465 | { 466 | IntPtr currentProcess = GetCurrentProcess(); 467 | bool remote = processId != GetProcessId(currentProcess); 468 | SafeProcessHandle processHandle = null; 469 | SafeObjectHandle objectHandle = null; 470 | try 471 | { 472 | if (remote) 473 | { 474 | processHandle = OpenProcess(ProcessAccessRights.PROCESS_DUP_HANDLE, true, processId); 475 | if (DuplicateHandle(processHandle.DangerousGetHandle(), handle, currentProcess, out objectHandle, 0, false, DuplicateHandleOptions.DUPLICATE_SAME_ACCESS)) 476 | { 477 | handle = objectHandle.DangerousGetHandle(); 478 | } 479 | } 480 | return GetHandleTypeToken(handle); 481 | } 482 | finally 483 | { 484 | if (remote) 485 | { 486 | if (processHandle != null) 487 | { 488 | processHandle.Close(); 489 | } 490 | if (objectHandle != null) 491 | { 492 | objectHandle.Close(); 493 | } 494 | } 495 | } 496 | } 497 | private static bool GetHandleTypeFromToken(string token, out SystemHandleType handleType) 498 | { 499 | for (int i = 1; i < handleTypeTokenCount; i++) 500 | { 501 | if (handleTypeTokens[i] == token) 502 | { 503 | handleType = (SystemHandleType)i; 504 | return true; 505 | } 506 | } 507 | handleType = SystemHandleType.OB_TYPE_UNKNOWN; 508 | return false; 509 | } 510 | private static bool GetHandleType(IntPtr handle, out SystemHandleType handleType) 511 | { 512 | string token = GetHandleTypeToken(handle); 513 | return GetHandleTypeFromToken(token, out handleType); 514 | } 515 | private static bool GetHandleType(IntPtr handle, int processId, out SystemHandleType handleType) 516 | { 517 | string token = GetHandleTypeToken(handle, processId); 518 | return GetHandleTypeFromToken(token, out handleType); 519 | } 520 | 521 | private static bool ConvertDevicePathToDosPath(string devicePath, out string dosPath) 522 | { 523 | EnsureDeviceMap(); 524 | int i = devicePath.Length; 525 | while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1) 526 | { 527 | string drive; 528 | if (deviceMap.TryGetValue(devicePath.Substring(0, i), out drive)) 529 | { 530 | dosPath = string.Concat(drive, devicePath.Substring(i)); 531 | return dosPath.Length != 0; 532 | } 533 | } 534 | dosPath = string.Empty; 535 | return false; 536 | } 537 | 538 | private static void EnsureDeviceMap() 539 | { 540 | if (deviceMap == null) 541 | { 542 | Dictionary localDeviceMap = BuildDeviceMap(); 543 | Interlocked.CompareExchange(ref deviceMap, localDeviceMap, null); 544 | } 545 | } 546 | 547 | private static Dictionary BuildDeviceMap() 548 | { 549 | string[] logicalDrives = Environment.GetLogicalDrives(); 550 | Dictionary localDeviceMap = new Dictionary(logicalDrives.Length); 551 | StringBuilder lpTargetPath = new StringBuilder(MAX_PATH); 552 | foreach (string drive in logicalDrives) 553 | { 554 | string lpDeviceName = drive.Substring(0, 2); 555 | QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH); 556 | localDeviceMap.Add(NormalizeDeviceName(lpTargetPath.ToString()), lpDeviceName); 557 | } 558 | localDeviceMap.Add(networkDevicePrefix.Substring(0, networkDevicePrefix.Length - 1), "\\"); 559 | return localDeviceMap; 560 | } 561 | 562 | private static string NormalizeDeviceName(string deviceName) 563 | { 564 | if (string.Compare(deviceName, 0, networkDevicePrefix, 0, networkDevicePrefix.Length, StringComparison.InvariantCulture) == 0) 565 | { 566 | string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDevicePrefix.Length) + 1); 567 | return string.Concat(networkDevicePrefix, shareName); 568 | } 569 | return deviceName; 570 | } 571 | 572 | /// 573 | /// Gets the open files enumerator. 574 | /// 575 | /// The process id. 576 | /// 577 | public static IEnumerable GetProcessOpenFiles(int processId) 578 | { 579 | return new OpenFiles(processId); 580 | } 581 | 582 | public static List GetLockingProcesses(string path) 583 | { 584 | List processes = new List(); 585 | foreach (Process p in Process.GetProcesses()) 586 | { 587 | try 588 | { 589 | if (GetProcessOpenFiles(p.Id).Contains(path)) 590 | processes.Add(p); 591 | } 592 | catch { }//some processes will fail 593 | } 594 | return processes; 595 | } 596 | } 597 | } 598 | -------------------------------------------------------------------------------- /WalkmanLib.RestartManager.cs: -------------------------------------------------------------------------------- 1 | // RestartManager method 2 | //https://stackoverflow.com/a/3504251/2999220 3 | //https://stackoverflow.com/a/20623311/2999220 4 | //https://stackoverflow.com/a/20623302/2999220 5 | //https://gist.github.com/mlaily/9423f1855bb176d52a327f5874915a97 6 | //https://docs.microsoft.com/en-us/archive/msdn-magazine/2007/april/net-matters-restart-manager-and-generic-method-compilation 7 | //https://devblogs.microsoft.com/oldnewthing/?p=8283 8 | 9 | 10 | using System; 11 | using System.Collections.Generic; 12 | using System.Runtime.InteropServices; 13 | using System.Diagnostics; 14 | using System.ComponentModel; 15 | 16 | namespace WalkmanLib 17 | { 18 | public static class RestartManager 19 | { 20 | const int CCH_RM_MAX_APP_NAME = 255; 21 | const int CCH_RM_MAX_SVC_NAME = 63; 22 | const int ERROR_MORE_DATA = 234; 23 | 24 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/ns-restartmanager-rm_process_info 25 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 26 | public struct ProcessInfo 27 | { 28 | public UniqueProcess Process; 29 | 30 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_APP_NAME + 1)] 31 | public string AppName; 32 | 33 | [MarshalAs(UnmanagedType.ByValTStr, SizeConst = CCH_RM_MAX_SVC_NAME + 1)] 34 | public string ServiceShortName; 35 | 36 | public AppType ApplicationType; 37 | public uint AppStatus; 38 | public uint TSSessionId; 39 | [MarshalAs(UnmanagedType.Bool)] 40 | public bool Restartable; 41 | } 42 | 43 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/ns-restartmanager-rm_unique_process 44 | [StructLayout(LayoutKind.Sequential)] 45 | public struct UniqueProcess 46 | { 47 | public uint ProcessID; 48 | System.Runtime.InteropServices.ComTypes.FILETIME ProcessStartTime; 49 | } 50 | 51 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/ne-restartmanager-rm_app_type 52 | // values: https://github.com/microsoft/msbuild/blob/2791d9d93e88325011eb6907579d6fdac0b1b62e/src/Tasks/LockCheck.cs#L101 53 | public enum AppType 54 | { 55 | RmUnknownApp = 0, 56 | RmMainWindow = 1, 57 | RmOtherWindow = 2, 58 | RmService = 3, 59 | RmExplorer = 4, 60 | RmConsole = 5, 61 | RmCritical = 1000 62 | } 63 | 64 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/nf-restartmanager-rmregisterresources 65 | [DllImport("rstrtmgr.dll", SetLastError = true, CharSet = CharSet.Unicode)] 66 | static extern int RmRegisterResources(uint pSessionHandle, 67 | uint nFiles, 68 | string[] rgsFilenames, 69 | uint nApplications, 70 | [In] UniqueProcess[] rgApplications, 71 | uint nServices, 72 | string[] rgsServiceNames); 73 | 74 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/nf-restartmanager-rmstartsession 75 | [DllImport("rstrtmgr.dll", SetLastError = true, CharSet = CharSet.Auto)] 76 | static extern int RmStartSession(out uint pSessionHandle, 77 | int dwSessionFlags, 78 | string strSessionKey); 79 | 80 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/nf-restartmanager-rmendsession 81 | [DllImport("rstrtmgr.dll", SetLastError = true)] 82 | static extern int RmEndSession(uint pSessionHandle); 83 | 84 | //https://docs.microsoft.com/en-us/windows/win32/api/restartmanager/nf-restartmanager-rmgetlist 85 | [DllImport("rstrtmgr.dll", SetLastError = true)] 86 | static extern int RmGetList(uint dwSessionHandle, 87 | out uint pnProcInfoNeeded, 88 | ref uint pnProcInfo, 89 | [In, Out] ProcessInfo[] rgAffectedApps, 90 | ref uint lpdwRebootReasons); 91 | 92 | public static ProcessInfo[] GetLockingProcessInfos(string path) 93 | { 94 | uint handle; 95 | if (RmStartSession(out handle, 0, Guid.NewGuid().ToString()) != 0) 96 | throw new Exception("Could not begin session. Unable to determine file lockers.", new Win32Exception()); 97 | 98 | try 99 | { 100 | uint ArrayLengthNeeded = 0, 101 | ArrayLength = 0, 102 | lpdwRebootReasons = 0; //RmRebootReasonNone; 103 | 104 | string[] resources = { path }; // Just checking on one resource. 105 | 106 | if (RmRegisterResources(handle, (uint)resources.Length, resources, 0, null, 0, null) != 0) 107 | throw new Exception("Could not register resource.", new Win32Exception()); 108 | 109 | switch (RmGetList(handle, out ArrayLengthNeeded, ref ArrayLength, null, ref lpdwRebootReasons)) 110 | { 111 | case ERROR_MORE_DATA: 112 | ProcessInfo[] processInfos = new ProcessInfo[ArrayLengthNeeded]; 113 | ArrayLength = ArrayLengthNeeded; 114 | 115 | if (RmGetList(handle, out ArrayLengthNeeded, ref ArrayLength, processInfos, ref lpdwRebootReasons) != 0) 116 | throw new Exception("Could not list processes locking resource.", new Win32Exception()); 117 | 118 | return processInfos; 119 | case 0: 120 | return new ProcessInfo[0]; 121 | default: 122 | throw new Exception("Could not list processes locking resource. Failed to get size of result.", new Win32Exception()); 123 | } 124 | } 125 | finally 126 | { 127 | RmEndSession(handle); 128 | } 129 | } 130 | 131 | public static List GetLockingProcesses(string path) 132 | { 133 | List processes = new List(); 134 | foreach (ProcessInfo pI in GetLockingProcessInfos(path)) 135 | { 136 | try 137 | { 138 | Process process = Process.GetProcessById((int)pI.Process.ProcessID); 139 | processes.Add(process); 140 | } 141 | catch (ArgumentException) { } 142 | } 143 | return processes; 144 | } 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /WalkmanLib.SystemHandles.cs: -------------------------------------------------------------------------------- 1 | // Get all system open handles method - uses NTQuerySystemInformation and NTQueryObject 2 | //https://gist.github.com/i-e-b/2290426 3 | //https://stackoverflow.com/a/13735033/2999220 4 | //https://stackoverflow.com/a/6351168/2999220 5 | 6 | 7 | using System; 8 | using System.Collections.Concurrent; 9 | using System.Collections.Generic; 10 | using System.ComponentModel; 11 | using System.Diagnostics; 12 | using System.IO; 13 | using System.Runtime.ConstrainedExecution; 14 | using System.Runtime.InteropServices; 15 | using System.Text; 16 | using System.Threading; 17 | 18 | namespace WalkmanLib 19 | { 20 | class SystemHandles 21 | { 22 | #region Native Methods 23 | 24 | #region Enums 25 | 26 | //https://pinvoke.net/default.aspx/Enums.NtStatus 27 | //https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55 28 | protected enum NTSTATUS : uint 29 | { 30 | STATUS_SUCCESS = 0x00000000, 31 | STATUS_BUFFER_OVERFLOW = 0x80000005, 32 | STATUS_INFO_LENGTH_MISMATCH = 0xC0000004 33 | } 34 | 35 | //https://www.pinvoke.net/default.aspx/ntdll/SYSTEM_INFORMATION_CLASS.html 36 | protected enum SYSTEM_INFORMATION_CLASS 37 | { 38 | SystemBasicInformation = 0x00, 39 | SystemProcessorInformation = 0x01, 40 | SystemPerformanceInformation = 0x02, 41 | SystemTimeOfDayInformation = 0x03, 42 | SystemPathInformation = 0x04, 43 | SystemProcessInformation = 0x05, 44 | SystemCallCountInformation = 0x06, 45 | SystemDeviceInformation = 0x07, 46 | SystemProcessorPerformanceInformation = 0x08, 47 | SystemFlagsInformation = 0x09, 48 | SystemCallTimeInformation = 0x0A, 49 | SystemModuleInformation = 0x0B, 50 | SystemLocksInformation = 0x0C, 51 | SystemStackTraceInformation = 0x0D, 52 | SystemPagedPoolInformation = 0x0E, 53 | SystemNonPagedPoolInformation = 0x0F, 54 | SystemHandleInformation = 0x10, 55 | SystemObjectInformation = 0x11, 56 | SystemPageFileInformation = 0x12, 57 | SystemVdmInstemulInformation = 0x13, 58 | SystemVdmBopInformation = 0x14, 59 | SystemFileCacheInformation = 0x15, 60 | SystemPoolTagInformation = 0x16, 61 | SystemInterruptInformation = 0x17, 62 | SystemDpcBehaviorInformation = 0x18, 63 | SystemFullMemoryInformation = 0x19, 64 | SystemLoadGdiDriverInformation = 0x1A, 65 | SystemUnloadGdiDriverInformation = 0x1B, 66 | SystemTimeAdjustmentInformation = 0x1C, 67 | SystemSummaryMemoryInformation = 0x1D, 68 | SystemMirrorMemoryInformation = 0x1E, 69 | SystemPerformanceTraceInformation = 0x1F, 70 | SystemObsolete0 = 0x20, 71 | SystemExceptionInformation = 0x21, 72 | SystemCrashDumpStateInformation = 0x22, 73 | SystemKernelDebuggerInformation = 0x23, 74 | SystemContextSwitchInformation = 0x24, 75 | SystemRegistryQuotaInformation = 0x25, 76 | SystemExtendServiceTableInformation = 0x26, 77 | SystemPrioritySeperation = 0x27, 78 | SystemVerifierAddDriverInformation = 0x28, 79 | SystemVerifierRemoveDriverInformation = 0x29, 80 | SystemProcessorIdleInformation = 0x2A, 81 | SystemLegacyDriverInformation = 0x2B, 82 | SystemCurrentTimeZoneInformation = 0x2C, 83 | SystemLookasideInformation = 0x2D, 84 | SystemTimeSlipNotification = 0x2E, 85 | SystemSessionCreate = 0x2F, 86 | SystemSessionDetach = 0x30, 87 | SystemSessionInformation = 0x31, 88 | SystemRangeStartInformation = 0x32, 89 | SystemVerifierInformation = 0x33, 90 | SystemVerifierThunkExtend = 0x34, 91 | SystemSessionProcessInformation = 0x35, 92 | SystemLoadGdiDriverInSystemSpace = 0x36, 93 | SystemNumaProcessorMap = 0x37, 94 | SystemPrefetcherInformation = 0x38, 95 | SystemExtendedProcessInformation = 0x39, 96 | SystemRecommendedSharedDataAlignment = 0x3A, 97 | SystemComPlusPackage = 0x3B, 98 | SystemNumaAvailableMemory = 0x3C, 99 | SystemProcessorPowerInformation = 0x3D, 100 | SystemEmulationBasicInformation = 0x3E, 101 | SystemEmulationProcessorInformation = 0x3F, 102 | SystemExtendedHandleInformation = 0x40, 103 | SystemLostDelayedWriteInformation = 0x41, 104 | SystemBigPoolInformation = 0x42, 105 | SystemSessionPoolTagInformation = 0x43, 106 | SystemSessionMappedViewInformation = 0x44, 107 | SystemHotpatchInformation = 0x45, 108 | SystemObjectSecurityMode = 0x46, 109 | SystemWatchdogTimerHandler = 0x47, 110 | SystemWatchdogTimerInformation = 0x48, 111 | SystemLogicalProcessorInformation = 0x49, 112 | SystemWow64SharedInformationObsolete = 0x4A, 113 | SystemRegisterFirmwareTableInformationHandler = 0x4B, 114 | SystemFirmwareTableInformation = 0x4C, 115 | SystemModuleInformationEx = 0x4D, 116 | SystemVerifierTriageInformation = 0x4E, 117 | SystemSuperfetchInformation = 0x4F, 118 | SystemMemoryListInformation = 0x50, 119 | SystemFileCacheInformationEx = 0x51, 120 | SystemThreadPriorityClientIdInformation = 0x52, 121 | SystemProcessorIdleCycleTimeInformation = 0x53, 122 | SystemVerifierCancellationInformation = 0x54, 123 | SystemProcessorPowerInformationEx = 0x55, 124 | SystemRefTraceInformation = 0x56, 125 | SystemSpecialPoolInformation = 0x57, 126 | SystemProcessIdInformation = 0x58, 127 | SystemErrorPortInformation = 0x59, 128 | SystemBootEnvironmentInformation = 0x5A, 129 | SystemHypervisorInformation = 0x5B, 130 | SystemVerifierInformationEx = 0x5C, 131 | SystemTimeZoneInformation = 0x5D, 132 | SystemImageFileExecutionOptionsInformation = 0x5E, 133 | SystemCoverageInformation = 0x5F, 134 | SystemPrefetchPatchInformation = 0x60, 135 | SystemVerifierFaultsInformation = 0x61, 136 | SystemSystemPartitionInformation = 0x62, 137 | SystemSystemDiskInformation = 0x63, 138 | SystemProcessorPerformanceDistribution = 0x64, 139 | SystemNumaProximityNodeInformation = 0x65, 140 | SystemDynamicTimeZoneInformation = 0x66, 141 | SystemCodeIntegrityInformation = 0x67, 142 | SystemProcessorMicrocodeUpdateInformation = 0x68, 143 | SystemProcessorBrandString = 0x69, 144 | SystemVirtualAddressInformation = 0x6A, 145 | SystemLogicalProcessorAndGroupInformation = 0x6B, 146 | SystemProcessorCycleTimeInformation = 0x6C, 147 | SystemStoreInformation = 0x6D, 148 | SystemRegistryAppendString = 0x6E, 149 | SystemAitSamplingValue = 0x6F, 150 | SystemVhdBootInformation = 0x70, 151 | SystemCpuQuotaInformation = 0x71, 152 | SystemNativeBasicInformation = 0x72, 153 | SystemErrorPortTimeouts = 0x73, 154 | SystemLowPriorityIoInformation = 0x74, 155 | SystemBootEntropyInformation = 0x75, 156 | SystemVerifierCountersInformation = 0x76, 157 | SystemPagedPoolInformationEx = 0x77, 158 | SystemSystemPtesInformationEx = 0x78, 159 | SystemNodeDistanceInformation = 0x79, 160 | SystemAcpiAuditInformation = 0x7A, 161 | SystemBasicPerformanceInformation = 0x7B, 162 | SystemQueryPerformanceCounterInformation = 0x7C, 163 | SystemSessionBigPoolInformation = 0x7D, 164 | SystemBootGraphicsInformation = 0x7E, 165 | SystemScrubPhysicalMemoryInformation = 0x7F, 166 | SystemBadPageInformation = 0x80, 167 | SystemProcessorProfileControlArea = 0x81, 168 | SystemCombinePhysicalMemoryInformation = 0x82, 169 | SystemEntropyInterruptTimingInformation = 0x83, 170 | SystemConsoleInformation = 0x84, 171 | SystemPlatformBinaryInformation = 0x85, 172 | SystemPolicyInformation = 0x86, 173 | SystemHypervisorProcessorCountInformation = 0x87, 174 | SystemDeviceDataInformation = 0x88, 175 | SystemDeviceDataEnumerationInformation = 0x89, 176 | SystemMemoryTopologyInformation = 0x8A, 177 | SystemMemoryChannelInformation = 0x8B, 178 | SystemBootLogoInformation = 0x8C, 179 | SystemProcessorPerformanceInformationEx = 0x8D, 180 | SystemCriticalProcessErrorLogInformation = 0x8E, 181 | SystemSecureBootPolicyInformation = 0x8F, 182 | SystemPageFileInformationEx = 0x90, 183 | SystemSecureBootInformation = 0x91, 184 | SystemEntropyInterruptTimingRawInformation = 0x92, 185 | SystemPortableWorkspaceEfiLauncherInformation = 0x93, 186 | SystemFullProcessInformation = 0x94, 187 | SystemKernelDebuggerInformationEx = 0x95, 188 | SystemBootMetadataInformation = 0x96, 189 | SystemSoftRebootInformation = 0x97, 190 | SystemElamCertificateInformation = 0x98, 191 | SystemOfflineDumpConfigInformation = 0x99, 192 | SystemProcessorFeaturesInformation = 0x9A, 193 | SystemRegistryReconciliationInformation = 0x9B, 194 | SystemEdidInformation = 0x9C, 195 | SystemManufacturingInformation = 0x9D, 196 | SystemEnergyEstimationConfigInformation = 0x9E, 197 | SystemHypervisorDetailInformation = 0x9F, 198 | SystemProcessorCycleStatsInformation = 0xA0, 199 | SystemVmGenerationCountInformation = 0xA1, 200 | SystemTrustedPlatformModuleInformation = 0xA2, 201 | SystemKernelDebuggerFlags = 0xA3, 202 | SystemCodeIntegrityPolicyInformation = 0xA4, 203 | SystemIsolatedUserModeInformation = 0xA5, 204 | SystemHardwareSecurityTestInterfaceResultsInformation = 0xA6, 205 | SystemSingleModuleInformation = 0xA7, 206 | SystemAllowedCpuSetsInformation = 0xA8, 207 | SystemDmaProtectionInformation = 0xA9, 208 | SystemInterruptCpuSetsInformation = 0xAA, 209 | SystemSecureBootPolicyFullInformation = 0xAB, 210 | SystemCodeIntegrityPolicyFullInformation = 0xAC, 211 | SystemAffinitizedInterruptProcessorInformation = 0xAD, 212 | SystemRootSiloInformation = 0xAE, 213 | SystemCpuSetInformation = 0xAF, 214 | SystemCpuSetTagInformation = 0xB0, 215 | SystemWin32WerStartCallout = 0xB1, 216 | SystemSecureKernelProfileInformation = 0xB2, 217 | SystemCodeIntegrityPlatformManifestInformation = 0xB3, 218 | SystemInterruptSteeringInformation = 0xB4, 219 | SystemSuppportedProcessorArchitectures = 0xB5, 220 | SystemMemoryUsageInformation = 0xB6, 221 | SystemCodeIntegrityCertificateInformation = 0xB7, 222 | SystemPhysicalMemoryInformation = 0xB8, 223 | SystemControlFlowTransition = 0xB9, 224 | SystemKernelDebuggingAllowed = 0xBA, 225 | SystemActivityModerationExeState = 0xBB, 226 | SystemActivityModerationUserSettings = 0xBC, 227 | SystemCodeIntegrityPoliciesFullInformation = 0xBD, 228 | SystemCodeIntegrityUnlockInformation = 0xBE, 229 | SystemIntegrityQuotaInformation = 0xBF, 230 | SystemFlushInformation = 0xC0, 231 | SystemProcessorIdleMaskInformation = 0xC1, 232 | SystemSecureDumpEncryptionInformation = 0xC2, 233 | SystemWriteConstraintInformation = 0xC3, 234 | SystemKernelVaShadowInformation = 0xC4, 235 | SystemHypervisorSharedPageInformation = 0xC5, 236 | SystemFirmwareBootPerformanceInformation = 0xC6, 237 | SystemCodeIntegrityVerificationInformation = 0xC7, 238 | SystemFirmwarePartitionInformation = 0xC8, 239 | SystemSpeculationControlInformation = 0xC9, 240 | SystemDmaGuardPolicyInformation = 0xCA, 241 | SystemEnclaveLaunchControlInformation = 0xCB, 242 | SystemWorkloadAllowedCpuSetsInformation = 0xCC, 243 | SystemCodeIntegrityUnlockModeInformation = 0xCD, 244 | SystemLeapSecondInformation = 0xCE, 245 | SystemFlags2Information = 0xCF, 246 | SystemSecurityModelInformation = 0xD0, 247 | SystemCodeIntegritySyntheticCacheInformation = 0xD1, 248 | MaxSystemInfoClass = 0xD2 249 | } 250 | 251 | //https://www.pinvoke.net/default.aspx/Enums.OBJECT_INFORMATION_CLASS 252 | protected enum OBJECT_INFORMATION_CLASS 253 | { 254 | ObjectBasicInformation = 0, 255 | ObjectNameInformation = 1, 256 | ObjectTypeInformation = 2, 257 | ObjectAllTypesInformation = 3, 258 | ObjectHandleInformation = 4 259 | } 260 | 261 | //https://docs.microsoft.com/en-za/windows/win32/procthread/process-security-and-access-rights 262 | //https://www.pinvoke.net/default.aspx/Enums.ProcessAccess 263 | protected enum PROCESS_ACCESS_RIGHTS 264 | { 265 | PROCESS_TERMINATE = 0x00000001, 266 | PROCESS_CREATE_THREAD = 0x00000002, 267 | PROCESS_SET_SESSION_ID = 0x00000004, 268 | PROCESS_VM_OPERATION = 0x00000008, 269 | PROCESS_VM_READ = 0x00000010, 270 | PROCESS_VM_WRITE = 0x00000020, 271 | PROCESS_DUP_HANDLE = 0x00000040, 272 | PROCESS_CREATE_PROCESS = 0x00000080, 273 | PROCESS_SET_QUOTA = 0x00000100, 274 | PROCESS_SET_INFORMATION = 0x00000200, 275 | PROCESS_QUERY_INFORMATION = 0x00000400, 276 | PROCESS_SUSPEND_RESUME = 0x00000800, 277 | PROCESS_QUERY_LIMITED_INFORMATION = 0x00001000, 278 | DELETE = 0x00010000, 279 | READ_CONTROL = 0x00020000, 280 | WRITE_DAC = 0x00040000, 281 | WRITE_OWNER = 0x00080000, 282 | STANDARD_RIGHTS_REQUIRED = 0x000F0000, 283 | SYNCHRONIZE = 0x00100000, 284 | 285 | PROCESS_ALL_ACCESS = STANDARD_RIGHTS_REQUIRED | SYNCHRONIZE | 0xFFFF 286 | } 287 | 288 | //https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle#DUPLICATE_CLOSE_SOURCE 289 | protected enum DUPLICATE_HANDLE_OPTIONS 290 | { 291 | DUPLICATE_CLOSE_SOURCE = 0x00000001, 292 | DUPLICATE_SAME_ACCESS = 0x00000002 293 | } 294 | 295 | //http://www.jasinskionline.com/TechnicalWiki/SYSTEM_HANDLE_INFORMATION-WinApi-Struct.ashx 296 | internal enum SYSTEM_HANDLE_FLAGS : byte 297 | { 298 | PROTECT_FROM_CLOSE = 0x01, 299 | INHERIT = 0x02 300 | } 301 | 302 | //https://www.winehq.org/pipermail/wine-patches/2005-October/021642.html 303 | //https://github.com/olimsaidov/autorun-remover/blob/b558df6487ae1cb4cb998fab3330c07bb7de0f21/NativeAPI.pas#L108 304 | internal enum SYSTEM_HANDLE_TYPE 305 | { 306 | UNKNOWN = 00, 307 | TYPE = 01, 308 | DIRECTORY = 02, 309 | SYMBOLIC_LINK = 03, 310 | TOKEN = 04, 311 | PROCESS = 05, 312 | THREAD = 06, 313 | JOB = 07, 314 | EVENT = 08, 315 | EVENT_PAIR = 09, 316 | MUTANT = 10, 317 | UNKNOWN_11 = 11, 318 | SEMAPHORE = 12, 319 | TIMER = 13, 320 | PROFILE = 14, 321 | WINDOW_STATION = 15, 322 | DESKTOP = 16, 323 | SECTION = 17, 324 | KEY = 18, 325 | PORT = 19, 326 | WAITABLE_PORT = 20, 327 | ADAPTER = 21, 328 | CONTROLLER = 22, 329 | DEVICE = 23, 330 | DRIVER = 24, 331 | IO_COMPLETION = 25, 332 | FILE = 28, 333 | 334 | // From my own research 335 | TP_WORKER_FACTORY, 336 | ALPC_PORT, 337 | KEYED_EVENT, 338 | SESSION, 339 | IO_COMPLETION_RESERVE, 340 | WMI_GUID, 341 | USER_APC_RESERVE, 342 | IR_TIMER, 343 | COMPOSITION, 344 | WAIT_COMPLETION_PACKET, 345 | DXGK_SHARED_RESOURCE, 346 | DXGK_SHARED_SYNC_OBJECT, 347 | DXGK_DISPLAY_MANAGER_OBJECT, 348 | DXGK_COMPOSITION_OBJECT, 349 | OTHER 350 | } 351 | 352 | #endregion 353 | 354 | #region Structs 355 | 356 | //https://www.codeproject.com/script/Articles/ViewDownloads.aspx?aid=18975&zep=OpenedFileFinder%2fUtils.h&rzp=%2fKB%2fshell%2fOpenedFileFinder%2f%2fopenedfilefinder_src.zip 357 | [StructLayout(LayoutKind.Sequential)] 358 | protected struct SYSTEM_HANDLE_INFORMATION 359 | { 360 | //public IntPtr dwCount; 361 | public uint dwCount; 362 | 363 | // see https://stackoverflow.com/a/38884095/2999220 - MarshalAs doesn't allow variable sized arrays 364 | //[MarshalAs(UnmanagedType.ByValArray, ArraySubType = UnmanagedType.Struct)] 365 | //public SYSTEM_HANDLE[] Handles; 366 | public IntPtr Handles; 367 | } 368 | 369 | //https://stackoverflow.com/a/5163277/2999220 370 | //http://www.jasinskionline.com/TechnicalWiki/SYSTEM_HANDLE_INFORMATION-WinApi-Struct.ashx 371 | [StructLayout(LayoutKind.Sequential)] 372 | internal struct SYSTEM_HANDLE 373 | { 374 | /// Handle Owner Process ID 375 | public uint dwProcessId; 376 | /// Object Type 377 | public byte bObjectType; 378 | /// Handle Flags 379 | public SYSTEM_HANDLE_FLAGS bFlags; 380 | /// Handle Value 381 | public ushort wValue; 382 | /// Object Pointer 383 | IntPtr pAddress; 384 | /// Access Mask 385 | public uint dwGrantedAccess; 386 | } 387 | 388 | //https://docs.microsoft.com/en-us/windows/win32/api/ntdef/ns-ntdef-_unicode_string 389 | //https://www.pinvoke.net/default.aspx/Structures/UNICODE_STRING.html 390 | [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] 391 | protected struct UNICODE_STRING 392 | { 393 | public readonly ushort Length; 394 | public readonly ushort MaximumLength; 395 | [MarshalAs(UnmanagedType.LPWStr)] 396 | public readonly string Buffer; 397 | 398 | public UNICODE_STRING(string s) 399 | { 400 | Length = (ushort)(s.Length * 2); 401 | MaximumLength = (ushort)(Length + 2); 402 | Buffer = s; 403 | } 404 | } 405 | 406 | //https://www.pinvoke.net/default.aspx/Structures.GENERIC_MAPPING 407 | //http://www.jasinskionline.com/technicalwiki/GENERIC_MAPPING-WinApi-Struct.ashx 408 | [StructLayout(LayoutKind.Sequential)] 409 | protected struct GENERIC_MAPPING 410 | { 411 | public uint GenericRead; 412 | public uint GenericWrite; 413 | public uint GenericExecute; 414 | public uint GenericAll; 415 | } 416 | 417 | //http://www.jasinskionline.com/technicalwiki/OBJECT_NAME_INFORMATION-WinApi-Struct.ashx 418 | [StructLayout(LayoutKind.Sequential)] 419 | protected struct OBJECT_NAME_INFORMATION 420 | { 421 | public UNICODE_STRING Name; 422 | } 423 | 424 | //https://docs.microsoft.com/en-za/windows-hardware/drivers/ddi/ntifs/ns-ntifs-__public_object_type_information 425 | //http://www.jasinskionline.com/technicalwiki/OBJECT_TYPE_INFORMATION-WinApi-Struct.ashx 426 | [StructLayout(LayoutKind.Sequential)] 427 | protected struct OBJECT_TYPE_INFORMATION 428 | { 429 | public UNICODE_STRING TypeName; 430 | public int ObjectCount; 431 | public int HandleCount; 432 | int Reserved1; 433 | int Reserved2; 434 | int Reserved3; 435 | int Reserved4; 436 | public int PeakObjectCount; 437 | public int PeakHandleCount; 438 | int Reserved5; 439 | int Reserved6; 440 | int Reserved7; 441 | int Reserved8; 442 | public int InvalidAttributes; 443 | public GENERIC_MAPPING GenericMapping; 444 | public int ValidAccess; 445 | byte Unknown; 446 | public byte MaintainHandleDatabase; 447 | public int PoolType; 448 | public int PagedPoolUsage; 449 | public int NonPagedPoolUsage; 450 | } 451 | 452 | #endregion 453 | 454 | #region Methods 455 | 456 | //https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntquerysysteminformation 457 | [DllImport("ntdll.dll")] 458 | protected static extern NTSTATUS NtQuerySystemInformation( 459 | [In] SYSTEM_INFORMATION_CLASS SystemInformationClass, 460 | [Out] IntPtr SystemInformation, 461 | [In] uint SystemInformationLength, 462 | [Out] out uint ReturnLength 463 | ); 464 | 465 | //https://docs.microsoft.com/en-us/windows/win32/api/winternl/nf-winternl-ntqueryobject 466 | [DllImport("ntdll.dll")] 467 | protected static extern NTSTATUS NtQueryObject( 468 | [In] IntPtr Handle, 469 | [In] OBJECT_INFORMATION_CLASS ObjectInformationClass, 470 | [In] IntPtr ObjectInformation, 471 | [In] uint ObjectInformationLength, 472 | [Out] out uint ReturnLength 473 | ); 474 | 475 | //https://docs.microsoft.com/en-za/windows/win32/api/processthreadsapi/nf-processthreadsapi-openprocess 476 | [DllImport("kernel32.dll", SetLastError = true)] 477 | protected static extern IntPtr OpenProcess( 478 | [In] PROCESS_ACCESS_RIGHTS dwDesiredAccess, 479 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 480 | [In] uint dwProcessId 481 | ); 482 | 483 | //https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-duplicatehandle 484 | [DllImport("kernel32.dll", SetLastError = true)] 485 | [return: MarshalAs(UnmanagedType.Bool)] 486 | protected static extern bool DuplicateHandle( 487 | [In] IntPtr hSourceProcessHandle, 488 | [In] IntPtr hSourceHandle, 489 | [In] IntPtr hTargetProcessHandle, 490 | [Out] out IntPtr lpTargetHandle, 491 | [In] uint dwDesiredAccess, 492 | [In, MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, 493 | [In] DUPLICATE_HANDLE_OPTIONS dwOptions 494 | ); 495 | 496 | //https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess 497 | [DllImport("kernel32.dll")] 498 | protected static extern IntPtr GetCurrentProcess(); 499 | 500 | //https://docs.microsoft.com/en-us/windows/win32/api/handleapi/nf-handleapi-closehandle 501 | [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)] 502 | [DllImport("kernel32.dll", SetLastError = true)] 503 | [return: MarshalAs(UnmanagedType.Bool)] 504 | protected static extern bool CloseHandle( 505 | [In] IntPtr hObject 506 | ); 507 | 508 | //https://docs.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-querydosdevicea 509 | //https://docs.microsoft.com/en-us/windows/win32/api/fileapi/nf-fileapi-querydosdevicew 510 | [DllImport("kernel32.dll", SetLastError = true)] 511 | protected static extern uint QueryDosDevice( 512 | [In] string lpDeviceName, 513 | [Out] StringBuilder lpTargetPath, 514 | [In] uint ucchMax 515 | ); 516 | 517 | #endregion 518 | 519 | #endregion 520 | 521 | #region Public Methods 522 | 523 | #region GetSystemHandles 524 | 525 | /// Gets all the open handles on the system. Use GetHandleInfo to retrieve proper type and name information. 526 | /// Enumerable list of system handles 527 | internal static IEnumerable GetSystemHandles() 528 | { 529 | uint length = 0x1000; 530 | IntPtr ptr = IntPtr.Zero; 531 | bool done = false; 532 | try 533 | { 534 | while (!done) 535 | { 536 | ptr = Marshal.AllocHGlobal((int)length); 537 | uint wantedLength; 538 | switch (NtQuerySystemInformation( 539 | SYSTEM_INFORMATION_CLASS.SystemHandleInformation, 540 | ptr, length, out wantedLength)) 541 | { 542 | case NTSTATUS.STATUS_SUCCESS: 543 | done = true; // can't double-break in C# 544 | break; 545 | case NTSTATUS.STATUS_INFO_LENGTH_MISMATCH: 546 | length = Math.Max(length, wantedLength); 547 | Marshal.FreeHGlobal(ptr); 548 | ptr = IntPtr.Zero; 549 | break; 550 | default: 551 | throw new Exception("Failed to retrieve system handle information.", new Win32Exception()); 552 | } 553 | } 554 | 555 | long handleCount = IntPtr.Size == 4 ? Marshal.ReadInt32(ptr) : Marshal.ReadInt64(ptr); 556 | long offset = IntPtr.Size; 557 | long size = Marshal.SizeOf(typeof(SYSTEM_HANDLE)); 558 | 559 | for (long i = 0; i < handleCount; i++) 560 | { 561 | SYSTEM_HANDLE struc = Marshal.PtrToStructure((IntPtr)((long)ptr + offset)); 562 | yield return struc; 563 | 564 | offset += size; 565 | } 566 | } 567 | finally 568 | { 569 | if (ptr != IntPtr.Zero) 570 | Marshal.FreeHGlobal(ptr); 571 | } 572 | } 573 | 574 | #endregion 575 | 576 | #region GetHandleInfo 577 | 578 | internal struct HandleInfo 579 | { 580 | public uint ProcessID; 581 | public ushort HandleID; 582 | public uint GrantedAccess; 583 | public byte RawType; 584 | public SYSTEM_HANDLE_FLAGS Flags; 585 | public string Name; 586 | public string TypeString; 587 | public SYSTEM_HANDLE_TYPE Type; 588 | } 589 | 590 | private static ConcurrentDictionary rawTypeMap = new ConcurrentDictionary(); 591 | 592 | private static SYSTEM_HANDLE_TYPE HandleTypeFromString(string typeString) 593 | { 594 | switch (typeString) 595 | { 596 | case null: 597 | return SYSTEM_HANDLE_TYPE.UNKNOWN; 598 | case "Directory": 599 | return SYSTEM_HANDLE_TYPE.DIRECTORY; 600 | case "SymbolicLink": 601 | return SYSTEM_HANDLE_TYPE.SYMBOLIC_LINK; 602 | case "Token": 603 | return SYSTEM_HANDLE_TYPE.TOKEN; 604 | case "Process": 605 | return SYSTEM_HANDLE_TYPE.PROCESS; 606 | case "Thread": 607 | return SYSTEM_HANDLE_TYPE.THREAD; 608 | case "Job": 609 | return SYSTEM_HANDLE_TYPE.JOB; 610 | case "Event": 611 | return SYSTEM_HANDLE_TYPE.EVENT; 612 | case "Mutant": 613 | return SYSTEM_HANDLE_TYPE.MUTANT; 614 | case "Semaphore": 615 | return SYSTEM_HANDLE_TYPE.SEMAPHORE; 616 | case "Timer": 617 | return SYSTEM_HANDLE_TYPE.TIMER; 618 | case "WindowStation": 619 | return SYSTEM_HANDLE_TYPE.WINDOW_STATION; 620 | case "Desktop": 621 | return SYSTEM_HANDLE_TYPE.DESKTOP; 622 | case "Section": 623 | return SYSTEM_HANDLE_TYPE.SECTION; 624 | case "Key": 625 | return SYSTEM_HANDLE_TYPE.KEY; 626 | case "IoCompletion": 627 | return SYSTEM_HANDLE_TYPE.IO_COMPLETION; 628 | case "File": 629 | return SYSTEM_HANDLE_TYPE.FILE; 630 | case "TpWorkerFactory": 631 | return SYSTEM_HANDLE_TYPE.TP_WORKER_FACTORY; 632 | case "ALPC Port": 633 | return SYSTEM_HANDLE_TYPE.ALPC_PORT; 634 | case "KeyedEvent": 635 | return SYSTEM_HANDLE_TYPE.KEYED_EVENT; 636 | case "Session": 637 | return SYSTEM_HANDLE_TYPE.SESSION; 638 | case "IoCompletionReserve": 639 | return SYSTEM_HANDLE_TYPE.IO_COMPLETION_RESERVE; 640 | case "WmiGuid": 641 | return SYSTEM_HANDLE_TYPE.WMI_GUID; 642 | case "UserApcReserve": 643 | return SYSTEM_HANDLE_TYPE.USER_APC_RESERVE; 644 | case "IRTimer": 645 | return SYSTEM_HANDLE_TYPE.IR_TIMER; 646 | case "Composition": 647 | return SYSTEM_HANDLE_TYPE.COMPOSITION; 648 | case "WaitCompletionPacket": 649 | return SYSTEM_HANDLE_TYPE.WAIT_COMPLETION_PACKET; 650 | case "DxgkSharedResource": 651 | return SYSTEM_HANDLE_TYPE.DXGK_SHARED_RESOURCE; 652 | case "DxgkSharedSyncObject": 653 | return SYSTEM_HANDLE_TYPE.DXGK_SHARED_SYNC_OBJECT; 654 | case "DxgkDisplayManagerObject": 655 | return SYSTEM_HANDLE_TYPE.DXGK_DISPLAY_MANAGER_OBJECT; 656 | case "DxgkCompositionObject": 657 | return SYSTEM_HANDLE_TYPE.DXGK_COMPOSITION_OBJECT; 658 | default: 659 | return SYSTEM_HANDLE_TYPE.OTHER; 660 | } 661 | } 662 | 663 | /// 664 | /// Gets the handle type and name, and puts the other properties into more user-friendly fields. 665 | /// 666 | /// This function gets typeInfo from an internal type map (rawType to typeString) that is built as types are retrieved. 667 | /// To get full type information of handle types that could not be retrieved, 668 | /// either put the handles into a list, build a second map and apply them retroactively, 669 | /// or call this function on all System Handles beforehand with getting names Disabled. 670 | /// 671 | /// Handle struct returned by GetSystemHandles 672 | /// False (default) to ignore certain names that cause the system query to hang. Only set to true in a thread that can be killed. 673 | /// Set this to only attempt to get Handle names for a specific handle type. Set to int.MaxValue to disable getting file names. 674 | /// HandleInfo struct with retrievable information populated. 675 | internal static HandleInfo GetHandleInfo(SYSTEM_HANDLE handle, bool getAllNames = false, SYSTEM_HANDLE_TYPE onlyGetNameFor = SYSTEM_HANDLE_TYPE.UNKNOWN) 676 | { 677 | HandleInfo handleInfo = new HandleInfo 678 | { 679 | ProcessID = handle.dwProcessId, 680 | HandleID = handle.wValue, 681 | GrantedAccess = handle.dwGrantedAccess, 682 | RawType = handle.bObjectType, 683 | Flags = handle.bFlags, 684 | Name = null, 685 | TypeString = null, 686 | Type = SYSTEM_HANDLE_TYPE.UNKNOWN 687 | }; 688 | 689 | // get type from cached map if it exists 690 | if (rawTypeMap.ContainsKey(handleInfo.RawType)) 691 | { 692 | handleInfo.TypeString = rawTypeMap[handleInfo.RawType]; 693 | handleInfo.Type = HandleTypeFromString(handleInfo.TypeString); 694 | } 695 | 696 | IntPtr sourceProcessHandle = IntPtr.Zero; 697 | IntPtr handleDuplicate = IntPtr.Zero; 698 | try 699 | { 700 | sourceProcessHandle = OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_DUP_HANDLE, true, handleInfo.ProcessID); 701 | 702 | // To read info about a handle owned by another process we must duplicate it into ours 703 | // For simplicity, current process handles will also get duplicated; remember that process handles cannot be compared for equality 704 | if (!DuplicateHandle(sourceProcessHandle, (IntPtr)handleInfo.HandleID, GetCurrentProcess(), out handleDuplicate, 0, false, DUPLICATE_HANDLE_OPTIONS.DUPLICATE_SAME_ACCESS)) 705 | return handleInfo; 706 | 707 | // Get the object type if it hasn't been retrieved from cache map above 708 | if (!rawTypeMap.ContainsKey(handleInfo.RawType)) 709 | { 710 | uint length; 711 | NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, IntPtr.Zero, 0, out length); 712 | 713 | IntPtr ptr = IntPtr.Zero; 714 | try 715 | { 716 | ptr = Marshal.AllocHGlobal((int)length); 717 | if (NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectTypeInformation, ptr, length, out length) != NTSTATUS.STATUS_SUCCESS) 718 | return handleInfo; 719 | 720 | OBJECT_TYPE_INFORMATION typeInfo = Marshal.PtrToStructure(ptr); 721 | handleInfo.TypeString = typeInfo.TypeName.Buffer; 722 | } 723 | finally 724 | { 725 | Marshal.FreeHGlobal(ptr); 726 | } 727 | 728 | rawTypeMap.TryAdd(handleInfo.RawType, handleInfo.TypeString); 729 | handleInfo.Type = HandleTypeFromString(handleInfo.TypeString); 730 | } 731 | 732 | // Get the object name 733 | if (handleInfo.TypeString != null && 734 | // only check onlyGetNameFor if it isn't UNKNOWN 735 | (onlyGetNameFor == SYSTEM_HANDLE_TYPE.UNKNOWN || handleInfo.Type == onlyGetNameFor) && 736 | (getAllNames == true || ( 737 | // this type can hang for ~15mins, but excluding it cuts a lot of results, and it does eventually resolve... 738 | //!(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x120089 && handleInfo.Flags == 0x00 ) && 739 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x120089 && handleInfo.Flags == SYSTEM_HANDLE_FLAGS.INHERIT) && 740 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x120189 && handleInfo.Flags == 0x00 ) && 741 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x120189 && handleInfo.Flags == SYSTEM_HANDLE_FLAGS.INHERIT) && 742 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x12019f && handleInfo.Flags == 0x00 ) && 743 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x12019f && handleInfo.Flags == SYSTEM_HANDLE_FLAGS.INHERIT) && 744 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x1a019f && handleInfo.Flags == 0x00 ) && 745 | !(handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && handleInfo.GrantedAccess == 0x1a019f && handleInfo.Flags == SYSTEM_HANDLE_FLAGS.INHERIT) 746 | )))// don't query some objects that get stuck (NtQueryObject hangs on NamedPipes) 747 | { 748 | uint length; 749 | NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, IntPtr.Zero, 0, out length); 750 | 751 | IntPtr ptr = IntPtr.Zero; 752 | try 753 | { 754 | ptr = Marshal.AllocHGlobal((int)length); 755 | if (NtQueryObject(handleDuplicate, OBJECT_INFORMATION_CLASS.ObjectNameInformation, ptr, length, out length) != NTSTATUS.STATUS_SUCCESS) 756 | return handleInfo; 757 | 758 | OBJECT_NAME_INFORMATION nameInfo = Marshal.PtrToStructure(ptr); 759 | handleInfo.Name = nameInfo.Name.Buffer; 760 | } 761 | finally 762 | { 763 | Marshal.FreeHGlobal(ptr); 764 | } 765 | } 766 | } 767 | finally 768 | { 769 | CloseHandle(sourceProcessHandle); 770 | if (handleDuplicate != IntPtr.Zero) 771 | CloseHandle(handleDuplicate); 772 | } 773 | 774 | return handleInfo; 775 | } 776 | 777 | #endregion 778 | 779 | #region CloseSystemHandle 780 | 781 | // https://www.codeproject.com/Articles/18975/Listing-Used-Files 782 | /// Attempts to close a handle in a different process. Fails silently if the handle exists but could not be closed. 783 | /// Process ID of the process containing the handle to close 784 | /// Handle value in the target process to close 785 | internal static void CloseSystemHandle(uint ProcessID, ushort HandleID) 786 | { 787 | IntPtr sourceProcessHandle = IntPtr.Zero; 788 | IntPtr handleDuplicate = IntPtr.Zero; 789 | try 790 | { 791 | sourceProcessHandle = OpenProcess(PROCESS_ACCESS_RIGHTS.PROCESS_DUP_HANDLE, true, ProcessID); 792 | if ((int)sourceProcessHandle < 1) 793 | throw new ArgumentException("Process ID Not Found!", "ProcessID", new Win32Exception()); 794 | 795 | // always returns false, no point in checking 796 | DuplicateHandle(sourceProcessHandle, (IntPtr)HandleID, GetCurrentProcess(), out handleDuplicate, 0, false, DUPLICATE_HANDLE_OPTIONS.DUPLICATE_CLOSE_SOURCE); 797 | if ((int)handleDuplicate < 1 && Marshal.GetLastWin32Error() == 6) // ERROR_INVALID_HANDLE: The handle is invalid. 798 | throw new ArgumentException("Handle ID Not Found!", "HandleID", new Win32Exception(6)); 799 | } 800 | finally 801 | { 802 | CloseHandle(sourceProcessHandle); 803 | if (handleDuplicate != IntPtr.Zero) 804 | CloseHandle(handleDuplicate); 805 | } 806 | } 807 | 808 | #endregion 809 | 810 | #region ConvertDevicePathToDosPath 811 | 812 | private static Dictionary deviceMap; 813 | private const string networkDeviceQueryDosDevicePrefix = "\\Device\\LanmanRedirector\\"; 814 | private const string networkDeviceSystemHandlePrefix = "\\Device\\Mup\\"; 815 | private const int MAX_PATH = 260; 816 | 817 | private static string NormalizeDeviceName(string deviceName) 818 | { 819 | if (string.Compare( // if deviceName.StartsWith(networkDeviceQueryDosDevicePrefix) 820 | deviceName, 0, 821 | networkDeviceQueryDosDevicePrefix, 0, 822 | networkDeviceQueryDosDevicePrefix.Length, StringComparison.InvariantCulture) == 0) 823 | { 824 | string shareName = deviceName.Substring(deviceName.IndexOf('\\', networkDeviceQueryDosDevicePrefix.Length) + 1); 825 | return string.Concat(networkDeviceSystemHandlePrefix, shareName); 826 | } 827 | return deviceName; 828 | } 829 | 830 | private static Dictionary BuildDeviceMap() 831 | { 832 | string[] logicalDrives = Environment.GetLogicalDrives(); 833 | Dictionary localDeviceMap = new Dictionary(logicalDrives.Length); 834 | 835 | StringBuilder lpTargetPath = new StringBuilder(MAX_PATH); 836 | foreach (string drive in logicalDrives) 837 | { 838 | string lpDeviceName = drive.Substring(0, 2); 839 | 840 | QueryDosDevice(lpDeviceName, lpTargetPath, MAX_PATH); 841 | 842 | localDeviceMap.Add( 843 | NormalizeDeviceName(lpTargetPath.ToString()), 844 | lpDeviceName 845 | ); 846 | } 847 | // add a map so \\COMPUTER\ shares get picked up correctly - these will come as \Device\Mup\COMPUTER\share 848 | localDeviceMap.Add( 849 | // remove the last slash from networkDeviceSystemHandlePrefix: 850 | networkDeviceSystemHandlePrefix.Substring(0, networkDeviceSystemHandlePrefix.Length - 1), 851 | "\\"); 852 | return localDeviceMap; 853 | } 854 | 855 | private static void EnsureDeviceMap() 856 | { 857 | if (deviceMap == null) 858 | { 859 | Dictionary localDeviceMap = BuildDeviceMap(); 860 | Interlocked.CompareExchange(ref deviceMap, localDeviceMap, null); 861 | } 862 | } 863 | 864 | /// 865 | /// Converts a device path to a DOS path. Requires a trailing slash if just the device path is passed. 866 | /// Returns string.Empty if no device is found. 867 | /// 868 | /// Full path including a device. Device paths usually start with \Device\HarddiskVolume[n]\ 869 | /// DOS Path or string.Empty if none found 870 | public static string ConvertDevicePathToDosPath(string devicePath) 871 | { 872 | EnsureDeviceMap(); 873 | int i = devicePath.Length; 874 | 875 | // search in reverse, to catch network shares that are mapped before returning general network path 876 | while (i > 0 && (i = devicePath.LastIndexOf('\\', i - 1)) != -1) 877 | { 878 | string drive; 879 | if (deviceMap.TryGetValue(devicePath.Remove(i), out drive)) 880 | return string.Concat(drive, devicePath.Substring(i)); 881 | } 882 | return devicePath; 883 | } 884 | 885 | #endregion 886 | 887 | #region GetFileHandles / GetLockingProcesses 888 | 889 | /// 890 | /// Searches through all the open handles on the system, and returns handles with a path containing . 891 | /// If on a network share, should refer to the deepest mapped drive. 892 | /// 893 | /// Path to look for handles to. 894 | /// Enumerable list of handles matching 895 | internal static IEnumerable GetFileHandles(string filePath) 896 | { 897 | if (File.Exists(filePath)) 898 | filePath = new FileInfo(filePath).FullName; 899 | else if (Directory.Exists(filePath)) 900 | filePath = new DirectoryInfo(filePath).FullName; 901 | 902 | foreach (SYSTEM_HANDLE systemHandle in GetSystemHandles()) 903 | { 904 | HandleInfo handleInfo = GetHandleInfo(systemHandle, onlyGetNameFor: SYSTEM_HANDLE_TYPE.FILE); 905 | if (handleInfo.Type == SYSTEM_HANDLE_TYPE.FILE && !String.IsNullOrEmpty(handleInfo.Name)) 906 | { 907 | handleInfo.Name = ConvertDevicePathToDosPath(handleInfo.Name); 908 | if (handleInfo.Name.Contains(filePath)) 909 | yield return handleInfo; 910 | } 911 | } 912 | } 913 | 914 | /// 915 | /// Gets a list of processes locking . 916 | /// Processes that can't be retrieved by PID (if they have exited) will be excluded. 917 | /// If on a network share, should refer to the deepest mapped drive. 918 | /// 919 | /// Path to look for locking processes. 920 | /// List of processes locking . 921 | public static List GetLockingProcesses(string filePath) 922 | { 923 | List processes = new List(); 924 | foreach (HandleInfo handleInfo in GetFileHandles(filePath)) 925 | { 926 | try 927 | { 928 | Process process = Process.GetProcessById((int)handleInfo.ProcessID); 929 | processes.Add(process); 930 | } // process has exited 931 | catch (ArgumentException) { } 932 | } 933 | return processes; 934 | } 935 | 936 | #endregion 937 | 938 | #endregion 939 | } 940 | } 941 | --------------------------------------------------------------------------------