读/写‘ Extended’文件属性(C #)

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

123218 次浏览

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.

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.

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.

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

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!

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

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

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

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