Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C#

Open With (.NET)

4.98/5 (19 votes)
17 Jun 2014CPOL4 min read 22.3K   931  
Application selection for a File to open it with

Introduction

This is about a DLL for opening a file with an application that the user can select from a collection of applications that supports the files extension.

This DLL has a UserControl which demonstrates how it works, a WindowsForm Formular that you call with a static function and can give you the Diagnostics.Process back from the opened application like Process.Start does.

Background

I need an Open With function for a software I am currently working on, like the Windows function from the Contextmenu of a file and if the user selects an application to open with, I need the Diagnostics.Process for it to see when the application is closed to save the file back in my software. The problem was .NET does not provide as a function.

Using the Code

The base class structure looks like the following diagram:

Image 1

Class Filelink

Filelocation which is the Filepath with the execute name from an application.

Params which are the optional Params (Verbs).

PreParams are used for like DLL where rundll32.exe must be executed.

The ToString method will return a string with Filelocation, Params and PreParams for the Filelink Object.

Class cApplicationData

This class contains relevant data for an application like the Icon and Productname.

Class OpenWith

This class has a Dictionary<String, cApplication> in which you can find all applications for the extension for which you created OpenWith, Key of the Dictionary is the Productname. UserChoiceApplication is the cApplicationData for the default selection.

How We Get the Applications

You create a new Instance from the OpenWith class with a Fileextension, this class will check the Registry for the current Fileextension and create the Collection of cApplicationData.

First of all, it will check in the Registrykey "HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts".

There can be found the "UserChoice" Subkey for the default application, next check is in the Subkey "OpenWithList" for the applications for this extension.

C#
using (RegistryKey rkReg = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" + strFileExtension))
            {
                if (rkReg != null)
                {
                    //Check subkey for User choice Programm
                    using (RegistryKey rkUserChoice = rkReg.OpenSubKey("UserChoice"))
                    {
                        if (rkUserChoice != null)
                        {
                            mHaveUserChoice = true;
                            string tempUserChoiceName = (string)rkUserChoice.GetValue("ProgId");
                            tempUserChoiceName = tempUserChoiceName.Replace("Applications\\", "");

                            mProgUserChoice = new cApplicationData(tempUserChoiceName);
                            if (mProgUserChoice == null)
                            {
                                mHaveUserChoice = false;
                            }
                            rkUserChoice.Close();
                        }
                        else
                        {
                            mHaveUserChoice = false;
                        }
                    }

                    if (mProgrammList != null)
                    {
                        mProgrammList.Clear();
                    }
                    else
                    {
                        mProgrammList = new Dictionary<string, cApplicationData>();
                    }

                    //Check subkey for OpenWithList
                    using (RegistryKey rkOpenWithList = rkReg.OpenSubKey("OpenWithList"))
                    {
                        if (rkOpenWithList != null)
                        {
                            string[] strKeyArray = rkOpenWithList.GetValueNames();

                            foreach (string strKeyName in strKeyArray)
                            {
                                string tempValue = (string)rkOpenWithList.GetValue(strKeyName);
                                if (tempValue.Contains("."))
                                {
                                    cApplicationData appData = new cApplicationData(tempValue);

                                    if (appData != null && appData.Havefilelinks && !string.IsNullOrEmpty(appData.Productname))
                                    {
                                        Log.Livelog.Log(string.Format("Add New Key to Programmlist Applicationkey:'{0}'  Productname:'{1}'", tempValue, appData.Productname));
                                        mProgrammList.Add(appData.Productname, appData);
                                    }
                                }
                            }
                            rkOpenWithList.Close();
                        }
                    }

After this, we are looking in the Registrykey "HKEY_CLASSES_ROOT" which have Subkeys with Fileextensions. There we check the Subkey for the current extension "OpenWithProgids" which contains another list of applications for the extension.

C#
//Check extension in HKEY_CLASSES_ROOT
                   using (RegistryKey rkExtensionClassRoot = Registry.ClassesRoot.OpenSubKey(strFileExtension + "\\OpenWithProgids"))
                   {
                       if (rkExtensionClassRoot != null)
                       {
                           string[] strKeyArray = rkExtensionClassRoot.GetValueNames();

                           foreach (string strKeyName in strKeyArray)
                           {
                               cApplicationData appData = new cApplicationData(strKeyName);

                               if (appData != null && appData.Havefilelinks && !string.IsNullOrEmpty(appData.Productname) && !mProgrammList.ContainsKey(appData.Productname))
                               {
                                   Log.Livelog.Log(string.Format("Add New Key to Programmlist Applicationkey:'{0}' Productname:'{1}'", strKeyName, appData.Productname));
                                   mProgrammList.Add(appData.Productname, appData);
                               }
                           }
                           rkExtensionClassRoot.Close();
                       }
                   }

And the last Registry, we are looking for applications if an application supports this Fileextension and is not in our Collection. This Registry entries can be found in "HKEY_CLASSES_ROOT\Applications", each Subkey is an Application and can have the Subkey "SupportedTypes" where the Values Names are Fileextensionsnames.

C#
//Check Application Support File Extension
RegistryKey rkApplic = Registry.ClassesRoot.OpenSubKey("Applications");
if (rkApplic != null)
{
    string[] strSubKeys = rkApplic.GetSubKeyNames();
    if (strSubKeys != null)
    {
        //for each Application 
        foreach (string mApp in strSubKeys)
        {
            
                //Check for Subkey SupportedTypes
                RegistryKey rkSup = rkApplic.OpenSubKey(mApp + "\\SupportedTypes");
                if (rkSup != null)
                {
                    string[] strNames = rkSup.GetValueNames();
                    if (strNames.Contains<string>(strFileExtension))
                    {
                        //File Extension is Supported by this Application
                        cApplicationData appData = new cApplicationData(mApp);
                        
                        if (appData != null && appData.Havefilelinks && 
                        !string.IsNullOrEmpty(appData.Productname) && 
                        !mProgrammList.ContainsKey(appData.Productname))
                        {
                                Log.Livelog.Log(string.Format("Add New Key to 
                                Programmlist Applicationkey:'{0}' Productname:'{1}'", mApp, appData.Productname));
                                mProgrammList.Add(appData.Productname, appData);
                        }
                    }
                }
        }
    }
}

Create cApplicationData

Create a new Instance for cApplicationData with the Registryname of an application.

First of all, it will try to find the application in "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\".

C#
//get app path from CurrentVersion
using (RegistryKey rkAppPaths = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\" + mAppRegName))
{
    if (rkAppPaths != null)
    {
        string[] strKeyName = rkAppPaths.GetValueNames();
        if (strKeyName != null)
        {
            foreach (string mkey in strKeyName)
            {
                if (string.IsNullOrEmpty(mkey))
                {
                    string strFilelink = (string)rkAppPaths.GetValue(mkey);

                    Filelink fl = NormalizeToFilelink(strFilelink);

                    if (fl != null)
                    {
                        mOpenFileLink = fl;
                        mHaveFilelinks = true;

                        GetApplicationFileInfo(fl.Filelocation);
                        fl = null;
                    }
                    else
                    {
                        Log.Livelog.Log(string.Format("No Filelink Class for '{0}'", strFilelink));
                    }
                }
            }
        }
    }

If the application can't find it there, it checks the Subkey "HKEY_CLASSES_ROOT\Applications" and if the application also can't find it there, it checks if it is a Subkey for "HKEY_CLASSES_ROOT".

C#
string strSubkeyRoot = "Applications\\";

//If not in CurrentVersion then Check there
RegistryKey rkAppShell = Registry.ClassesRoot.OpenSubKey(strSubkeyRoot + mAppRegName + "\\shell");
if (rkAppShell == null)
{
    //try without Subkey
    if (!string.IsNullOrEmpty(strSubkeyRoot))
    {
        rkAppShell = Registry.ClassesRoot.OpenSubKey(mAppRegName + "\\shell");
    }
}

Under the Subkey "shell\command", there can be Multiple Value Names like "open", "edit", "play", "print" and "preview", for each of this names are FileLink classes where you can access the Filepath and its params. After getting a Filelink, it will try to get the IO.FileInfo and Diagnostics.FileVersionInfo if they are null. The next step is to get the Icon from the FileLink for the application.

C#
System.Drawing.Icon ico = System.Drawing.Icon.ExtractAssociatedIcon(mAppFilelink);
if (ico != null)
{
    mAppIcon = ico;
}

Controls

Image 2

ucOpenWith

This control should be loaded with the Filename that you would open with an application. ApplicationProcess is a readonly property for the Diagnostics.Process of the selected application, FormatGrid can be overridden if necessary. In the InitOpenWith method, the ProcessStart.OpenWith is called with the extension from the File to get all applications. If an application is selected, the control starts the application with the file as parameter and fires the ApplicationProcessStart Event. If the Property ShowGridToolTip is set to true, the Name Cell in the Grid can have a Tooltip with the Companyname.

frmOpenWithDialog

This Windows Form has the property ApplicationProcess which is a readonly Diagnostics.Process for the selected application. The Form should be loaded with the Filename.

ProcessOpen

With the ProcessOpen class, you can call a static method to show frmOpenWithDialog as Dialog, it has one overload where you can get the Diagnostics.Process for the selected application.

Overloads for ShowDialog

C#
/// <summary>
/// Shows OpenWith Dialog for the File
/// </summary>
/// <param name="strFullFilename">Fullfilename (Path and Filename)</param>
/// <returns>DialogResult.OK if Application selected</returns>
public static DialogResult ShowDialog(string strFullFilename)
{
    return ShowDialog(strFullFilename, "");
}

/// <summary>
/// Shows OpenWith Dialog for the File
/// </summary>
/// <param name="strFullFilename">Fullfilename (Path and Filename)</param>
/// <param name="strFormularTitle">Formular Text</param>
/// <returns>DialogResult.OK if Application selected</returns>
public static DialogResult ShowDialog(string strFullFilename, string strFormularTitle)
{
    return ShowDialog(strFullFilename, strFormularTitle, false);
}

/// <summary>
/// Shows OpenWith Dialog for the File
/// </summary>
/// <param name="strFullFilename">Fullfilename (Path and Filename)</param>
/// <param name="strFormularTitle">Formular Text</param>
/// <param name="bShowgridToolTip">Shows the Application Companyname as Grid Tooltip (default=False)</param>
/// <returns>DialogResult.OK if Application selected</returns>
public static DialogResult ShowDialog(string strFullFilename, string strFormularTitle, bool bShowgridToolTip)
{
    if (!string.IsNullOrEmpty(strFullFilename))
    {
        DialogResult dlgReturn = DialogResult.No;

        using (frmOpenWithDialog frm = new frmOpenWithDialog())
        {
            frm.Load(strFullFilename);

            if (!string.IsNullOrEmpty(strFormularTitle))
            {
                frm.Text = strFormularTitle;
            }
            if (bShowgridToolTip)
            {
                frm.ShowGridToolTip = bShowgridToolTip;
            }

            dlgReturn = frm.ShowDialog();
        }

        return dlgReturn;
    }
    else
    {
        throw new ArgumentNullException("strFullFilename");
    }
}

/// <summary>
/// Shows OpenWith Dialog for the File
/// </summary>
/// <param name="strFullFilename">Fullfilename (Path and Filename)</param>
/// <param name="prc">Diagnositcs.Process for the Applcation</param>
/// <param name="strFormularTitle">Formular Text</param>
/// <param name="bShowgridToolTip">Shows the Application Companyname as Grid Tooltip (default=False)</param>
/// <returns>DialogResult.OK if Application selected</returns>
public static DialogResult ShowDialog(string strFullFilename, out System.Diagnostics.Process prc, string strFormularTitle, bool bShowgridToolTip)
{
    if (!string.IsNullOrEmpty(strFullFilename))
    {
        DialogResult dlgReturn = DialogResult.No;

        using (frmOpenWithDialog frm = new frmOpenWithDialog())
        {
            frm.Load(strFullFilename);

            if (!string.IsNullOrEmpty(strFormularTitle))
            {
                frm.Text = strFormularTitle;
            }
            if (bShowgridToolTip)
            {
                frm.ShowGridToolTip = bShowgridToolTip;
            }

            dlgReturn = frm.ShowDialog();

            prc = frm.ApplicationProcess;
        }

        return dlgReturn;
    }
    else
    {
        throw new ArgumentNullException("strFullFilename");
    }
}

/// <summary>
/// Shows OpenWith Dialog for the File
/// </summary>
/// <param name="strFullFilename">Fullfilename (Path and Filename)</param>
/// <param name="prc">Diagnositcs.Process for the Applcation</param>
/// <param name="strFormularTitle">Formular Text</param>
/// <returns>DialogResult.OK if Application selected</returns>
public static DialogResult ShowDialog(string strFullFilename, out System.Diagnostics.Process prc, string strFormularTitle)
{
    return ShowDialog(strFullFilename, out prc, strFullFilename, false);
}

/// <summary>
/// Shows OpenWith Dialog for the File
/// </summary>
/// <param name="strFullFilename">Fullfilename (Path and Filename)</param>
/// <param name="prc">Diagnositcs.Process for the Applcation</param>
/// <returns>DialogResult.OK if Application selected</returns>
public static DialogResult ShowDialog(string strFullFilename, out System.Diagnostics.Process prc)
{
    return ShowDialog(strFullFilename,out prc, "");
}

Overloads for static calls Start

C#
/// <summary>
/// Start an Application from Filelink with File
/// </summary>
/// <param name="fl">Filelink for the Application</param>
/// <param name="File">File to open</param>
/// <returns>the Diagnostics.Process for the Application</returns>
public static Process Start(ProcessStart.Filelink fl, string File)
{
    if (fl != null)
    {
        return ProcessOpen.Start(fl.PreParams, fl.Filelocation, fl.Params, File);
    } else
    {
        throw new ArgumentNullException("fl", "ProcessStart.Filelink can't be Null when starting a Process");
    }
}

/// <summary>
/// Start an Application from the Parameter with a File
/// </summary>
/// <param name="PreParam">Preparams for the Application (rundll32)</param>
/// <param name="Path">Application Path</param>
/// <param name="Params">Application parameter</param>
/// <param name="File">File to open</param>
/// <returns>the Diagnostics.Process for the Application</returns>
public static Process Start(string PreParam, string Path, string Params, string File)
{
    string strCommandPromt = "";
    bool bHavePreParams = false;

    //needed for .dll (rundll32)
    if (!string.IsNullOrEmpty(PreParam))
    {
        strCommandPromt =  PreParam ;
        bHavePreParams = true;
    } else
    {
        strCommandPromt = Path;
    }

    if (!string.IsNullOrEmpty(Params))
    {
        if (Params.Contains("%1"))
        {
            if (bHavePreParams)
            {
                //problem with dll with qoute by the target file path
                Params = "  \"" + Path + "\"  " + " " + Params.Replace("%1", " " + File + " ");
            }
            else
            {
                Params = Params.Replace("%1", "  \"" + File + "\"  ");
            }
        }
        //Ignore other Verbs at this moment
        if (Params.Contains("%"))
        {
            //Replace % and Wildcard with Empty
            Params = System.Text.RegularExpressions.Regex.Replace(Params, @"%\d+", "");
        }
    } else
    {
        //add the file to the command
        Params = "  \"" + File + "\"";
    }

    ProcessStartInfo psi = new ProcessStartInfo(strCommandPromt);
    psi.Arguments = Params;

    return Process.Start(psi);
}

OpenWithDialog opened with a .txt file:

Image 3

OpenWithDialog opened with a .jpg file:

Image 4

Points of Interest

TODO

  • Applications which have DLL as filename are currently not supported
  • Applications with DelegateExecute are not implemented
  • Implement automatic test

At this time, this has only been tested under Windows 8.1 and Windows 7.

History

  • Version 1.0 released 17.06.2014
  • New Version 1.1 released 19.06.2014, DLL Application support, Code changing, new overloads in ProgressOpen

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)