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:
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.
using (RegistryKey rkReg = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" + strFileExtension))
{
if (rkReg != null)
{
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>();
}
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 Subkey
s with Fileextension
s. There we check the Subkey
for the current extension "OpenWithProgids
" which contains another list of applications for the extension.
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
.
RegistryKey rkApplic = Registry.ClassesRoot.OpenSubKey("Applications");
if (rkApplic != null)
{
string[] strSubKeys = rkApplic.GetSubKeyNames();
if (strSubKeys != null)
{
foreach (string mApp in strSubKeys)
{
RegistryKey rkSup = rkApplic.OpenSubKey(mApp + "\\SupportedTypes");
if (rkSup != null)
{
string[] strNames = rkSup.GetValueNames();
if (strNames.Contains<string>(strFileExtension))
{
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\".
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".
string strSubkeyRoot = "Applications\\";
RegistryKey rkAppShell = Registry.ClassesRoot.OpenSubKey(strSubkeyRoot + mAppRegName + "\\shell");
if (rkAppShell == null)
{
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.
System.Drawing.Icon ico = System.Drawing.Icon.ExtractAssociatedIcon(mAppFilelink);
if (ico != null)
{
mAppIcon = ico;
}
Controls
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
public static DialogResult ShowDialog(string strFullFilename)
{
return ShowDialog(strFullFilename, "");
}
public static DialogResult ShowDialog(string strFullFilename, string strFormularTitle)
{
return ShowDialog(strFullFilename, strFormularTitle, false);
}
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");
}
}
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");
}
}
public static DialogResult ShowDialog(string strFullFilename, out System.Diagnostics.Process prc, string strFormularTitle)
{
return ShowDialog(strFullFilename, out prc, strFullFilename, false);
}
public static DialogResult ShowDialog(string strFullFilename, out System.Diagnostics.Process prc)
{
return ShowDialog(strFullFilename,out prc, "");
}
Overloads for static calls Start
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");
}
}
public static Process Start(string PreParam, string Path, string Params, string File)
{
string strCommandPromt = "";
bool bHavePreParams = false;
if (!string.IsNullOrEmpty(PreParam))
{
strCommandPromt = PreParam ;
bHavePreParams = true;
} else
{
strCommandPromt = Path;
}
if (!string.IsNullOrEmpty(Params))
{
if (Params.Contains("%1"))
{
if (bHavePreParams)
{
Params = " \"" + Path + "\" " + " " + Params.Replace("%1", " " + File + " ");
}
else
{
Params = Params.Replace("%1", " \"" + File + "\" ");
}
}
if (Params.Contains("%"))
{
Params = System.Text.RegularExpressions.Regex.Replace(Params, @"%\d+", "");
}
} else
{
Params = " \"" + File + "\"";
}
ProcessStartInfo psi = new ProcessStartInfo(strCommandPromt);
psi.Arguments = Params;
return Process.Start(psi);
}
OpenWithDialog
opened with a .txt file:
OpenWithDialog
opened with a .jpg file:
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