c# default application - how to get the file content? [duplicate] - c#

I'd like to to associate a file extension to the current executable in C#.
This way when the user clicks on the file afterwards in explorer, it'll run my executable with the given file as the first argument.
Ideally it'd also set the icon for the given file extensions to the icon for my executable.
Thanks all.

There doesn't appear to be a .Net API for directly managing file associations but you can use the Registry classes for reading and writing the keys you need to.
You'll need to create a key under HKEY_CLASSES_ROOT with the name set to your file extension (eg: ".txt"). Set the default value of this key to a unique name for your file type, such as "Acme.TextFile". Then create another key under HKEY_CLASSES_ROOT with the name set to "Acme.TextFile". Add a subkey called "DefaultIcon" and set the default value of the key to the file containing the icon you wish to use for this file type. Add another sibling called "shell". Under the "shell" key, add a key for each action you wish to have available via the Explorer context menu, setting the default value for each key to the path to your executable followed by a space and "%1" to represent the path to the file selected.
For instance, here's a sample registry file to create an association between .txt files and EmEditor:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\.txt]
#="emeditor.txt"
[HKEY_CLASSES_ROOT\emeditor.txt]
#="Text Document"
[HKEY_CLASSES_ROOT\emeditor.txt\DefaultIcon]
#="%SystemRoot%\\SysWow64\\imageres.dll,-102"
[HKEY_CLASSES_ROOT\emeditor.txt\shell]
[HKEY_CLASSES_ROOT\emeditor.txt\shell\open]
[HKEY_CLASSES_ROOT\emeditor.txt\shell\open\command]
#="\"C:\\Program Files\\EmEditor\\EMEDITOR.EXE\" \"%1\""
[HKEY_CLASSES_ROOT\emeditor.txt\shell\print]
[HKEY_CLASSES_ROOT\emeditor.txt\shell\print\command]
#="\"C:\\Program Files\\EmEditor\\EMEDITOR.EXE\" /p \"%1\""

Also, if you decide to go the registry way, keep in mind that current user associations are under HKEY_CURRENT_USER\Software\Classes. It might be better to add your application there instead of local machine classes.
If your program will be run by limited users, you won't be able to modify CLASSES_ROOT anyway.

If you use ClickOnce deployment, this is all handled for you (at least, in VS2008 SP1); simply:
Project Properties
Publish
Options
File Associatons
(add whatever you need)
(note that it must be full-trust, target .NET 3.5, and be set for offline usage)
See also MSDN: How to: Create File Associations For a ClickOnce Application

Here's a complete example:
public class FileAssociation
{
public string Extension { get; set; }
public string ProgId { get; set; }
public string FileTypeDescription { get; set; }
public string ExecutableFilePath { get; set; }
}
public class FileAssociations
{
// needed so that Explorer windows get refreshed after the registry is updated
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);
private const int SHCNE_ASSOCCHANGED = 0x8000000;
private const int SHCNF_FLUSH = 0x1000;
public static void EnsureAssociationsSet()
{
var filePath = Process.GetCurrentProcess().MainModule.FileName;
EnsureAssociationsSet(
new FileAssociation
{
Extension = ".binlog",
ProgId = "MSBuildBinaryLog",
FileTypeDescription = "MSBuild Binary Log",
ExecutableFilePath = filePath
},
new FileAssociation
{
Extension = ".buildlog",
ProgId = "MSBuildStructuredLog",
FileTypeDescription = "MSBuild Structured Log",
ExecutableFilePath = filePath
});
}
public static void EnsureAssociationsSet(params FileAssociation[] associations)
{
bool madeChanges = false;
foreach (var association in associations)
{
madeChanges |= SetAssociation(
association.Extension,
association.ProgId,
association.FileTypeDescription,
association.ExecutableFilePath);
}
if (madeChanges)
{
SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSH, IntPtr.Zero, IntPtr.Zero);
}
}
public static bool SetAssociation(string extension, string progId, string fileTypeDescription, string applicationFilePath)
{
bool madeChanges = false;
madeChanges |= SetKeyDefaultValue(#"Software\Classes\" + extension, progId);
madeChanges |= SetKeyDefaultValue(#"Software\Classes\" + progId, fileTypeDescription);
madeChanges |= SetKeyDefaultValue($#"Software\Classes\{progId}\shell\open\command", "\"" + applicationFilePath + "\" \"%1\"");
return madeChanges;
}
private static bool SetKeyDefaultValue(string keyPath, string value)
{
using (var key = Registry.CurrentUser.CreateSubKey(keyPath))
{
if (key.GetValue(null) as string != value)
{
key.SetValue(null, value);
return true;
}
}
return false;
}

There may be specific reasons why you choose not to use an install package for your project but an install package is a great place to easily perform application configuration tasks such registering file extensions, adding desktop shortcuts, etc.
Here's how to create file extension association using the built-in Visual Studio Install tools:
Within your existing C# solution, add a new project and select project type as Other Project Types -> Setup and Deployment -> Setup Project (or try the Setup Wizard)
Configure your installer (plenty of existing docs for this if you need help)
Right-click the setup project in the Solution explorer, select View -> File Types, and then add the extension that you want to register along with the program to run it.
This method has the added benefit of cleaning up after itself if a user runs the uninstall for your application.

To be specific about the "Windows Registry" way:
I create keys under HKEY_CURRENT_USER\Software\Classes (like Ishmaeel said)
and follow the instruction answered by X-Cubed.
The sample code looks like:
private void Create_abc_FileAssociation()
{
/***********************************/
/**** Key1: Create ".abc" entry ****/
/***********************************/
Microsoft.Win32.RegistryKey key1 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software", true);
key1.CreateSubKey("Classes");
key1 = key1.OpenSubKey("Classes", true);
key1.CreateSubKey(".abc");
key1 = key1.OpenSubKey(".abc", true);
key1.SetValue("", "DemoKeyValue"); // Set default key value
key1.Close();
/*******************************************************/
/**** Key2: Create "DemoKeyValue\DefaultIcon" entry ****/
/*******************************************************/
Microsoft.Win32.RegistryKey key2 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software", true);
key2.CreateSubKey("Classes");
key2 = key2.OpenSubKey("Classes", true);
key2.CreateSubKey("DemoKeyValue");
key2 = key2.OpenSubKey("DemoKeyValue", true);
key2.CreateSubKey("DefaultIcon");
key2 = key2.OpenSubKey("DefaultIcon", true);
key2.SetValue("", "\"" + "(The icon path you desire)" + "\""); // Set default key value
key2.Close();
/**************************************************************/
/**** Key3: Create "DemoKeyValue\shell\open\command" entry ****/
/**************************************************************/
Microsoft.Win32.RegistryKey key3 = Microsoft.Win32.Registry.CurrentUser.OpenSubKey("Software", true);
key3.CreateSubKey("Classes");
key3 = key3.OpenSubKey("Classes", true);
key3.CreateSubKey("DemoKeyValue");
key3 = key3.OpenSubKey("DemoKeyValue", true);
key3.CreateSubKey("shell");
key3 = key3.OpenSubKey("shell", true);
key3.CreateSubKey("open");
key3 = key3.OpenSubKey("open", true);
key3.CreateSubKey("command");
key3 = key3.OpenSubKey("command", true);
key3.SetValue("", "\"" + "(The application path you desire)" + "\"" + " \"%1\""); // Set default key value
key3.Close();
}
Just show you guys a quick demo, very easy to understand. You could modify those key values and everything is good to go.

The code below is a function the should work, it adds the required values in the windows registry. Usually i run SelfCreateAssociation(".abc") in my executable. (form constructor or onload or onshown) It will update the registy entry for the current user, everytime the executable is executed. (good for debugging, if you have some changes).
If you need detailed information about the registry keys involved check out this MSDN link.
https://msdn.microsoft.com/en-us/library/windows/desktop/dd758090(v=vs.85).aspx
To get more information about the general ClassesRoot registry key. See this MSDN article.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms724475(v=vs.85).aspx
public enum KeyHiveSmall
{
ClassesRoot,
CurrentUser,
LocalMachine,
}
/// <summary>
/// Create an associaten for a file extension in the windows registry
/// CreateAssociation(#"vendor.application",".tmf","Tool file",#"C:\Windows\SYSWOW64\notepad.exe",#"%SystemRoot%\SYSWOW64\notepad.exe,0");
/// </summary>
/// <param name="ProgID">e.g. vendor.application</param>
/// <param name="extension">e.g. .tmf</param>
/// <param name="description">e.g. Tool file</param>
/// <param name="application">e.g. #"C:\Windows\SYSWOW64\notepad.exe"</param>
/// <param name="icon">#"%SystemRoot%\SYSWOW64\notepad.exe,0"</param>
/// <param name="hive">e.g. The user-specific settings have priority over the computer settings. KeyHive.LocalMachine need admin rights</param>
public static void CreateAssociation(string ProgID, string extension, string description, string application, string icon, KeyHiveSmall hive = KeyHiveSmall.CurrentUser)
{
RegistryKey selectedKey = null;
switch (hive)
{
case KeyHiveSmall.ClassesRoot:
Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(extension).SetValue("", ProgID);
selectedKey = Microsoft.Win32.Registry.ClassesRoot.CreateSubKey(ProgID);
break;
case KeyHiveSmall.CurrentUser:
Microsoft.Win32.Registry.CurrentUser.CreateSubKey(#"Software\Classes\" + extension).SetValue("", ProgID);
selectedKey = Microsoft.Win32.Registry.CurrentUser.CreateSubKey(#"Software\Classes\" + ProgID);
break;
case KeyHiveSmall.LocalMachine:
Microsoft.Win32.Registry.LocalMachine.CreateSubKey(#"Software\Classes\" + extension).SetValue("", ProgID);
selectedKey = Microsoft.Win32.Registry.LocalMachine.CreateSubKey(#"Software\Classes\" + ProgID);
break;
}
if (selectedKey != null)
{
if (description != null)
{
selectedKey.SetValue("", description);
}
if (icon != null)
{
selectedKey.CreateSubKey("DefaultIcon").SetValue("", icon, RegistryValueKind.ExpandString);
selectedKey.CreateSubKey(#"Shell\Open").SetValue("icon", icon, RegistryValueKind.ExpandString);
}
if (application != null)
{
selectedKey.CreateSubKey(#"Shell\Open\command").SetValue("", "\"" + application + "\"" + " \"%1\"", RegistryValueKind.ExpandString);
}
}
selectedKey.Flush();
selectedKey.Close();
}
/// <summary>
/// Creates a association for current running executable
/// </summary>
/// <param name="extension">e.g. .tmf</param>
/// <param name="hive">e.g. KeyHive.LocalMachine need admin rights</param>
/// <param name="description">e.g. Tool file. Displayed in explorer</param>
public static void SelfCreateAssociation(string extension, KeyHiveSmall hive = KeyHiveSmall.CurrentUser, string description = "")
{
string ProgID = System.Reflection.Assembly.GetExecutingAssembly().EntryPoint.DeclaringType.FullName;
string FileLocation = System.Reflection.Assembly.GetExecutingAssembly().Location;
CreateAssociation(ProgID, extension, description, FileLocation, FileLocation + ",0", hive);
}

The file associations are defined in the registry under HKEY_CLASSES_ROOT.
There's a VB.NET example here that I'm you can port easily to C#.

There are two cmd tools that have been around since Windows 7 which make it very easy to create simple file associations. They are assoc and ftype. Here's a basic explanation of each command.
Assoc - associates a file extension (like '.txt') with a "file type."
FType - defines an executable to run when the user opens a given "file type."
Note that these are cmd tools and not executable files (exe). This means that they can only be run in a cmd window, or by using ShellExecute with "cmd /c assoc." You can learn more about them at the links or by typing "assoc /?" and "ftype /?" at a cmd prompt.
So to associate an application with a .bob extension, you could open a cmd window (WindowKey+R, type cmd, press enter) and run the following:
assoc .bob=BobFile
ftype BobFile=c:\temp\BobView.exe "%1"
This is much simpler than messing with the registry and it is more likely to work in future windows version.
Wrapping it up, here is a C# function to create a file association:
public static int setFileAssociation(string[] extensions, string fileType, string openCommandString) {
int v = execute("cmd", "/c ftype " + fileType + "=" + openCommandString);
foreach (string ext in extensions) {
v = execute("cmd", "/c assoc " + ext + "=" + fileType);
if (v != 0) return v;
}
return v;
}
public static int execute(string exeFilename, string arguments) {
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.CreateNoWindow = false;
startInfo.UseShellExecute = true;
startInfo.FileName = exeFilename;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.Arguments = arguments;
try {
using (Process exeProcess = Process.Start(startInfo)) {
exeProcess.WaitForExit();
return exeProcess.ExitCode;
}
} catch {
return 1;
}
}

Related

How to fix: Application Icon overrides File Icon

I have written a WPF application, running on Windows 10. I have two icons: One for the application, one for the associated file type (.rwa). When I install this application I use the register to set the file association, pointing to my File Icon as DefaultIcon. This all works and the shortcut to my application shows the App Icon.
I can double click on a .rwa file Windows asks me what application to use and I select my application. However, as soon as I have done this the file icons change to the application icon!
EDIT: There is extensive information on Microsoft site https://learn.microsoft.com/en-us/windows/desktop/shell/customizing-file-types-bumper on file types, but they mention it does not work for Windows 10, at least after a certain version. Therefore, I am not sure if the changes in the registry I make are correct.
This is what I do:
/// <summary>Makes the association.</summary>
/// <param name="extension">The extension to associate.</param>
/// <param name="progID">The program identifier.</param>
/// <param name="description">The description.</param>
/// <param name="icon">The full path to the icon.</param>
/// <param name="application">The name of the application.</param>
/// <param name="exe">The full path to the executable.</param>
public static void MakeAssocisation(string extension,
string progID, string description, string icon, string application, string exe)
{
using (var User_Classes = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\", true))
using (var CurrentVersion = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\", true))
using (var User_Explorer = CurrentVersion.CreateSubKey("Explorer\\FileExts\\." + extension))
{
string applicationPath = application;
// Register the application
using (var UserClassesApplications = CurrentVersion.CreateSubKey("App Paths"))
{
UserClassesApplications.CreateSubKey(exe).SetValue("", application);
}
// Create ProgID
if (!string.IsNullOrEmpty(progID))
{
using (var progId_key = User_Classes.CreateSubKey(progID))
{
progId_key.SetValue("", description);
progId_key.CreateSubKey("DefaultIcon").SetValue("", "\"" + icon + "\"");
}
}
// Now the extension
using (var User_Ext = User_Classes.CreateSubKey("." + extension))
{
User_Ext.SetValue("", progID);
User_Ext.CreateSubKey("DefaultIcon").SetValue("", "\"" + icon + "\"");
}
User_Explorer.CreateSubKey("OpenWithProgids").SetValue(progID, "0");
using (RegistryKey User_Choice = User_Explorer.OpenSubKey("UserChoice"))
{
if (User_Choice != null) User_Explorer.DeleteSubKey("UserChoice");
}
SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
}
When I have made the registry changes I find that the .exe is correctly showing the App Icon and the associated files (.rwa) show the correct icon. If I start the application (e.g., clicking on the short cut) then everything works correctly. However, when I double click a .rwa file I get asked with which application to open (this is what is different in Windows 10; it does not use the association defined in the registry). I select my application and it correctly starts it up but it also changes the icon of my .rwa files!
Panagiotis was right: In my setting of the Register, I missed some information. After some experimentation and by looking at other applications in the registry I came up with the following working code:
/// <summary>Makes the association.</summary>
/// <param name="extension">The extension to associate.</param>
/// <param name="progID">The program identifier.</param>
/// <param name="description">The description.</param>
/// <param name="icon">The full path to the icon.</param>
/// <param name="application">The full path to the executable.</param>
/// <param name="exe">The executable.</param>
public static void MakeAssociation(string extension,
string progID, string description, string icon, string application, string exe)
{
using (var User_Classes = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\", true))
using (var CurrentVersion = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\", true))
using (var User_Explorer = CurrentVersion.CreateSubKey("Explorer\\FileExts\\." + extension))
{
// Register the application
using (var UserClassesApplications = CurrentVersion.CreateSubKey("App Paths"))
{
UserClassesApplications.CreateSubKey(exe).SetValue("", application);
}
// Create ProgID
if (!string.IsNullOrEmpty(progID))
{
using (var progId_key = User_Classes.CreateSubKey(progID))
{
progId_key.SetValue("", description);
using (var command = progId_key.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command"))
{
command.SetValue("", "\"" + application + "\" \"%1\"");
}
progId_key.CreateSubKey("DefaultIcon").SetValue("", "\"" + icon + "\"");
}
}
// Now the extension
using (var User_Ext = User_Classes.CreateSubKey("." + extension))
{
User_Ext.SetValue("", progID);
User_Ext.CreateSubKey("DefaultIcon").SetValue("", "\"" + icon + "\"");
}
User_Explorer.CreateSubKey("OpenWithProgids").SetValue(progID, "0");
using (RegistryKey User_Choice = User_Explorer.OpenSubKey("UserChoice"))
{
if (User_Choice != null) User_Explorer.DeleteSubKey("UserChoice");
User_Explorer.CreateSubKey("UserChoice").SetValue("ProgId", progID);
}
SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}
}
I also found that the path parameters could not include things like %LocalAppData%. It had to be the expanded path.

External Tools Visual Studio

I created an application to run, but build an External Tool from the application for Visual Studio. Up until 15.4.0 the application worked fine, but they modified the registry. Based on Microsoft's changes, the External Tools are saved under this sub key:
SOFTWARE\Microsoft\VisualStudio\14.0_Config\External Tools
When you observe the key, you clearly see folders (sub keys) of tools, Guid, Error Lookup, and a couple others that are included with Visual Studio. The issue though, if you manually create the tool in Visual Studio the key information is dumped directly to the root External Tool key.
So if I do either of these approaches:
var user = RegistryKey.OpenBaseKey(RegistryHive.CurrenUser, RegistryView.Default);
var tools = user.OpenSubKey(#"SOFTWARE\Microsoft\VisualStudio\14.0_Config\External Tools", true);
var path = Environment.GetCommandLineArgs()[0];
tools.SetValue("ToolArg", "$(ItemPath)");
tools.SetValue("ToolCmd", path);
tools.SetValue("ToolDir", String.Empty);
tools.SetValue("ToolOpt", 0x12);
tools.SetValue("ToolsSourceKey", String.Empty);
tools.SetValue("ToolTitle", "My Tool");
var user = RegistryKey.OpenBaseKey(RegistryHive.CurrenUser, RegistryView.Default);
var tools = user.OpenSubKey(#"SOFTWARE\Microsoft\VisualStudio\14.0_Config\External Tools", true);
var sub = tools.CreateSubKey("MyTool");
var path = Environment.GetCommandLineArgs()[0];
sub.SetValue("ToolArg", "$(ItemPath)");
sub.SetValue("ToolCmd", path);
sub.SetValue("ToolDir", String.Empty);
sub.SetValue("ToolOpt", 0x12);
sub.SetValue("ToolsSourceKey", String.Empty);
sub.SetValue("ToolTitle", "My Tool");
The tool doesn't appear in the list, or toolbar. Is there something different for Visual Studio 2017 15.5.* that makes this no longer work from code? To make matters worst, the key doesn't always appear when created in Visual Studio 2017 manually.
In Visual Studio 2017, External Tools are stored in a private registry hive in the user's local application data folder. If you run Sysinternals Process Monitor tool, you'll see Visual Studio reading/writing to a key that starts with \REGISTRY\A\ - that's how you know it's a private registry hive. To update them, you will need to load that registry hive by P/Invoking RegLoadAppKey and attaching to the resulting handle. An example of that can be found here:
RegLoadAppKey working fine on 32-bit OS, failing on 64-bit OS, even if both processes are 32-bit
The title may seem misleading at first, but the example given in the question shows exactly how to call RegLoadAppKey and open a sub key underneath.
The next thing you'll have to contend with is finding the private registry hive. Visual Studio stores the private registry hive in a sub folder of the user's local application data folder. The sub folder name will start with Microsoft\VisualStudio\15.0_ and will then be followed by a 32-bit hexadecimal value. I'm not really sure what that value is, or how to gracefully discover it. It's different for each user. My approach was to select the newest folder that starts with "15.0" and assume it's correct. If someone has a better way to identify this folder, I would love to see it.
I've dubbed the combination of version number and hexadecimal string a "version tag". You'll need to keep track of it, because the version tag will be used again as a sub key in the private registry hive.
Putting it all together, I created a VisualStudioContext class that locates the private registry hive and loads the root key.
public class VisualStudioContext : IDisposable
{
public string VersionTag { get; }
public string UserFolder { get; }
public string PrivateRegistryPath { get; }
public SafeRegistryHandle RegistryHandle { get; }
public RegistryKey RootKey { get; }
private static readonly Lazy<VisualStudioContext> LazyInstance = new Lazy<VisualStudioContext>(() => new VisualStudioContext());
public static VisualStudioContext Instance => LazyInstance.Value;
private VisualStudioContext()
{
try
{
string localAppDataFolder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
string vsFolder = $"{localAppDataFolder}\\Microsoft\\VisualStudio";
var vsFolderInfo = new DirectoryInfo(vsFolder);
DateTime lastDateTime = DateTime.MinValue;
foreach (DirectoryInfo dirInfo in vsFolderInfo.GetDirectories("15.0_*"))
{
if (dirInfo.CreationTime <= lastDateTime)
continue;
UserFolder = dirInfo.FullName;
lastDateTime = dirInfo.CreationTime;
}
if (UserFolder == null)
throw new Exception($"No Visual Studio folders found in \"{vsFolder}\"");
}
catch (Exception ex)
{
throw new Exception("Unable to open Visual Studio folder", ex);
}
VersionTag = Path.GetFileName(UserFolder);
PrivateRegistryPath = $"{UserFolder}\\privateregistry.bin";
int handle = RegistryNativeMethods.RegLoadAppKey(PrivateRegistryPath);
RegistryHandle = new SafeRegistryHandle(new IntPtr(handle), true);
RootKey = RegistryKey.FromHandle(RegistryHandle);
}
public void Dispose()
{
RootKey?.Close();
RegistryHandle?.Dispose();
}
public class Exception : ApplicationException
{
public Exception(string message) : base(message)
{
}
public Exception(string message, Exception innerException) : base(message, innerException)
{
}
}
internal static class RegistryNativeMethods
{
[Flags]
public enum RegSAM
{
AllAccess = 0x000f003f
}
private const int REG_PROCESS_APPKEY = 0x00000001;
// approximated from pinvoke.net's RegLoadKey and RegOpenKey
// NOTE: changed return from long to int so we could do Win32Exception on it
[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegLoadAppKey(String hiveFile, out int hKey, RegSAM samDesired, int options, int reserved);
public static int RegLoadAppKey(String hiveFile)
{
int hKey;
int rc = RegLoadAppKey(hiveFile, out hKey, RegSAM.AllAccess, REG_PROCESS_APPKEY, 0);
if (rc != 0)
{
throw new Win32Exception(rc, "Failed during RegLoadAppKey of file " + hiveFile);
}
return hKey;
}
}
}
You can use it open the External Tools key like this:
using (var context = VisualStudioContext.Instance)
{
RegistryKey keyExternalTools =
context.RootKey.OpenSubKey($"Software\\Microsoft\\VisualStudio\\{context.VersionTag}\\External Tools", true);
// Do something interesting here
}
It seems you can register external tools in VS 2017 and 2019 by adding a <UserCreatedTool> in the <ExternalTools> section of the Settings/CurrentSettings.vssettings file in the local %AppData% directory of the respective VS version.
Simply create an external tool in VS manually, and search for the entry you created.
This describes how I found out ;)

Retrieving file installation path from registry

I am creating a WPF utility which needs to access the registry of the local machine, to then find out the installation path of the program.
I've navigated to the key via Regedit and it gives a Name, Type and Data, within the Data it shows the installation path, I would like to extract the installation path.
I know I need to navigate to this key within the registry:
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\App Paths\
then I need to access a folder within this key with the information regarding the installation path.
-
I solved my problem, to anyone who wants a solution in the future if your still stuck after this please message me, I found it was hard to find the resources.
RegistryKey key = Registry.LocalMachine.OpenSubKey(#"Software\Microsoft\Windows\CurrentVersion\App Paths\myexe.exe");
string regFilePath = null;
object objRegisteredValue = key.GetValue("");
registeredFilePath = value.ToString();
To read registry keys you should use Microsot.Windows.RegistryKey class, class Registry can open for you the RegistryKey.
This question was very helpful to me. I came up with a helper class, wanting to play with the new Tuples.
Example usage:
public string SkypeExePath => InstalledApplicationPaths.GetInstalledApplicationPath( "lync.exe" );
The class:
public static class InstalledApplicationPaths
{
public static string GetInstalledApplicationPath( string shortName )
{
var path = GetInstalledApplicationPaths().SingleOrDefault( x => x?.ExectuableName.ToLower() == shortName.ToLower() )?.Path;
return path;
}
public static IEnumerable<(string ExectuableName, string Path)?> GetInstalledApplicationPaths()
{
using ( RegistryKey key = Registry.LocalMachine.OpenSubKey( #"Software\Microsoft\Windows\CurrentVersion\App Paths" ) )
{
foreach ( var subkeyName in key.GetSubKeyNames() )
{
using ( RegistryKey subkey = key.OpenSubKey( subkeyName ) )
{
yield return (subkeyName, subkey.GetValue( "" )?.ToString());
}
}
}
}
}

Get a list of installed programs with application icons

I need to get a list of installed program on local machine with application icons. Below is the code snippet that am using to get the list of installed program and installed directory path.
/// <summary>
/// Gets a list of installed software and, if known, the software's install path.
/// </summary>
/// <returns></returns>
private string Getinstalledsoftware()
{
//Declare the string to hold the list:
string Software = null;
//The registry key:
string SoftwareKey = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
using (RegistryKey rk = Registry.LocalMachine.OpenSubKey(SoftwareKey))
{
//Let's go through the registry keys and get the info we need:
foreach (string skName in rk.GetSubKeyNames())
{
using (RegistryKey sk = rk.OpenSubKey(skName))
{
try
{
//If the key has value, continue, if not, skip it:
if (!(sk.GetValue("DisplayName") == null))
{
//Is the install location known?
if (sk.GetValue("InstallLocation") == null)
Software += sk.GetValue("DisplayName") + " - Install path not known\n"; //Nope, not here.
else
Software += sk.GetValue("DisplayName") + " - " + sk.GetValue("InstallLocation") + "\n"; //Yes, here it is...
}
}
catch (Exception ex)
{
//No, that exception is not getting away... :P
}
}
}
}
return Software;
}
Now the issue is how i can get the installed application icon ?
Thanks in advance.
To determine if its an update, there will be a key called IsMinorUpgrade. This is present and set to a 1 for updates. If it's 0 or not present, then it's not an update.
To get an icon from an executable, use this code:
VB:
Public Function IconFromFilePath(filePath As String) As Icon
Dim result As Icon = Nothing
Try
result = Icon.ExtractAssociatedIcon(filePath)
Catch ''# swallow and return nothing. You could supply a default Icon here as well
End Try
Return result
End Function
C#:
public Icon IconFromFilePath(string filePath)
{
Icon result = null;
try {
result = Icon.ExtractAssociatedIcon(filePath);
} catch { }
return result;
}
To extract icon of installed windows application first we need to figure out the location of icon for the installed windows application. This information is stored in registry at following locations -
Key name - HEKY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall
Value - DisplayIcon
Key name - HKEY_CLASSES_ROOT\Installer\Products{productID}
Value - ProductIcon
For more detail and code to get application icons -
http://newapputil.blogspot.in/2015/06/extract-icons-of-installed-windows_17.html

How can I test programmatically if a path/file is a shortcut?

I need to test if a file is a shortcut. I'm still trying to figure out how stuff will be set up, but I might only have it's path, I might only have the actual contents of the file (as a byte[]) or I might have both.
A few complications include that I it could be in a zip file (in this cases the path will be an internal path)
Shortcuts can be manipulated using the COM objects in SHELL32.DLL.
In your Visual Studio project, add a reference to the COM library "Microsoft Shell Controls And Automation" and then use the following:
/// <summary>
/// Returns whether the given path/file is a link
/// </summary>
/// <param name="shortcutFilename"></param>
/// <returns></returns>
public static bool IsLink(string shortcutFilename)
{
string pathOnly = System.IO.Path.GetDirectoryName(shortcutFilename);
string filenameOnly = System.IO.Path.GetFileName(shortcutFilename);
Shell32.Shell shell = new Shell32.ShellClass();
Shell32.Folder folder = shell.NameSpace(pathOnly);
Shell32.FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null)
{
return folderItem.IsLink;
}
return false; // not found
}
You can get the actual target of the link as follows:
/// <summary>
/// If path/file is a link returns the full pathname of the target,
/// Else return the original pathnameo "" if the file/path can't be found
/// </summary>
/// <param name="shortcutFilename"></param>
/// <returns></returns>
public static string GetShortcutTarget(string shortcutFilename)
{
string pathOnly = System.IO.Path.GetDirectoryName(shortcutFilename);
string filenameOnly = System.IO.Path.GetFileName(shortcutFilename);
Shell32.Shell shell = new Shell32.ShellClass();
Shell32.Folder folder = shell.NameSpace(pathOnly);
Shell32.FolderItem folderItem = folder.ParseName(filenameOnly);
if (folderItem != null)
{
if (folderItem.IsLink)
{
Shell32.ShellLinkObject link = (Shell32.ShellLinkObject)folderItem.GetLink;
return link.Path;
}
return shortcutFilename;
}
return ""; // not found
}
You can simply check the extension and/or contents of this file. It contains a special GUID in the header.
Read [this document][1].
Link deleted, for me it goes to a porn site
If you are using .NET 6.0, then you can use FileInfo or DirectoryInfo. Both have a property named LinkTarget. If it is a shortcut, then LinkTarget will be a string targeting the original file/folder. Otherwise, it will be null.
Suppose there is a folder shortcut named "folder", then:
var info = new DirectoryInfo("path/to/the/folder/shortcut");
bool isShortcut = info.LinkTarget != null;
Check the extension? (.lnk)

Categories