读/写“扩展”文件属性 (C#)

发布于 2024-11-28 06:30:15 字数 120 浏览 1 评论 0 原文

我正在尝试找出如何在 C# 中读取/写入扩展文件属性 例如您可以在 Windows 资源管理器中看到的评论、比特率、访问日期、类别等。 有什么想法如何做到这一点? 编辑:我将主要读取/写入视频文件(AVI/DIVX/...)

I'm trying to find out how to read/write to the extended file properties in C#
e.g. Comment, Bit Rate, Date Accessed, Category etc that you can see in Windows explorer.
Any ideas how to do this?
EDIT: I'll mainly be reading/writing to video files (AVI/DIVX/...)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

思念绕指尖 2024-12-05 06:30:15

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

注意,您必须从“引用”对话框的 COM 选项卡添加对 Microsoft Shell 控件和自动化 的引用。

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

For those of not crazy about VB, here it is in c#:

Note, you have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.

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)}");
        }
    }
}
荒人说梦 2024-12-05 06:30:15

解决方案 2016

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

  • Microsoft 的 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。你不能
    编辑其 AuthorTitle 属性。
  • 用Word打开,编辑并保存
    它。现在你可以了。

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

进一步的主题:
Microsoft Docs:实现属性处理程序

Solution 2016

Add following NuGet packages to your project:

  • Microsoft.WindowsAPICodePack-Shell by Microsoft
  • Microsoft.WindowsAPICodePack-Core by Microsoft

Read and Write Properties

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();

Important:

The file must be a valid one, created by the specific assigned software. Every file type has specific extended file properties and not all of them are writable.

If you right-click a file on desktop and cannot edit a property, you wont be able to edit it in code too.

Example:

  • Create txt file on desktop, rename its extension to docx. You can't
    edit its Author or Title property.
  • Open it with Word, edit and save
    it. Now you can.

So just make sure to use some try catch

Further Topic:
Microsoft Docs: Implementing Property Handlers

演出会有结束 2024-12-05 06:30:15

有一篇针对 ID3 阅读器的CodeProject 文章。以及 kixtart.org 上的帖子其中包含其他属性的更多信息。基本上,您需要调用 GetDetailsOf()< shell32.dll文件夹 shell 对象上的 /code> 方法

There's a CodeProject article for an ID3 reader. And a thread at kixtart.org that has more information for other properties. Basically, you need to call the GetDetailsOf() method on the folder shell object for shell32.dll.

岁月静好 2024-12-05 06:30:15

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的引用> 对话框。

This sample in VB.NET reads all extended properties:

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

You have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the References dialog.

青萝楚歌 2024-12-05 06:30:15

谢谢你们这个话题!当我想找出 exe 文件版本时,它对我很有帮助。然而,我需要自己弄清楚所谓的扩展属性的最后一点。

如果在 Windows 资源管理器中打开 exe(或 dll)文件的属性,您将看到“版本”选项卡以及该文件的“扩展属性”视图。我想访问这些值之一。

解决这个问题的方法是使用属性索引器FolderItem.ExtendedProperty,如果删除属性名称中的所有空格,您将获得该值。例如,文件版本变为 FileVersion,然后就可以了。

希望这对其他人有帮助,只是想我将此信息添加到此线程中。干杯!

Thank you guys for this thread! It helped me when I wanted to figure out an exe's file version. However, I needed to figure out the last bit myself of what is called Extended Properties.

If you open properties of an exe (or dll) file in Windows Explorer, you get a Version tab, and a view of Extended Properties of that file. I wanted to access one of those values.

The solution to this is the property indexer FolderItem.ExtendedProperty and if you drop all spaces in the property's name, you'll get the value. E.g. File Version goes FileVersion, and there you have it.

Hope this helps anyone else, just thought I'd add this info to this thread. Cheers!

街道布景 2024-12-05 06:30:15

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

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

GetDetailsOf() Method - Retrieves details about an item in a folder. For example, its size, type, or the time of its last modification. File Properties may vary based on the Windows-OS version.

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);
 }
幸福还没到 2024-12-05 06:30:15

Jerker 的答案稍微简单一些。以下是可从 MS 运行的示例代码< /a>:

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.
}

Jerker's answer is little simpler. Here's sample code which works from MS:

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

For those who can't reference shell32 statically, you can invoke it dynamically like this:

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.
}
酸甜透明夹心 2024-12-05 06:30:15
  • 在查看了该线程和其他地方的许多解决方案之后
    下面的代码被放在一起。这只是为了读取一个属性。
  • 我无法得到
    Shell32.FolderItem2.ExtendedProperty 函数可以工作,应该是
    获取一个字符串值并返回正确的值和类型
    属性...这对我来说始终是空的,并且开发人员参考资源非常薄弱。
  • WindowsApiCodePack 似乎
    已被微软放弃,这给我们带来了下面的代码。

使用:

string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
  1. 将返回您想要的扩展属性的值
    给定文件和属性名称的字符串。
  2. 仅循环直到找到指定的属性 - 直到
    所有属性都像一些示例代码一样被发现
  3. 将在 Windows 版本(例如 Windows Server 2008)上工作,您将收到错误 “如果只是尝试创建通常为 Shell32 对象。

    public static string GetExtendedFileProperty(string filePath, string propertyName)
    {
        字符串值 = string.Empty;
        字符串baseFolder = Path.GetDirectoryName(filePath);
        字符串文件名 = Path.GetFileName(filePath);
    
        //为 Windows Server 8 环境加载和执行 Shell 对象的方法,否则您会收到“无法将类型为‘System.__ComObject’的 COM 对象转换为接口类型‘Shell32.Shell’”
        输入 shellAppType = Type.GetTypeFromProgID("Shell.Application");
        对象 shell = Activator.CreateInstance(shellAppType);
        Shell32.Folder shellFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { baseFolder });
    
        //Parsename会在Shell32.Folder对象中找到我要查找的特定文件
        Shell32.FolderItem 文件夹项 = shellFolder.ParseName(文件名);
        if (文件夹项!= null)
        {
            for (int i = 0; i < Short.MaxValue; i++)
            {
                //获取属性索引i的属性名称
                字符串属性 = shellFolder.GetDetailsOf(null, i);
    
                //循环完所有可能的属性后将为空,跳出循环
                if (String.IsNullOrEmpty(属性)) 中断;
    
                //如果这不是指定的属性,则跳到下一个属性
                if (属性!=属性名称) 继续;    
    
                //读取属性值
                值 = shellFolder.GetDetailsOf(folderitem, i);
            }
        }
        //如果没有找到指定属性的值,则返回 string.Empty
        返回值;
    }
    

  • After looking at a number of solutions on this thread and elsewhere
    the following code was put together. This is only to read a property.
  • I could not get the
    Shell32.FolderItem2.ExtendedProperty function to work, it is supposed
    to take a string value and return the correct value and type for that
    property... this was always null for me and developer reference resources were very thin.
  • The WindowsApiCodePack seems
    to have been abandoned by Microsoft which brings us the code below.

Use:

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;
    }
    
清旖 2024-12-05 06:30:15

这是一个用于读取(而不是写入)扩展属性的解决方案,基于我在此页面和 有关 shell32 对象的帮助

需要明确的是,这是一个黑客行为。看起来此代码仍将在 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 上,您绝对应该使用 Windows.Storage 库,其中包含 SystemPhotoProperties、SystemMusicProperties 等。
https://learn.microsoft.com/ en-us/windows/uwp/files/quickstart-getting-file-properties

最后,我发布了一个使用 WindowsAPICodePack 的更好的解决方案那里

Here is a solution for reading - not writing - the extended properties based on what I found on this page and at help with shell32 objects.

To be clear this is a hack. It looks like this code will still run on Windows 10 but will hit on some empty properties. Previous version of Windows should use:

        var i = 0;
        while (true)
        {
            ...
            if (String.IsNullOrEmpty(header)) break;
            ...
            i++;

On Windows 10 we assume that there are about 320 properties to read and simply skip the empty entries:

    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;
    }

As mentioned you need to reference the Com assembly Interop.Shell32.

If you get an STA related exception, you will find the solution here:

Exception when using Shell32 to get File extended properties

I have no idea what those properties names would be like on a foreign system and couldn't find information about which localizable constants to use in order to access the dictionary. I also found that not all the properties from the Properties dialog were present in the dictionary returned.

BTW this is terribly slow and - at least on Windows 10 - parsing dates in the string retrieved would be a challenge so using this seems to be a bad idea to start with.

On Windows 10 you should definitely use the Windows.Storage library which contains the SystemPhotoProperties, SystemMusicProperties etc.
https://learn.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties

And finally, I posted a much better solution that uses WindowsAPICodePack there

嘿咻 2024-12-05 06:30:15

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

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

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

I'm not sure what types of files you are trying to write the properties for but taglib-sharp is an excellent open source tagging library that wraps up all this functionality nicely. It has a lot of built in support for most of the popular media file types but also allows you to do more advanced tagging with pretty much any file.

EDIT: I've updated the link to taglib sharp. The old link no longer worked.

EDIT: Updated the link once again per kzu's comment.

半夏半凉 2024-12-05 06:30:15

StorageFile 类 可以读取和写入文件的扩展属性。 StorageFolder 类 可以用于读取文件夹的属性,但大多数文件夹的属性无法更新。可以使用以下方式访问该文件:

StorageFile StorageObject = await StorageFile.GetFileFromPathAsync(filepath);

要读取视频文件的 Director,扩展属性名称为 System.Video.Director;要读取的属性列表可以指定为:

List<string> propertyNames = new();
propertyNames.Add("System.Video.Director");

并使用以下方式检索属性:

IDictionary<string, object> extraProperties = await StorageObject.Properties.RetrievePropertiesAsync(propertyNames);

然后结果位于 extraProperties[PropertyName] 中。对于诸如 Director 之类的属性,对象是字符串数组,但对于其他属性,对象可能是字符串或其他内容。

要更新属性,请使用 List> ,如下所示:

List<KeyValuePair<string, object>> PropertiesToSave = new();
string[] Directors = new string[] { "Steven Spielberg", "George Miller" };
PropertiesToSave.Add(new KeyValuePair<string, object>("System.Video.Director", Directors));
try
{
    await StorageObject.Properties.SavePropertiesAsync(PropertiesToSave);
}
catch (Exception ex)
{
    // error
}

在我的测试中,当我更新属性然后立即读回它时,我得到了之前的结果,但该属性是后来检查时更新了。

如果您的项目属性的目标操作系统版本为 10.0.17763.0 或更高版本,那么您就可以访问这些类。该文档将这些类称为 UWP 和 WinRT,但它们在 .Net 内置中可用。

The StorageFile Class can read and write extended properties for files. The StorageFolder Class can be used to read properties for folders but most of the properties for folders cannot be updated. The file can be accessed using:

StorageFile StorageObject = await StorageFile.GetFileFromPathAsync(filepath);

To read the Director for a video file the extended property name is System.Video.Director; a list of properties to be read can be specified as in:

List<string> propertyNames = new();
propertyNames.Add("System.Video.Director");

And the properties retrieved using:

IDictionary<string, object> extraProperties = await StorageObject.Properties.RetrievePropertiesAsync(propertyNames);

Then the results are in extraProperties[PropertyName]. For a property such as Director the object is a string array but for other properties the object might be a string or something else.

To update a property a List<KeyValuePair<string, object>> is used as in:

List<KeyValuePair<string, object>> PropertiesToSave = new();
string[] Directors = new string[] { "Steven Spielberg", "George Miller" };
PropertiesToSave.Add(new KeyValuePair<string, object>("System.Video.Director", Directors));
try
{
    await StorageObject.Properties.SavePropertiesAsync(PropertiesToSave);
}
catch (Exception ex)
{
    // error
}

In my testing, when I updated a property then immediately read it back I got the previous results but the property was updated when they were checked later.

If your project's properties have 10.0.17763.0 or greater for the Target OS Version then you have access to the classes. The documentation refers to the classes as being UWP and WinRT but they are available in .Net built-in.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文