读取/写入'C#'扩展文件属性

112

我正在尝试找出如何在C#中读写扩展文件属性,例如在Windows资源管理器中可以看到的注释、比特率、访问日期和类别等信息。你有什么想法吗? 编辑:我主要会读写视频文件(AVI/DIVX/...)


4
这个问题显然 没有 得到解答,因为被接受的答案仅展示了如何获取扩展属性,而 没有说明如何设置扩展属性 - Dmitri Nesteruk
1
设置扩展属性请参见https://dev59.com/NG435IYBdhLWcg3wkBBe - VoteCoffee
10个回答

90

对于那些不喜欢VB的人,这里提供C#版本:

请注意,您需要从“引用”对话框的COM选项卡中添加对“Microsoft Shell Controls and Automation”的引用。

public static void Main(string[] args)
{
    List<string> arrHeaders = new List<string>();

    Shell32.Shell shell = new Shell32.Shell();
    Shell32.Folder objFolder;

    objFolder = shell.NameSpace(@"C:\temp\testprop");

    for( int i = 0; i < short.MaxValue; i++ )
    {
        string header = objFolder.GetDetailsOf(null, i);
        if (String.IsNullOrEmpty(header))
            break;
        arrHeaders.Add(header);
    }

    foreach(Shell32.FolderItem2 item in objFolder.Items())
    {
        for (int i = 0; i < arrHeaders.Count; i++)
        {
            Console.WriteLine(
              $"{i}\t{arrHeaders[i]}: {objFolder.GetDetailsOf(item, i)}");
        }
    }
}

4
如何设置一个文本文件的作者或出版商等属性?我使用的是Windows 7系统,使用了这个工具,它显示了空白的作者和出版商以及其他282个属性。 - Vaibhav Garg
2
@Vainbhav - 你不能设置这些。 - csharptest.net
28
你需要在“引用”对话框的“COM”选项卡中添加对“Microsoft Shell Controls and Automation”的引用。 - csharptest.net
4
如何进行设置更好地在这个问题中得到解答:https://dev59.com/NG435IYBdhLWcg3wkBBe - Nicolas Raoul
1
在Windows 10中,它只返回52个字段,不包括帧速率、帧高度、宽度等。 - Minh Nguyen
显示剩余12条评论

34

解决方案2016

将以下NuGet包添加到您的项目中:

  • Microsoft.WindowsAPICodePack-Shell(由Microsoft提供)
  • Microsoft.WindowsAPICodePack-Core(由Microsoft提供)

读写属性

using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;

string filePath = @"C:\temp\example.docx";
var file = ShellFile.FromFilePath(filePath);

// Read and Write:

string[] oldAuthors = file.Properties.System.Author.Value;
string oldTitle = file.Properties.System.Title.Value;

file.Properties.System.Author.Value = new string[] { "Author #1", "Author #2" };
file.Properties.System.Title.Value = "Example Title";

// Alternate way to Write:

ShellPropertyWriter propertyWriter =  file.Properties.GetPropertyWriter();
propertyWriter.WriteProperty(SystemProperties.System.Author, new string[] { "Author" });
propertyWriter.Close();

重要提示:

文件必须是由特定的指定软件创建的有效文件。每种文件类型都有特定的扩展文件属性,并非所有属性都可写。

如果您在桌面上右键单击文件并且无法编辑属性,则您也无法在代码中进行编辑。

示例:

  • 在桌面上创建 txt 文件,将其扩展名更改为 docx。 您无法编辑其 作者标题 属性。
  • 使用 Word 打开它,进行编辑和保存。现在可以编辑。

因此,请确保使用一些 try catch

更多主题: Microsoft Docs:实现属性处理程序


我没有看到来自Microsoft的这些名称的任何软件包。 - Jacob Brewer
2
@JacobBrewer,实际上这个包是由“acdvorak”上传的:https://www.nuget.org/packages/Microsoft.WindowsAPICodePack-Shell/。在Visual Studio NuGet-Package-Manager中,它显示为“由Microsoft提供”。其他名称类似的软件包是它的分支,可能也能正常工作甚至更好。我不知道... - Martin Schneider

27

谢谢,我应该能够从这里整理出我需要的东西。 - David Hayes

26

这是VB.NET示例,用于读取所有扩展属性:

Sub Main()
        Dim arrHeaders(35)

        Dim shell As New Shell32.Shell
        Dim objFolder As Shell32.Folder

        objFolder = shell.NameSpace("C:\tmp")

        For i = 0 To 34
            arrHeaders(i) = objFolder.GetDetailsOf(objFolder.Items, i)
        Next
        For Each strFileName In objfolder.Items
            For i = 0 To 34
                Console.WriteLine(i & vbTab & arrHeaders(i) & ": " & objfolder.GetDetailsOf(strFileName, i))
            Next
        Next

    End Sub

您需要在“引用”对话框的COM选项卡中添加对Microsoft Shell Controls and Automation的引用。


2
值得注意的是,在Windows 7(至少)上,您可以将arrHeaders的大小增加到略大于280,并获得足够的额外元数据。我发现这一点是在寻找从WTV文件(Windows 7 Media Center记录的电视节目)获取元数据的方法时发现的。 - Richard
1
第一个 for i = 0 to 34 循环应该是 for i = 0 to Integer.MaxValue。然后测试 objFolder.GetDetailsOf(objFolder.Items, i) 的返回值。如果它返回 null 或空格,则您已经获得了所有标题。 - Tim Murphy
很抱歉,@TimMurphy,这并不正确(至少在Windows 10或7上)。有些标题是空的,所以您需要遍历所有索引。(当然,由于它们不是数百万个,您可以将循环限制为1,000。) - skst

8

GetDetailsOf() 方法 - 从文件夹中检索有关项目的详细信息,例如其大小、类型或最后修改时间。文件属性可能因 Windows-OS 版本而异。

List<string> arrHeaders = new List<string>();

 Shell shell = new ShellClass();
 Folder rFolder = shell.NameSpace(_rootPath);
 FolderItem rFiles = rFolder.ParseName(filename);

 for (int i = 0; i < short.MaxValue; i++)
 {
      string value = rFolder.GetDetailsOf(rFiles, i).Trim();
      arrHeaders.Add(value);
 }

我已经复制并使用了上面相同的代码。但是,在这里运行时我遇到了COM异常 Folder rFolder = shell.NameSpace(_rootPath);。顺便说一下,我正在使用Windows 8操作系统。 - DonMax
1
那是什么错误?请确保您正在使用正确版本的Shell32.dll。检查一下这个链接,它可能会有用 - RajeshKdev
1
以上链接加1。您建议的链接运行良好。我需要您的另外一点帮助,即如何传递单个文件以获取元数据?因为它只接受传递文件夹。 - DonMax

8
谢谢大家提供这个帖子!当我想要找到一个exe文件的文件版本时,它帮助了我。但是,我需要自己弄清楚所谓的扩展属性的最后一位。
如果您在Windows资源管理器中打开exe(或dll)文件的属性,则会获得版本选项卡和该文件扩展属性的视图。我想访问其中一个值。
解决此问题的方法是使用FolderItem.ExtendedProperty属性索引器,如果您删除属性名称中的所有空格,则会获得该值。例如,文件版本变为FileVersion,然后您就可以得到它。
希望这对其他人有所帮助,只是想将此信息添加到此主题中。干杯!

2
这也确保了你的代码(至少大部分)仍然可以在非英语语言的机器上运行。 - Ed Norris
1
这里有一个小改进,FolderItem不包含ExtendedProperty()。但是FolderItem2包含。 - Mixxiphoid
这个答案比其他答案简单一些。我在这里提供了可行的示例代码:https://dev59.com/J3VC5IYBdhLWcg3wqzHV#46648086 - nawfal

7
  • 在查看了此主题和其他地方的许多解决方案后,我们编写了以下代码。这仅用于读取属性。
  • 我无法使Shell32.FolderItem2.ExtendedProperty函数正常工作,它应该接受一个字符串值并返回该属性的正确值和类型...对我来说始终为null,并且开发人员参考资源非常有限。
  • WindowsApiCodePack 似乎已被微软放弃,因此我们使用下面的代码。

用途:

string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
  1. Will return you the value of the extended property you want as a string for the given file and property name.
  2. Only loops until it found the specified property - not until all properties are discovered like some sample code
  3. Will work on Windows versions like Windows server 2008 where you will get the error "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'" if just trying to create the Shell32 Object normally.

    public static string GetExtendedFileProperty(string filePath, string propertyName)
    {
        string value = string.Empty;
        string baseFolder = Path.GetDirectoryName(filePath);
        string fileName = Path.GetFileName(filePath);
    
        //Method to load and execute the Shell object for Windows server 8 environment otherwise you get "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'"
        Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
        Object shell = Activator.CreateInstance(shellAppType);
        Shell32.Folder shellFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { baseFolder });
    
        //Parsename will find the specific file I'm looking for in the Shell32.Folder object
        Shell32.FolderItem folderitem = shellFolder.ParseName(fileName);
        if (folderitem != null)
        {
            for (int i = 0; i < short.MaxValue; i++)
            {
                //Get the property name for property index i
                string property = shellFolder.GetDetailsOf(null, i);
    
                //Will be empty when all possible properties has been looped through, break out of loop
                if (String.IsNullOrEmpty(property)) break;
    
                //Skip to next property if this is not the specified property
                if (property != propertyName) continue;    
    
                //Read value of property
                value = shellFolder.GetDetailsOf(folderitem, i);
            }
        }
        //returns string.Empty if no value was found for the specified property
        return value;
    }
    

1
请注意,您可以将shell转换为任何一个IShellDispatch接口(1-6),然后直接调用成员,而无需使用_InvokeMember()_Shell32.IShellDispatch ishell = (Shell32.IShellDispatch)shell; Shell32.Folder shellFolder = ishell.NameSpace(baseFolder); - skst

7

Jerker的答案更简单。这是一个可以工作的示例代码,来自微软

var folder = new Shell().NameSpace(folderPath);
foreach (FolderItem2 item in folder.Items())
{
    var company = item.ExtendedProperty("Company");
    var author = item.ExtendedProperty("Author");
    // Etc.
}

对于那些无法静态引用shell32的人,您可以像这样动态调用它:
var shellAppType = Type.GetTypeFromProgID("Shell.Application");
dynamic shellApp = Activator.CreateInstance(shellAppType);
var folder = shellApp.NameSpace(folderPath);
foreach (var item in folder.Items())
{
    var company = item.ExtendedProperty("Company");
    var author = item.ExtendedProperty("Author");
    // Etc.
}

2
这里提供了一个解决方案,可以读取(而不是写入)基于扩展属性的信息,该方案基于我在此页面和 help with shell32 objects 上找到的内容。
需要明确的是,这是一种hack。尽管该代码看起来仍然可以在Windows 10上运行,但会出现一些空属性。之前的Windows版本应该使用:
        var i = 0;
        while (true)
        {
            ...
            if (String.IsNullOrEmpty(header)) break;
            ...
            i++;

在Windows 10上,我们假设有大约320个属性可读取,并简单地跳过空条目:
    private Dictionary<string, string> GetExtendedProperties(string filePath)
    {
        var directory = Path.GetDirectoryName(filePath);
        var shell = new Shell32.Shell();
        var shellFolder = shell.NameSpace(directory);
        var fileName = Path.GetFileName(filePath);
        var folderitem = shellFolder.ParseName(fileName);
        var dictionary = new Dictionary<string, string>();
        var i = -1;
        while (++i < 320)
        {
            var header = shellFolder.GetDetailsOf(null, i);
            if (String.IsNullOrEmpty(header)) continue;
            var value = shellFolder.GetDetailsOf(folderitem, i);
            if (!dictionary.ContainsKey(header)) dictionary.Add(header, value);
            Console.WriteLine(header +": " + value);
        }
        Marshal.ReleaseComObject(shell);
        Marshal.ReleaseComObject(shellFolder);
        return dictionary;
    }

如上所述,您需要引用Com组件Interop.Shell32。
如果出现STA相关异常,您可以在此处找到解决方案: 使用Shell32获取文件扩展属性时出现异常 我不知道外国系统上这些属性名称会是什么样子,也找不到有关使用哪些可本地化常量来访问字典的信息。我还发现,返回的字典中并非所有属性都来自“属性”对话框。
顺便说一句,这种方法非常慢,在Windows 10上解析检索到的字符串中的日期将是一个挑战,因此从一开始就使用它似乎是个坏主意。
在Windows 10上,您应该绝对使用包含SystemPhotoProperties、SystemMusicProperties等的Windows.Storage库。 https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties 最后,我发布了一个更好的解决方案,它使用了WindowsAPICodePack 在这里

1

我不确定您要为哪些类型的文件编写属性,但taglib-sharp是一个出色的开源标记库,可以很好地封装所有这些功能。它具有对大多数流行媒体文件类型的内置支持,但也允许您使用几乎任何文件进行更高级的标记。

编辑:我已更新taglib sharp的链接。旧链接已不再有效。

编辑:根据kzu的评论,再次更新了链接。


这看起来非常有趣,我将主要查看视频文件(AVI,DIVX等)。谢谢指引。 - David Hayes
taglib-sharp的链接似乎已经失效了:-(这很奇怪,因为...http://developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples... 的Wiki指向该URL。 - SteveC
谢谢,SteveC。在我发布这篇文章时,两个链接都是有效的,我不确定哪一个是官方网站,现在看来novell是现在这个库的正确去处。 - mockobject
感谢kzu提醒,我已经更新了原回答中的链接,指向github位置。 - mockobject

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