views:

92

answers:

4

I've written a program that edits a specific filetype , and I want to give the user the option to set my application as the default editor for this filetype (since I don't want an installer) on startup.

I've tried to write a re-useable method that associates a file for me (preferably on any OS, although I'm running Vista) by adding a key to HKEY_CLASSES_ROOT, and am using it with my application, but it doesn't seem to work.

  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\.ucs");
    CurrentUser = CurrentUser.OpenSubKey("UserChoice", RegistryKeyPermissionCheck.ReadWriteSubTree, System.Security.AccessControl.RegistryRights.FullControl);
    CurrentUser.SetValue("Progid", KeyName, RegistryValueKind.String);
    CurrentUser.Close();
}

Any idea why it doesn't work? An example use might be

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

The part of the method that uses "CurrentUser" seems to work if I do the same using regedit, but using my application it doesn't.

+4  A: 

You can do that in a managed way via ClickOnce. No fussing with the registry yourself. This is available via tooling (i.e. no xml) in VS2008 and above (including Express) on Project Properties => Publish => Options => File Associations

Marc Gravell
Nice answer, but unfortunately I'm using VS2005 so will I have to wait until I get VS2010?
fneep
+1  A: 

You are using an old version of Visual Studio, Vista is going to treat your program as a "legacy" Windows app. And redirect the registry writes you make. Include a manifest in your program so you'll look Vista-aware. This manifest is automatically included by VS2008 and up.

Beware that this still won't solve the problem for your user, she's very unlikely to run your app with UAC turned off. You'll need to write a separate app that has a manifest as linked and asks for administrator privileges. It needs the manifest with requestedExecutionLevel set to requireAdministrator.

Hans Passant
I'm unable to add a manifest to the project because the actual exe isn't in my project. Any way to make it appear so I can add a resource? (going add -> existing item and selecting the .exe in the obj folder just copies it)
fneep
You are likely to break things if this really is a legacy app. But you can inject a manifest with the mt.exe SDK tool.
Hans Passant
+2  A: 

If you write the keys into HKEY_CURRENT_USER\Software\Classes instead of HKEY_CLASSES_ROOT, this should work without administrator privileges under Vista and later.

gammelgul
A: 

The answer was a lot simpler than I expected. Windows Explorer has its own override for the open with application, and I was trying to modify it in the last lines of code. If you just delete the Explorer override, then the file association will work.

I also told explorer that I had changed a file association by calling the unmanaged function 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\\.ucs", 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);
fneep