如何正确检索与 Windows 上的扩展关联的打开命令
How do I correctly retrieve the open command associated with an extension on Windows?
我正在尝试找出用于打开给定扩展名文件的可执行文件,因此如果扩展名没有,我可以显示该可执行文件的图标。
我知道文件类型的HKEY_CLASSES_ROOT注册表项中的开放谓词,但我发现它的值并不总是正确的。
例如,我目前正在OS X上运行Windows。PDF文件的默认关联是Safari。我通过资源管理器将默认关联更改为Adobe Reader。注册表中的打开谓词仍然是 Safari,但是当我双击 PDF 文件时,它会使用 Adobe Reader 打开。32 位和 64 位注册表具有相同的值。
是否有更好的方法来检索文件类型与 .NET 或 winapi 的关联?
最好的
办法可能是使用Assoc*
组函数,例如通过 PInvoke AssocQueryKey()
和AssocQueryString()
。但是,我不知道 .NET 框架的许多化身附带的众多类之一是否已经为您包装了它。但命令行管理程序 API 为你提供了一个检索此信息的选项。
另请注意备注部分,其中指出上述函数是 IQueryAssociations
接口的包装器,这使得你更有可能从 .NET 中获得所需的另一条更直接的路由。
旧式函数是这个:FindExecutable()
。不过,不要使用它。它使用与ShellExecute()
相同的有缺陷的错误代码魔法。
另请参阅 Windows 的解答:列出和启动与扩展关联的应用程序
我的代码包括检查以防止一些常见错误...希望有帮助:-)
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
}
}
}
您还需要检查HKEY_USERS\{User}\Software\Classes。 用户可以覆盖自己的默认应用程序。
相关文章:
- 是否可以通过C++扩展强制多个python进程共享同一内存
- static_assert在宏中,但也可以扩展到可以用作函数参数的东西
- 如何将这个C++哈希表转换为动态扩展和收缩,而不是使用硬设置的最大值
- 扩展光电二极管探测器以支持多个传感器
- 关联容器的下界复杂性:成员函数与非成员函数
- C++中的VLA,扩展名为std=C++11
- OpenGL 和 GLM 矩阵无法正确扩展,总是按比例缩小
- std::future_error:无关联状态
- 基于范围的 for 循环:迭代使用一个元素扩展的向量
- C++返回 Numpy 数组的 Python 扩展模块
- 扩展可变参数模板中的变量名称
- 扩展C++生成的代码的模板参数类型名称
- 我想通过带有C++和Python的插件创建一个可扩展的应用程序
- VSCode IntelliSense无法识别SDL框架的SDL_image扩展库
- 如何调用 ShellExecute 以使用 C++ 打开具有特定程序的文件,而无需关联相同的文件扩展名
- 在没有关联的小部件插件的QT设计器中注册扩展程序
- 如何正确检索与 Windows 上的扩展关联的打开命令
- 如何获取与某个扩展相关联的程序列表
- OS X 将扩展与应用程序关联
- 如何打开程序与其文件扩展名关联的文件