please, either allow System.IO all over HDD, or drastically improve speed of StorageFolder queries

John Torjo 861 Reputation points
2019-11-01T11:26:09.967+00:00

My preference would clearly be to just have access to System.IO, once I've requested

This is clearly possible, since a Destkop Bridge app uses System.IO and can do as it pleases. It would clearly be the best case scenario.

Otherwise, StorageFolder queries should become insanely faster.

I have a 7200rpm HDD, and a folder with 797 pictures. Using System.IO to enumerate the files and get their length is <1ms.

Using a StorageFolder query is simply worse than javascript from 20 years ago - it takes over 9 seconds.

That is >9000 slower.

No matter how you slice or dice it, I don't even have words for how slow this is.

StorageFileQueryResult query_result = last_query_;  
if (dir != last_dir_) {  
	var folder = await StorageFolder.GetFolderFromPathAsync(dir);  
  
	// https://blogs.msdn.microsoft.com/adamdwilson/2017/12/20/fast-file-enumeration-with-partially-initialized-storagefiles/  
	// note: does not seem to be any big diff compared to .GetFilesAsync()  
	QueryOptions query = new QueryOptions() {  
		FolderDepth = FolderDepth.Shallow,  
		//Filter out all files that have WIP enabled on them  
		ApplicationSearchFilter = "System.Security.EncryptionOwners:[] ",  
		IndexerOption = IndexerOption.UseIndexerWhenAvailable,  
		SortOrder = { new SortEntry { AscendingOrder = false, PropertyName = "System.DateModified" }}  
	};  
	query.SetPropertyPrefetch(PropertyPrefetchOptions.BasicProperties, new List());  
	query_result = folder.CreateFileQueryWithOptions(query);  
	last_query_ = query_result;  
}  
  
uint start = 0, len = uint.MaxValue;  
foreach (var f in (await query_result.GetFilesAsync(start,len))) {  
	media.Add(new media_info {  
		full_file_name = f.Path,  
		is_video = is_video(f.Path),  
		name = f.Name,  
		write_date = f.DateCreated.Date,  
		width = 0, height = 0,   
		thumbnail_source = null,  
	});  
}  
Universal Windows Platform (UWP)
{count} votes

3 additional answers

Sort by: Most helpful
  1. Richard Zhang-MSFT 6,936 Reputation points
    2019-11-05T01:29:16.523+00:00

    Hi,

    You use IndexerOption = IndexerOption.UseIndexerWhenAvailable in your code. If you want a faster query speed, this value is not recommended.

    I recommend using IndexerOption.OnlyUseIndexerAndOptimizeForIndexedProperties, because only this value tells the system to return partial StorageFiles to your app.

    From document, you can see this:

    This tells the system to return partial StorageFiles to your app. Any other IndexerOption value will return full StorageFiles and not see any performance gains.

    The IndexerOption.OnlyUseIndexerAndOptimizeForIndexedProperties is valid on Windows 10 1709(16299).


    I have a 7200rpm HDD, and a folder with 797 pictures. Using System.IO to enumerate the files and get their length is <1ms.

    In the description, you find that System.IO queries are much faster than StorageFolder. If you plan to use System.IO for file queries, this is possible.

    You can get a handle to a file from StorageFile using the IStorageItemHandleAccess interface. Once the file handle is obtained, you can initialize System.IO where UWP can directly access ( By default apps have direct file access only to their ApplicationData and InstalledLocations ) , and you can access all Win32 file APIs, like open a brokered handle directly with [CreateFileFromApp](https://learn.microsoft.com/en-us/previous-versions/windows/desktop/legacy/mt846585(v=vs.85)) or CreateFile2 to get a faster query speed.


  2. Fay Wang - MSFT 5,196 Reputation points
    2019-11-08T14:55:25.243+00:00

    Hello,​

    Welcome to our Microsoft Q&A platform!

    >>Should I understand that a StorageFolder supports the IStorageItemHandleAccess inteface?

    For files and folders, there are separate interfaces. The IStorageItemHandleAccess interface only work with files, so you can try the IStorageFolderHandleAccess. You can access the COM interface directly through C#, but you need to define the interface yourself. And you could get a HANDLE by using a StorageFolder and a filename under the belongs to the folder, then you can use the handle to do next. The following code is an example about how to use IStorageFolderHandleAccess.

    [ComImport]  
    [Guid("DF19938F-5462-48A0-BE65-D2A3271A08D6")]  
    [InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]  
    public interface IStorageFolderHandleAccess  
    {  
        uint Create(string filename, uint creationOptions , uint access, uint sharing, uint option, IntPtr opLockHandler, ref IntPtr handle);  
    };  
    ​  
    ​public sealed partial class MainPage : Page​  
    {​  
        public MainPage()​  
        {​  
            this.InitializeComponent();​  
        }​  
    ​  
        private async void MyFunction(object sender, TextControlPasteEventArgs e)​  
        {​  
    ​  
            StorageFolder storageFolder = await KnownFolders.PicturesLibrary.GetFolderAsync("Camera Roll");  
            var comInterface = Marshal.GetComInterfaceForObject(storageFolder, typeof(IStorageFolderHandleAccess));  
            var storageHandleAccess = (IStorageFolderHandleAccess)Marshal.GetObjectForIUnknown(comInterface);  
             
            const uint HAO_READ = 0x120089;  
            const uint HAO_WRITE = 0x120116;  
    
            IntPtr handle = IntPtr.Zero;  
            storageHandleAccess.Create("fileName", 0x3, HAO_READ | HAO_WRITE, 0, 0, IntPtr.Zero, ref handle);  
    
            var safeHandle = new SafeFileHandle(handle, true);  
            ......  
    ​  
        }​  
    }  
    

    Once you get the safeHandle handle of the specific file from its folder, you can use it to file read and write using the System.IO API.

    >>How can I enumerate all files from a StorageFolder, using System.IO

    If you want to enumerate files from localFolder or installedLocation, you can directly use System.IO to enumerate. For example:

    string root = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;  
    string path = root + @"\Assets";  
      
    DirectoryInfo dirinfo = new DirectoryInfo(path);  
    FileInfo[] files = dirinfo.GetFiles();  
    

    If you want to enumerate files from other folders that you do no have the direct access, you need to use win32 api to achieve it(e.g. FindFirstFile/FindNextFile).

    Thanks.

    2 people found this answer helpful.

  3. David Hollowell -MSFT 21 Reputation points
    2019-11-27T16:12:55.747+00:00

    Hi jtorjo, We have a work item on this being considered for a future release of Windows 10. I can't guarantee when or if this will release, but I did request a release note to be added when/if it does.
    You can see any updates here: https://learn.microsoft.com/en-us/windows/uwp/whats-new/windows-10-build-18362. There's a "what's new" section, typically found on the left side of that page, which will include any release notes.
    Please also note, Q&A is a great place to discuss things like this, but in the future, please consider using Feedback Hub to submit similar requests. You can open feedback hub by clicking the Start button and typing "Feedback Hub".
    Best Regards, -David

    2 people found this answer helpful.