|   |  | 1 |  | using System; | 
|   |  | 2 |  | using System.Collections.Generic; | 
|   |  | 3 |  | using System.Diagnostics; | 
|   |  | 4 |  | using System.Linq; | 
|   |  | 5 |  | using System.Text; | 
|   |  | 6 |  |  | 
|   |  | 7 |  | namespace LockCheck; | 
|   |  | 8 |  |  | 
|   |  | 9 |  | /// <summary> | 
|   |  | 10 |  | /// Provides information about a process that is holding a lock on a file. | 
|   |  | 11 |  | /// </summary> | 
|   |  | 12 |  | [DebuggerDisplay("{ProcessId} {StartTime} {ExecutableName}")] | 
|   |  | 13 |  | public abstract class ProcessInfo | 
|   |  | 14 |  | { | 
|   | 2 | 15 |  |     protected ProcessInfo(int processId, DateTime startTime) | 
|   |  | 16 |  |     { | 
|   | 2 | 17 |  |         ProcessId = processId; | 
|   | 2 | 18 |  |         StartTime = startTime; | 
|   | 2 | 19 |  |     } | 
|   |  | 20 |  |  | 
|   |  | 21 |  |     /// <summary> | 
|   |  | 22 |  |     /// The process identifier of the process holding a lock on a file. | 
|   |  | 23 |  |     /// </summary> | 
|   | 2 | 24 |  |     public int ProcessId { get; } | 
|   |  | 25 |  |  | 
|   |  | 26 |  |     /// <summary> | 
|   |  | 27 |  |     /// The start time (local) of the process holding a lock o a file. | 
|   |  | 28 |  |     /// </summary> | 
|   | 2 | 29 |  |     public DateTime StartTime { get; } | 
|   |  | 30 |  |  | 
|   |  | 31 |  |     /// <summary> | 
|   |  | 32 |  |     /// The process ID of this process' parent. | 
|   |  | 33 |  |     /// </summary> | 
|   | 2 | 34 |  |     public int? ParentProcessId { get; protected set; } | 
|   |  | 35 |  |  | 
|   |  | 36 |  |     /// <summary> | 
|   |  | 37 |  |     /// The executable name of the process holding a lock. | 
|   |  | 38 |  |     /// </summary> | 
|   | 2 | 39 |  |     public string? ExecutableName { get; protected set; } | 
|   |  | 40 |  |  | 
|   |  | 41 |  |     /// <summary> | 
|   |  | 42 |  |     /// The descriptive application name, if available. Otherwise | 
|   |  | 43 |  |     /// the same as the executable name or another informative | 
|   |  | 44 |  |     /// string. | 
|   |  | 45 |  |     /// </summary> | 
|   | 2 | 46 |  |     public string? ApplicationName { get; protected set; } | 
|   |  | 47 |  |  | 
|   |  | 48 |  |     /// <summary> | 
|   |  | 49 |  |     /// The owner of the process. | 
|   |  | 50 |  |     /// </summary> | 
|   | 2 | 51 |  |     public string? Owner { get; protected set; } | 
|   |  | 52 |  |  | 
|   |  | 53 |  |     /// <summary> | 
|   |  | 54 |  |     /// The full path to the process' executable, if available. | 
|   |  | 55 |  |     /// </summary> | 
|   | 2 | 56 |  |     public string? ExecutableFullPath { get; protected set; } | 
|   |  | 57 |  |  | 
|   |  | 58 |  |     /// <summary> | 
|   |  | 59 |  |     /// The platform specific session ID of the process. | 
|   |  | 60 |  |     /// </summary> | 
|   |  | 61 |  |     /// <value> | 
|   |  | 62 |  |     /// On Windows, the Terminal Services ID. On Linux | 
|   |  | 63 |  |     /// the process' session ID. | 
|   |  | 64 |  |     /// </value> | 
|   | 2 | 65 |  |     public int SessionId { get; protected set; } | 
|   |  | 66 |  |  | 
|   |  | 67 |  |     /// <summary> | 
|   |  | 68 |  |     /// A platform specific string that specifies the type of lock, if available. | 
|   |  | 69 |  |     /// </summary> | 
|   | 2 | 70 |  |     public string? LockType { get; protected set; } | 
|   |  | 71 |  |  | 
|   |  | 72 |  |     /// <summary> | 
|   |  | 73 |  |     /// A platform specific string that specifies the mode of the lock, if available. | 
|   |  | 74 |  |     /// </summary> | 
|   | 2 | 75 |  |     public string? LockMode { get; protected set; } | 
|   |  | 76 |  |  | 
|   |  | 77 |  |     /// <summary> | 
|   |  | 78 |  |     /// A platform specific string that specifies the access lock requested, if available. | 
|   |  | 79 |  |     /// </summary> | 
|   | 2 | 80 |  |     public string? LockAccess { get; protected set; } | 
|   |  | 81 |  |  | 
|   |  | 82 |  |     /// <summary> | 
|   |  | 83 |  |     /// A platform specific flag that indicates if the curren process is critical to the system's function. | 
|   |  | 84 |  |     /// </summary> | 
|   | 2 | 85 |  |     public bool? IsCritical { get; protected set; } | 
|   |  | 86 |  |  | 
|   |  | 87 |  |     /// <summary> | 
|   |  | 88 |  |     /// A platform specific process key. On Windows this is the value also available in ETW. | 
|   |  | 89 |  |     /// </summary> | 
|   | 2 | 90 |  |     public ulong? ProcessStartKey { get; protected set; } | 
|   |  | 91 |  |  | 
|   |  | 92 |  |     public override int GetHashCode() | 
|   |  | 93 |  |     { | 
|   |  | 94 |  | #if NET | 
|   | 2 | 95 |  |         return HashCode.Combine(ProcessId, StartTime); | 
|   |  | 96 |  | #else | 
|   |  | 97 |  |         int h1 = ProcessId.GetHashCode(); | 
|   |  | 98 |  |         int h2 = StartTime.GetHashCode(); | 
|   |  | 99 |  |         return ((h1 << 5) + h1) ^ h2; | 
|   |  | 100 |  | #endif | 
|   |  | 101 |  |     } | 
|   |  | 102 |  |  | 
|   |  | 103 |  |     public override bool Equals(object? obj) | 
|   |  | 104 |  |     { | 
|   | 2 | 105 |  |         var other = obj as ProcessInfo; | 
|   | 2 | 106 |  |         if (other != null) | 
|   |  | 107 |  |         { | 
|   | 2 | 108 |  |             return other.ProcessId == ProcessId && other.StartTime == StartTime; | 
|   |  | 109 |  |         } | 
|   | 0 | 110 |  |         return false; | 
|   |  | 111 |  |     } | 
|   |  | 112 |  |  | 
|   | 2 | 113 |  |     public override string ToString() => ProcessId + "@" + StartTime.ToString("O"); | 
|   |  | 114 |  |  | 
|   |  | 115 |  |     public string ToString(string? format) | 
|   |  | 116 |  |     { | 
|   | 2 | 117 |  |         string baseFormat = ToString(); | 
|   |  | 118 |  |  | 
|   | 2 | 119 |  |         if (format != null) | 
|   |  | 120 |  |         { | 
|   | 2 | 121 |  |             if (format == "F") | 
|   |  | 122 |  |             { | 
|   | 2 | 123 |  |                 return $"{baseFormat}/{ApplicationName}"; | 
|   |  | 124 |  |             } | 
|   |  | 125 |  |         } | 
|   |  | 126 |  |  | 
|   | 2 | 127 |  |         return baseFormat; | 
|   |  | 128 |  |     } | 
|   |  | 129 |  |  | 
|   |  | 130 |  |     public static void Format(StringBuilder sb, IEnumerable<ProcessInfo> lockers, IEnumerable<string> fileNames, int? ma | 
|   |  | 131 |  |     { | 
|   | 2 | 132 |  |         if (fileNames == null) | 
|   | 2 | 133 |  |             throw new ArgumentNullException(nameof(fileNames)); | 
|   |  | 134 |  |  | 
|   | 2 | 135 |  |         if (lockers == null || !lockers.Any()) | 
|   | 2 | 136 |  |             return; | 
|   |  | 137 |  |  | 
|   | 2 | 138 |  |         int count = lockers.Count(); | 
|   | 2 | 139 |  |         int max = maxProcesses == -1 || !maxProcesses.HasValue ? int.MaxValue : maxProcesses.Value; | 
|   |  | 140 |  |  | 
|   | 2 | 141 |  |         sb.AppendFormat("File {0} locked by: ", string.Join(", ", fileNames)); | 
|   | 2 | 142 |  |         foreach (ProcessInfo locker in lockers.Take(max)) | 
|   |  | 143 |  |         { | 
|   | 2 | 144 |  |             sb.AppendLine($"[{locker.ApplicationName}, pid={locker.ProcessId}, owner={ownerOverwrite ?? locker.Owner}, s | 
|   |  | 145 |  |         } | 
|   |  | 146 |  |  | 
|   | 2 | 147 |  |         if (count > max) | 
|   |  | 148 |  |         { | 
|   | 2 | 149 |  |             sb.AppendLine($"[{count - max} more processes...]"); | 
|   |  | 150 |  |         } | 
|   | 2 | 151 |  |     } | 
|   |  | 152 |  | } |