mirror of
https://github.com/rmcrackan/Libation.git
synced 2026-09-13 14:17:15 -04:00
An in-app upgrade overlays files Windows has never seen. Smart App Control blocks unsigned files it does not recognise, so upgrading in place under enforcement is precisely how a working install becomes one that cannot start, which is what #1873, #1876 and #1967 all describe. Read VerifiedAndReputablePolicyState under HKLM\SYSTEM\CurrentControlSet\Control\CI\Policy to find out. The read needs no elevation and cannot raise a UAC prompt: UAC prompts only on an explicit elevation request, and HKLM is readable by standard users. Only the value 1 counts as enforcing. A missing key, a missing value, or anything unrecognised counts as not enforcing, because the cost of guessing wrong is telling someone to disable Smart App Control, which cannot be undone, on a PC that was never blocking anything. When enforcing, the upgrade notification becomes a notice with the download link instead of an update prompt, and the flow stops before downloading even if a UI ignores that. Classic honoured no such flag at all, so its dialog now takes one; its two prompt labels had to be promoted from designer locals to fields to carry the explanation. The blocked-file dialog now states the setting it found rather than asking the user to go and look, and startup logs the state, the install folder, and any cloud sync root containing it, so a report answers these without a round trip. Co-authored-by: rmcrackan <rmcrackan@gmail.com>
86 lines
2.8 KiB
C#
86 lines
2.8 KiB
C#
using Microsoft.Win32;
|
|
using System;
|
|
using System.Runtime.Versioning;
|
|
|
|
namespace LibationFileManager;
|
|
|
|
/// <summary>Enforcement state of Windows Application Control, which on consumer PCs is Smart App Control.</summary>
|
|
public enum ApplicationControlState
|
|
{
|
|
/// <summary>Not Windows, or the state could not be read or recognised.</summary>
|
|
Unknown,
|
|
Off,
|
|
/// <summary>Blocking unsigned code that the reputation service does not recognise.</summary>
|
|
Enforcing,
|
|
/// <summary>Observing only. This mode never blocks.</summary>
|
|
Evaluation,
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public static class ApplicationControlPolicy
|
|
{
|
|
private const string PolicyKeyPath = @"SYSTEM\CurrentControlSet\Control\CI\Policy";
|
|
private const string PolicyValueName = "VerifiedAndReputablePolicyState";
|
|
|
|
private static ApplicationControlState? _state;
|
|
|
|
/// <summary>
|
|
/// Read once per process. Changing the setting requires a reboot, so the answer cannot go stale
|
|
/// while Libation runs.
|
|
/// </summary>
|
|
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;
|
|
}
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
public static bool IsEnforcing => GetState() is ApplicationControlState.Enforcing;
|
|
|
|
/// <summary>
|
|
/// 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.
|
|
/// </summary>
|
|
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?);
|
|
}
|
|
}
|