I have set some custom properties on a file in cloud file workspace using StorageProviderItemProperties.SetASync(IStorageItem, IEnumerable).
I am now trying to retrieve the values later using below code :
StorageFile file;
var properties = await file.Properties.RetrievePropertiesAsync(["System.StorageProviderCustomStates"])
It will provide me a single result in properties dictionary with a value of byte[]. I would like to deserialize it into something meaningful. I have tried to :
Please suggest if there is some structure to this data that we can work with or any alternative way to extract custom statuses previously set on a file.
Usage
IStorageItem storageItem = await GetStorageItemAsync(path)
using(var propertyStore = new StorageItemPropertyProvider())
{
if(storageItem is IStorageItemProperties storageItemProperties)
{
return await propertyStore.GetPropertiesAsync(storageItemProperties);
}
}
Required Classes ( Using Vanara library)
public sealed class StorageItemPropertyProvider : IDisposable
{
private IPropertyStore? propertyStore;
private bool isDisposed = false;
public StorageItemPropertyProvider()
{
propertyStore = CreatePropertyStore() ?? throw new InvalidOperationException("Failed to create IPropertyStore");
}
/// <summary>
/// Creates an instance of IPropertyStore using PSCreateMemoryPropertyStore function from Vanara
/// </summary>
/// <returns></returns>
private static IPropertyStore? CreatePropertyStore()
{
Guid IID_IPropertyStore = typeof(IPropertyStore).GUID;
PSCreateMemoryPropertyStore(IID_IPropertyStore, out object propStore).ThrowIfFailed();
return propStore as IPropertyStore;
}
/// <summary>
/// Retrieves the StorageProviderItemProperty of a storage item that were previously set on the storage item
/// </summary>
public async Task<Dictionary<string, StorageProviderItemProperty>> GetPropertiesAsync(IStorageItemProperties storageItem)
{
ThrowIfDisposed();
Dictionary<string, StorageProviderItemProperty> properties = new();
var customStates = (await storageItem.Properties.RetrievePropertiesAsync(["System.StorageProviderCustomStates"])).FirstOrDefault();
if (customStates.Value is byte[] customStatesValue)
{
LoadByteArrayIntoPersistStream(customStatesValue);
//System.ItemCustomState.StateList holds the IDs of the custom states
using PROPVARIANT stateList = GetProperty("System.ItemCustomState.StateList");
if (stateList.Value is string[] states && states.Any())
{
//System.ItemCustomState.Values holds the values for each ID in System.ItemCustomState.StateList
using PROPVARIANT valueList = GetProperty("System.ItemCustomState.Values");
string[] values = valueList.Value as string[] ?? new string[states.Count()];
//System.ItemCustomState.IconReferences holds the values for each ID in System.ItemCustomState.StateList
using PROPVARIANT iconList = GetProperty("System.ItemCustomState.IconReferences");
string[] icons = iconList.Value as string[] ?? new string[states.Count()];
int index = 0;
foreach (var propertyID in states)
{
properties.Add(propertyID, new StorageProviderItemProperty()
{
Id = int.Parse(propertyID),
Value = (values)[index],
IconResource = (icons)[index]
});
index++;
}
}
}
return properties;
}
/// <summary>
/// Loads the byte array representation of serialized StorageItemProperties of a IStorageITem into the IPersistSerializedPropStorage
/// </summary>
/// <param name="data"></param>
/// <exception cref="InvalidOperationException">When IPropertyStore is not a IPersistSerializedPropStorage</exception>
private void LoadByteArrayIntoPersistStream(byte[] data)
{
if (data is not null)
{
// Allocate memory for the byte array
IntPtr pData = Marshal.AllocHGlobal(data.Length);
Marshal.Copy(data, 0, pData, data.Length);
if (propertyStore is IPersistSerializedPropStorage persistStream)
{
// Load the byte array into IPersistStream
persistStream.SetPropertyStorage(pData, (uint)data.Length);
}
else
{
throw new InvalidOperationException("PropertyStore does not support IPersistSerializedPropStorage");
}
// Clean up
Marshal.FreeHGlobal(pData);
}
}
/// <summary>
/// Retrieves the property value for the given property key
/// </summary>
/// <param name="propertyKey">Canonical name of the property</param>
/// <returns></returns>
PROPVARIANT GetProperty(string propertyKey)
{
PROPVARIANT result = new();
PropSys.PSGetPropertyKeyFromName(propertyKey, out var key);
propertyStore?.GetValue(key, result);
return result;
}
/// <summary>
/// Throws <see cref="ObjectDisposedException"/> if the object is disposed
/// </summary>
/// <exception cref="ObjectDisposedException"></exception>
void ThrowIfDisposed()
{
if (isDisposed)
{
throw new ObjectDisposedException(nameof(StorageItemPropertyProvider));
}
}
/// <inheritdoc/>
public void Dispose()
{
if (!isDisposed)
{
Marshal.ReleaseComObject(propertyStore);
propertyStore = null;
isDisposed = true;
}
}
}