2010-04-21 4 views
36

J'ai écrit un programme qui modifie un type de fichier spécifique et je souhaite donner à l'utilisateur la possibilité de définir mon application comme éditeur par défaut pour ce type de fichier (puisque je ne veux pas d'installateur) 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'exécute Vista) en ajoutant une clé à HKEY_CLASSES_ROOT, et je l'utilise avec mon application, mais ça ne semble pas fonctionner.Associer l'extension de fichier à l'application

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

Toute idée pourquoi il ne fonctionne pas? Un exemple d'utilisation peut être

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

La partie de la méthode qui utilise « CurrentUser » semble fonctionner si je fais la même chose en utilisant regedit, mais en utilisant mon application, il ne fonctionne pas.

+0

Avez-vous essayé l'exécution de votre programme en tant qu'administrateur? –

+0

Je suis un administrateur système, donc oui. – User2400

+0

UAC signifie que votre application ne fonctionne pas en tant qu'administrateur, sauf si vous l'exigez explicitement. Vous exécutez Vista, Vista inclut UAC. Pouvez-vous vérifier si le programme s'exécute en tant qu'administrateur? –

Répondre

26

La réponse était beaucoup plus simple que ce à quoi je m'attendais. Windows Explorer a son propre override pour l'open avec application, 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 fichier fonctionnera.

Je explorateur aussi dit que j'avais changé une association de fichier en appelant la fonction non géré 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); 
+8

Je sais que c'est vieux et vous l'avez peut-être déjà remarqué, mais j'ai remarqué dans ce segment de code et dans votre premier post que la première ligne de CurrentUser = vous avez l'extension .ucs codée en dur dans votre appel OpenSubKey(). –

+0

A travaillé très bien pour moi! mais attention - nécessite des droits administratifs –

+0

La suppression de sous-clé pour des extensions connues comme .png fonctionne mais dès que j'avise l'explorateur, il restaure le UserChoice. Comment puis-je empêcher l'explorateur de restaurer le UserChoice? – HGMamaci

17

Vous pouvez le faire dans un géré façon via ClickOnce. Pas de problème avec le registre vous-même. Ceci est disponible via l'outillage (ie pas de xml) dans VS2008 et supérieur (y compris Express) sur Propriétés du projet => Publier => Options => Associations de fichiers

+2

Bonne réponse, mais malheureusement, je suis en train d'utiliser VS2005, donc je vais avoir attendre jusqu'à ce que je reçois VS2010? – User2400

+0

C'est gentil et tout, mais, comment utilisez-vous cette méthode et définissez-vous des arguments de ligne de commande personnalisés? Cette méthode vous oblige à utiliser 'app.exe"% 1 "', mais que se passe-t-il si je le veux faire 'app.exe/config"% 1 "'? – Andy

3

Vous utilisez une ancienne version de Visual Studio, Vista va traiter votre programme en tant qu'application Windows "héritée". Et rediriger les écritures de registre que vous faites. Incluez a manifest dans votre programme pour que vous soyez conscient de Vista. Ce manifeste est automatiquement inclus par VS2008 et plus.

Attention, cela ne résout toujours pas le problème pour votre utilisateur, il est très peu probable que votre application ne fonctionne pas avec l'UAC éteint. Vous devrez écrire une application distincte dont un manifeste est lié et demander des privilèges d'administrateur. Il a besoin du manifeste avec requestedExecutionLevel défini sur requireAdministrator.

+0

Je ne parviens pas à ajouter un manifeste au projet car l'exe réel n'est pas dans mon projet. Un moyen de le faire apparaître afin que je puisse ajouter une ressource? (Aller ajouter -> élément existant et en sélectionnant le fichier .exe dans le dossier obj juste le copier) – User2400

+0

Vous êtes susceptible de casser des choses si c'est vraiment une application héritée. Mais vous pouvez injecter un manifeste avec l'outil SDK mt.exe. –

7

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 plus tard.

3

solution ci-dessus ne fonctionne pas pour moi avec Windows 10. Voici ma solution d'ouvrir le fichier avec .myExt % localappdata% \ MyApp \ MyApp.exe pour l'utilisateur actuel. Optimisé après avoir lu les 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); 
+0

De [cette réponse] (http://stackoverflow.com/a/13535039/1497596): * "La classe' RegistryKey' implémente 'IDisposable' et vous devriez donc placer vos clés dans une instruction' using'. "* Ou Sinon, vous devriez appeler [Close or Dispose] (http://stackoverflow.com/a/37059244/1497596) sur 'RegistryKey' lorsque vous avez terminé. Cela signifie que ** chaîner les appels à 'CreateSubKey' comme montré dans cet exemple est une mauvaise idée **. – DavidRR

+0

D'accord avec vous c'est plus propre mais il est toujours intéressant de comprendre quel est le pire effet secondaire que je peux attendre du code ci-dessus. Des idées? – sofsntp

+0

Si vous ne disposez pas d'une ressource * non gérée * (par exemple, 'RegistryKey'), votre application souffrira de fuites de mémoire. Voir [Relation entre les fuites de ressources et les fuites de mémoire et les performances] (http://stackoverflow.com/q/11879701/1497596) et [Interface IDisposable] (https://msdn.microsoft.com/fr-fr/library/System .IDisposable.aspx). Notez que les exemples de code dans la question et la réponse acceptée incluent des appels à 'RegistryKey.Close' selon les besoins. – DavidRR

3

Si vous utilisez Visual Studio 2015, installez l'extension d'installation et de déploiement. Créez un Assistant Installation, puis joignez-y votre fichier .exe. Faites un clic droit sur votre programme principal dans l'explorateur de solutions allez dans -view, -file types, puis faites un clic 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 créez le programme d'installation MSI.

NOTE: J'ai relu votre question et j'ai réalisé que vous ne vouliez pas d'installateur. Désolé à ce sujet, bien que vous devriez envisager d'en utiliser un, car il vous donne beaucoup plus de personnalisation sur votre programme (s).

6

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([email protected]"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; 
    } 
+0

Merci, c'est parfait! Cela fonctionne dans Windows 10 – zoran

Questions connexes