< Summary - Results for net8.0, Release

Information
Class: LockCheck.ExceptionUtils
Assembly: LockCheck
File(s): D:\a\LockCheck\LockCheck\src\LockCheck\ExceptionUtils.cs
Tag: 117_11660770947
Line coverage
86%
Covered lines: 20
Uncovered lines: 3
Coverable lines: 23
Total lines: 152
Line coverage: 86.9%
Branch coverage
77%
Covered branches: 14
Total branches: 18
Branch coverage: 77.7%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
IsFileLocked(...)25%7.46440%
RethrowWithLockingInformation(...)100%22100%
RethrowWithLockingInformation(...)91.67%1212100%

File(s)

D:\a\LockCheck\LockCheck\src\LockCheck\ExceptionUtils.cs

#LineLine coverage
 1using System;
 2using System.IO;
 3using System.Linq;
 4using System.Runtime.InteropServices;
 5using System.Text;
 6using static LockCheck.Windows.NativeMethods;
 7#if NETFRAMEWORK
 8using System.Reflection;
 9#endif
 10
 11namespace LockCheck;
 12
 13/// <summary>
 14/// Extension methods.
 15/// </summary>
 16public static class ExceptionUtils
 17{
 18#if NETFRAMEWORK
 19    // In .NET Framework the Exception.HResult property has no setter.
 20    private static readonly Lazy<MethodInfo> s_setErrorCodeMethod = new(
 21        () => typeof(Exception).GetMethod("SetErrorCode",
 22            BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
 23            null, [typeof(int)], null));
 24#endif
 25
 26    /// <summary>
 27    /// Determines if the current <see cref="IOException"/> is likely due to a locked file condition.
 28    /// </summary>
 29    /// <param name="exception">The exception to check.</param>
 30    /// <returns><c>true</c> if the <paramref name="exception"/> is due to a locked file, <c>false</c> otherwise.</retur
 31    public static bool IsFileLocked(this IOException exception)
 32    {
 233        if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
 34        {
 235            return Windows.Extensions.IsFileLocked(exception);
 36        }
 37#if NET
 038        if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
 39        {
 040            return Linux.Extensions.IsFileLocked(exception);
 41        }
 42#endif
 43
 044        return false;
 45    }
 46
 47    /// <summary>
 48    /// Throws a new <see cref="IOException"/> that contains the given exception as an inner exception,
 49    /// if the <paramref name="ex"/> is due to a file being locked. Otherwise does not throw and exception.
 50    /// </summary>
 51    /// <param name="ex">The exception to check and rethrow</param>
 52    /// <param name="fileName">The file name to check for being locked.</param>
 53    /// <param name="features">Optional features.</param>
 54    /// <example>
 55    /// <![CDATA[
 56    ///    string fileName = "C:\\temp\\foo.txt";
 57    ///    try
 58    ///    {
 59    ///        var file2 = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
 60    ///    }
 61    ///    catch (Exception ex)
 62    ///    {
 63    ///        if (!ex.RethrowWithLockingInformation(fileName))
 64    ///        {
 65    ///            // RethrowWithLockingInformation() has not rethrown the exception, because it
 66    ///            // didn't appear that "fileName" is locked. Rethrow ourselves - or do something
 67    ///            // else..
 68    ///            throw;
 69    ///        }
 70    ///    }
 71    /// ]]>
 72    /// </example>
 73    /// <returns>
 74    /// <c>false</c> when <paramref name="ex"/> has not been rethrown.
 75    /// </returns>
 76    public static bool RethrowWithLockingInformation(this Exception ex, string fileName, LockManagerFeatures features = 
 77    {
 278        if (fileName == null)
 279            throw new ArgumentNullException(nameof(fileName));
 80
 281        return RethrowWithLockingInformation(ex, [fileName], features);
 82    }
 83
 84    /// <summary>
 85    /// Throws a new <see cref="IOException"/> that contains the given exception as an inner exception,
 86    /// if the <paramref name="ex"/> is due to a file being locked. Otherwise does not throw and exception.
 87    /// </summary>
 88    /// <param name="ex">The exception to check and rethrow</param>
 89    /// <param name="fileNames">The file names to check for being locked.</param>
 90    /// <param name="features">Optional features.</param>
 91    /// <param name="maxProcesses">Maximum number of processes to include in the output.
 92    /// If this value is <c>null</c> an internal default will be applied.
 93    /// If this value is <c>-1</c> all found processes will be output.</param>
 94    /// <example>
 95    /// <![CDATA[
 96    ///    string fileName = "C:\\temp\\foo.txt";
 97    ///    try
 98    ///    {
 99    ///        var file2 = File.Open(fileName, FileMode.Open, FileAccess.ReadWrite);
 100    ///    }
 101    ///    catch (Exception ex)
 102    ///    {
 103    ///        if (!ex.RethrowWithLockingInformation(fileName))
 104    ///        {
 105    ///            // RethrowWithLockingInformation() has not rethrown the exception, because it
 106    ///            // didn't appear that "fileName" is locked. Rethrow ourselves - or do something
 107    ///            // else..
 108    ///            throw;
 109    ///        }
 110    ///    }
 111    /// ]]>
 112    /// </example>
 113    /// <returns>
 114    /// <c>false</c> when <paramref name="ex"/> has not been rethrown.
 115    /// </returns>
 116    public static bool RethrowWithLockingInformation(this Exception ex, string[] fileNames, LockManagerFeatures features
 117    {
 2118        if (fileNames == null)
 2119            throw new ArgumentNullException(nameof(fileNames));
 120
 2121        if (fileNames.Length > 0)
 122        {
 2123            if (ex is IOException ioEx && ioEx.IsFileLocked())
 124            {
 125                // It is a race to get the lockers, while they are still there. So do this as early as possible.
 2126                var lockers = LockManager.GetLockingProcessInfos(fileNames, features);
 127
 2128                if (lockers.Any())
 129                {
 130                    // Alter behavior of Format(). It would return int.MaxValue on null also.
 131                    // Here we want to return a baked in default in this case.
 2132                    int max = maxProcesses == -1 ? int.MaxValue : maxProcesses.GetValueOrDefault(10);
 2133                    var sb = new StringBuilder();
 2134                    sb.Append(ex.Message);
 2135                    sb.Append(' ');
 2136                    ProcessInfo.Format(sb, lockers, fileNames, max);
 137
 2138                    var exception = new IOException(sb.ToString(), ex);
 139#if NETFRAMEWORK
 140                    s_setErrorCodeMethod.Value?.Invoke(exception, [ex.HResult]);
 141#endif
 142#if NET
 2143                    exception.HResult = ex.HResult;
 144#endif
 145
 2146                    throw exception;
 147                }
 148            }
 149        }
 2150        return false;
 151    }
 152}