using Microsoft.Win32; using System; using System.Runtime.Versioning; namespace LibationFileManager; /// Enforcement state of Windows Application Control, which on consumer PCs is Smart App Control. public enum ApplicationControlState { /// Not Windows, or the state could not be read or recognised. Unknown, Off, /// Blocking unsigned code that the reputation service does not recognise. Enforcing, /// Observing only. This mode never blocks. Evaluation, } /// /// Reads whether Windows is enforcing Application Control, so Libation can explain a blocked file /// and avoid an in-app upgrade that would leave the install unable to start. /// public static class ApplicationControlPolicy { private const string PolicyKeyPath = @"SYSTEM\CurrentControlSet\Control\CI\Policy"; private const string PolicyValueName = "VerifiedAndReputablePolicyState"; private static ApplicationControlState? _state; /// /// Read once per process. Changing the setting requires a reboot, so the answer cannot go stale /// while Libation runs. /// public static ApplicationControlState GetState() { if (_state is ApplicationControlState cached) return cached; var state = ApplicationControlState.Unknown; if (OperatingSystem.IsWindows()) { try { state = ReadWindowsState(); } catch (Exception ex) { // Covers Windows blocking Microsoft.Win32.Registry.dll itself, which is exactly the // situation this check describes, so it must not become a second failure. Serilog.Log.Logger.Debug(ex, "Could not read the Application Control policy state"); } } _state = state; return state; } /// /// True only when Windows is actively blocking. Anything unreadable or unrecognised counts as not /// blocking: the alternative is telling someone to turn Smart App Control off - which cannot be /// undone - on a PC that was never blocking anything. /// public static bool IsEnforcing => GetState() is ApplicationControlState.Enforcing; /// /// Interprets the raw VerifiedAndReputablePolicyState value. Null covers both an absent key and an /// absent value: Windows builds that never shipped Smart App Control have the key without the value. /// public static ApplicationControlState FromPolicyValue(int? policyValue) => policyValue switch { 0 => ApplicationControlState.Off, 1 => ApplicationControlState.Enforcing, 2 => ApplicationControlState.Evaluation, _ => ApplicationControlState.Unknown, }; [SupportedOSPlatform("windows")] private static ApplicationControlState ReadWindowsState() { using var key = Registry.LocalMachine.OpenSubKey(PolicyKeyPath); return FromPolicyValue(key?.GetValue(PolicyValueName) as int?); } }