c++c++-cxc++-winrt

How do I create a std::filesystem::exists() compatible path from a Windows::Storage::StorageFile?


I am using a FileOpenPicker to get a file (StorageFile^) in a Windows Universal App. That seems to work fine, and if I output the StorageFile->Path->Data(), it returns the expected file path (C:...\MyFile.txt).

When I try to open it and dump the contents to a string things go wrong (the string receives nothing), so as the first step, I was trying to verify the file path with std::filesystem::exists().

To snip it down to the relevant bit:

void MyClass::MyFunction(StorageFile^ InFile)
{
    std::filesystem::path FilePath = InFile->Path->Data();

    if (std::filesystem::exists(FilePath))
    {
        //Do things
    }
}

When I run this, I get an exception:

Exception thrown at 0x7780A842 in MyApp.exe: Microsoft C++ exception: std::filesystem::filesystem_error at memory location 0x039BC480.
Unhandled exception at 0x7AACF2F6 (ucrtbased.dll) in MyApp.exe: An invalid parameter was passed to a function that considers invalid parameters fatal.

Seemingly, the path I attempt to pass into std::filesystem::exists() is invalid.

Any help pointing out where I am going wrong would be very appreciated!

This question had originally been marked as a duplicate of this question however that solution does not seem to work as it requires CLI(?) whilst I think I am using WinRT (however I can not find where in my project or settings to check that beyond having winrt available in includes).


Solution

  • The key things to remember about StorageFile in the Windows Runtime (using C++/CX or C++/WinRT) is that (a) it's not necessarily a file on disk, and (b) even if it is a file on disk you don't necessary have permissions to open it directly.

    The only 'generally safe' pattern you can use for doing traditional file I/O operations on the StorageFile instance you are given by the UWP FilePicker is to create a temp directory copy from it and then parse the temp copy:

    C++/CX

    #include <ppltasks.h>
    using namespace concurrency;
    
    using Windows::Storage;
    using Windows::Storage::Pickers;
    
    auto openPicker = ref new FileOpenPicker();
    openPicker->ViewMode = PickerViewMode::Thumbnail; 
    openPicker->SuggestedStartLocation = PickerLocationId::PicturesLibrary; 
    openPicker->FileTypeFilter->Append(".dds"); 
    
    create_task(openPicker->PickSingleFileAsync()).then([](StorageFile^ file)
    {
        if (file)
        {
            auto tempFolder = Windows::Storage::ApplicationData::Current->TemporaryFolder;
            create_task(file->CopyAsync(tempFolder, file->Name, NameCollisionOption::GenerateUniqueName)).then([](StorageFile^ tempFile)
            {
                if (tempFile)
                {
                    std::filesystem::path FilePath = tempFile->Path->Data();
    ...
                }
            });
        }
    });
    

    C++/WinRT

    #include "winrt/Windows.Storage.h"
    #include "winrt/Windows.Storage.Pickers.h"
    
    using namespace winrt::Windows::Storage;
    using namespace winrt::Windows::Storage::Pickers;
    
    FileOpenPicker openPicker;
    openPicker.ViewMode(PickerViewMode::Thumbnail);
    openPicker.SuggestedStartLocation(PickerLocationId::PicturesLibrary);
    openPicker.FileTypeFilter().Append(L".dds");
    
    auto file = co_await openPicker.PickSingleFileAsync();
    if (file)
    {
        auto tempFolder = ApplicationData::Current().TemporaryFolder();
        auto tempFile = co_await file.CopyAsync(tempFolder, file.Name(), NameCollisionOption::GenerateUniqueName);
        if (tempFile)
        {
            std::filesystem::path FilePath = tempFile.Path().c_str();
    ...
        }
    }
    

    See Microsoft Docs