Griffin
Griffin

Reputation: 21

Custom Url Protocol registration broken in Win11

We have a few applications that appear to have stopped working correctly since Windows 11, specifically with the removal of IE.

People can paste hyperlinks from the client software (two apps are WinForms, two are WPF) into a office document. On clicking these links, it would launch the software and by interpreting the parameters, navigate them to the record they linked.

A typical hyperlink would have an address such as: TestProject://Launchitem,Item1,e2ac1e5f-42d2-4362-b5fb-d3736619f698

Everything after the : would be passed as parameters.

The registry gets this added programmatically, on app start-up (to register the protocol):

[HKEY_CLASSES_ROOT\testProject]
"URL Protocol"=""
@="testProject: test project App Protocol"

[HKEY_CLASSES_ROOT\testProject\DefaultIcon]
@="C:\\Program Files (x86)\\Test\\AppProtocol.exe,0"

[HKEY_CLASSES_ROOT\testProject\shell]

[HKEY_CLASSES_ROOT\testProject\shell\open]

[HKEY_CLASSES_ROOT\testProject\shell\open\command]
@="C:\\Program Files (x86)\\Test\\ \"%1\""


Current User:

[HKEY_CURRENT_USER\Software\Classes\testProject]
"URL Protocol"=""
@="testProject: test project App Protocol"

[HKEY_CURRENT_USER\Software\Classes\testProject\DefaultIcon]
@="C:\\Program Files (x86)\\Test\\AppProtocol.exe,0"

[HKEY_CURRENT_USER\Software\Classes\testProject\shell]

[HKEY_CURRENT_USER\Software\Classes\testProject\shell\open]

[HKEY_CURRENT_USER\Software\Classes\testProject\shell\open\command]
@="C:\\Program Files (x86)\\Test\\ \"%1\""


This is to prevent the security warning in Office docs:

[HKEY_CURRENT_USER\Software\Policies\Microsoft\office\16.0\common\Security\Trusted Protocols\All Applications]

[HKEY_CURRENT_USER\Software\Policies\Microsoft\office\16.0\common\Security\Trusted Protocols\All Applications\testProject:]

Unfortunately, this no longer works under Windows 11 - does anybody know how to register a custom App Protocol correctly?

resulting error

This is the code that has worked for years (many samples on stack overflow suggest similar):

    internal static class AppProtocolRegistryHelper
    {
        private const string AppClassKey = @"SOFTWARE\CLASSES\testProject\";
        private const string ShellOpenCommandKey = @"shell\open\command\";
        private const string UrlEnding = " \"%1\"";
        private const string UrlProtocol = "URL Protocol";
        private const string OfficeExistsKey = @"SOFTWARE\Microsoft\Office";
        private const string OfficeRootKey = @"Software\Policies\Microsoft\Office\";
        private const string OfficePolicy = @"{0}\Common\Security\Trusted Protocols\All Applications\testProject:";

        public static void ConfigureRegistry()
        {
            RegisterUriProtocol();
            RegisterTrustedProtocols();
        }

        private static void RegisterUriProtocol()
        {
            var classKey = Registry.CurrentUser.OpenSubKey(AppClassKey, true) ??
                Registry.CurrentUser.CreateSubKey(AppClassKey);

            if (classKey == null)
                return;

            if (classKey.GetValue(UrlProtocol) == null)
                classKey.SetValue(UrlProtocol, string.Empty, RegistryValueKind.String);

            var commandKey = classKey.OpenSubKey(ShellOpenCommandKey, true) ??
                classKey.CreateSubKey(ShellOpenCommandKey);

            if (commandKey == null)
                return;

            commandKey.SetValue("", string.Format("\"{0}\" {1}", Assembly.GetExecutingAssembly().Location, UrlEnding));
        }

        private static void RegisterTrustedProtocols()
        {
            if (Registry.CurrentUser.OpenSubKey(OfficeExistsKey) == null)
                return;
            var officeKey = Registry.CurrentUser.OpenSubKey(OfficeRootKey, true) ??
                            Registry.CurrentUser.CreateSubKey(OfficeRootKey);


            using (var reg = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Office"))
            {
                if (reg != null)
                {
                    foreach (string subkey in reg.GetSubKeyNames())
                    {
                        RegisterTrustedProtocol(officeKey, subkey);
                    }
                }
            }
        }

        private static void RegisterTrustedProtocol(RegistryKey officeKey, string versionKey)
        {
            Single.TryParse(versionKey, out Single versionValue);
            if (versionValue > 0)
            {
                var versionPath = string.Format(OfficePolicy, versionKey);
                if (officeKey.OpenSubKey(versionPath) != null)
                    return;
                officeKey.CreateSubKey(versionPath);
            }
        }
    }

Upvotes: 2

Views: 589

Answers (0)

Related Questions