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 :
- Deserialize it in to an object using MemoryStream. Deserialization failed.
- Convert to string System.Text.Encoding.UTF8.GetString(byte[]), however, it did not provide any structured data that can be used to extract custom status (values and icons) that were set. I have also tried with different Encoding like ASCII and Unicode without success.
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.
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 :
- Deserialize it in to an object using MemoryStream. Deserialization failed.
- Convert to string System.Text.Encoding.UTF8.GetString(byte[]), however, it did not provide any structured data that can be used to extract custom status (values and icons) that were set. I have also tried with different Encoding like ASCII and Unicode without success.
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.
Share Improve this question asked Mar 26 at 6:42 Nish26Nish26 9879 silver badges16 bronze badges 3 |1 Answer
Reset to default 0Usage
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;
}
}
}
IPersistStream
and thenIPersistStream::Load
(the values are an array of bytes that you must convert to a stream). After that you can useIPropertyStore
's methods likeGetValue
, etc. If it doesn't work instead ofIPersistStream
getIPersistSerializedPropStorage
and useIPersistSerializedPropStorage::SetPropertyStorage
and pass a pointer to the array of bytes – Simon Mourier Commented Mar 26 at 8:02