Search code examples
c#.netregistryclickoncewindows-shell

Open filetype (xml) with clickonce application


I have a clickonce application that is available offline and online. It is used to open and edit .xml files. It can open .xml files if their path is given as startup argument like

"c:Path\Name.appref-ms" "Xmlpath\name.xml"

using the console window.

Now i want to add a registry entry, so i can rightclick any .xml and find my application under open with. I added a key under HKCR\.xml\OpenWithProgids and a key HKCR\myapp.xml\shell\open\command but i cant figure out, how to make that command. My understanding is, that the command should be the same as the one i can use in console window, so i tried

"c:Path\Name.appref-ms" "%1"

This does not seem to work though, I also tried a lot of different uses of quotationmarks but always get

name.xml is not a valid win 32 application

as error message. Does anyone know, if what I want to do is possible and how to do it?


Additional Info:
- if i use the executable file it works, just can't seem to do it with the clickonce thingy

- the application uses the argument like this:

if(AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData!=null)
{ 
    //Do something 
}

Solution

  • First of all, this is the result, how the shell extension can look like (greyed out the other extensions): enter image description here

    You need to add a few entries into the registry. I wrote a registry helper class for it. You might either use this, or just apply the keys. You only need to adjust the names and call the method EnsureRegistryKeyIntegrity(). Note: I added a configuration switch to enable or disable the shell extension feature. This might be usefull if you don't want to blow up your shell to much.

    One important thing: You need to run the method with administrator privileges, otherwise you will receive an exception.

    internal static class RegistryHelper
    {
        ////////////////////////////////////////////////
        #region Statics
    
        /// <summary>
        /// A dictionary of registry roots and their subkeys to handle.
        /// </summary>
        private static readonly Dictionary<RegistryHive, string[]> RegistryKeys = new Dictionary<RegistryHive, string[]>
        {
            {
                RegistryHive.ClassesRoot,
                new []
                {
                    @"*\shell\QuickHash",
                    @"*\shell\QuickHash\command",
                    @"Directory\shell\QuickHash",
                    @"Directory\shell\QuickHash\command"
                }
            }
        };
    
        /// <summary>
        /// The registry default value for the command keys.
        /// </summary>
        private static readonly string RegistryCommandValue;
    
        #endregion
    
        ////////////////////////////////////////////////
        #region Constructors
    
        static RegistryHelper()
        {
            RegistryCommandValue = String.Format("\"{0}\" /file \"%1\"", Assembly.GetExecutingAssembly().Location);
        }
    
        #endregion
    
        ////////////////////////////////////////////////
        #region Public Methods
    
        /// <summary>
        /// Ensures that all required registry keys exist and adjusts their values if required.
        /// </summary>
        public static void EnsureRegistryKeyIntegrity()
        {
            foreach (var registryRoot in RegistryKeys.Keys)
            {
                foreach (var registryKeyName in RegistryKeys[registryRoot])
                {
                    if (((App)Application.Current).Config.EnableExplorerContextMenu)
                    {
                        var regKey = GetOrAddKey(registryRoot, registryKeyName);
                        AdjustKey(regKey);
                        regKey.Close();
                    }
                    else
                    {
                        DeleteKey(registryRoot, registryKeyName);
                    }
                }
            }
        }
    
        #endregion
    
        ////////////////////////////////////////////////
        #region Private Methods
    
        /// <summary>
        /// Gets or adds a specific key for a specific registry root.
        /// </summary>
        /// <param name="registryRoot">The registry root.</param>
        /// <param name="registryKeyName">The registry key.</param>
        /// <returns>Returns the gotten or added registry key.</returns>
        private static RegistryKey GetOrAddKey(RegistryHive registryRoot, string registryKeyName)
        {
            switch (registryRoot)
            {
                case RegistryHive.ClassesRoot:
                    return Registry.ClassesRoot.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.ClassesRoot.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                case RegistryHive.CurrentUser:
                    return Registry.CurrentUser.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.CurrentUser.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                case RegistryHive.LocalMachine:
                    return Registry.LocalMachine.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.LocalMachine.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                case RegistryHive.Users:
                    return Registry.Users.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.Users.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                case RegistryHive.PerformanceData:
                    return Registry.PerformanceData.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.PerformanceData.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                case RegistryHive.CurrentConfig:
                    return Registry.CurrentConfig.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.CurrentConfig.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                case RegistryHive.DynData:
                    // DynData is obsolete
                    return Registry.PerformanceData.OpenSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree, RegistryRights.SetValue) ??
                           Registry.PerformanceData.CreateSubKey(registryKeyName, RegistryKeyPermissionCheck.ReadWriteSubTree);
                default:
                    throw new ArgumentOutOfRangeException("registryRoot");
            }
        }
    
        /// <summary>
        /// Deletes an unused registry key.
        /// </summary>
        /// <param name="registryRoot">The registry root.</param>
        /// <param name="registryKeyName">The registry key.</param>
        private static void DeleteKey(RegistryHive registryRoot, string registryKeyName)
        {
            const string missingRightsText = "You don't have the permissions to perform this action.";
            const string missingRightsCaption = "Error";
            try
            {
                switch (registryRoot)
                {
                    case RegistryHive.ClassesRoot:
                        Registry.ClassesRoot.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    case RegistryHive.CurrentUser:
                        Registry.CurrentUser.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    case RegistryHive.LocalMachine:
                        Registry.LocalMachine.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    case RegistryHive.Users:
                        Registry.Users.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    case RegistryHive.PerformanceData:
                        Registry.PerformanceData.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    case RegistryHive.CurrentConfig:
                        Registry.CurrentConfig.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    case RegistryHive.DynData:
                        // DynData is obsolete
                        Registry.PerformanceData.DeleteSubKeyTree(registryKeyName, false);
                        break;
                    default:
                        throw new ArgumentOutOfRangeException("registryRoot");
                }
            }
            catch (SecurityException)
            {
                MessageBox.Show(missingRightsText, missingRightsCaption, MessageBoxButton.OK, MessageBoxImage.Error);
            }
            catch (UnauthorizedAccessException)
            {
                MessageBox.Show(missingRightsText, missingRightsCaption, MessageBoxButton.OK, MessageBoxImage.Error);
            }
        }
    
        /// <summary>
        /// Adjusts the registry keys value.
        /// </summary>
        /// <param name="regKey">The registry key to adjust.</param>
        private static void AdjustKey(RegistryKey regKey)
        {
            if (regKey.Name.EndsWith("QuickHash"))
            {
                SetExplorerShellName(regKey);
                SetExplorerShellIcon(regKey);
                return;
            }
            if (regKey.Name.EndsWith("command"))
            {
                var keyDefaultValue = regKey.GetValue("") as String;
                if (String.IsNullOrEmpty(keyDefaultValue)
                 || keyDefaultValue != RegistryCommandValue)
                {
                    regKey.SetValue(null, RegistryCommandValue, RegistryValueKind.String);
                }
                return;
            }
    
            throw new NotSupportedException("Given registry key is not supported.");
        }
    
        private static void SetExplorerShellName(RegistryKey regKey)
        {
            const string quickHashDisplayName = "Quick Hash";
            var keyDefaultValue = regKey.GetValue("") as String;
            if (String.IsNullOrEmpty(keyDefaultValue)
                || keyDefaultValue != quickHashDisplayName)
            {
                regKey.SetValue(null, quickHashDisplayName, RegistryValueKind.String);
            }
        }
    
        private static void SetExplorerShellIcon(RegistryKey regKey)
        {
            var executingAssembly = (new Uri(Assembly.GetExecutingAssembly().CodeBase)).AbsolutePath;
            regKey.SetValue("Icon", String.Format("{0},0", executingAssembly));
        }
    
        #endregion
    }