在Windows中查找打开特定文件类型的默认应用程序

64

我正在使用C#开发一个针对.NET Framework 2.0的应用程序,需要找到打开特定文件类型的默认应用程序。

我知道如果只想使用该应用程序打开文件,可以使用以下代码:

System.Diagnostics.Process.Start( "C:\...\...\myfile.html" );

在默认浏览器中打开HTML文档,或者

System.Diagnostics.Process.Start( "C:\...\...\myfile.txt" );

在默认文本编辑器中打开文本文件。

然而,我想要做的是在默认文本编辑器中打开没有必须为.txt扩展名的文件(例如),所以我需要能够找到用于打开.txt文件的默认应用程序,这将使我能够直接调用它。

我猜测我需要P/Invoke某些Win32 API才能做到这一点,但是通过Google和MSDN进行快速查找并没有发现任何有趣的信息;我确实找到了很多与我寻找的完全无关的页面,但是没有找到类似的内容。

6个回答

81

所有当前的答案都是不可靠的。注册表是一个实现细节,这样的代码在我的Windows 8.1机器上确实存在问题。正确的方法是使用Win32 API,具体来说是AssocQueryString

using System.Runtime.InteropServices;

[DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
public static extern uint AssocQueryString(
    AssocF flags, 
    AssocStr str,  
    string pszAssoc, 
    string pszExtra, 
    [Out] StringBuilder pszOut, 
    ref uint pcchOut
); 

[Flags]
public enum AssocF
{
    None = 0,
    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,
    Init_IgnoreUnknown = 0x400,
    Init_Fixed_ProgId = 0x800,
    Is_Protocol = 0x1000,
    Init_For_File = 0x2000
}

public enum AssocStr
{
    Command = 1,
    Executable,
    FriendlyDocName,
    FriendlyAppName,
    NoOpen,
    ShellNewValue,
    DDECommand,
    DDEIfExec,
    DDEApplication,
    DDETopic,
    InfoTip,
    QuickTip,
    TileInfo,
    ContentType,
    DefaultIcon,
    ShellExtension,
    DropTarget,
    DelegateExecute,
    Supported_Uri_Protocols,
    ProgID,
    AppID,
    AppPublisher,
    AppIconReference,
    Max
}

相关文档:

使用示例:

static string AssocQueryString(AssocStr association, string extension)
{
    const int S_OK = 0;
    const int S_FALSE = 1;

    uint length = 0;
    uint ret = AssocQueryString(AssocF.None, association, extension, null, null, ref length);
    if (ret != S_FALSE)
    {
        throw new InvalidOperationException("Could not determine associated string");
    }

    var sb = new StringBuilder((int)length); // (length-1) will probably work too as the marshaller adds null termination
    ret = AssocQueryString(AssocF.None, association, extension, null, sb, ref length);
    if (ret != S_OK)
    {
        throw new InvalidOperationException("Could not determine associated string"); 
    }

    return sb.ToString();
}

4
这个评论被投票否决,但对我来说有效,只需要进行一些小的修改(已在上面编辑)。你需要使用"using System.Runtime.InteropServices"。我找不到枚举值,所以从MSDN上复制:ASSOCFASOCSTR。为了始终访问文本编辑器,我使用AssocStr.ASSOCSTR_EXECUTABLE和".txt"调用此方法。 - Giles
4
我非常喜欢并欣赏这个答案,但我也很难找到AssocF和AssocStr的参考资料。最终我在另一个stackoverflow的答案中找到了这些标志的实现: https://dev59.com/8nRA5IYBdhLWcg3w9irq 投了一票,谢谢! - turkinator
3
AssocStr.Executable 在处理像 .png 这样的图像文件扩展名时无法正常工作。你该如何解决它们的关联应用程序? - IngoB
2
今天我遇到了这个SO问题,并且和@IngoB一样遇到了.png扩展名的问题。差异似乎源于扩展名是由“完整”的EXE还是UWP(它们仍然被称为这个,对吧:)应用程序处理...例如,在我的情况下,.png由Photos应用程序处理,因此虽然AssocStr.Executable会失败,但请求AssocStr.AppId将返回Microsoft.Windows.Photos_8wekyb3d8bbwe!App。然后可以使用类似于此答案的代码运行:https://dev59.com/cZ7ha4cB1Zd3GeqPlpcx#41906116 - NFrank
1
@pwrigshihanomoronimo 谢谢,已更正为 WayBack Machine 链接,并向微软提交了有关错误链接的反馈(当我点击您提到的文档中的 ASSOCF 时,我被带到了另一个枚举)。 - Ohad Schneider
显示剩余3条评论

17

您可以在注册表部分 HKEY_CLASSES_ROOT 下检查扩展名和操作详细信息。有关此内容的文档,请参见MSDN。或者,您可以使用IQueryAssociations 接口。


1
在我看来,使用FindExecutable是更好的方式:https://dev59.com/zWkw5IYBdhLWcg3w9vF-#9540278 - this.myself

8

当然,这是个低级错误。

HKEY_CLASSES_ROOT\.txt

包含对...的引用

HKEY_CLASSES_ROOT\txtfile

其中包含子键。

HKEY_CLASSES_ROOT\txtfile\shell\open\command

这段文字提到了记事本。

好的,已经排好序,非常感谢!

Bart


1
这是 %SystemRoot%\system32\NOTEPAD.EXE %1。在我的情况下,这不是默认程序。此外,您不能使用 Process.Start 来启动它,因为其中包含了 %SystemRoot% 字面量和 %1 参数。所有这些都需要在代码中进行特殊处理,而这种处理无法完全和确定地覆盖所有情况,因为谁知道在这样的字符串中可能会有什么其他意外情况。 - bytecode77

5

2
我冒昧将那段代码转换为C#并进行了一些修改:https://dev59.com/imgu5IYBdhLWcg3wYWFX#17773554 - Ohad Schneider

3
一个晚一点的答案,但是有一个好的NUGET包可以处理文件关联:File Association 链接 NUGET File Association 使用很简单,例如将所有允许的文件扩展名添加到上下文菜单中:
private void OnMenuSourceFileOpening(object sender, ...)
{   // open a context menu with the associated files + ".txt" files
    if (File.Exists(this.SelectedFileName))
    {
        string fileExt = Path.GetExtension(this.SelectedFileNames);
        string[] allowedExtensions = new string[] { fileExt, ".txt" };
        var fileAssociations = allowedExtensions
            .Select(ext => new FileAssociationInfo(ext));
        var progInfos = fileAssociations
            .Select(fileAssoc => new ProgramAssociationInfo (fileAssoc.ProgID));
        var toolstripItems = myProgInfos
            .Select(proginfo => new ToolStripLabel (proginfo.Description) { Tag = proginfo });
        // add also the prog info as Tag, for easy access
        //  when the toolstrip item is selected
        // of course this can also be done in one long linq statement

        // fill the context menu:
        this.contextMenu1.Items.Clear();
        this.contextMenuOpenSourceFile.Items.AddRange (toolstripItems.ToArray());
    }
}

3
你可以直接查询注册表。首先获取HKEY_CLASSES_ROOT\.ext下的Default条目。这将给你类名。例如,.txt的默认值为txtfile。
然后打开HKEY_CLASSES_ROOT\txtfile\Shell\Open\Command。这将给你使用的默认命令。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接