.netwpfopenfiledialogmtpfileopenpicker

How to import multiple files from a smartphone in a WPF app?


We encounter a problem using Microsoft.Win32.OpenFileDialog in a WPF app (targeting .NET Framework 4.7.2).

We want to be able to import multiple files from a smartphone connected to a computer via USB connection. However, it seems impossible to do so using the OpenFileDialog, because it fires message box "Cannot open multiple items from this location. Try selecting a single item instead.". You can reproduce it using this simple code and selecting at least 2 pictures from your smartphone:

var openFileDialog = new Microsoft.Win32.OpenFileDialog();
openFileDialog.Multiselect = true;
openFileDialog.ShowDialog();

This problem is known (see here and here) and related to the MTP protocol. The problem does not occur with a smartphone connected using the alternative "USB Mas Storage" (UMS), but this option is not available on modern smartphones.

The UWP FileOpenPicker seems to handle better the selection of multiple items (see here to see how to use it in a WPF app) but we accounter an InvalidCastException when using PickMultipleFilesAsync. The problem is described here and the answers suggest that FileOpenPicker cannot be used in WPF .NET Framework apps.

As said in the comments, an other way to achieve this would be to implement a file picker from skratch, using the MTP protocol for the smartphone devices. The downside of this approach is that the .NET and UWP file pickers offer a lots of great features and are fully integrated to the OS.

We have run out of ideas to solve this problem, so my question is:

Is there a way to import multiple files from a smartphone using the Windows file picker in a WPF app targeting .NET Framework?

The same question has been asked on the Microsoft Q&A.


Solution

  • I'm a bit late to reply but I found the solution thanks to the Microsoft Q&A thread.

    As far as I know, it's not possible to select multiple files from a MTP device using the Microsoft.Win32.OpenFileDialog because you can not set all the IFileOpenDialog options.

    As a result, you need to use your own IFileOpenDialog to set the options: be sure to add the FOS_ALLOWMULTISELECT option and remove the FOS_FORCEFILESYSTEM:

    ComFileDialog.IFileOpenDialog fileOpenDialog = null;
    ComFileDialog.IShellItemArray shellItemArray = null;
    ComFileDialog.IShellItem shellItem = null;
    try
    {
        // Set options
        fileOpenDialog = (ComFileDialog.IFileOpenDialog)new ComFileDialog.FileOpenDialogRCW();
        fileOpenDialog.GetOptions(out var options);
        options |= ComFileDialog.FOS_ALLOWMULTISELECT | ComFileDialog.FOS_FILEMUSTEXIST | ComFileDialog.FOS_PATHMUSTEXIST;
        fileOpenDialog.SetOptions(options);
    
        // Show window
        if (fileOpenDialog.Show() != ComFileDialog.S_OK)
        {
            return;
        }
    
        // Get results
        if (fileOpenDialog.GetResults(out shellItemArray) != ComFileDialog.S_OK)
        {
            return;
        }
    
        uint items = 0;
        if (shellItemArray.GetCount(out items) != ComFileDialog.S_OK)
        {
            return;
        }
    
        // Result loop
        for (uint item = 0; item < items; item++)
        {
            try
            {
                if (shellItemArray.GetItemAt(item, out shellItem) != ComFileDialog.S_OK)
                {
                    continue;
                }
    
                // Use the IShellItem
            }
            finally
            {
                if (shellItem != null)
                {
                    Marshal.ReleaseComObject(shellItem);
                    shellItem = null;
                }
            }
        }
    }
    finally
    {
        if (fileOpenDialog != null)
        {
            Marshal.ReleaseComObject(fileOpenDialog);
        }
        if (shellItemArray != null)
        {
            Marshal.ReleaseComObject(shellItemArray);
        }
        if (shellItem != null)
        {
            Marshal.ReleaseComObject(shellItem);
        }
    }
    

    You can find find the Window Shell API Interfaces on pinvoke.net: