如何在 C# 中根据文件扩展名获取文件类型信息?(不是 MIME)

20

如何像资源管理器一样,基于文件扩展名获取通用的文件类型描述?不是MIME类型,而是最终用户看到的信息,例如:

.doc = Microsoft Office Word 97 - 2003 文档 .zip = ZIP 文件 .avi = 视频文件。

另外,我怎么可以获取似乎可用的“次要”信息,这些信息我猜想不是基于扩展名的。就像在“视频文件”上它可以给出电影的‘长度’或者在doc文件上有多少页等等...

5个回答

30

谢谢Dan,好的.. 这回答了我的第一个问题。可惜不是第二个。注意:不是所有内容都会打印出来.. 感谢 PInvoke.net

using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Diagnostics;


namespace WindowsFormsApplication1
{
    static class Program
    {
        [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);

        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
            Debug.WriteLine(FileExtentionInfo(AssocStr.Command, ".doc"), "Command");
            Debug.WriteLine(FileExtentionInfo(AssocStr.DDEApplication, ".doc"), "DDEApplication");
            Debug.WriteLine(FileExtentionInfo(AssocStr.DDEIfExec, ".doc"), "DDEIfExec");
            Debug.WriteLine(FileExtentionInfo(AssocStr.DDETopic, ".doc"), "DDETopic");
            Debug.WriteLine(FileExtentionInfo(AssocStr.Executable, ".doc"), "Executable");
            Debug.WriteLine(FileExtentionInfo(AssocStr.FriendlyAppName, ".doc"), "FriendlyAppName");
            Debug.WriteLine(FileExtentionInfo(AssocStr.FriendlyDocName, ".doc"), "FriendlyDocName");
            Debug.WriteLine(FileExtentionInfo(AssocStr.NoOpen, ".doc"), "NoOpen");
            Debug.WriteLine(FileExtentionInfo(AssocStr.ShellNewValue, ".doc"), "ShellNewValue");

            //  DDEApplication: WinWord
            //DDEIfExec: Ñﻴ߾
            //  DDETopic: System
            //  Executable: C:\Program Files (x86)\Microsoft Office\Office12\WINWORD.EXE
            //  FriendlyAppName: Microsoft Office Word
            //  FriendlyDocName: Microsoft Office Word 97 - 2003 Document


        }

        public static string FileExtentionInfo(AssocStr assocStr, string doctype)
        {
            uint pcchOut = 0;
            AssocQueryString(AssocF.Verify, assocStr, doctype, null, null, ref pcchOut);

            StringBuilder pszOut = new StringBuilder((int)pcchOut);
            AssocQueryString(AssocF.Verify, assocStr, doctype, null, 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
        }

    }
}

9

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

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



    }
}

1
非常感谢您!这个解决方案应该会得到很高的赞同,它立即起效,而且非常有效,而其他答案则不是完整的解决方案。这个解决方案将所有内容整合到一个可直接使用的类中,并且立即生效。 - Nicholas Petersen

4
直接从注册表中读取此类内容通常不是一个好主意(请参见Raymond Chen的博客以了解所有血腥细节)。在这种特定情况下,您需要的API是shlwapi.h中的AssocQueryString
以下是C++代码:
TCHAR buf[1024];
DWORD sz = sizeof(buf) / sizeof(TCHAR);
AssocQueryString(ASSOCF_INIT_DEFAULTTOSTAR, ASSOCSTR_FRIENDLYDOCNAME, L".sql", NULL, buf, &sz);

您可以通过C++/CLI公开一个友好的.NET API来使用此功能,也可以直接通过P/Invoke调用它。

3

XP中未知文件类型的一些额外if语句.. 如果不使用FriendlyDocName,可能无法正确处理除FriendlyDocName以外的其他内容,但这只是一个例子:

public static string FileExtentionInfo(AssocStr assocStr, string doctype)
{
   if ((doctype.Length <= 1) || !doctype.StartsWith(".")) return "";

   uint pcchOut = 0;
   AssocQueryString(AssocF.Verify, assocStr, doctype, null, null, ref pcchOut);

   if (pcchOut == 0) return (doctype.Trim('.').ToUpper() + " File");

   StringBuilder pszOut = new StringBuilder((int)pcchOut);
   AssocQueryString(AssocF.Verify, assocStr, doctype, null, pszOut, ref pcchOut);
   return pszOut.ToString();
}

干得好Pjanssen,我刚发现原始函数无法在XP上正常工作!干得好,省了我自己去做的时间! - JustAPleb

0

好老的FileSystemObject已经内置了这个功能。

如果您不介意使用它,那么以下代码非常简短。

在Windows Form应用程序中添加对Microsoft Scripting Runtime的引用并尝试此代码。

private void Form1_Load(object sender, EventArgs e) {
  getSometypes();
}
private void getSometypes()
{
  System.Diagnostics.Debug.WriteLine(getFileType(".txt"));
  System.Diagnostics.Debug.WriteLine(getFileType(".doc"));
  System.Diagnostics.Debug.WriteLine(getFileType(".xlsx"));
}    
private string getFileType(object ext)
{
  Scripting.FileSystemObject fso = new Scripting.FileSystemObject();
  string tempPath = System.IO.Path.GetTempPath();
  string tempFile = "";
  tempFile = tempPath + "tmp" + ext;
  System.IO.File.WriteAllText(tempFile, "");
  var f = fso.GetFile(tempFile);
  string t = f.Type;
  f.Delete();
  return t;
}

getFiletype函数创建一个带有指定扩展名的临时文件,然后使用FileSystemObject打开该文件并返回其类型,这就是您想要的类型描述。getSometypes函数将它们写入输出窗口。

在这种情况下(瑞典语):

文本文档
Microsoft Word 97-2003 文档
Microsoft Excel 电子表格

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