Свяжите расширение файла с приложением

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

Я попытался написать многоразовый метод, который связывает для меня файл (желательно в любой ОС, хотя я использую 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, но с использованием моего приложения - нет.


person User2400    schedule 21.04.2010    source источник
comment
вы пробовали запускать свою программу как администратор?   -  person Colin Newell    schedule 21.04.2010
comment
UAC означает, что ваше приложение не запускается от имени администратора, если вы явно этого не потребуете. Вы запускаете Vista, Vista включает UAC. Можете ли вы дважды проверить, запускается ли программа от имени администратора?   -  person Benjamin Podszun    schedule 21.04.2010
comment
Я пробовал Запуск от имени администратора, плюс UAC все равно отключен, но файл все еще не связан после запуска программы.   -  person User2400    schedule 21.04.2010
comment
Я думаю, что предпоследняя строка в вашем методе может быть неправильной. Я не думаю, что вы хотите установить CurrentUser в качестве подключа.   -  person Maestro1024    schedule 25.05.2011
comment
Связанный вопрос Связь типа файла с приложением (C #)   -  person Deanna    schedule 17.05.2013


Ответы (9)


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

Я также сказал проводнику, что изменил ассоциацию файлов, вызвав неуправляемую функцию 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);
person User2400    schedule 23.04.2010
comment
Я знаю, что это устарело, и вы, возможно, уже это заметили, но я заметил в этом сегменте кода и в вашем первом сообщении, что первая строка CurrentUser = у вас есть расширение .ucs, жестко закодированное в вашем вызове OpenSubKey (). - person Chuck Savage; 21.04.2011
comment
Сработало очень хорошо для меня! но обратите внимание - требуются административные права - person Kamornik Cola; 09.07.2017
comment
Удаление подключей для известных расширений, таких как .png, работает, но как только я уведомляю проводника, он восстанавливает UserChoice. Как я могу остановить проводник, восстанавливающий UserChoice? - person HGMamaci; 03.02.2018

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

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;
    }
person Kirill Osenkov    schedule 29.06.2017
comment
это сэкономило мне много времени. отлично работает на windows 10. Отличная работа! - person seabass; 25.10.2018
comment
Было бы неплохо добавить функцию для удаления ассоциации файлов. - person Slion; 02.04.2019


Приведенное выше решение не сработало для меня с 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);
person sofsntp    schedule 03.11.2016
comment
Из этого ответа: Класс RegistryKey реализует IDisposable, поэтому вам следует заключить свои ключи в выражение using. Или, в качестве альтернативы, вы должны вызвать Close or Dispose на RegistryKey, когда закончите с этим. Это означает, что связывание вызовов CreateSubKey, как показано в этом примере, - плохая идея. - person DavidRR; 05.01.2017
comment
Согласитесь с вами, это чище, но все же интересно понять, какой наихудший побочный эффект я могу ожидать от приведенного выше кода. Идеи? - person sofsntp; 06.01.2017
comment
Если вы не удалите неуправляемый ресурс (например, RegistryKey), ваше приложение пострадает от утечки памяти. См. Связь между утечками ресурсов и утечками памяти и производительностью и IDisposable Interface. Обратите внимание, что примеры кода как в вопросе, так и в принятом ответе включают вызовы RegistryKey.Close по мере необходимости. - person DavidRR; 06.01.2017
comment
ой. потратить 30 секунд на просмотр этого кода - все неправильно. Ключ User_Extension создает ключ .ext в CURRENT_USER. Должно быть CLASSES_ROOT. Я перестал следить за этим. Если это не так, вероятно, все остальное тоже неверно. - person Andy; 30.01.2017
comment
@Sonic, нет, это лучше, потому что CURRENT_USER \ Software \ Classes является текущим пользовательским эквивалентом CLASSES_ROOT и не требует прав администратора. Код неверен по другим причинам. - person Kirill Osenkov; 29.06.2017
comment
У меня это сработало из коробки, tyvm. - person Jhollman; 15.07.2021

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

person gammelgul    schedule 21.04.2010

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

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

person Hans Passant    schedule 21.04.2010
comment
Я не могу добавить манифест в проект, потому что фактического исполняемого файла нет в моем проекте. Есть ли способ заставить его появиться, чтобы я мог добавить ресурс? (добавляем - ›существующий элемент и выбираем .exe в папке obj, просто копируем его) - person User2400; 22.04.2010
comment
Вы можете что-то сломать, если это действительно устаревшее приложение. Но вы можете внедрить манифест с помощью инструмента SDK mt.exe. - person Hans Passant; 22.04.2010

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

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

person Community    schedule 15.11.2016

фактический способ связать расширение вашего файла с вашей собственной программой:

using Microsoft.Win32;
using System;
using System.IO;
using System.Runtime.InteropServices;

private static void RegisterForFileExtension(string extension, string applicationPath)
    {
        RegistryKey FileReg = Registry.CurrentUser.CreateSubKey("Software\\Classes\\" + extension);
        FileReg.CreateSubKey("shell\\open\\command").SetValue("", applicationPath + " %1");
        FileReg.Close();

        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);
person Avathillion    schedule 20.02.2020

линия

FileReg.CreateSubKey ("оболочка \ открыть \ команда"). SetValue ("", applicationPath + "% 1");

следует изменить на

FileReg.CreateSubKey ("оболочка \ открыть \ команда"). SetValue ("", $ "\" {applicationPath} \ "\"% 1 \ "");

если вы не хотите иметь проблемы с пробелами в пути, например:

C: \ моя папка \ мой файл.txt

person karpediemnow    schedule 17.04.2020