• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Aldaviva / Unfucked / 23970378020

03 Apr 2026 04:19AM UTC coverage: 45.265% (-0.4%) from 45.693%
23970378020

push

github

Aldaviva
Fixed some documentation comment XML

648 of 1535 branches covered (42.21%)

1152 of 2545 relevant lines covered (45.27%)

183.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/Windows/WindowsExtensions.cs
1
using ManagedWinapi.Windows;
2
using System.ComponentModel;
3
using System.Diagnostics;
4
using System.Diagnostics.Contracts;
5
using System.Runtime.InteropServices;
6
using System.Security.Claims;
7
using System.Security.Principal;
8
using System.Windows;
9
using Unfucked.Windows;
10

11
namespace Unfucked;
12

13
/// <summary>
14
/// Methods that make it easier to work with Managed Windows API (mwinapi, https://mwinapi.sourceforge.net).
15
/// </summary>
16
public static class WindowsExtensions {
17

18
    /// <param name="window">An mwinapi <see cref="SystemWindow"/> that wraps a Win32 window.</param>
19
    extension(SystemWindow window) {
20

21
        /// <summary>
22
        /// Get the base filename, without a file extension, of the executable that is running the process for a window.
23
        /// </summary>
24
        /// <returns>Executable file basename for the process of <paramref name="window"/>, or <c>null</c> if the process has already exited.</returns>
25
        [Pure]
26
        public string? ProcessExecutableBasename {
27
            get {
28
                try {
29
                    if (SystemWindow.GetWindowThreadProcessId(window.HWnd, out int pid) != 0) {
×
30
                        try {
31
                            using Process process = Process.GetProcessById(pid);
×
32
                            return process.ProcessName;
×
33
                        } catch (InvalidOperationException) {
×
34
                            return null;
×
35
                        } catch (ArgumentException) {
×
36
                            return null;
×
37
                        }
38
                    }
39
                } catch (ArgumentException) {}
×
40

41
                return null;
×
42
            }
×
43
        }
44

45
        [DllImport("user32.dll", SetLastError = true)]
46
        private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
47

48
    }
49

50
    /// <summary>
51
    /// Convert a rectangle from mwinapi to Win32 format.
52
    /// </summary>
53
    /// <param name="mwinapiRect">A <see cref="RECT"/> from mwinapi.</param>
54
    /// <returns>A <see cref="Rect"/> with the same coordinates as <paramref name="mwinapiRect"/>.</returns>
55
    [Pure]
56
    public static Rect ToWindowsRect(this RECT mwinapiRect) {
57
        return new Rect(mwinapiRect.Left, mwinapiRect.Top, mwinapiRect.Width, mwinapiRect.Height);
×
58
    }
59

60
    /// <summary>
61
    /// Convert a rectangle from Win32 to mwinapi format.
62
    /// </summary>
63
    /// <param name="windowsRect">A <see cref="RECT"/> from Win32.</param>
64
    /// <returns>A <see cref="RECT"/> with the same coordinates as <paramref name="windowsRect"/>.</returns>
65
    [Pure]
66
    public static RECT ToMwinapiRect(this Rect windowsRect) {
67
        return new RECT((int) windowsRect.X, (int) windowsRect.Y, (int) windowsRect.Right, (int) windowsRect.Bottom);
×
68
    }
69

70
    private const string GROUP_SID     = "http://schemas.microsoft.com/ws/2008/06/identity/claims/groupsid";
71
    private const string DENY_ONLY_SID = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/denyonlysid";
72

73
    private static readonly SecurityIdentifier localAccountAndAdminMember = new("S-1-5-114");
×
74
    private static readonly SecurityIdentifier builtinAdministrators      = new(WellKnownSidType.BuiltinAdministratorsSid, null);
×
75

76
    extension(WindowsIdentity identity) {
77

78
        /// <summary>
79
        /// Determine whether the given identity represents a Windows user in the <c>Administrators</c> group, and if they are currently running elevated.
80
        /// </summary>
81
        /// <returns>
82
        /// <para><see cref="AdministratorElevation.ELEVATED_ADMIN"/> if the user is already elevated because they are a member of any administrators group (local, domain, or system), and has all of their admin permissions enabled because the identity's process is running elevated, UAC is off, or Admin Approval Mode is disabled)</para>
83
        /// <para><see cref="AdministratorElevation.UNELEVATED_ADMIN"/> if the user is not elevated but could if they wanted to, because they are a member of any administrators group, but the process this user identity came from is not running elevated</para>
84
        /// <para><see cref="AdministratorElevation.NOT_ADMIN"/> if the user is not elevated and couldn't even if they wanted to, because they are a Limited or Standard user since they are not a member of any administrators group, so they cannot elevate without a different, administrator user's credentials</para>
85
        /// </returns>
86
        [Pure]
87
        public AdministratorElevation AdministratorElevation {
88
            get {
89
                SecurityIdentifier? domainAdmins = identity.User?.AccountDomainSid is {} domainSid ? new SecurityIdentifier(WellKnownSidType.AccountDomainAdminsSid, domainSid) : null;
×
90

91
                foreach (Claim claim in identity.Claims) {
×
92
                    if (claim.Value == localAccountAndAdminMember.Value || claim.Value == builtinAdministrators.Value || (domainAdmins != null && claim.Value == domainAdmins.Value)) {
×
93
                        switch (claim.Type) {
×
94
                            case GROUP_SID:
95
                                return AdministratorElevation.ELEVATED_ADMIN;
×
96
                            case DENY_ONLY_SID:
97
                                return AdministratorElevation.UNELEVATED_ADMIN;
×
98
                        }
99
                    }
100
                }
101
                return AdministratorElevation.NOT_ADMIN;
×
102
            }
×
103
        }
104

105
    }
106

107
    private static readonly IDictionary<string, uint> CLUSTER_SIZE_CACHE = new Dictionary<string, uint>();
×
108

109
    extension(FileInfo file) {
110

111
        /// <summary>
112
        /// Get the size of a file on disk, which can be smaller than the logical <see cref="FileInfo.Length"/> if the file is compressed by NTFS or is sparse (like a partially downloaded file).
113
        /// </summary>
114
        /// <remarks>By margnus1: <see href="https://stackoverflow.com/a/3751135/979493"/></remarks>
115
        public ulong LengthOnDisk {
116
            get {
117
                uint clusterSize = CLUSTER_SIZE_CACHE.GetOrAdd(file.Directory!.Root.FullName, () => {
×
118
                    if (0 == GetDiskFreeSpaceW(file.Directory.Root.FullName, out uint sectorsPerCluster, out uint bytesPerSector, out _, out _)) throw new Win32Exception();
×
119
                    return sectorsPerCluster * bytesPerSector;
×
120
                }, out _);
×
121

122
                uint  losize = GetCompressedFileSizeW(file.FullName, out uint hisize);
×
123
                ulong size   = ((ulong) hisize << 32) | losize;
×
124
                return (size + clusterSize - 1) / clusterSize * clusterSize;
×
125
            }
126
        }
127

128
    }
129

130
    [DllImport("kernel32.dll")]
131
    private static extern uint GetCompressedFileSizeW([MarshalAs(UnmanagedType.LPWStr)] string lpFileName, out uint lpFileSizeHigh);
132

133
    [DllImport("kernel32.dll", SetLastError = true)]
134
    private static extern int GetDiskFreeSpaceW([MarshalAs(UnmanagedType.LPWStr)] string lpRootPathName, out uint lpSectorsPerCluster, out uint lpBytesPerSector, out uint lpNumberOfFreeClusters,
135
                                                out uint lpTotalNumberOfClusters);
136

137
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc