Windows - Adding Directory with Subdirectories to left navigation pane - c#

I want to add a directory node to the left navigation pane. It should contain links to subdirectories that are actually on the file system. Currenty it just displays the contents of the last registered path under "Test".
I illustrated the problem: Left side is current state. Right side (photomontage) is how I expect it.
Is this possible or do I have to link to a directory where I add symlinks to my target directories?
That's what I have done so far (basically from a similar SO question and the "Integrate a Cloud Storage Provider" Windows help Page):
class Program
{
static void Main(string[] args)
{
string[] defaultFolders = { "C:\\Intel\\Logs", "C:\\Python27" };
var sm = new ShellextensionManger();
var guid = Guid.Parse("{03d6d437-c11c-49fc-9dcd-aa65e15b77ea}");
sm.createShellFolder(guid.ToString(), "Test", defaultFolders, "C:\\icon.ico");
sm.removeShellFolder(guid.ToString());
}
}
internal class ShellextensionManger
{
private const uint SORT_ORDER_VERY_TOP = 1;
public ShellextensionManger() {}
public void createShellFolder(string strGUID, string strFolderTitle, IEnumerable<string> strTargetFolderPaths, string strIconPath)
{
RegistryKey localKey, keyTemp, rootKey;
localKey = getRegistryKeyArcgIndependent(RegistryHive.CurrentUser);
// Add your CLSID and name your extension
rootKey = localKey.CreateSubKey(#"Software\Classes\CLSID\{" + strGUID + "}");
rootKey.SetValue("", strFolderTitle, RegistryValueKind.String);
// Add your extension to the Navigation Pane and make it visible
rootKey.SetValue("System.IsPinnedToNameSpaceTree", unchecked((int)0x1), RegistryValueKind.DWord);
// Set the location for your extension in the Navigation Pane
rootKey.SetValue("SortOrderIndex", unchecked((int) SORT_ORDER_VERY_TOP), RegistryValueKind.DWord);
// Set the image for your icon
keyTemp = rootKey.CreateSubKey(#"DefaultIcon");
keyTemp.SetValue("", strIconPath, RegistryValueKind.ExpandString);
keyTemp.Close();
// Provide the dll that hosts your extension.
keyTemp = rootKey.CreateSubKey(#"InProcServer32");
keyTemp.SetValue("", #"%systemroot%\system32\shell32.dll", RegistryValueKind.ExpandString);
keyTemp.Close();
/*
* Define the instance object
* Indicate that your namespace extension should function like other file folder structures in File Explorer. For more information about shell instance objects, see Creating Shell Extensions with Shell Instance Objects.
*/
keyTemp = rootKey.CreateSubKey(#"Instance");
keyTemp.SetValue("CLSID", "{0E5AAE11-A475-4c5b-AB00-C66DE400274E}", RegistryValueKind.String);
keyTemp.Close();
foreach (String path in strTargetFolderPaths) {
keyTemp = rootKey.CreateSubKey(#"Instance\InitPropertyBag");
// Provide the file system attributes of the target folder
keyTemp.SetValue("Attributes", unchecked((int) (FileAttributes.Directory & FileAttributes.ReadOnly)), RegistryValueKind.DWord);
// Set the path for the sync root
keyTemp.SetValue("TargetFolderPath", path, RegistryValueKind.ExpandString);
keyTemp.Close();
}
// Set appropriate shell flags
keyTemp = rootKey.CreateSubKey(#"ShellFolder");
keyTemp.SetValue("FolderValueFlags", unchecked((int)0x28), RegistryValueKind.DWord);
// Set the appropriate flags to control your shell behavior
keyTemp.SetValue("Attributes", unchecked((int)0xF080004D), RegistryValueKind.DWord);
keyTemp.Close();
rootKey.Close();
// Register your extension in the namespace root
keyTemp = localKey.CreateSubKey(#"Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{" + strGUID + "}");
keyTemp.SetValue("", strFolderTitle, RegistryValueKind.String);
keyTemp.Close();
// Hide your extension from the Desktop
keyTemp = localKey.CreateSubKey(#"Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel");
keyTemp.SetValue("{" + strGUID + "}", unchecked((int)0x1), RegistryValueKind.DWord);
keyTemp.Close();
}
public void removeShellFolder(string strGUID)
{
RegistryKey localKey;
localKey = getRegistryKeyArcgIndependent(RegistryHive.CurrentUser);
localKey.DeleteSubKeyTree(#"Software\Classes\CLSID\{" + strGUID + "}", false);
localKey.DeleteSubKey(#"Software\Microsoft\Windows\CurrentVersion\Explorer\Desktop\NameSpace\{" + strGUID + "}", false);
localKey.DeleteSubKey(#"Software\Microsoft\Windows\CurrentVersion\Explorer\HideDesktopIcons\NewStartPanel", false);
}
private RegistryKey getRegistryKeyArcgIndependent(RegistryHive registryHive) {
RegistryView registryView = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32;
return RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, registryView);
}
}
Please note that I remove that entry at the end of the main for debugging purpose. Set a breakpoint if you try the code.

Related

Is it possible to create a program that can redirect folder icon directory depending on the file location?

Background
Yo, guys. So first off, here is an anime folder that displays all my anime series with different folder icons. All of the said custom icons are located in a hidden folder named "icons". This format is what I've laid out in other folders of their corresponding years.
Problem/Situation
As we all know, when we move/tamper the "icons" folder or the directory of the anime folder, the link gets broken and the custom icons of the anime series disappear. This problem can be remedied by either assigning the custom icons again or renaming the directory.
I tend to copy anime folders to other hard drives. What I want to know if it's possible to create a program that automatically fixes the directory link of the anime folder depending on the location it's in.
For example, let's say I copy the 2018 anime folder from directory "G:\2018" and place it in Desktop. The custom folder icon of, let's say, Slow Start is still G:\2018\icons\Slow_Start_Folder_Icon.ico. The program, when run, will automatically change the directory link to C:\Users****\Desktop\2018\icons\Slow_Start_Folder_Icon.ico. Of course, simultaneously with the other anime folders.
Any enlightening answers are greatly appreciated. Thanks in advance also.
UPDATE 3: This is my current working code, with ideas and advice gotten from #Jimi. Tested and it worked for me.
private static void UpdateIcons() //for updating folder icons to new directory
{
int start, end;
string filedir = Environment.CurrentDirectory, olddir = null, newdir = null;
IEnumerable<string> Ini = Directory.EnumerateFiles(filedir, "desktop.ini", SearchOption.AllDirectories);
foreach (string deskini in Ini)
{
File.SetAttributes(deskini, FileAttributes.Archive);
foreach (var line in File.ReadAllLines(deskini))
{
if (line.Contains("IconResource"))
{
start = line.IndexOf("icons");
Console.WriteLine(line);
Console.WriteLine(line.Length.ToString());
Console.WriteLine(line.IndexOf("icons").ToString());
if (line.Contains(","))
{
end = line.IndexOf(",");
olddir = line.Substring(start, (end - start));
Console.Write(end - start);
}
else
{
end = line.Length - 1;
Console.Write(end);
olddir = line.Substring(start, (end - start + 1));
Console.Write(end - start);
}
newdir = File.ReadAllText(deskini);
newdir = newdir.Replace(line, "IconResource=" + filedir + "\\" + olddir);
File.WriteAllText(deskini, newdir, Encoding.Unicode);
Console.WriteLine(olddir);
Console.WriteLine(newdir);
}
}
File.SetAttributes(deskini, FileAttributes.Hidden);
}
}
The application accepts a command line parameter, where the Icons repository (a folder name) can be specified. If none is provided, it assumes the Icons repository is named "icons", found in the current executing path.
Assembly.GetExecutingAssembly is used to determine the executable path. The Location property returns a standard-formatted Path.
Directory.GetDirectories is used to get the directory listing, including the SubDirectories, of the directories in the current path. The current path and the icons directory is then excluded from the list, since those Folders are not subject to customization.
Directory.GetFiles() is the use to build the list of the icons files contained in the Icons folder.
To update the Folders customization (Explorer can be somewhat lazy when it comes to update these changes: it uses the IconCache.db storage when not otherwise instructed) we PInvoke SHGetSetFolderCustomSettings.
This Shell API creates or updates the desktop.ini file, located inside each customized folder. This file references the Icon used as the Folder Custom Icon.
This function allows a swift update of the folder new aspect. The update is, in this case, immediate. No need to delete the icons cache or restart Explorer.
Related informations:
How to Customize Folders with Desktop.ini
This document is part of The Windows Shell document, The Book of Shell operations.
An old definition of SHFOLDERCUSTOMSETTINGS, which is missing in the official documentation.
Compile this code as Release and copy the exeutable in the upper level folder where the customized sub folders are listed.
NOTE:
In this example, I'm randomly picking the Icon to assign to a folder from the Icons repository (using a Random value). You need define your own logic to assign the right icon to a specific Folder.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
public enum FolderCustomSettingsMask : uint
{
InfoTip = 0x00000004,
Clsid = 0x00000008,
IconFile = 0x00000010,
Logo = 0x00000020,
Flags = 0x00000040
}
public enum FolderCustomSettingsRW : uint
{
Read = 0x00000001,
ForceWrite = 0x00000002,
ReadWrite = Read | ForceWrite
}
[SecurityCritical]
[DllImport("Shell32.dll", CharSet = CharSet.Auto)]
static extern uint SHGetSetFolderCustomSettings(ref SHFOLDERCUSTOMSETTINGS pfcs, string pszPath, FolderCustomSettingsRW dwReadWrite);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
struct SHFOLDERCUSTOMSETTINGS
{
public uint dwSize;
public FolderCustomSettingsMask dwMask;
public IntPtr pvid;
public string pszWebViewTemplate;
public uint cchWebViewTemplate;
public string pszWebViewTemplateVersion;
public string pszInfoTip;
public uint cchInfoTip;
public IntPtr pclsid;
public uint dwFlags;
public string pszIconFile;
public uint cchIconFile;
public int iIconIndex;
public string pszLogo;
public uint cchLogo;
}
static void Main(string[] args)
{
string CurrentPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
string IconsFolder = args.Length > 0 ? args[0] : Path.Combine(CurrentPath, #"icons");
UpdateFolderIcons(CurrentPath, IconsFolder);
Console.WriteLine($"Press any key to terminate...");
Console.ReadKey();
}
[SecuritySafeCritical]
public static void UpdateFolderIcons(string BaseFolder, string IconsFolder)
{
Console.WriteLine($"Current path: {BaseFolder}");
Console.WriteLine($"Processing...");
Console.WriteLine();
List<string> ExcludeDirList = new List<string>() {BaseFolder, IconsFolder};
List<string> DirList = Directory.GetDirectories(BaseFolder, "*", SearchOption.AllDirectories).ToList();
DirList = DirList.Except(ExcludeDirList).ToList();
if (DirList.Count == 0) {
Console.WriteLine($"No directories found!");
return;
}
Console.WriteLine($"{DirList.Count} Directories found");
Console.WriteLine("Updating...");
Console.WriteLine();
int UpdateCount = 0;
List<string> IconNames = Directory.GetFiles(Path.Combine(BaseFolder, IconsFolder), "*").ToList();
if (IconNames.Count == 0) {
Console.WriteLine($"No icons found in {IconsFolder}");
return;
}
foreach (string sPath in DirList)
{
string IconName = IconNames[random.Next(0, IconNames.Count)];
SHFOLDERCUSTOMSETTINGS FolderSettings = new SHFOLDERCUSTOMSETTINGS {
dwMask = FolderCustomSettingsMask.IconFile,
pszIconFile = IconName,
iIconIndex = 0
};
uint hResult = SHGetSetFolderCustomSettings(
ref FolderSettings, sPath + char.MinValue, FolderCustomSettingsRW.ForceWrite);
UpdateCount += 1;
}
Console.WriteLine($"{UpdateCount} Files Updated");
Console.WriteLine();
}

Installing an Outlook addin for Local Machine registry hive

I am using regasm.exe to install my Outlook plugin on the end users' machines. However, I noticed that in a terminal server environment, my addin wouldn't load for all users. I found out that the addin registry keys were being created under the current user hive and not the local machine hive.
Is there something i can add to my plugin which will enable regasm to create/delete the keys under HKLM as well as current user when a user installs or uninstalls the plugin?
I had other, but similar issues with regasm.exe, as I wanted to avoid the UAC in windows when registering the AddIn, so I wrote a com registrar by myself, which does the proper registry keys to register the com addin.
You would have to change this code accordingly to have the registry keys in those places were you want them, and obviously have similar code to rollback those changes on uninstalling your add-in
class ComAddinRegistrar
{
private static string CLSID = "{CLSID}";
private static string ASSEMBLY_NAME = "Outlook.Addin";
private static string ASSEMBLY_DLL = "Outlook.Addin.dll";
private static string VERSION = "1.0.0.0";
private static string IMPLEMENTED_CATEGORY = "{CATEGORY_ID}";
public static void RegisterAddin(bool is64Bit)
{
// Get path of dll:
var apPpath = System.Reflection.Assembly.GetExecutingAssembly().Location;
apPpath = Path.GetDirectoryName(apPpath);
var path = ASSEMBLY_DLL;
var dllPath = Path.Combine(apPpath, path);
// Com registration
RegistryKey rkey;
if (is64Bit)
rkey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64)
.OpenSubKey("Software").OpenSubKey("Classes", true);
else
rkey = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32)
.OpenSubKey("Software").OpenSubKey("Classes", true);
var comAssemblyNameKey = rkey.CreateSubKey(ASSEMBLY_NAME); //HKCU/Software/Classes/Outlook.Addin
comAssemblyNameKey.SetValue("", ASSEMBLY_NAME); // default value
var clsidAssemblyNameComKey = comAssemblyNameKey.CreateSubKey("CLSID"); //HKCU/Software/Classes/Outlook.Addin/{CLSID}
clsidAssemblyNameComKey.SetValue("", CLSID); // default value
RegistryKey clsidComKey;
if (is64Bit)
clsidComKey = rkey.CreateSubKey("CLSID").CreateSubKey(CLSID); //HKCU\Software\Classes\CLSID\{CLSID}
else
clsidComKey = rkey.CreateSubKey("Wow6432Node").CreateSubKey("CLSID").CreateSubKey(CLSID); //HKCU\Software\Classes\CLSID\{CLSID}
clsidComKey.SetValue("", ASSEMBLY_NAME); // default value
var inProcServerKey = clsidComKey.CreateSubKey("InprocServer32"); //HKCU\Software\Classes\CLSID\{CLSID}\InProcServer32
inProcServerKey.SetValue("", "mscoree.dll");
inProcServerKey.SetValue("ThreadingModel", "Both");
inProcServerKey.SetValue("Class", "Outlook.Addin.Addin");
inProcServerKey.SetValue("Assembly", $"Outlook.Addin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
inProcServerKey.SetValue("RuntimeVersion", "v4.0.30319");
inProcServerKey.SetValue("CodeBase", dllPath);
var versionKey = inProcServerKey.CreateSubKey(VERSION); //HKCU\Software\Classes\CLSID\{CLSID}\InProcServer32\1.0.0.0
versionKey.SetValue("Class", "Outlook.Addin.Addin");
versionKey.SetValue("Assembly", $"Outlook.Addin, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");
versionKey.SetValue("RuntimeVersion", "v4.0.30319");
versionKey.SetValue("CodeBase", dllPath);
var progIdKey = clsidComKey.CreateSubKey("ProgId"); //HKCU\Software\Classes\CLSID\{CLSID}\ ProgId
progIdKey.SetValue("", ASSEMBLY_NAME);
var implementedCategoryKey = clsidComKey.CreateSubKey("ImplementedCategories"); //HKCU\Software\Classes\CLSID\{CLSID}\ImplementedCategories
implementedCategoryKey.CreateSubKey(IMPLEMENTED_CATEGORY);
//AddIn registration
//HKEY_CURRENT_USER\SOFTWARE\Microsoft\Office\Outlook\Addins\Outlook.Addin
var okey = Registry.CurrentUser.OpenSubKey("SOFTWARE")
.OpenSubKey("Microsoft")
.OpenSubKey("Office")
.OpenSubKey("Outlook")
.OpenSubKey("Addins", true);
var addinKey = okey.CreateSubKey(ASSEMBLY_NAME);
addinKey.SetValue("FileName", dllPath);
addinKey.SetValue("FriendlyName", "My addin");
addinKey.SetValue("Description", "some addin description");
addinKey.SetValue("LoadBehavior", 3, RegistryValueKind.DWord);
}
}
Found the answer by digging through my sample NetOffice Outlook addin project!
All you need are a couple of public static methods with the ComRegisterFunctionAttribute and ComUnregisterFunctionAttribute attributes. Regasm.exe will automatically execute the code in these methods when your plugin is installed or uninstalled.
See below for the code I am using which is slightly modified from the template to support the HKLM registry key. Add these methods to your Outlook addin class.
Alek's answer can be used if you wish to handle these manually.
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
try
{
// add codebase value
Assembly thisAssembly = Assembly.GetAssembly(typeof(OutlookPlugin));
RegistryKey key = Registry.ClassesRoot.CreateSubKey("CLSID\\{" + type.GUID.ToString().ToUpper() + "}\\InprocServer32\\1.0.0.0");
key.SetValue("CodeBase", thisAssembly.CodeBase);
key.Close();
key = Registry.ClassesRoot.CreateSubKey("CLSID\\{" + type.GUID.ToString().ToUpper() + "}\\InprocServer32");
key.SetValue("CodeBase", thisAssembly.CodeBase);
key.Close();
// add bypass key
// http://support.microsoft.com/kb/948461
key = Registry.ClassesRoot.CreateSubKey("Interface\\{000C0601-0000-0000-C000-000000000046}");
string defaultValue = key.GetValue("") as string;
if (null == defaultValue)
key.SetValue("", "Office .NET Framework Lockback Bypass Key");
key.Close();
// add outlook addin key
Registry.ClassesRoot.CreateSubKey(#"CLSID\{" + type.GUID.ToString().ToUpper() + #"}\Programmable");
Registry.CurrentUser.CreateSubKey(_addinOfficeRegistryKey + _prodId);
RegistryKey rk = Registry.CurrentUser.OpenSubKey(_addinOfficeRegistryKey + _prodId, true);
rk.SetValue("LoadBehavior", Convert.ToInt32(3));
rk.SetValue("FriendlyName", _addinFriendlyName);
rk.SetValue("Description", _addinDescription);
rk.Close();
//Add registry key under HKLM
RegistryKey regHKLM = Registry.LocalMachine;
regHKLM = regHKLM.CreateSubKey(#"SOFTWARE\Microsoft\Office\Outlook\Addins\" + _prodId);
regHKLM.SetValue("Friendly Name", _addinFriendlyName);
regHKLM.SetValue("Description", _addinDescription);
regHKLM.SetValue("LoadBehavior", 3, RegistryValueKind.DWord);
}
catch (System.Exception ex)
{
string details = string.Format("{1}{1}Details:{1}{1}{0}", ex.Message, Environment.NewLine);
MessageBox.Show("An error occured." + details, "Register " + _prodId, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
try
{
Registry.ClassesRoot.DeleteSubKey(#"CLSID\{" + type.GUID.ToString().ToUpper() + #"}\Programmable", false);
Registry.CurrentUser.DeleteSubKey(_addinOfficeRegistryKey + _prodId, false);
Registry.LocalMachine.DeleteSubKey(#"SOFTWARE\Microsoft\Office\Outlook\Addins\" + _prodId);
}
catch (System.Exception throwedException)
{
string details = string.Format("{1}{1}Details:{1}{1}{0}", throwedException.Message, Environment.NewLine);
MessageBox.Show("An error occured." + details, "Unregister " + _prodId, MessageBoxButtons.OK, MessageBoxIcon.Error);
}
}

Make a custom file extension in C# (Windows 7)

I am trying to make a file extension associated to my application like make any file with .ztr work directly with my app when user double clicks that file.
I am using VS2012 , C# , Windows 7
I did simulate this example here.
Sure with some modification like fix Import Dll put also failed.
public static void Register (string Ext,string Program,string Ico)
{
string prog = Path.GetFileName(Program);
RegistryKey file = Registry.CurrentUser.CreateSubKey("Software\\Classes\\"+Ext);//.osa
RegistryKey App = Registry.CurrentUser.CreateSubKey("Software\\Classes\\Applications\\" + prog);//osama.exe
RegistryKey Link = Registry.CurrentUser.CreateSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" + Ext);//.osa
file.CreateSubKey("DefultIcon").SetValue("", Ico);
file.CreateSubKey("PerceivedType").SetValue("","Text");
App.CreateSubKey("shell\\open\\command").SetValue("", "\"" + Application.ExecutablePath + "\"%1");
App.CreateSubKey("shell\\edit\\command").SetValue("", "\"" + Application.ExecutablePath + "\"%1");
App.CreateSubKey("DefultIcon").SetValue("", Ico);
//There is Change here you
Link.CreateSubKey("UserChoice").SetValue("Progid", Program);
Invoke.SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}

C# code to change browser download options

I am trying to do following with c#.
1) Open Firefox Browser-->Tools-->Options-->General Tab--->Downloads--->Always ask me where to save file.
I want to do this whole process in my application with c#. I want that when download window opens, the radio button in "Always ask me where to save file" option gets checked automatically.
I have tried from various links, but all is in vain.
Here is the full code, console application.
Summary: preferences file is located in application roaming folder, something like this on windows 7:
C:\Users\MYNAME\AppData\Roaming\Mozilla\Firefox\Profiles\d9i9jniz.default\prefs.js
We alter this file so that it includes "user_pref("browser.download.useDownloadDir", false);"
Restart firefox, and done. Only run this application when firefox is not running.
static void Main(string[] args)
{
if (isFireFoxOpen())
{
Console.WriteLine("Firefox is open, close it");
}
else
{
string pathOfPrefsFile = GetPathOfPrefsFile();
updateSettingsFile(pathOfPrefsFile);
Console.WriteLine("Done");
}
Console.ReadLine();
}
private static void updateSettingsFile(string pathOfPrefsFile)
{
string[] contentsOfFile = File.ReadAllLines(pathOfPrefsFile);
// We are looking for "user_pref("browser.download.useDownloadDir", true);"
// This needs to be set to:
// "user_pref("browser.download.useDownloadDir", false);"
List<String> outputLines = new List<string>();
foreach (string line in contentsOfFile)
{
if (line.StartsWith("user_pref(\"browser.download.useDownloadDir\""))
{
Console.WriteLine("Found it already in file, replacing");
}
else
{
outputLines.Add(line);
}
}
// Finally add the value we want to the end
outputLines.Add("user_pref(\"browser.download.useDownloadDir\", false);");
// Rename the old file preferences for safety...
File.Move(pathOfPrefsFile, Path.GetDirectoryName(pathOfPrefsFile) + #"\" + Path.GetFileName(pathOfPrefsFile) + ".OLD." + Guid.NewGuid().ToString());
// Write the new file.
File.WriteAllLines(pathOfPrefsFile, outputLines.ToArray());
}
private static string GetPathOfPrefsFile()
{
// Get roaming folder, and get the profiles.ini
string iniFilePath = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Mozilla\Firefox\profiles.ini";
// Profiles.ini tells us what folder the preferences file is in.
string contentsOfIni = File.ReadAllText(iniFilePath);
int locOfPath = contentsOfIni.IndexOf("Path=Profiles");
int endOfPath = contentsOfIni.IndexOf(".default", locOfPath);
int startOfPath = locOfPath + "Path=Profiles".Length + 1;
int countofCopy = ((endOfPath + ".default".Length) - startOfPath);
string path = contentsOfIni.Substring(startOfPath, countofCopy);
string toReturn = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) + #"\Mozilla\Firefox\Profiles\" + path + #"\prefs.js";
return toReturn;
}
public static bool isFireFoxOpen()
{
foreach (Process proc in Process.GetProcesses())
{
if (proc.ProcessName == "firefox")
{
return true;
}
}
return false;
}
What have you tried?
Firefox settings are stored in your profile, so I'd guess you can change the contents of the given file. Type about:config to find the setting you're looking for, I guess it's in the browser.download tree, alter it (after you made sure the browser isn't running) and you should be good to go.

How to associate a file extension to the current executable in 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;
}
}

Categories

Resources