Some files has Summary tab in their Properties, This tab include information like Title, Author, Comments. Is there any way in C# to read the Comments of the file. I have to read only comments from image files like jpg.
The comments and other answer are good places to search. Here is some complete code to help you out. Ensure you reference shell32.dll first and the namespace Shell32. I've done this in LINQPad so it's a touch different.
Pick a test file and folder:
var folder = "...";
var file = "...";
Get the Shell objects:
// For our LINQPad Users
// var shellType = Type.GetTypeFromProgID("Shell.Application");
// dynamic app = Activator.CreateInstance(shellType);
Shell32.Shell app = new Shell32.Shell();
Get the folder and file objects:
var folderObj = app.NameSpace(folder);
var filesObj = folderObj.Items();
Find the possible headers:
var headers = new Dictionary<string, int>();
for( int i = 0; i < short.MaxValue; i++ )
{
string header = folderObj.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header))
break;
if (!headers.ContainsKey(header)) headers.Add(header, i);
}
You can print these out if you like - that's all possible headers available in that directory. Let's use the header 'Comments' as an example:
var testFile = filesObj.Item(file);
Console.WriteLine("{0} -> {1}", testFile.Name, folderObj.GetDetailsOf(testFile, headers["Comments"]));
Modify as needed!
The shell (shell32.dll) will help you to solve this poroblem. I recently found this great article on the MSDN (http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/94430444-283b-4a0e-9ca5-7375c8420622).
There is also a codeproject on reading ID3 tags.
Related
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/...)
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)}");
}
}
}
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
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.
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.
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);
}
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");
Will return you the value of the extended property you want as a
string for the given file and property name.
Only loops until it found the specified property - not until
all properties are discovered like some sample code
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
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.
I'd like to know if it's possible to set/edit a file extended properties (Explorer: Right-click > Properties > Details) using the Windows API Code Pack.
var shellFile = Microsoft.WindowsAPICodePack.Shell.ShellObject.FromParsingName(filePath);
var artistName = shellFile.Properties.GetProperty(SystemProperties.System.Music.DisplayArtist).ValueAsObject.ToString();
var duration = TimeSpan.FromMilliseconds(Convert.ToDouble(shellFile.Properties.GetProperty(SystemProperties.System.Media.Duration).ValueAsObject) * 0.0001);
I use these few lines to get the properties I want, but I don't know how to edit one of them (the artist name for example).
I know I can use taglib-sharp, but I'll use it only if there is no solution without external code.
Thanks you all for taking the time to help me.
I found a way to edit some properties with ShellPropertyWriter but some properties are read-only.
var shellFile = ShellFile.FromParsingName(filePath);
ShellPropertyWriter w = shellFile.Properties.GetPropertyWriter();
try
{
w.WriteProperty(SystemProperties.System.Author, new string[] { "MyTest", "Test" });
w.WriteProperty(SystemProperties.System.Music.Artist, new string[] { "MyTest", "Test" });
w.WriteProperty(SystemProperties.System.Music.DisplayArtist, "Test");
}
catch (Exception ex)
{
}
w.Close();
In this sample, the 2 first occurences of ShellPropertyWriter.WriteProperty() will do exactly the same, edit the "Contributing artists" field of the file (Explorer: Right-click > Properties > Details). The third call will throw an "Access denied" exception.
Some are editable, others are not. Just need to try.
You can write to the ShellFile directly by setting the value of the properties without ShellPropertyWriter:
var shellFile = ShellFile.FromFilePath(filePath);
shellFile.Properties.System.Author.Value = new string[] { "MyTest", "Test" };
shellFile.Properties.System.Music.Artist.Value = new string[] { "MyTest", "Test" };
shellFile.Properties.System.Music.DisplayArtist.Value = "Test";
Just be aware, that to be able to edit codec-specific fields of a file, it's necessary to have the codec installed on the computer.
I'm trying to use libgit2sharp to get a previous version of a file. I would prefer the working directory to remain as is, at the very least restored to previous condition.
My initial approach was to try to stash, checkout path on the file I want, save that to a string variable, then stash pop. Is there a way to stash pop? I can't find it easily. Here's the code I have so far:
using (var repo = new Repository(DirectoryPath, null))
{
var currentCommit = repo.Head.Tip.Sha;
var commit = repo.Commits.Where(c => c.Sha == commitHash).FirstOrDefault();
if (commit == null)
return null;
var sn = "Stash Name";
var now = new DateTimeOffset(DateTime.Now);
var diffCount = repo.Diff.Compare().Count();
if(diffCount > 0)
repo.Stashes.Add(new Signature(sn, "x#y.com", now), options: StashModifiers.Default);
repo.CheckoutPaths(commit.Sha, new List<string>{ path }, CheckoutModifiers.None, null, null);
var fileText = File.ReadAllText(path);
repo.CheckoutPaths(currentCommit, new List<string>{path}, CheckoutModifiers.None, null, null);
if(diffCount > 0)
; // stash Pop?
}
If there's an easier approach than using Stash, that would work great also.
Is there a way to stash pop? I can't find it easily
Unfortunately, Stash pop requires merging which isn't available yet in libgit2.
I'm trying to use libgit2sharp to get a previous version of a file. I would prefer the working directory to remain as is
You may achieve such result by opening two instances of the same repository, each of them pointing to different working directories. The Repository constructor accepts a RepositoryOptions parameter which should allow you to do just that.
The following piece of code demonstrates this feature. This creates an additional instance (otherRepo) that you can use to retrieve a different version of the file currently checked out in your main working directory.
string repoPath = "path/to/your/repo";
// Create a temp folder for a second working directory
string tempWorkDir = Path.Combine(Path.GetTempPath(), "tmp_wd");
Directory.CreateDirectory(newWorkdir);
// Also create a new index to not alter the main repository
string tempIndex = Path.Combine(Path.GetTempPath(), "tmp_idx");
var opts = new RepositoryOptions
{
WorkingDirectoryPath = tempWorkDir,
IndexPath = tempIndex
};
using (var mainRepo = new Repository(repoPath))
using (var otherRepo = new Repository(mainRepo.Info.Path, opts))
{
string path = "file.txt";
// Do your stuff with mainrepo
mainRepo.CheckoutPaths("HEAD", new[] { path });
var currentVersion = File.ReadAllText(Path.Combine(mainRepo.Info.WorkingDirectory, path));
// Use otherRepo to temporarily checkout previous versions of files
// Thank to the passed in RepositoryOptions, this checkout will not
// alter the workdir nor the index of the main repository.
otherRepo.CheckoutPaths("HEAD~2", new [] { path });
var olderVersion = File.ReadAllText(Path.Combine(otherRepo.Info.WorkingDirectory, path));
}
You can get a better grasp of this RepositoryOptions type by taking a look at the tests in RepositoryOptionFixture that exercise it.
Suppose I have a program which allows a user to upload any kind of file. Along with getting generic information such as file type and file size, I would like to attempt to grab any extra information (such as document properties like author, last revised, etc) that may be transported along with the document.
Since I don't have any knowledge about the incoming document/file ahead of time, I can't simply use classes that are specific to, say Microsoft Office docs. I need to do this generically and then construct a dynamic object or dictionary to hold any found key/value results.
Is this possible? If so, how? Any help is appreciated!
I found a few answers on StackOverflow for this, but none gave me a nice, clean dictionary of document properties. Here is what I finally came up with and it seems to be working nicely (you will need to reference "Microsoft Shell Controls and Automation" from the COM folder and add using Shell32; to your code:
public static Dictionary<string,string> GetDocumentMetadata(string fileName)
{
var properties = new Dictionary<string,string>();
var arrHeaders = new List<string>();
var shell = new Shell();
var objFolder = shell.NameSpace(HttpContext.Current.Server.MapPath("~/RawFiles"));
var file = objFolder.ParseName(fileName);
for (var i = 0; i < short.MaxValue; i++)
{
var header = objFolder.GetDetailsOf(null, i);
if (String.IsNullOrEmpty(header))
break;
arrHeaders.Add(header);
}
for (var i = 0; i < arrHeaders.Count; i++)
{
var value = objFolder.GetDetailsOf(file, i);
if (!String.IsNullOrEmpty(value))
{
properties.Add(arrHeaders[i], value);
}
}
return properties;
}
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/...)
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)}");
}
}
}
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
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.
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.
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);
}
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");
Will return you the value of the extended property you want as a
string for the given file and property name.
Only loops until it found the specified property - not until
all properties are discovered like some sample code
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
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.