Read/Write 'Extended' file properties (C#)
Asked Answered
B

11

113

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/...)

Brutalize answered 20/10, 2008 at 21:56 Comment(2)
This question is clearly not answered since the accepted answer only shows how to get the extended properties and not how to set them.Holliehollifield
For setting the extended properties see #5338183Jugate
S
90

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)}");
        }
    }
}
Silin answered 19/1, 2010 at 19:16 Comment(17)
how would one set one of these values? for e.g. Author or publisher for a .txt file. I am on win 7 and used this and it does show a blank author and publisher and 282 other propertiesBlakemore
@Vainbhav - You can't set these.Silin
You have to add a reference to Microsoft Shell Controls and Automation from the COM tab of the Refences dialog.Silin
Be aware that this solution does not work on Windows XP. When accessing Shell32 from XP you get a COM exception.Blakeblakelee
I have used this in XP, what COM exception are you seeing?Silin
How to SET them is better covered in this question: #5338183Masaccio
@Silin I get a NullReferenceException on the line with string header = objFolder.GetDetailsOf(null, i);Runesmith
This is better than the VB code since the number of headers is usually higher. I got more output with this logic than the hardcoded 35Working
I have copied and used the same code above. But, I am getting COM exception at runime here objFolder = shell.NameSpace(@"C:\temp\testprop");. FYI, I am using Windows 8 OS.Angelitaangell
It's good, but what about writing file properties as it is mentioned in question title?Unstrained
@Unstrained it's not possible for some as they are defined by the file format.Silin
Is there a way to do this without COM? How about creating a new blank file with properties?Unshapen
In Windows 10 it only returns 52 fields not including Frame rate, Frame Height, Width,.... ?Hurter
As the previous user mentioned, this doesn't return the video properties. Is there an alternative?Skat
The reason this doesn't return all of the properties is that it breaks out of the loop if a returned header is empty. if (String.IsNullOrEmpty(header)) break; Since some headers are empty, you need to delete this and loop through all possible indexes. (Of course, since there can't be that many properties, you can reasonably limit the loop to 1,000 instead of short.MaxValue.)Seaborg
What package or library do I have to install in order to access Shell?Wizened
how would this work from the newly released .NET5.0? Is it possible to use PInvoke or so? C#/WinRT is not the way to go: github.com/microsoft/CsWinRT/issues/591Dellora
L
34

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

Lemkul answered 23/6, 2016 at 9:2 Comment(2)
I don't see any packages by these names from MicrosoftTippett
@JacobBrewer It is actually the one that was uploaded by "acdvorak": nuget.org/packages/Microsoft.WindowsAPICodePack-Shell . In Visual Studio NuGet-Package-Manager it is shown as "by Microsoft". The other packages with similar names are forks of it and might also work properly or even bettter. I don't know...Lemkul
D
27

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.

Durham answered 20/10, 2008 at 22:17 Comment(1)
Thanks, I should be able to pull together what I need from thisBrutalize
L
26

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.

Litman answered 28/11, 2008 at 12:28 Comment(3)
Worth noting that on Windows 7 (at least) you can increase the size of arrHeaders to just over 280 and get back plenty of additional meta-data. I found this out when I was looking for a way to get meta-data from a WTV file (Windows 7 Media Center recorded television show).Labourer
The first for i = 0 to 34 loop should be for i = 0 to Integer.MaxValue. Then test the return value of objFolder.GetDetailsOf(objFolder.Items, i). If it returns null or whitespace space then you have all the headers.Espouse
@TimMurphy, unfortunately, that's not correct (at least on Windows 10 or 7). Some headers are empty, so you need to loop through all of the indexes. (Of course, since there aren't millions of them, you could limit the loop to 1,000.)Seaborg
P
8

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!

Progression answered 23/8, 2010 at 12:25 Comment(3)
This also ensures that your code will (at least mostly) still work on non-English language machines.Immutable
One small improvement here, FolderItem does not contain ExtendedProperty(). FolderItem2 does however.Phalangeal
This answer is tad simpler than others. I have given sample code that works here: https://mcmap.net/q/193558/-read-write-39-extended-39-file-properties-cStiff
F
8

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);
 }
Farika answered 27/2, 2013 at 12:1 Comment(3)
I have copied and used the same code above. But, I am getting COM exception at runime here Folder rFolder = shell.NameSpace(_rootPath);. FYI, I am using Windows 8 OS.Angelitaangell
What is that error? Make sure you are using the correct version of Shell32.dll. Check this it maybe UsefulFarika
+1 for the above link. The link that you suggested is working fine. I need one more help from you. ie, How can I pass the single file to get the metadata?? since, it accepts passing only the folder.Angelitaangell
S
7

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.
}
Stiff answered 9/10, 2017 at 13:49 Comment(0)
C
7
  • 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;
    }
    
Chairmanship answered 23/4, 2018 at 1:31 Comment(1)
Note that instead of using InvokeMember(), you can cast shell to any of the IShellDispatch interfaces (1-6) and call the member directly. Shell32.IShellDispatch ishell = (Shell32.IShellDispatch)shell; Shell32.Folder shellFolder = ishell.NameSpace(baseFolder);Seaborg
S
2

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

Sakmar answered 10/8, 2019 at 19:56 Comment(0)
G
1

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.

Grados answered 20/10, 2008 at 22:42 Comment(4)
This looks very interesting, I'll mainly be looking at video files (AVI, DIVX etc). Thanks for the pointerBrutalize
The taglib-sharp link seems to be dead :-( - which is weird as the wiki at ... developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples ... points to that URLTetrahedron
Thanks, SteveC, at the time I posted this both links were valid and I wasn't sure which was the official place to go, looks like novell is the right site to go to for this lib now.Grados
Thanks for the heads up kzu, I have updated the link in my original answer to point to the github location as well.Grados
O
0

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.

Oatcake answered 9/2 at 5:44 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.