diff --git a/src/Common/Helpers/GPOHelper.cs b/src/Common/Helpers/GPOHelper.cs
new file mode 100644
index 0000000..fec0fc6
--- /dev/null
+++ b/src/Common/Helpers/GPOHelper.cs
@@ -0,0 +1,93 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using Microsoft.Win32;
+using Serilog;
+
+namespace WindowsAdvancedSettings.Common.Helpers;
+
+public class GPOHelper
+{
+ private static readonly ILogger _log = Log.ForContext("SourceContext", nameof(GPOHelper));
+
+ private enum GpoRuleConfigured
+ {
+ WrongValue = -3, // The policy is set to an unrecognized value
+ Unavailable = -2, // Couldn't access registry
+ NotConfigured = -1, // Policy is not configured
+ Disabled = 0, // Policy is disabled
+ Enabled = 1, // Policy is enabled
+ }
+
+ // Registry path where gpo policy values are stored
+ private const string PoliciesScopeMachine = "HKEY_LOCAL_MACHINE";
+ private const string PoliciesPath = @"\SOFTWARE\Policies\Microsoft\Windows\WindowsAdvancedSettings";
+
+ // Registry value names
+ private const string PolicyConfigureEnabledWindowsAdvancedSettings = "ConfigureEnabledWindowsAdvancedSettings";
+
+ private static GpoRuleConfigured GetConfiguredValue(string registryValueName)
+ {
+ try
+ {
+ var rawValue = Registry.GetValue(
+ keyName: PoliciesScopeMachine + PoliciesPath,
+ valueName: registryValueName,
+ defaultValue: GpoRuleConfigured.NotConfigured);
+
+ _log.Debug($"Registry value {registryValueName} set to {rawValue}");
+
+ // Value will be null if the subkey specified by keyName does not exist.
+ if (rawValue == null)
+ {
+ return GpoRuleConfigured.NotConfigured;
+ }
+ else if (rawValue is not int && rawValue is not GpoRuleConfigured)
+ {
+ return GpoRuleConfigured.WrongValue;
+ }
+ else
+ {
+ return (GpoRuleConfigured)rawValue;
+ }
+ }
+ catch (System.Security.SecurityException)
+ {
+ // The user does not have the permissions required to read from the registry key.
+ return GpoRuleConfigured.Unavailable;
+ }
+ catch (System.IO.IOException)
+ {
+ // The RegistryKey that contains the specified value has been marked for deletion.
+ return GpoRuleConfigured.Unavailable;
+ }
+ catch (System.ArgumentException)
+ {
+ // keyName does not begin with a valid registry root.
+ return GpoRuleConfigured.NotConfigured;
+ }
+ }
+
+ private static bool EvaluateConfiguredValue(string registryValueName, GpoRuleConfigured defaultValue)
+ {
+ var configuredValue = GetConfiguredValue(registryValueName);
+ if (configuredValue < 0)
+ {
+ if (configuredValue != GpoRuleConfigured.NotConfigured)
+ {
+ // Only log an error if a configuration was attempted but was incorrect. NotConfigured is expected state.
+ _log.Error($"Registry value {registryValueName} set to {configuredValue}, using default {defaultValue} instead.");
+ }
+
+ configuredValue = defaultValue;
+ }
+
+ return configuredValue == GpoRuleConfigured.Enabled;
+ }
+
+ public static bool GetConfiguredEnabledWindowsAdvancedSettingsValue()
+ {
+ var defaultValue = GpoRuleConfigured.Enabled;
+ return EvaluateConfiguredValue(PolicyConfigureEnabledWindowsAdvancedSettings, defaultValue);
+ }
+}
diff --git a/src/Common/WindowsAdvancedSettings.admx b/src/Common/WindowsAdvancedSettings.admx
new file mode 100644
index 0000000..e540dc2
--- /dev/null
+++ b/src/Common/WindowsAdvancedSettings.admx
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Common/gpo/assets/WindowsAdvancedSettings.admx b/src/Common/gpo/assets/WindowsAdvancedSettings.admx
new file mode 100644
index 0000000..f50c07e
--- /dev/null
+++ b/src/Common/gpo/assets/WindowsAdvancedSettings.admx
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Common/gpo/assets/en-US/WindowsAdvancedSettings.adml b/src/Common/gpo/assets/en-US/WindowsAdvancedSettings.adml
new file mode 100644
index 0000000..13435e7
--- /dev/null
+++ b/src/Common/gpo/assets/en-US/WindowsAdvancedSettings.adml
@@ -0,0 +1,30 @@
+
+
+
+ Windows Advanced Settings
+ Windows Advanced Settings
+
+
+ Microsoft Windows Advanced Settings
+
+ Windows Advanced Settings version 0.1900.* or later
+
+
+ This policy configures the enabled state for Windows Advanced Settings.
+
+ If you enable this setting, Windows Advanced Settings will be always enabled and the user won't be able to disable it.
+
+ If you disable this setting, Windows Advanced Settings will be always disabled and the user won't be able to enable it.
+
+ If you don't configure this setting, users are able to enable or disable Windows Advanced Settings.
+
+ This policy will override any enabled state policies for individual Windows Advanced Settings features.
+
+
+ Configure Windows Advanced Settings enabled state
+
+
+
+
+
diff --git a/src/FileExplorerGitIntegration/Program.cs b/src/FileExplorerGitIntegration/Program.cs
index e63ca02..6a03df2 100644
--- a/src/FileExplorerGitIntegration/Program.cs
+++ b/src/FileExplorerGitIntegration/Program.cs
@@ -76,6 +76,13 @@ private static void AppActivationRedirected(object? sender, Microsoft.Windows.Ap
private static void HandleCOMServerActivation()
{
+ var gpoPolicyEnabled = GPOHelper.GetConfiguredEnabledWindowsAdvancedSettingsValue();
+ if (!gpoPolicyEnabled)
+ {
+ Log.Information($"Windows Advanced Settings is disabled by policy, exiting.");
+ return;
+ }
+
Log.Information($"Activating COM Server");
RepositoryCache cache = new RepositoryCache();
diff --git a/test/AdvancedSettings.Tester/ConfigureFolderPath.cs b/src/FileExplorerSourceControlIntegration/ConfigureFolderPath.cs
similarity index 52%
rename from test/AdvancedSettings.Tester/ConfigureFolderPath.cs
rename to src/FileExplorerSourceControlIntegration/ConfigureFolderPath.cs
index 2de9356..24dbabb 100644
--- a/test/AdvancedSettings.Tester/ConfigureFolderPath.cs
+++ b/src/FileExplorerSourceControlIntegration/ConfigureFolderPath.cs
@@ -1,12 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using FileExplorerSourceControlIntegration;
using Microsoft.Internal.Windows.DevHome.Helpers.FileExplorer;
using Serilog;
-using Windows.Storage;
-namespace AdvancedSettings.Tester;
+namespace FileExplorerSourceControlIntegration;
public enum ProviderType
{
@@ -14,28 +12,21 @@ public enum ProviderType
Release,
}
-internal class ConfigureFolderPath
+public class ConfigureFolderPath
{
private static readonly string _releaseProviderGuidString = "1212F95B-257E-414e-B44F-F26634BD2627";
private static readonly string _devProviderGuidString = "40FE4D6E-C9A0-48B4-A83E-AAA1D002C0D5";
- private static readonly Guid _releaseProvider = new(_releaseProviderGuidString);
- private static readonly Guid _devProvider = new(_devProviderGuidString);
-
- public static void DisplayStatus()
- {
- foreach (var folderInfo in ExtraFolderPropertiesWrapper.GetRegisteredFolderInfos())
- {
- var providerName = GetProviderName(folderInfo.HandlerClsid);
- Console.WriteLine($"{providerName}: {folderInfo.RootFolderPath} {{{folderInfo.HandlerClsid}}} {folderInfo.AppId}");
- }
- }
+ public static readonly Guid ReleaseProvider = new(_releaseProviderGuidString);
+ public static readonly Guid DevProvider = new(_devProviderGuidString);
+ public static readonly Guid CurrentProvider = typeof(SourceControlProvider).GUID;
public static void AddPath(string provider, string path)
{
try
{
var providerType = (ProviderType)Enum.Parse(typeof(ProviderType), provider, true);
- AddPath(providerType, path);
+ var providerGuid = GetProvider(providerType);
+ AddPath(providerGuid, path);
}
catch (Exception ex)
{
@@ -43,10 +34,8 @@ public static void AddPath(string provider, string path)
}
}
- public static void AddPath(ProviderType providerType, string path)
+ public static void AddPath(Guid provider, string path)
{
- var provider = GetProvider(providerType);
- Console.WriteLine($"Registering source folder: {path} for provider {providerType}");
try
{
if (!Directory.Exists(path))
@@ -79,28 +68,50 @@ public static void RemovePath(string path)
}
}
- private static Guid GetProvider(ProviderType providerType)
+ public static Guid GetProvider(ProviderType providerType)
{
return providerType switch
{
- ProviderType.Release => _releaseProvider,
- _ => _devProvider,
+ ProviderType.Release => ReleaseProvider,
+ _ => DevProvider,
};
}
- private static string GetProviderName(Guid guid)
+ public static void RemoveAllForProvider(string provider)
{
- if (guid.Equals(_devProvider))
+ try
{
- return "DEV";
+ var providerType = (ProviderType)Enum.Parse(typeof(ProviderType), provider, true);
+ var providerGuid = GetProvider(providerType);
+ RemoveAllForProvider(providerGuid);
}
- else if (guid.Equals(_releaseProvider))
+ catch (Exception ex)
{
- return "REL";
+ Log.Error(ex, $"Invalid provider: {provider}");
}
- else
+ }
+
+ public static void RemoveAllForProvider(Guid provider)
+ {
+ Log.Information($"Removing all registered folders for provider: {provider}");
+ try
+ {
+ foreach (var folderInfo in ExtraFolderPropertiesWrapper.GetRegisteredFolderInfos())
+ {
+ if (folderInfo.HandlerClsid.Equals(provider))
+ {
+ RemovePath(folderInfo.RootFolderPath);
+ }
+ }
+ }
+ catch (Exception ex)
{
- return "UNK";
+ Log.Error(ex, $"An exception occurred while enumerating the folder properties.");
}
}
+
+ public static void RemoveAllForCurrentProvider()
+ {
+ RemoveAllForProvider(typeof(SourceControlProvider).GUID);
+ }
}
diff --git a/src/FileExplorerSourceControlIntegration/Program.cs b/src/FileExplorerSourceControlIntegration/Program.cs
index cd8980e..0010bea 100644
--- a/src/FileExplorerSourceControlIntegration/Program.cs
+++ b/src/FileExplorerSourceControlIntegration/Program.cs
@@ -52,6 +52,14 @@ public static async Task Main([System.Runtime.InteropServices.WindowsRuntime.Rea
private static void HandleCOMServerActivation()
{
+ var gpoPolicyEnabled = GPOHelper.GetConfiguredEnabledWindowsAdvancedSettingsValue();
+ if (!gpoPolicyEnabled)
+ {
+ Log.Information($"Windows Advanced Settings is disabled by policy, removing all registered entries for this provider and exiting.");
+ ConfigureFolderPath.RemoveAllForCurrentProvider();
+ return;
+ }
+
Log.Information($"Activating COM Server");
using var sourceControlProviderServer = new SourceControlProviderServer();
var sourceControlProviderInstance = new SourceControlProvider();
diff --git a/test/AdvancedSettings.Tester/Program.cs b/test/AdvancedSettings.Tester/Program.cs
index f194b7b..f36256d 100644
--- a/test/AdvancedSettings.Tester/Program.cs
+++ b/test/AdvancedSettings.Tester/Program.cs
@@ -1,8 +1,10 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT License.
-using AdvancedSettings.Tester;
+using FileExplorerSourceControlIntegration;
using Microsoft.Extensions.Configuration;
+using Microsoft.Internal.Windows.DevHome.Helpers.FileExplorer;
+using Microsoft.Windows.DevHome.SDK;
using Serilog;
using Windows.Storage;
@@ -21,17 +23,24 @@ private static void Main([System.Runtime.InteropServices.WindowsRuntime.ReadOnly
if (args.Length == 0)
{
- ConfigureFolderPath.DisplayStatus();
+ DisplayStatus();
}
else if (args.Length > 2 && args[0].Equals("add", StringComparison.OrdinalIgnoreCase))
{
+ Console.WriteLine($"Registering source folder: {args[1]} for provider {args[2]}");
ConfigureFolderPath.AddPath(args[1], args[2]);
- ConfigureFolderPath.DisplayStatus();
+ DisplayStatus();
}
else if (args.Length > 1 && args[0].Equals("remove", StringComparison.OrdinalIgnoreCase))
{
+ Console.WriteLine($"Removing path {args[1]}");
ConfigureFolderPath.RemovePath(args[1]);
- ConfigureFolderPath.DisplayStatus();
+ DisplayStatus();
+ }
+ else if (args.Length > 1 && args[0].Equals("removeall", StringComparison.OrdinalIgnoreCase))
+ {
+ Console.WriteLine($"Removing all folders for provider: {args[1]}");
+ ConfigureFolderPath.RemoveAllForProvider(args[1]);
}
else
{
@@ -41,6 +50,31 @@ private static void Main([System.Runtime.InteropServices.WindowsRuntime.ReadOnly
Log.CloseAndFlush();
}
+ private static void DisplayStatus()
+ {
+ foreach (var folderInfo in ExtraFolderPropertiesWrapper.GetRegisteredFolderInfos())
+ {
+ var providerName = GetProviderName(folderInfo.HandlerClsid);
+ Console.WriteLine($"{providerName}: {folderInfo.RootFolderPath} {{{folderInfo.HandlerClsid}}} {folderInfo.AppId}");
+ }
+ }
+
+ private static string GetProviderName(Guid guid)
+ {
+ if (guid.Equals(ConfigureFolderPath.DevProvider))
+ {
+ return "DEV";
+ }
+ else if (guid.Equals(ConfigureFolderPath.ReleaseProvider))
+ {
+ return "REL";
+ }
+ else
+ {
+ return "UNK";
+ }
+ }
+
private static void DisplayHelp()
{
var help = "WASTester Usage:\n" +