web-dev-qa-db-fra.com

Associer l'extension de fichier à l'application

J'ai écrit un programme qui modifie un type de fichier spécifique, et je veux donner à l'utilisateur la possibilité de définir mon application comme éditeur par défaut pour ce type de fichier (car je ne veux pas d'installation) au démarrage.

J'ai essayé d'écrire une méthode réutilisable qui associe un fichier pour moi (de préférence sur n'importe quel système d'exploitation, bien que j'utilise Vista) en ajoutant une clé à HKEY_CLASSES_ROOT, et je l'utilise avec mon application, mais ce n'est pas le cas semblent fonctionner.

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();
}

Une idée pourquoi ça ne marche pas? Un exemple d'utilisation pourrait être

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

La partie de la méthode qui utilise "CurrentUser" semble fonctionner si je fais de même en utilisant regedit, mais en utilisant mon application, ce n'est pas le cas.

51
User2400

La réponse était beaucoup plus simple que ce à quoi je m'attendais. L'Explorateur Windows a son propre remplacement pour l'application open with, et j'essayais de le modifier dans les dernières lignes de code. Si vous supprimez simplement le remplacement de l'explorateur, l'association de fichiers fonctionnera.

J'ai également dit à Explorer que j'avais modifié une association de fichiers en appelant la fonction non managée 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);
32
User2400

Voici un exemple complet:

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;
    }
19
Kirill Osenkov

Vous pouvez le faire de manière gérée via ClickOnce . Pas de problème avec le registre vous-même. Ceci est disponible via l'outillage (c'est-à-dire pas de xml) dans VS2008 et supérieur (y compris Express) sur les propriétés du projet => Publier => Options => Associations de fichiers

17
Marc Gravell

La solution ci-dessus n'a pas fonctionné pour moi avec Windows 10. Voici ma solution pour ouvrir des fichiers avec l'extension .myExt avec% localappdata%\MyApp\MyApp.exe pour l'utilisateur actuel. Optimisé après lecture des commentaires.

 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
sofsntp

Si vous écrivez les clés dans HKEY_CURRENT_USER\Software\Classes au lieu de HKEY_CLASSES_ROOT, cela devrait fonctionner sans privilèges d'administrateur sous Vista et versions ultérieures.

7
gammelgul

Vous utilisez une ancienne version de Visual Studio, Vista va traiter votre programme comme une application Windows "héritée". Et redirigez les écritures de registre que vous faites. Incluez n manifeste dans votre programme pour que vous ayez l'air compatible avec Vista. Ce manifeste est automatiquement inclus par VS2008 et versions ultérieures.

Sachez que cela ne résoudra toujours pas le problème pour votre utilisateur, il est très peu probable qu'elle exécute votre application avec l'UAC désactivé. Vous devrez écrire une application distincte qui a un manifeste lié et demande des privilèges d'administrateur. Il a besoin du manifeste avec le jeu requiredExecutionLevel défini sur requireAdministrator.

4
Hans Passant

Si vous utilisez Visual Studio 2015, installez l'extension de configuration et de déploiement. Créez un Assistant d'installation, puis attachez-y votre fichier .exe. Cliquez avec le bouton droit sur votre programme principal dans l'explorateur de solutions, accédez à -view, -file types, puis cliquez avec le bouton droit sur les types de fichiers et sélectionnez Ajouter un nouveau type de fichier. Modifiez toutes les propriétés selon vos besoins, puis générez le programme d'installation MSI.

NOTE : J'ai relu votre question et j'ai réalisé que vous ne vouliez pas d'installation. Désolé pour cela, bien que vous devriez envisager d'en utiliser un car il vous donne beaucoup plus de personnalisation sur vos programmes.

4
Bernie G.