【问题标题】:How do I correctly retrieve the open command associated with an extension on Windows?如何正确检索与 Windows 上的扩展关联的打开命令?
【发布时间】:2012-11-19 20:00:20
【问题描述】:

我试图找出用于打开给定扩展名文件的可执行文件,因此如果扩展名没有,我可以显示该可执行文件的图标。

我知道 HKEY_CLASSES_ROOT 注册表项中文件类型的开放动词,但我发现它的值并不总是正确的。

例如,我目前在 OS X 上的 Parallels 中运行 Windows。PDF 文件的默认关联是 Safari。我通过资源管理器将默认关联更改为 Adob​​e Reader。注册表中的打开动词仍然是 Safari,但是当我双击 PDF 文件时,它会使用 Adob​​e Reader 打开。 32 位和 64 位注册表具有相同的值。

有没有更好的方法来检索文件类型的关联,无论是使用 .NET 还是 winapi?

【问题讨论】:

  • 最大的问题是文件类型有“类”,然后有扩展名。它们可以通过多种方式组合。
  • 是否有某种 .NET 函数或 winapi 调用可以处理这个问题?
  • 看我的回答。我不太熟悉 .NET 世界,但我在回答中给出了一些建议,它们甚至可以帮助您找到描述的 Win32/Shell API 内容的现有 .NET 类。

标签: c# c++ winapi windows-shell


【解决方案1】:

您最好的选择可能是使用Assoc* 函数组,例如AssocQueryKey()AssocQueryString() via PInvoke。但是,我不知道 .NET 框架附带的众多类中的一个是否已经为您包装了它。但 Shell API 为您提供了检索此信息的选项。

还请注意备注部分,其中指出上述函数是IQueryAssociations 接口的包装器,这使得您更有可能从 .NET 中获得另一条更直接的途径。

旧式函数是这个:FindExecutable()。不过,不要使用它。它使用与ShellExecute() 相同的有缺陷的错误代码魔法。

另请参阅Windows: List and Launch applications associated with an extension 的答案

【讨论】:

    【解决方案2】:

    我的代码包括检查以防止一些常见错误...希望它有所帮助:-)

    using System;
    using System.Diagnostics;
    using System.IO;
    using System.Runtime.InteropServices;
    using System.Text;
    
    namespace HQ.Util.Unmanaged
    {
        /// <summary>
        /// Usage: string executablePath = FileAssociation.GetExecFileAssociatedToExtension(pathExtension, "open");
        /// Usage: string command FileAssociation.GetExecCommandAssociatedToExtension(pathExtension, "open");
        /// </summary>
        public static class FileAssociation
        {
            /// <summary>
            /// 
            /// </summary>
            /// <param name="ext"></param>
            /// <param name="verb"></param>
            /// <returns>Return null if not found</returns>
            public static string GetExecCommandAssociatedToExtension(string ext, string verb = null)
            {
                if (ext[0] != '.')
                {
                    ext = "." + ext;
                }
    
                string  executablePath = FileExtentionInfo(AssocStr.Command, ext, verb);
    
                // Ensure to not return the default OpenWith.exe associated executable in Windows 8 or higher
                if (!string.IsNullOrEmpty(executablePath) && File.Exists(executablePath) &&
                    !executablePath.ToLower().EndsWith(".dll"))
                {
                    if (executablePath.ToLower().EndsWith("openwith.exe"))
                    {
                        return null; // 'OpenWith.exe' is th windows 8 or higher default for unknown extensions. I don't want to have it as associted file
                    }
                    return executablePath;
                }
                return executablePath;
            }
    
            /// <summary>
            /// 
            /// </summary>
            /// <param name="ext"></param>
            /// <param name="verb"></param>
            /// <returns>Return null if not found</returns>
            public static string GetExecFileAssociatedToExtension(string ext, string verb = null)
            {
                if (ext[0] != '.')
                {
                    ext = "." + ext;
                }
    
                string executablePath = FileExtentionInfo(AssocStr.Executable, ext, verb); // Will only work for 'open' verb
                if (string.IsNullOrEmpty(executablePath))
                {
                    executablePath = FileExtentionInfo(AssocStr.Command, ext, verb); // required to find command of any other verb than 'open'
    
                    // Extract only the path
                    if (!string.IsNullOrEmpty(executablePath) && executablePath.Length > 1) 
                    {
                        if (executablePath[0] == '"')
                        {
                            executablePath = executablePath.Split('\"')[1];
                        }
                        else if (executablePath[0] == '\'')
                        {
                            executablePath = executablePath.Split('\'')[1];
                        }
                    }
                }
    
                // Ensure to not return the default OpenWith.exe associated executable in Windows 8 or higher
                if (!string.IsNullOrEmpty(executablePath) && File.Exists(executablePath) &&
                    !executablePath.ToLower().EndsWith(".dll"))
                {
                    if (executablePath.ToLower().EndsWith("openwith.exe"))
                    {
                        return null; // 'OpenWith.exe' is th windows 8 or higher default for unknown extensions. I don't want to have it as associted file
                    }
                    return executablePath;
                }
                return executablePath;
            }
    
            [DllImport("Shlwapi.dll", SetLastError = true, CharSet = CharSet.Auto)]
            static extern uint AssocQueryString(AssocF flags, AssocStr str, string pszAssoc, string pszExtra, [Out] StringBuilder pszOut, [In][Out] ref uint pcchOut);
    
            private static string FileExtentionInfo(AssocStr assocStr, string doctype, string verb)
            {
                uint pcchOut = 0;
                AssocQueryString(AssocF.Verify, assocStr, doctype, verb, null, ref pcchOut);
    
                Debug.Assert(pcchOut != 0);
                if (pcchOut == 0)
                {
                    return "";
                }
    
                StringBuilder pszOut = new StringBuilder((int)pcchOut);
                AssocQueryString(AssocF.Verify, assocStr, doctype, verb, pszOut, ref pcchOut);
                return pszOut.ToString();
            }
    
            [Flags]
            public enum AssocF
            {
                Init_NoRemapCLSID = 0x1,
                Init_ByExeName = 0x2,
                Open_ByExeName = 0x2,
                Init_DefaultToStar = 0x4,
                Init_DefaultToFolder = 0x8,
                NoUserSettings = 0x10,
                NoTruncate = 0x20,
                Verify = 0x40,
                RemapRunDll = 0x80,
                NoFixUps = 0x100,
                IgnoreBaseClass = 0x200
            }
    
            public enum AssocStr
            {
                Command = 1,
                Executable,
                FriendlyDocName,
                FriendlyAppName,
                NoOpen,
                ShellNewValue,
                DDECommand,
                DDEIfExec,
                DDEApplication,
                DDETopic
            }
    
    
    
        }
    }
    

    【讨论】:

    • 干得好。 FindExecutableA 停止为我工作后来到这里。
    【解决方案3】:

    您还需要检查 HKEY_USERS\{User}\Software\Classes。用户可以覆盖他们自己的默认应用程序。

    【讨论】:

    • 我的注册表中的 HKEY_USERS\{guid}_CLASSESHKEY_USERS\{guid}\Software\Classes 键有旧的(也是不正确的)关联。
    猜你喜欢
    • 1970-01-01
    • 2011-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-11
    • 1970-01-01
    • 2021-04-24
    相关资源
    最近更新 更多