Please enable JavaScript to display the menu.
Synchronously Getting File Listing in a Directory in Universal Windows App in C++

ysflight.com

I've been trying to port my code base to Universal Windows Platform.  Overall, I like the API structure.  Nicely organized.  Cleaner than legacy Win32 API.  OpenGL ES 2.0 is available.  Many of my test codes using OpenGL 2.0/ES 2.0 runs on Universal Windows Platform now.  The next version of CMake, version 3.8, will allow linking .targets file as a library.  A .targets file comes with a NuGet package, in which OpenGL ES 2.0 is distributed.  You won't have to go out of CMake environment to set up your build to use OpenGL ES2.0.  Actually, I did an unlikly thing for this feature.  I contributed some lines of code to enable this feature to CMake.  But, CMake was very well written in an expandable way.  I think all I wrote was 20 or 30 lines of code, and CMake was able to support .targets file as a library.

I hit a wall, however, while I was porting my class to get directory listing.  The trouble was that many Universal Windows API functions require asynchronous processing.  It appeared so.  When you just want to get file listing of a directory, you are forced to use asynchronous processing.  What it means that getting file listing of the directory cannot be completed within one execution of a function.  You set up a request to get a file listing in a function, and the function should exit, whatsoever.  Then, another function, called continuation, is called when the file listing is ready.  It is just like an event handler.  In the continuation, you finally get the file listing.

It is similar to the difference of the Alchemy Pot of Dragon Quest 8 between the Play Station and the iOS versions.  In the iOS version, you will get a new item as soon as you mix ingredients.  But, in the Play Station version, you put ingradients in, and then walk some distance until you get a new item.  The Alchemy Pot in the iOS version is a synchronous processing, and the Play Station version is an asynchronous processing.

Or, it is similar to the difference of the modal-dialog implementations between Win32 and iOS.  In Win32, if you want to show a message and wait until the user presses "Yes" or "No" button, there is a convenient function called MessageBox.  Once you call MessageBox function, the function won't return until the user makes the selection.  Then, your program can continue processing depending on the return value of the function.  In iOS, there is a thing called modal dialog, but the function to create a modal dialog doesn't block.  If you want to do something based on the user's selection, you create a modal dialog box, and connect an event handler for "Yes" and "No" buttons.  Do whatever you need to do in the event handler.  Therefore, you need to write more than one function to show a dialog box, and process Yes or No.  From the programmer's point of view, Win32-style MessageBox is convenient. 

Requiring an asynchronous processing for just getting a file listing is obvioiusly an overkill.  Why the hell Microsoft made it this wrong?  I can only imagine their point.  Maybe they are concerned about getting a file listing from a cloud storage.  It may take a while.  If the GUI thread is blocked, the program may be unresponsive until the listing is ready.  But, it is a programmer's jurisdiction.  The programmer should be able to choose between (1) sorry, but the user must wait, (2) use asynchronous (concurrent) processing like the UWP API wants, and (3) use multi-threading.

Then, I found that I can push the asynchronous procesing forward by running an event loop.  All I needed to do was to run an event-loop until the directory listing is ready.  In fact, I tried and somewhat worked.  A similar solution is proposed in the following URL.  I found this solution independently though.

http://stackoverflow.com/questions/19917466/how-to-wait-for-an-iasyncaction

But, this solution had a potential danger of re-entrancy.  You probably need to get a file-listing in one of the event-handlers.  So, you do it while processing an event.  But, if you run an event loop, another event hadler may be called from the event loop.  It may yield an unwanted running state.

What I felt odd was that there seems to be an 'await' keyword in C#.  In C++ API, Concurrency::task<>::wait() should correspond to this await keyword.  When I tried it in my program, however, I only received "Invalid Parameter" exception.  Useless!  I though it was useless.  But, I found the following information.

https://social.msdn.microsoft.com/Forums/sqlserver/en-US/02299772-22a2-451a-9ae7-72dad6d3396c/ctaskwait-in-a-catchblock-throws-exception?forum=wpdevelop

They are discussing about UI thread.  Wait.  I had a similar situation before.  What was it?  Yes, it was a font library.  I wanted to render fonts on an off-screen buffer.  So that I can draw on the window using OpenGL textures.  But, some font-related classes could not be created from the UI thread.  But, later that font class turned out to be useless because I did not have access to the RGBA bitmap.  And then I knew Win2D library serves much better, and I forgot about the useless font class.

What I learned from the useless font library was that some functions are unavailable in the main UI thread.  Is that it?  Do I just have to use wait() in a different thread?  How about doing it from a different thread using std::thread?  Actually, that was it.  In a separate thread, wait() works as advertised.  I can synchronouslly get file listing in the sub-thread using wait().  The main thread can wait until the sub-thread is over by std::thread::join().  It's a beautiful solution with no danger of re-entrancy.

Sorry, but I ask my users to wait until the file listing is ready in my program.  The user may have to endure unresponsive GUI for less than 0.1 seconds.  I hope my users are patient enough to wait for a fraction of second.

Below it the code I wrote for experiment.

uwpdirlist.h

#ifndef UWP_DIRLIST_IS_INCLUDED
#define UWP_DIRLIST_IS_INCLUDED

#include <vector>
#include <string>

class YsUwpFileList
{
public:
    class File
    {
    public:
        std::basic_string <wchar_t> nm;
        bool isDir;
    };
    std::vector <File> fileList;

private:
    bool res;

public:
    bool MakeFileList(const wchar_t fulPath[]);
private:
    void RunInThread(const wchar_t fulPath[]);
};

#endif

uwpfilelist.cpp

#include "uwpdirlist.h"
#include <thread>

#include <ppltasks.h> // To use Concurrency namespace

using namespace Windows::Storage;
using namespace Windows::Foundation::Collections;  // To use IVectorView
using namespace Concurrency;                       // To use create_task

bool YsUwpFileList::MakeFileList(const wchar_t fulPath[])
{
    std::thread t(&YsUwpFileList::RunInThread,this,fulPath);
    t.join();
    return this->res;
}
void YsUwpFileList::RunInThread(const wchar_t fulPath[])
{
    this->fileList.clear();
    this->res=true;
    Platform::String^ uwpPath=ref new Platform::String(fulPath);

    auto getFolderTask=create_task(Windows::Storage::StorageFolder::GetFolderFromPathAsync(uwpPath));
    getFolderTask.wait();
    try
    {
        auto folder=getFolderTask.get();
        auto getItemsTask=create_task(folder->GetItemsAsync());
        getItemsTask.wait();
        auto itemList=getItemsTask.get();

        for(int i=0; i<itemList->Size; ++i)
        {
            auto itm=itemList->GetAt(i);
            bool isDir=false;
            if((itm->Attributes&FileAttributes::Directory)==FileAttributes::Directory)
            {
                isDir=true;
            }
            std::basic_string <wchar_t> fn=itm->Name->Data();

            File newFile;
            fileList.push_back(newFile);
            fileList.back().nm=fn;
            fileList.back().isDir=isDir;
        }
    }
    catch(Platform::COMException^)
    {
        this->res=false;
    }
}

You can also download a fully-working example below.  This program will show directory listing of the application directory on the main window.

[Download Example]

You need CMake 3.8-rc0 or newer to compile.  Go to the terminal, change directory to the build directory, and then type:

cmake <sourceDir> -G "Visual Studio 14 2015" -DCMAKE_SYSTEM_NAME=WindowsStore "-DCMAKE_SYSTEM_VERSION=10.0"

Open Project.sln and compile.  You need to enclose -DCMAKE_SYSTEM_VERSION=10.0 with " " if you run from Power Shell.  Probably "." has a special meaning in Power Shell.  I haven't figured yet.

Comments are welcome.  Send E-Mail to: 

Back to http://www.ysflight.com