windows-shellcloud-files-api

Retrieve custom status set on a StorageFile in cloud file api


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.


Solution

  • 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;
            }
        }
    }