Brows.Win32.PropertySystem 1.0.0

dotnet add package Brows.Win32.PropertySystem --version 1.0.0
                    
NuGet\Install-Package Brows.Win32.PropertySystem -Version 1.0.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Brows.Win32.PropertySystem" Version="1.0.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Brows.Win32.PropertySystem" Version="1.0.0" />
                    
Directory.Packages.props
<PackageReference Include="Brows.Win32.PropertySystem" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Brows.Win32.PropertySystem --version 1.0.0
                    
#r "nuget: Brows.Win32.PropertySystem, 1.0.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Brows.Win32.PropertySystem@1.0.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Brows.Win32.PropertySystem&version=1.0.0
                    
Install as a Cake Addin
#tool nuget:?package=Brows.Win32.PropertySystem&version=1.0.0
                    
Install as a Cake Tool

Brows.Win32.PropertySystem

Win32 property system for .NET.

A small, dependency-free wrapper over the Windows Property System that lets you read and write file metadata — EXIF data, media tags, document properties, and anything else exposed by a registered property handler.

Install

dotnet add package Brows.Win32.PropertySystem

Targets net462, net48, net8.0-windows, and net10.0-windows.

Windows only. The library talks to propsys.dll, ole32.dll, oleaut32.dll, and shell32.dll through COM interop, so it has no package dependencies of its own.

Usage

Everything lives in the Brows.Win32 namespace.

Read a single property

using Brows.Win32;

var dateTaken = PropertySystem.GetPropertyDescription("System.Photo.DateTaken");
var value = PropertySystem.GetPropertyValue(@"C:\photos\example.jpg", dateTaken, throwOnError: true);

Console.WriteLine(value.Display);  // "8/14/2025 5:03 AM" — formatted for the current locale
Console.WriteLine(value.Object);   // 8/14/2025 10:03:00 AM — a DateTime, in UTC

Display is the value formatted by the property's own rules; Object is the raw managed value. The two can differ in more than formatting — date properties are stored in UTC but displayed in local time, as above.

A property the file has never had set still returns a PropertyValue, with an empty Display and a null Object.

Read everything a file carries

var descriptions = PropertySystem.EnumeratePropertyDescriptions(fileName, throwOnError: false);
foreach (var value in PropertySystem.EnumeratePropertyValues(fileName, descriptions, throwOnError: false)) {
    Console.WriteLine($"{value.Description.DisplayName}: {value.Display}");
}

Use EnumeratePropertyValues rather than repeated GetPropertyValue calls when you want several properties from the same file — it opens the property store once.

Write properties

var title = PropertySystem.GetPropertyDescription("System.Title");
PropertySystem.SetPropertyValue(fileName, title, "Sunset over the harbour");

Or write several at once, committed as a single change:

PropertySystem.SetPropertyValues(fileName, new[] {
    new KeyValuePair<PropertyDescription, object>(title, "Sunset over the harbour"),
    new KeyValuePair<PropertyDescription, object>(keywords, new[] { "sunset", "harbour" }),
}, ignoreError: null);

Copy metadata between files

PropertySystem.TransferPropertyValues(
    sourceFileName,
    destinationFileName,
    ignoreProperty: null,
    ignoreError: (property, hr) => true);

TransferPropertyValues copies every property it can read, including read-only and calculated ones such as System.Size, so you will normally want an ignoreError callback to skip the ones that cannot be written back.

Match property names

var wildcard = new PropertyWildcard("System.Photo.*");

wildcard.Matches("System.Photo.DateTaken");  // true
wildcard.Matches("System.Music.Artist");     // false

A * segment matches that segment and every segment after it. Without a *, the pattern is an exact match — System.Photo does not match System.Photo.DateTaken. Comparison is case-insensitive.

Resolve legacy property names

PropertyNameMap.GetCanonicalName("WhenTaken");   // "System.Photo.DateTaken"
PropertyNameMap.GetCanonicalNames("Copyright");  // ["System.Copyright", "System.Image.Copyright"]

Legacy names are matched case-insensitively, as Windows matches them. Unknown names return null; pass throwIfNotFound: true to get an ArgumentException instead. This is a lookup for legacy names only — a canonical name passed in returns null, since it needs no translation.

API

Type Purpose
PropertySystem Static entry point for reading and writing file metadata.
PropertyDescription A property in the schema: its CanonicalName and DisplayName.
PropertyValue A value read from a file: its Description, Display, and Object.
PropertyWildcard Matches property names against a dot-separated pattern.
PropertyNameMap Maps legacy property names onto the canonical names that replaced them.

Things to know

Canonical names are case-sensitive. System.Photo.DateTaken resolves; system.photo.datetaken does not. GetPropertyDescription throws a COMException with TYPE_E_ELEMENTNOTFOUND (0x8002802B) for a name that is not registered. Legacy names, by contrast, are matched case-insensitively, both by Windows and by PropertyNameMap.

Not every file type is writable. Writing goes through the property handler registered for the file's extension. Plain .txt files, for example, have no writable handler and fail with E_FAIL (0x80004005). JPEG, MP3, and Office documents work. Read-only and calculated properties such as System.Size fail with WINCODEC_ERR_PROPERTYNOTSUPPORTED (0x88982F41) regardless of file type.

The enumeration methods are lazy. EnumeratePropertyDescriptions and EnumeratePropertyValues are iterators, so argument validation and I/O failures surface when you start enumerating, not when you call the method. The throwOnError parameter controls whether a file that cannot be opened throws or simply yields nothing.

Legacy names are ambiguous in places. Microsoft's published legacy-name table lists a few names against more than one canonical name — Copyright and Status among them — so PropertyNameMap maps each legacy name to a list. GetCanonicalName returns the first entry, which is the one Windows' own alias resolution picks. The table also includes ten names, including the whole System.Fax.* set, that are no longer registered on current versions of Windows; they are retained for fidelity and marked in the source.

Building

dotnet build
dotnet test

Requires the .NET 10 SDK (see global.json) and Windows. The test suite exercises the real property system against real files, creating and deleting its own temporary directory as it runs.

License

MIT

Product Compatible and additional computed target framework versions.
.NET net8.0-windows7.0 is compatible.  net9.0-windows was computed.  net10.0-windows was computed.  net10.0-windows7.0 is compatible. 
.NET Framework net462 is compatible.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 is compatible.  net481 was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.
  • .NETFramework 4.6.2

    • No dependencies.
  • .NETFramework 4.8

    • No dependencies.
  • net10.0-windows7.0

    • No dependencies.
  • net8.0-windows7.0

    • No dependencies.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.0 53 9/23/2026