61

我编写了一个编辑特定文件类型的程序,我想在启动时让用户选择将我的应用程序设置为此文件类型的默认编辑器(因为我不需要安装程序)。

我试图编写一个可重用的方法,通过向 HKEY_CLASSES_ROOT 添加一个密钥来为我关联一个文件(最好在任何操作系统上,尽管我正在运行 Vista),并将它与我的应用程序一起使用,但它没有似乎工作。

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"); 

如果我使用 regedit 执行相同操作,则使用“CurrentUser”的方法部分似乎有效,但使用我的应用程序却没有。

4

9 回答 9

37

答案比我预期的要简单得多。Windows 资源管理器对 open with 应用程序有自己的覆盖,我试图在最后几行代码中对其进行修改。如果您只是删除资源管理器覆盖,则文件关联将起作用。

我还告诉资源管理器,我通过SHChangeNotify()使用 P/Invoke 调用非托管函数来更改文件关联

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
    var 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);
于 2010-04-23T10:30:46.103 回答
32

这是一个完整的例子:

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;
    }
于 2017-06-29T05:26:13.353 回答
18

您可以通过 ClickOnce以托管方式执行此操作。自己不要对注册表大惊小怪。这可通过项目属性 => 发布 => 选项 => 文件关联的 VS2008 及更高版本(包括 Express)中的工具(即无 xml)获得

于 2010-04-21T10:30:42.977 回答
10

上面的解决方案不适用于 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);
于 2016-11-03T15:47:53.913 回答
8

如果您将密钥写入HKEY_CURRENT_USER\Software\Classes而不是HKEY_CLASSES_ROOT,则在 Vista 及更高版本下,这应该可以在没有管理员权限的情况下工作。

于 2010-04-21T13:02:13.507 回答
4

您使用的是旧版本的 Visual Studio,Vista 会将您的程序视为“旧版”Windows 应用程序。并重定向您所做的注册表写入。在你的程序中包含一个清单,这样你就会看起来支持 Vista。VS2008 及更高版本自动包含此清单。

请注意,这仍然无法为您的用户解决问题,她不太可能在关闭 UAC 的情况下运行您的应用程序。您需要编写一个单独的应用程序,该应用程序具有链接清单并要求管理员权限。它需要将 requestExecutionLevel 设置为 requireAdministrator 的清单。

于 2010-04-21T12:52:36.227 回答
4

如果您使用的是 Visual Studio 2015,请安装设置和部署扩展。创建一个安装向导,然后将您的 .exe 文件附加到它。在解决方案资源管理器中右键单击您的主程序转到-view,-file types,然后右键单击文件类型并选择添加新文件类型。根据您的需要更改所有属性,然后构建 MSI 安装程序。

注意:我重新阅读了您的问题并意识到您不需要安装程序。对此感到抱歉,尽管您应该考虑使用一个,因为它为您的程序提供了更多的自定义。

于 2016-11-15T16:51:18.473 回答
0

将文件扩展名与您自己的程序相关联的实际方法:

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);
于 2020-02-20T14:36:28.017 回答
0

线

FileReg.CreateSubKey("shell\open\command").SetValue("", applicationPath + " %1");

应修改为

FileReg.CreateSubKey("shell\open\command").SetValue("", $"\"{applicationPath}\" \"%1\"");

如果您不希望路径中的空格有问题,例如:

C:\我的文件夹\我的文件.txt

于 2020-04-17T12:42:28.797 回答