Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

System File Association

, 16 Mar 2007
This article describes the use of several custom C# classes that can be used to create, view, edit and delete Windows file associations.
/*
* Copyright (c) 2006, Brendan Grant (grantb@dahat.com)
* All rights reserved.
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
*     * All original and modified versions of this source code must include the
*       above copyright notice, this list of conditions and the following
*       disclaimer.
*     * This code may not be used with or within any modules or code that is 
*       licensed in any way that that compels or requires users or modifiers
*       to release their source code or changes as a requirement for
*       the use, modification or distribution of binary, object or source code
*       based on the licensed source code. (ex: Cannot be used with GPL code.)
*     * The name of Brendan Grant may be used to endorse or promote products
*       derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY BRENDAN GRANT ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
* EVENT SHALL BRENDAN GRANT BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

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

using System.Xml.Serialization;


namespace BrendanGrant.Helpers.FileAssociation
{

   #region Public Enums
   /// <summary>
   /// Broad categories of system recognized file format types.
   /// </summary>
   public enum PerceivedTypes
   {
      /// <summary>
      /// No 
      /// </summary>
      None,
      /// <summary>
      /// Image file
      /// </summary>
      Image,
      /// <summary>
      /// Text file
      /// </summary>
      Text,
      /// <summary>
      /// Audio file
      /// </summary>
      Audio,
      /// <summary>
      /// Video file
      /// </summary>
      Video,
      /// <summary>
      /// Compressed file
      /// </summary>
      Compressed,
      /// <summary>
      /// System file
      /// </summary>
      System,
   }
   #endregion

   /// <summary>
   /// Provides instance methods for the creation, modification, and deletion of file extension associations in the Windows registry.
   /// </summary>
   public class FileAssociationInfo
   {
      private RegistryWrapper registryWrapper = new RegistryWrapper();

      /// <summary>
      /// Gets array containing known file extensions from HKEY_CLASSES_ROOT.
      /// </summary>
      /// <returns>String array containing extensions.</returns>
      public static string[] GetExtensions()
      {
         RegistryKey root = Registry.ClassesRoot;
         List<string> extensionList = new List<string>();

         string[] subKeys = root.GetSubKeyNames();

         foreach (string subKey in subKeys)
         {
            //TODO: Consider removing dot?
            if (subKey.StartsWith("."))
            {
               extensionList.Add(subKey);
            }
         }
         return extensionList.ToArray(); ;
      }


      private string extension;

      /// <summary>
      /// Gets or sets a value that determines the MIME type of the file.
      /// </summary>
      public string ContentType
      {
         get { return GetContentType(this); }
         set { SetContentType(this, value); }
      }

      /// <summary>
      /// Gets a value indicating whether the extension exists.
      /// </summary>
      public bool Exists
      {
         get
         {
            RegistryKey root = Registry.ClassesRoot;
            try
            {
               RegistryKey key = root.OpenSubKey(extension);

               if (key == null)
                  return false;

            }
            catch (Exception ex)
            {
               Console.WriteLine(ex.ToString());

               return false;
            }

            return true;
         }
      }

      /// <summary>
      /// Gets the name of the extension.
      /// </summary>
      public string Extension
      {
         get { return extension; }
         set { extension = value; }
      }

      /// <summary>
      /// Gets or sets array of containing program file names which should be displayed in the Open With List.
      /// </summary>
      /// <example>notepad.exe, wordpad.exe, othertexteditor.exe</example>
      public string[] OpenWithList
      {
         get { return GetOpenWithList(this); }
         set { SetOpenWithList(this, value); }
      }

      /// <summary>
      /// Gets or sets a value that determines the <see cref="PerceivedType"/> of the file.
      /// </summary>
      public PerceivedTypes PerceivedType
      {
         get { return GetPerceivedType(this); }
         set { SetPerceivedType(this, value); }
      }

      /// <summary>
      /// Gets or sets a value that indicates the filter component that is used to search for text within documents of this type.
      /// </summary>
      public Guid PersistentHandler
      {
         get { return GetPersistentHandler(this); }
         set { SetPersistentHandler(this, value); }
      }

      /// <summary>
      /// Gets or set a value that indicates the name of the associated application with the behavior to handle this extension.
      /// </summary>
      [XmlAttribute()]
      public string ProgID
      {
         get { return GetProgID(this); }
         set { SetProgID(this, value); }
      }

      /// <summary>
      /// Creates the extension key.
      /// </summary>
      public void Create()
      {
         Create(this);
      }

      /// <summary>
      /// Deletes the extension key.
      /// </summary>
      public void Delete()
      {
         Delete(this);
      }

      /// <summary>
      /// Verifies that given extension exists and is associated with given program id
      /// </summary>
      /// <param name="extension">Extension to be checked for.</param>
      /// <param name="progId">progId to be checked for.</param>
      /// <returns>True if association exists, false if it does not.</returns>
      public bool IsValid(string extension, string progId)
      {
         FileAssociationInfo fai = new FileAssociationInfo(extension);

         if (!fai.Exists)
            return false;

         if (progId != fai.ProgID)
            return false;

         return true;
      }

      /// <summary>
      /// Initializes a new instance of the <see cref="FileAssociationInfo"/>FileAssociationInfo class, which acts as a wrapper for a file extension within the registry.
      /// </summary>
      /// <param name="extension">The dot prefixed extension.</param>
      /// <example>FileAssociationInfo(".mp3")
      /// FileAssociationInfo(".txt")
      /// FileAssociationInfo(".doc")</example>
      public FileAssociationInfo(string extension)
      {
         this.extension = extension;
      }

      #region Public Functions - Creators

      /// <summary>
      /// Creates actual extension association key in registry for the specified extension and supplied attributes.
      /// </summary>
      /// <param name="progId">Name of expected handling program.</param>
      /// <returns>FileAssociationInfo instance referring to specified extension.</returns>
      public FileAssociationInfo Create(string progId)
      {
         return Create(progId, PerceivedTypes.None, string.Empty, null);
      }

      /// <summary>
      /// Creates actual extension association key in registry for the specified extension and supplied attributes.
      /// </summary>
      /// <param name="progId">Name of expected handling program.</param>
      /// <param name="perceivedType"><see cref="PerceivedTypes"/>PerceivedType of file type.</param>
      /// <returns>FileAssociationInfo instance referring to specified extension.</returns>
      public FileAssociationInfo Create(string progId, PerceivedTypes perceivedType)
      {
         return Create(progId, perceivedType, string.Empty, null);
      }

      /// <summary>
      /// Creates actual extension association key in registry for the specified extension and supplied attributes.
      /// </summary>
      /// <param name="progId">Name of expected handling program.</param>
      /// <param name="perceivedType"><see cref="PerceivedTypes"/>PerceivedType of file type.</param>
      /// <param name="contentType">MIME type of file type.</param>
      /// <returns>FileAssociationInfo instance referring to specified extension.</returns>
      public FileAssociationInfo Create(string progId, PerceivedTypes perceivedType, string contentType)
      {
         return Create(progId, PerceivedTypes.None, contentType, null);
      }

      /// <summary>
      /// Creates actual extension association key in registry for the specified extension and supplied attributes.
      /// </summary>
      /// <param name="progId">Name of expected handling program.</param>
      /// <param name="perceivedType"><see cref="PerceivedTypes"/>PerceivedType of file type.</param>
      /// <param name="contentType">MIME type of file type.</param>
      /// <param name="openwithList"></param>
      /// <returns>FileAssociationInfo instance referring to specified extension.</returns>
      public FileAssociationInfo Create(string progId, PerceivedTypes perceivedType, string contentType, string[] openwithList)
      {
         FileAssociationInfo fai = new FileAssociationInfo(extension);

         if (fai.Exists)
         {
            fai.Delete();
         }

         fai.Create();
         fai.ProgID = progId;

         if (perceivedType != PerceivedTypes.None)
            fai.PerceivedType = perceivedType;

         if (contentType != string.Empty)
            fai.ContentType = contentType;

         if (openwithList != null)
            fai.OpenWithList = openwithList;

         return fai;
      }

      #endregion

      #region Private Functions - Property backend

      /// <summary>
      /// Gets array of containing program file names which should be displayed in the Open With List.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <returns>Program file names</returns>
      protected string[] GetOpenWithList(FileAssociationInfo file)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         RegistryKey root = Registry.ClassesRoot;

         RegistryKey key = root.OpenSubKey(file.extension);

         key = key.OpenSubKey("OpenWithList");

         if (key == null)
         {
            return new string[0];
         }

         return key.GetSubKeyNames();

      }

      /// <summary>
      /// Sets array of containing program file names which should be displayed in the Open With List.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <param name="programList">Program file names</param>
      protected void SetOpenWithList(FileAssociationInfo file, string[] programList)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         RegistryKey root = Registry.ClassesRoot;

         RegistryKey key = root.OpenSubKey(file.extension, true);

         RegistryKey tmpkey = key.OpenSubKey("OpenWithList", true);

         if (tmpkey != null)
         {
            key.DeleteSubKeyTree("OpenWithList");
         }

         key = key.CreateSubKey("OpenWithList");

         foreach (string s in programList)
         {
            key.CreateSubKey(s);
         }

         ShellNotification.NotifyOfChange();
      }

      /// <summary>
      /// Gets or value that determines the <see cref="PerceivedType"/>PerceivedType of the file.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <returns><see cref="PerceivedTypes"/> that specifies Perceived Type of extension.</returns>
      protected PerceivedTypes GetPerceivedType(FileAssociationInfo file)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         object val = registryWrapper.Read(file.extension, "PerceivedType");
         PerceivedTypes actualType = PerceivedTypes.None;

         if (val == null)
            return actualType;

         try
         {
            actualType = (PerceivedTypes)Enum.Parse(typeof(PerceivedTypes), val.ToString(), true);
         }
         catch (Exception ex)
         {
            Console.WriteLine(ex.ToString());
         }


         return actualType;
      }

      /// <summary>
      /// Sets a value that determines the <see cref="PerceivedType"/>PerceivedType of the file.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <param name="type"><see cref="PerceivedTypes"/> to be set that specifies Perceived Type of extension.</param>
      protected void SetPerceivedType(FileAssociationInfo file, PerceivedTypes type)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         registryWrapper.Write(file.extension, "PerceivedType", type.ToString());

         ShellNotification.NotifyOfChange();
      }


      /// <summary>
      /// Gets a value that indicates the filter component that is used to search for text within documents of this type.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <returns>Guid of filter component.</returns>
      protected Guid GetPersistentHandler(FileAssociationInfo file)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         object val = registryWrapper.Read(file.extension + "\\PersistentHandler", string.Empty);

         if (val == null)
            return new Guid();
         else
            return new Guid(val.ToString());
      }

      /// <summary>
      /// Sets a value that indicates the filter component that is used to search for text within documents of this type.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <param name="persistentHandler">Guid of filter component.</param>
      protected void SetPersistentHandler(FileAssociationInfo file, Guid persistentHandler)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         if (persistentHandler == Guid.Empty)
            return;

         this.registryWrapper.Write(file.extension + "\\" + PersistentHandler, string.Empty, persistentHandler);

         ShellNotification.NotifyOfChange();
      }

      /// <summary>
      /// Gets a value that determines the MIME type of the file.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <returns>MIME content type of extension.</returns>
      protected string GetContentType(FileAssociationInfo file)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         object val = registryWrapper.Read(file.extension, "Content Type");

         if (val == null)
         {
            return string.Empty;
         }
         else
         {
            return val.ToString();
         }
      }

      /// <summary>
      /// Sets a value that determines the MIME type of the file.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <param name="type">MIME content type of extension.</param>
      protected void SetContentType(FileAssociationInfo file, string type)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         registryWrapper.Write(file.extension, "Content Type", type);

         ShellNotification.NotifyOfChange();
      }


      /// <summary>
      /// Gets a value that indicates the name of the associated application with the behavior to handle this extension.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <returns>Associated Program ID of handling program.</returns>
      protected string GetProgID(FileAssociationInfo file)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         object val = registryWrapper.Read(file.extension, string.Empty);

         if (val == null)
            return string.Empty;

         return val.ToString();
      }

      /// <summary>
      /// Set a value that indicates the name of the associated application with the behavior to handle this extension.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> that provides specifics of the extension to be changed.</param>
      /// <param name="progId">Associated Program ID of handling program.</param>
      protected void SetProgID(FileAssociationInfo file, string progId)
      {
         if (!file.Exists)
            throw new Exception("Extension does not exist");

         registryWrapper.Write(file.extension, string.Empty, progId);

         ShellNotification.NotifyOfChange();
      }

      #endregion

      /// <summary>
      /// Creates actual file extension entry in registry.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> instance that contains specifics on extension to be created.</param>
      protected void Create(FileAssociationInfo file)
      {
         if (file.Exists)
         {
            file.Delete();
         }

         RegistryKey root = Registry.ClassesRoot;

         root.CreateSubKey(file.extension);
      }

      /// <summary>
      /// Deletes actual file extension entry in registry.
      /// </summary>
      /// <param name="file"><see cref="FileAssociationInfo"/> instance that contains specifics on extension to be deleted.</param>
      protected void Delete(FileAssociationInfo file)
      {
         if (!file.Exists)
         {
            throw new Exception("Key not found.");
         }

         RegistryKey root = Registry.ClassesRoot;

         root.DeleteSubKeyTree(file.extension);
      }
   }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

Share

About the Author

Brendan Grant
Web Developer
United States United States
No Biography provided

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.150331.1 | Last Updated 16 Mar 2007
Article Copyright 2007 by Brendan Grant
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid