vbAccelerator - Contents of code file: IFilter_FileClasses.cs

using System;
using System.Collections;
using Microsoft.Win32;

namespace vbAccelerator.Components.Shell
{
   #region Public Enumerations
   /// <summary>
   /// Specifies registry hives for which ClassAssociations can be set
   /// </summary>
   public enum FileClassKeyHive : int
   {
      ClassesRoot,
      LocalMachine,
      CurrentUser
   }
   #endregion

   #region FileClassUtility
   public class FileClassUtility
   {
      public static Microsoft.Win32.RegistryKey GetRootKey(FileClassKeyHive
       baseKey)
      {
         RegistryKey rootKey = null;
         switch (baseKey)
         {
            case FileClassKeyHive.LocalMachine:
               rootKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Classes");
               break;
            case FileClassKeyHive.ClassesRoot:
               rootKey = Registry.ClassesRoot;
               break;
            case FileClassKeyHive.CurrentUser:
               rootKey = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes");
               break;
         }
         return rootKey;
      }
   }
   #endregion

   #region FileClasses
   /// <summary>
   /// A read-only collection of all the FileClasses currently installed
   /// </summary>
   public class FileClasseNames : ReadOnlyCollectionBase
   {
      private FileClassKeyHive m_baseKey;

      public string this[int index]
      {
         get
         {
            return (string)this.InnerList[index];
         }
      }

      public void Refresh()
      {
         this.InnerList.Clear();

         RegistryKey rootKey = FileClassUtility.GetRootKey(m_baseKey);
         string[] keys = rootKey.GetSubKeyNames();
         Hashtable candidateClasses = new Hashtable();
         int i = 0;
         while (!keys[i].StartsWith("."))
         {
            i++;
         }
         while (keys[i].StartsWith("."))
         {
            RegistryKey extKey = rootKey.OpenSubKey(keys[i]);            
            string candidate = (string)extKey.GetValue("", "");
            extKey.Close();
            if (candidate.Trim().Length > 0)
            {
               if (!candidateClasses.ContainsKey(candidate))
               {
                  candidateClasses.Add(candidate, keys[i]);
               }
            }
            i++;
         }
         while (i < keys.Length)
         {
            if (candidateClasses.ContainsKey(keys[i]))
            {
               this.InnerList.Add(keys[i]);
            }
            i++;
         }
         rootKey.Close();
      }

      public FileClasseNames(FileClassKeyHive baseKey)
      {
         m_baseKey = baseKey;
         Refresh();
      }
   }
   #endregion

   #region FileClass
   /// <summary>
   /// Provides an object which manages a FileClass in the registry 
   /// and each of the files associated with it
   /// </summary>
   public class FileClass : IComparable
   {
      private string m_className = "";
      private string m_defaultIcon = "";
      private bool m_isDirty = false;
      private FileClassKeyHive m_baseKey;

      private FileClassAssociations m_assoc;
      private FileClassExtensions m_extensions;

      /// <summary>
      /// Compares this object to another instance of the same type
      /// </summary>
      /// <param name="obj">FileClass to compare to</param>
      /// <returns>-1 if the item is less than this one, 0 if it is equal, 1 if
       the item is greater</returns>
      public virtual System.Int32 CompareTo ( System.Object obj )
      {
         return m_className.CompareTo( ((FileClass)obj).ClassName );
      }

      /// <summary>
      /// The base key in the registry we're working in 
      /// </summary>
      public FileClassKeyHive BaseKey
      {
         get
         {
            return m_baseKey;
         }
      }

      /// <summary>
      /// Collection of verbs associated with this class
      /// in the "shell" subsection
      /// </summary>
      public FileClassAssociations Associations
      {
         get
         {
            return this.m_assoc;
         }
      }

      public FileClassExtensions Extensions
      {
         get
         {
            return this.m_extensions;
         }
      }

      /// <summary>
      /// Gets the name of this file class
      /// </summary>
      public string ClassName
      {
         get
         {
            return this.m_className;            
         }
      }

      public string DefaultIcon
      {
         get
         {
            return this.m_defaultIcon;
         }
         set
         {
            this.m_defaultIcon = value;
            this.m_isDirty = true;
         }
      }

      /// <summary>
      /// Returns whether this object is dirty (requires saving) or not.
      /// </summary>
      public bool IsDirty
      {
         get
         {
            return this.m_isDirty;
         }
      }      


      /// <summary>
      /// Resets the object and reads all associated extensions
      /// and verbs from the registry
      /// </summary>
      public void Refresh()
      {         
         string compClassName = m_className.ToUpper();
         RegistryKey rootKey = FileClassUtility.GetRootKey(m_baseKey);
         RegistryKey classKey = rootKey.OpenSubKey(this.m_className);
         if (classKey != null)
         {
            // existing item
            RegistryKey defaultIconKey = classKey.OpenSubKey("DefaultIcon");
            if (defaultIconKey != null)
            {
               this.m_defaultIcon = (string)defaultIconKey.GetValue("", "");
            }
            else
            {
               this.m_defaultIcon = "";
            }
            this.m_isDirty = false;
         }
         else
         {
            // new item
            this.m_isDirty = true;
         }
         this.m_assoc = new FileClassAssociations(this);
         this.m_extensions = new FileClassExtensions(this);
      }

      /// <summary>
      /// saves this file class object, and all associated objects
      /// </summary>
      public void Save()
      {
         RegistryKey rootKey = FileClassUtility.GetRootKey(m_baseKey);
         
         // Class Key:
         RegistryKey classKey = rootKey.CreateSubKey(this.m_className);
         if (classKey == null)
         {
            throw new InvalidOperationException(
               String.Format("Could not create or open class key {0}",
                this.m_className));
         }
         
         // associated icon:
         if (this.m_defaultIcon.Length > 0)
         {
            RegistryKey defaultIconKey = classKey.CreateSubKey("DefaultIcon");
            if (defaultIconKey == null)
            {
               throw new InvalidOperationException(
                  String.Format("Could not create or open DefaultIcon key for
                   class key {0}", this.m_className));
            }
            defaultIconKey.SetValue("", this.m_defaultIcon);
            defaultIconKey.Close();
         }
         classKey.Close();

         // associations:
         if (this.m_assoc.IsDirty)
         {
            this.m_assoc.Save();
         }

         // file extensions:
         if (this.m_extensions.IsDirty)
         {
            this.m_extensions.Save();
         }
      }

      /// <summary>
      /// Creates a new instance of the object for the specified
      /// registry hive
      /// </summary>
      /// <param name="baseKey">Registry Hive</param>
      /// <param name="className">Class Name to create for</param>
      public FileClass(
         FileClassKeyHive baseKey,
         string className
         )
      {
         if ((className.ToUpper().Equals("CLSID")) ||
            (className.ToUpper().Equals("TYPELIB")))
         {
            throw new InvalidOperationException("Class name may not be CLSID or
             TypeLib");
         }
         this.m_baseKey = baseKey;
         this.m_className = className;
         Refresh();
      }

   }
   #endregion

   #region FileClassExtensions
   public class FileClassExtensions : CollectionBase
   {
      private FileClass m_owner = null;
      private bool m_isDirty = false;
      private Stack m_removeList = new Stack();

      /// <summary>
      /// Gets/sets whether this collection is dirty (requires saving) or not
      /// </summary>
      public bool IsDirty
      {
         get
         {
            return m_isDirty;
         }
      }
      
      /// <summary>
      /// Finds the index of the specified extension in the collection
      /// </summary>
      /// <param name="extension">Extension to search for, e.g. .cs</param>
      /// <returns>Index of the FileExtension item if found, -1
       otherwise</returns>
      public int IndexOf(string extension)
      {
         FileExtension junk = new FileExtension(this.m_owner, extension);      
            
         return this.InnerList.IndexOf(junk);
      }

      /// <summary>
      /// Gets the file extension with the specified index
      /// </summary>
      public FileExtension this[int index]
      {
         get
         {
            return (FileExtension)this.InnerList[index];
         }
      }

      /// <summary>
      /// Removes the extension with the specified index
      /// </summary>
      /// <param name="index">Index of item to remove</param>
      public void Remove(int index)
      {
         FileExtension fe = (FileExtension)this.InnerList[index];
         m_removeList.Push(fe);
         this.InnerList.RemoveAt(index);
         this.m_isDirty = true;
      }

      /// <summary>
      /// Add a new file extension to the files associated with this class
      /// </summary>
      /// <param name="fe">File extension to add</param>
      public void Add(FileExtension fe)
      {
         this.InnerList.Add(fe);
         this.m_isDirty = true;
      }

      /// <summary>
      /// Refreshes the list of extensions from the registry
      /// </summary>
      public void Refresh()
      {
         this.InnerList.Clear();
         string compClassName = this.m_owner.ClassName.ToUpper();   
         RegistryKey rootKey = FileClassUtility.GetRootKey(m_owner.BaseKey);
         foreach (string key in rootKey.GetSubKeyNames())
         {
            if (key.StartsWith("."))
            {
               RegistryKey assocKey = rootKey.OpenSubKey(key);
               string className = (string)assocKey.GetValue("", "");
               if (className.Length > 0)
               {
                  if (className.ToUpper().Equals(compClassName))
                  {
                     FileExtension assoc = new FileExtension(this.m_owner, key);
                     this.InnerList.Add(assoc);
                  }
               }
            }
         }
         this.m_isDirty = false;
      }

      public void Save()
      {
         RegistryKey rootKey = FileClassUtility.GetRootKey(m_owner.BaseKey);

         // remove any existing items:
         while (m_removeList.Count > 0)
         {
            FileExtension fe = (FileExtension)m_removeList.Pop();
            if ((fe.Extension.Trim().Length > 0) &&
             (fe.Extension.StartsWith(".")))
            {
               rootKey.DeleteSubKeyTree(fe.Extension);
            }
         }

         foreach (FileExtension fe in this.InnerList)
         {
            RegistryKey extKey = rootKey.CreateSubKey(fe.Extension);
            if (extKey == null)
            {
               throw new InvalidOperationException(
                  String.Format("Could not open or create extension key {0}",
                   fe.Extension));                  
            }
            extKey.SetValue("", m_owner.ClassName);
            extKey.Close();
         }
         
         rootKey.Close();

      }

      /// <summary>
      /// Constructs a new file extensions list for the
      /// specified file class.
      /// </summary>
      /// <param name="fileClass">Class to construct for</param>
      public FileClassExtensions(FileClass fileClass)
      {
         this.m_owner = fileClass;
         if (!m_owner.IsDirty) // not a new item
         {
            Refresh();
         }
         else
         {
            this.m_isDirty = true; // new item
         }
      }

      internal FileClassExtensions(
         FileClass fileClass,
         string[] extensions
         )
      {
         this.m_owner = fileClass;
         foreach (string ext in extensions)
         {
            FileExtension extC = new FileExtension(fileClass, ext);
            this.InnerList.Add(extC);
         }
      }
   }
   #endregion

   #region FileExtension
   /// <summary>
   /// A Class which stores details of an file extension
   /// </summary>
   public class FileExtension : IComparable
   {
      private string m_extension = "";
      private string m_displayName = "";
      private FileClass m_owner = null;
      private bool m_isDirty = false;

      public virtual System.Int32 CompareTo ( System.Object obj )
      {
         return m_extension.CompareTo( ((FileExtension)obj).Extension);
      }

      /// <summary>
      /// Gets/sets the display name for this file extension
      /// </summary>
      public string DisplayName
      {
         get
         {
            return this.m_displayName;
         }
         set
         {
            this.m_displayName = value;
            this.m_isDirty = true;
         }
      }

      public bool IsDirty
      {
         get
         {
            return this.m_isDirty;
         }
      }

      /// <summary>
      /// Gets the extension for this file extension
      /// </summary>
      public string Extension
      {
         get
         {
            return m_extension;
         }
      }

      public void Refresh()
      {
         RegistryKey rootKey = FileClassUtility.GetRootKey(m_owner.BaseKey);
         RegistryKey extensionKey = rootKey.OpenSubKey(this.m_extension);
         if (extensionKey != null)
         {
            this.m_isDirty = false;
            this.m_displayName = (string)extensionKey.GetValue("", "");
         }
         else
         {
            this.m_isDirty = true;
         }
      }

      /// <summary>
      /// Constructs a new instance of the file extension class
      /// for the specified FileExtensions collection
      /// </summary>
      /// <param name="fileClass">Owning FileClass</param>
      /// <param name="extension">Extension</param>
      public FileExtension(
         FileClass fileClass,
         string extension
         )
      {
         if (!extension.StartsWith("."))
         {
            throw new InvalidOperationException("File Extensions must start
             with '.'");
         }
         this.m_owner = fileClass;
         this.m_extension = extension;
         // always refresh, we may exist independently of the fileclass
         Refresh();
      }
   }
   #endregion


   #region FileClassAssociations
   /// <summary>
   /// Summary description for ClassAssociations.
   /// </summary>
   public class FileClassAssociations : CollectionBase
   {
      private FileClassKeyHive m_baseKey = FileClassKeyHive.LocalMachine;
      private Stack m_removeList = new Stack();
      private FileClass m_owner = null;
      private bool m_isDirty = false;

      /// <summary>
      /// Gets/sets whether this collection is dirty (requires saving) or not
      /// </summary>
      public bool IsDirty
      {
         get
         {
            return m_isDirty;
         }
      }

      /// <summary>
      /// Gets/sets the ClassAssociation at the specified index
      /// </summary>
      public FileClassAssociation this[int index]
      {
         get
         {
            return (FileClassAssociation)this.InnerList[index];
         }
         set
         {
            this.InnerList[index] = value;
            m_isDirty = true;
         }
      }

      /// <summary>
      /// Adds a new FileClassAssociation. Throws InvalidOperation
      /// if the item already exists
      /// </summary>
      /// <param name="ca">FileClassAssociation to add</param>
      /// <exception cref="InvalidOperationException">If the item already
       exists</exception>
      public void Add(FileClassAssociation ca)
      {
         foreach (FileClassAssociation a in this.InnerList)
         {
            if (ca.CompareTo(a) == 0)
            {
               throw new InvalidOperationException(
                  String.Format("The verb {0} is already a member of this
                   collection",
                  ca.Verb)
                  );
            }
         }
         this.InnerList.Add(ca);
         m_isDirty = true;
      }

      /// <summary>
      /// Returns the index of the class association with the specified verb, or
      /// -1 if it's not there
      /// </summary>
      /// <param name="verb">Verb to look for</param>
      /// <returns>Index of item with the specified verb, or -1 if it's not
       there</returns>
      public int IndexOf(string verb)
      {
         int index = -1;
         for (int i = 0; i < this.InnerList.Count; i++)
         {
            if (verb.Equals(((FileClassAssociation)this.InnerList[i]).Verb))
            {
               index = i;
               break;
            }
         }
         return index;
      }

      /// <summary>
      /// Removes an existing FileClassAssociation
      /// </summary>
      /// <param name="index">Index of association to remove</param>
      public void Remove(int index)
      {
         FileClassAssociation ca = this[index];
         this.m_removeList.Push(ca);
         this.InnerList.Remove(index);         
         m_isDirty = true;
      }

      /// <summary>
      /// Refreshes the object's data from the registry
      /// </summary>
      public void Refresh()
      {
         this.InnerList.Clear();

         RegistryKey rootKey = FileClassUtility.GetRootKey(m_baseKey);
         RegistryKey items = rootKey.OpenSubKey(m_owner.ClassName);
         
         RegistryKey shell = items.OpenSubKey("shell");
         if (shell != null)
         {
            foreach (string verb in shell.GetSubKeyNames())
            {
               RegistryKey verbKey = shell.OpenSubKey(verb);
               string menuText = (string)verbKey.GetValue("", "");
               RegistryKey commandKey = verbKey.OpenSubKey("command");
               string command = "";
               if (commandKey != null)
               {
                  command = (string)commandKey.GetValue("", "");
               }
               FileClassAssociation ca = new FileClassAssociation(
                  this, verb, menuText, command);
               this.InnerList.Add(ca);
            }
         }
         m_isDirty = false;
      }

      /// <summary>
      /// Saves the specified FileClassAssociations to the registry
      /// </summary>
      /// <exception cref="InvalidOperationException">If creating any registry
       keys fails</exception>
      public void Save()
      {
         RegistryKey rootKey = FileClassUtility.GetRootKey(m_baseKey);
         RegistryKey items = rootKey.OpenSubKey(m_owner.ClassName);
         RegistryKey shell = items.OpenSubKey("shell", true);
         if ((shell == null) && (this.InnerList.Count > 0))
         {
            // need to create the key
            shell = rootKey.CreateSubKey("shell");
            if (shell == null)
            {
               throw new InvalidOperationException("Unable to create shell
                key");
            }
         }

         while (m_removeList.Count > 0)
         {
            FileClassAssociation ca = (FileClassAssociation)m_removeList.Pop();
            shell.DeleteSubKeyTree(ca.Verb);
         }

         foreach (FileClassAssociation ca in this.InnerList)
         {
            if (ca.IsDirty)
            {
               RegistryKey verbKey = shell.CreateSubKey(ca.Verb);
               if (verbKey == null)
               {
                  throw new InvalidOperationException(
                     String.Format("Unable to create verb key {0}", ca.Verb));
               }
               if (ca.MenuText.Trim().Length > 0)
               {
                  verbKey.SetValue("", ca.MenuText);
               }
               RegistryKey commandKey = verbKey.CreateSubKey("command");
               if (commandKey == null)
               {
                  throw new InvalidOperationException(
                     String.Format("Unable to create command key for {0}",
                      ca.Command));
               }
               commandKey.SetValue("", ca.Command);
               commandKey.Close();
               verbKey.Close();
            }
         }
         
         shell.Close();
         items.Close();
         rootKey.Close();
         m_isDirty = false;
      }

      /// <summary>
      /// Returns the registry key containing this ClassAssociation's
      /// data
      /// </summary>
      /// <returns>Registry Key containing this ClassAssociation's
       data</returns>
      public Microsoft.Win32.RegistryKey RegistryKey()
      {
         RegistryKey key = FileClassUtility.GetRootKey(m_baseKey);
         return key.OpenSubKey(m_owner.ClassName);
      }

      /// <summary>
      /// Constructs a new instance of the class for the specified
      /// file class
      /// </summary>
      /// <param name="fileClass">The class to read or modify verbs in</param>
      public FileClassAssociations(FileClass fileClass)
      {
         m_owner = fileClass;
         m_baseKey = m_owner.BaseKey;
         // always refresh, we might exist regardless whether fileClass is new
         Refresh();
      }

   }
   #endregion

   #region FileClassAssociation
   /// <summary>
   /// Returns details of a command association 
   /// </summary>
   public class FileClassAssociation : IComparable
   {
      private string m_verb = "";
      private string m_menuText = "";
      private string m_command = "";
      private bool m_dirty = true;
      
      public virtual System.Int32 CompareTo ( System.Object obj )
      {
         return m_verb.CompareTo(((FileClassAssociation)obj).Verb);
      }
      public virtual System.Int32 CompareTo (FileClassAssociation ca)
      {
         return m_verb.CompareTo(ca.Verb);
      }

      /// <summary>
      /// Gets/sets the verb (the keyname in the registry) for this command.
      /// </summary>
      public string Verb
      {
         get
         {
            return this.m_verb;
         }
         set
         {
            this.m_verb = value;
            this.m_dirty = true;
         }
      }

      /// <summary>
      /// Gets/sets the menu text that will be displayed in the context menu
      /// for this command
      /// </summary>
      public string MenuText
      {
         get
         {
            return this.m_menuText;         
         }
         set
         {
            this.m_menuText = value;
            this.m_dirty = true;
         }
      }

      /// <summary>
      /// Gets/sets the command to execute
      /// </summary>
      public string Command
      {
         get
         {
            return this.m_command;
         }
         set
         {
            this.m_command = value;
            this.m_dirty = true;
         }
      }

      /// <summary>
      /// Gets/sets whether this item needs to be saved or not
      /// </summary>
      public bool IsDirty
      {
         get
         {
            return this.m_dirty;
         }
      }

      /// <summary>
      /// Creates a new class association
      /// </summary>
      /// <param name="verb">The verb (keyname in the registry) for this
       command.</param>
      /// <param name="menuText">The context menu text to display</param>
      /// <param name="command">The command to execute</param>
      public FileClassAssociation(string verb, string menuText, string command)
      {
         this.m_verb = verb;
         this.m_menuText = menuText;
         this.m_command = command;
         this.m_dirty = true;
      }

      /// <summary>
      /// Creates a existing class association associated with the 
      /// ClassAssociations Refresh method. Internal method.
      /// </summary>
      /// <param name="verb">The verb (keyname in the registry) for this
       command.</param>
      /// <param name="menuText">The context menu text to display</param>
      /// <param name="command">The command to execute</param>
      internal FileClassAssociation(FileClassAssociations owner, string verb,
       string menuText, string command)
      {
         this.m_dirty = false;
         this.m_verb = verb;
         this.m_menuText = menuText;
         this.m_command = command;
      }


   }
   #endregion

}