Связать расширение файла с приложением - PullRequest
50 голосов
/ 21 апреля 2010

Я написал программу, которая редактирует определенный тип файла, и я хочу дать пользователю возможность установить мое приложение в качестве редактора по умолчанию для этого типа файла (так как мне не нужен установщик) при запуске.

Я попытался написать повторно используемый метод, который связывает файл для меня (желательно в любой ОС, хотя я использую Vista), добавив ключ в HKEY_CLASSES_ROOT, и я использую его с моим приложением, но это не похоже на работу.

public static void SetAssociation(string Extension, string KeyName, string OpenWith, string FileDescription)
{
    RegistryKey BaseKey;
    RegistryKey OpenMethod;
    RegistryKey Shell;
    RegistryKey CurrentUser;

    BaseKey = Registry.ClassesRoot.CreateSubKey(Extension);
    BaseKey.SetValue("", KeyName);

    OpenMethod = Registry.ClassesRoot.CreateSubKey(KeyName);
    OpenMethod.SetValue("", FileDescription);
    OpenMethod.CreateSubKey("DefaultIcon").SetValue("", "\"" + OpenWith + "\",0");
    Shell = OpenMethod.CreateSubKey("Shell");
    Shell.CreateSubKey("edit").CreateSubKey("command").SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
    Shell.CreateSubKey("open").CreateSubKey("command").SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
    BaseKey.Close();
    OpenMethod.Close();
    Shell.Close();

    CurrentUser = Registry.CurrentUser.CreateSubKey(@"HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\FileExts\" + Extension);
    CurrentUser = CurrentUser.OpenSubKey("UserChoice", RegistryKeyPermissionCheck.ReadWriteSubTree, System.Security.AccessControl.RegistryRights.FullControl);
    CurrentUser.SetValue("Progid", KeyName, RegistryValueKind.String);
    CurrentUser.Close();
}

Есть идеи, почему это не работает? Пример использования может быть

SetAssociation(".ucs", "UCS_Editor_File", Application.ExecutablePath, "UCS File"); 

Часть метода, которая использует «CurrentUser», кажется, работает, если я делаю то же самое с помощью regedit, но с помощью моего приложения это не так.

Ответы [ 7 ]

32 голосов
/ 23 апреля 2010

Ответ оказался намного проще, чем я ожидал. Проводник Windows имеет свое собственное переопределение для приложения open with, и я пытался изменить его в последних строках кода. Если вы просто удалите переопределение Проводника, тогда будет работать сопоставление файлов.

Я также сказал проводнику, что изменил сопоставление файлов, вызвав неуправляемую функцию SHChangeNotify()

public static void SetAssociation(string Extension, string KeyName, string OpenWith, string FileDescription)
{
    // The stuff that was above here is basically the same

    // Delete the key instead of trying to change it
    CurrentUser = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\" + Extension, true);
    CurrentUser.DeleteSubKey("UserChoice", false);
    CurrentUser.Close();

    // Tell explorer the file association has been changed
    SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
}

[DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
18 голосов
/ 29 июня 2017

Вот полный пример:

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 = ".ucs",
                ProgId = "UCS_Editor_File",
                FileTypeDescription = "UCS File",
                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;
    }
16 голосов
/ 21 апреля 2010

Вы можете сделать это управляемым способом через ClickOnce .Не возиться с реестром самостоятельно.Это доступно с помощью инструментов (т.е. без XML) в VS2008 и выше (включая Express) в Свойствах проекта => Опубликовать => Параметры => Ассоциации файлов

7 голосов
/ 03 ноября 2016

Решение выше не работает для меня с Windows 10. Вот мое решение, чтобы открыть файлы с расширением .myExt с% localappdata% \ MyApp \ MyApp.exe для текущего пользователя. Оптимизирован после прочтения комментариев.

 String App_Exe = "MyApp.exe";
 String App_Path = "%localappdata%";
 SetAssociation_User("myExt", App_Path + App_Exe, App_Exe);

 public static void SetAssociation_User(string Extension, string OpenWith, string ExecutableName)
 {
    try {
                using (RegistryKey User_Classes = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Classes\\", true))
                using (RegistryKey User_Ext = User_Classes.CreateSubKey("." + Extension))
                using (RegistryKey User_AutoFile = User_Classes.CreateSubKey(Extension + "_auto_file"))
                using (RegistryKey User_AutoFile_Command = User_AutoFile.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command"))
                using (RegistryKey ApplicationAssociationToasts = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\ApplicationAssociationToasts\\", true))
                using (RegistryKey User_Classes_Applications = User_Classes.CreateSubKey("Applications"))
                using (RegistryKey User_Classes_Applications_Exe = User_Classes_Applications.CreateSubKey(ExecutableName))
                using (RegistryKey User_Application_Command = User_Classes_Applications_Exe.CreateSubKey("shell").CreateSubKey("open").CreateSubKey("command"))
                using (RegistryKey User_Explorer = Registry.CurrentUser.CreateSubKey("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\." + Extension))
                using (RegistryKey User_Choice = User_Explorer.OpenSubKey("UserChoice"))
                {
                    User_Ext.SetValue("", Extension + "_auto_file", RegistryValueKind.String);
                    User_Classes.SetValue("", Extension + "_auto_file", RegistryValueKind.String);
                    User_Classes.CreateSubKey(Extension + "_auto_file");
                    User_AutoFile_Command.SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
                    ApplicationAssociationToasts.SetValue(Extension + "_auto_file_." + Extension, 0);
                    ApplicationAssociationToasts.SetValue(@"Applications\" + ExecutableName + "_." + Extension, 0);
                    User_Application_Command.SetValue("", "\"" + OpenWith + "\"" + " \"%1\"");
                    User_Explorer.CreateSubKey("OpenWithList").SetValue("a", ExecutableName);
                    User_Explorer.CreateSubKey("OpenWithProgids").SetValue(Extension + "_auto_file", "0");
                    if (User_Choice != null) User_Explorer.DeleteSubKey("UserChoice");
                    User_Explorer.CreateSubKey("UserChoice").SetValue("ProgId", @"Applications\" + ExecutableName);
                }
                SHChangeNotify(0x08000000, 0x0000, IntPtr.Zero, IntPtr.Zero);
            }
            catch (Exception excpt)
            {
                //Your code here
            }
        }

  [DllImport("shell32.dll", CharSet = CharSet.Auto, SetLastError = true)]
  public static extern void SHChangeNotify(uint wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
7 голосов
/ 21 апреля 2010

Если вы запишите ключи в HKEY_CURRENT_USER\Software\Classes вместо HKEY_CLASSES_ROOT, это должно работать без прав администратора в Vista и более поздних версиях.

4 голосов
/ 15 ноября 2016

Если вы используете Visual Studio 2015, установите расширение для установки и развертывания. Создайте мастер установки, а затем прикрепите к нему файл .exe. Щелкните правой кнопкой мыши основную программу в обозревателе решений, перейдите к -view, -file types, а затем щелкните правой кнопкой мыши типы файлов и выберите Добавить новый тип файлов. Измените все свойства в соответствии с вашими потребностями, а затем соберите установщик MSI.

ПРИМЕЧАНИЕ : я перечитал ваш вопрос и понял, что вам не нужен установщик. Извините за это, хотя вы должны рассмотреть возможность использования одного, потому что это дает вам гораздо больше возможностей настройки ваших программ.

4 голосов
/ 21 апреля 2010

Вы используете старую версию Visual Studio, Vista будет рассматривать вашу программу как «устаревшее» приложение Windows.И перенаправить записи реестра вы делаете.Включите манифест в вашу программу, чтобы вы были в курсе Vista.Этот манифест автоматически включается VS2008 и выше.

Помните, что это по-прежнему не решит проблему для вашего пользователя, она вряд ли запустит ваше приложение с отключенным UAC.Вам нужно написать отдельное приложение, которое имеет манифест в виде ссылки и запрашивает права администратора.Для этого требуется манифест, для которого selectedExecutionLevel имеет значение requireAdministrator.

...