locked
Magic sauce for avoiding "Unhandled exception at 0x696C4E43 (msvcr110d.dll) in SmartRecorder.exe: An invalid parameter was passed to a function that considers invalid parameters fatal."

    Question

  • You cannot vote on your own post
    0

    I started by creating a C++ Grid App in Visual studio.  I have adapted the SampleDataSource.cpp and SampleDataSource.h source files to list a set of files from the user's documents directory.  I use the following code to try and load and enumerate these files:

    void SampleDataSource::loadIndex()
    {
        int x=3;
        IVectorView<StorageFolder^>^ folders;
        ++initializing;
        create_task(KnownFolders::DocumentsLibrary->GetFoldersAsync()).then([folders](IVectorView<StorageFolder^>^ folders) {
            int i;
            for (i = 0; i < folders->Size; ++i) {
                String^ x = folders->GetAt(i)->Path;
                if (x->Length() > 5) {
                       const char16 *suffix = x->End()-5;
                    if (suffix[0]=='.' && tolower(suffix[1])=='s' && tolower(suffix[2])=='m' && tolower(suffix[3])=='r' && tolower(suffix[4])=='d') {
                        int j = 3;
                        ++j;
                        _sampleDataSource->readSmartRecorderSession(folders->GetAt(i));
                    }
                }
            }
        }).wait();
        --initializing;
    }

    void SampleDataSource::readSmartRecorderSession(StorageFolder^ folder)
    {
        Windows::Globalization::Calendar^ cal = ref new Windows::Globalization::Calendar();
        DateTime TimeStamp = cal->GetDateTime();
        String^ x = folder->Path;
        const char16 *b = x->Begin();
        const char16 *e = x->End();
        const char16 *s = b;
        while (*b) {
            if (*b=='\\') s = b+1;
            ++b;
        }
        String^ title = ref new String(s,e-s-5);
        SmartRecorderSession^ session = ref new SmartRecorderSession(makeId(), title, TimeStamp);
        session->Subtitle = "";
        session->folder = folder;
        session->readData();
        if (session->samplingRate > 0) session->Subtitle = timeString(session->totalSampleCount / session->samplingRate);
    }

    Note that the "SmartRecorderSession" class is very similar to the SampleDataItem class generated by visual studio.

    It seems that where ever I place the call to loadIndex(), I'll eventually get the "invalid parameter" error.  I tried placing it in the "SampleDataSource" constructor and the program crashed on the first "create_task" encountered.  I also tried placing loadIndex in the LoadIndex function of GroupedItemsPage.xaml.cpp and got a similar result.

    How do I work around this crash?  Is there an alternative file system API that I can use to read this information?


    - Ken

    Saturday, August 25, 2012 8:46 PM

All replies

  • I should mention, my goal is to read in the set of documents (from the documents folder) that my app can handle when it is first started.  Where would be a good place for me to put the "loadIndex()" call?  Alternatively, it seems that the multithreading is the cause of the crashes.  Is there an alternative API that I can use to read the file system information that does not require threading?


    - Ken

    Sunday, August 26, 2012 12:27 AM
  • Doing this asynchronously is definitely the right approach. You don't want to hang your UI thread while initializing.

    Where are you getting the invalid parameter error? Which function is it? Is it always the same one?

    Can you provide a minimal sample which demonstrates the error?

    --Rob

    Wednesday, August 29, 2012 1:18 AM
    Owner
  • For a minimal example, create a new Grid app.  Then add the readSmartRecorderSession and loadIndex functions in SampleDataSource.cpp (except change SmartRecorderSession to SampleDataItem).  You will also have to delete a few of the lines filling in fields not present in SampleDataItem.  Then place the call to loadIndex in either the constructor for SampleDataSource or in the function LoadState in GroupedItemsPage.xaml.cpp.

    I'm seeing crashes on the first create_task in loadIndex().  I've also sometimes seen crashes in the call to "ref new SmartRecorderSession".

                                 - Ken


    - Ken

    Wednesday, August 29, 2012 1:34 AM
  • Here are step by step instructions on how to reproduce the crash.

    First, create a new grid app.  Second, add the following function to SampleDataSource.cpp:

    void SampleDataSource::loadIndex()
    {
        try {
            create_task(KnownFolders::DocumentsLibrary->GetFoldersAsync()).then([=](IVectorView<StorageFolder^>^ folders) {
                int i;
                for (i = 0; i < folders->Size; ++i) {
                    String^ x = folders->GetAt(i)->Path;
                    if (x->Length() > 5) {
                           const char16 *suffix = x->End()-5;
                        if (suffix[0]=='.' && tolower(suffix[1])=='s' && tolower(suffix[2])=='m' && tolower(suffix[3])=='r' && tolower(suffix[4])=='d') {
                            int j = 3;
                            ++j;
                            try {
                                create_task(folders->GetAt(i)->GetFileAsync(L"notes.xml")).then([=](StorageFile^ file)
                                {
                                    String^ path = folders->GetAt(i)->Path;
                                    int x = 1;
                                    create_task(FileIO::ReadTextAsync(file)).then([file](task<String^> task)
                                    {
                                        String^ buffer = task.get();
                                        int x = 3;
                                        x = x+1;
                                    }).wait();
                                }).wait();
                            } catch (Exception^ e) {
                                int x = 1;
                                x = x+1;
                            }
                            int y = 4;
                            y = y+1;
                        }
                    }
                }
            });
        } catch(Exception^ e) {
            int x = 5;
            x = x+1;
        }
    }

    Third, add

    static void loadIndex();

    to the SampleDataSource class in SampleDataSource.h.  You will also need to add the following two lines at the beginning of SampleDataSource.cpp:

    using namespace Windows::Storage;
    using namespace concurrency;

    Next, add a call to loadIndex() in GroupedItemsPage.xaml.cpp.  Add at the beginning of the file

    #include "DataModel\SampleDataSource.h"

    Next add:

    Data::SampleDataSource::loadIndex();

    in the LoadState method in that file.

    Next modify the Package.appxmanifest file to have the "Documents library" capability as well as the declarations to read ".xml" and ".smrd" files.

    Now add in the documents directory a few directories with the ".smrd" extension.  In some of those direcories, place a file called "notes.xml".

    Run the program.  It should not take long to get the "Unhandled exception..." error to appear.

                                                  - Ken


    - Ken

    Thursday, August 30, 2012 3:32 AM
  • Hi Ken,

    The problem is that you are blocking the UI thread. You can't wait() a task on a UI thread like you do here:

                                     create_task(FileIO::ReadTextAsync(file)).then([file](task<String^> task)
                                     {
                                         String^ buffer = task.get();
                                         int x = 3;
                                         x = x+1;
                                     }).wait();

    Windows Runtime functions which may take time are exposed as asynchronous functions so they can be called without hanging the UI thread. Waiting on them defeats this and can lead to a poor user experience. To prevent this, task.wait() raises an exception when it is called on a UI thread.

    Your code should follow the asynchronous model instead of trying to bypass it. You may need to refactor your code a bit so that followup actions occur in the task continuation instead of after it.

    See Asynchronous programming for more background on this.

    --Rob

    • Proposed as answer by BullyOwner Friday, October 5, 2012 3:30 AM
    Thursday, August 30, 2012 6:01 PM
    Owner
  • This may be a problem.  However, I removed all the "wait" calls and I am still getting the "Unhandled exception..." error.  Here is the revised code:

    void SampleDataSource::loadIndex()
    {
        try {
            create_task(KnownFolders::DocumentsLibrary->GetFoldersAsync()).then([=](IVectorView<StorageFolder^>^ folders) {
                int i;
                for (i = 0; i < folders->Size; ++i) {
                    String^ x = folders->GetAt(i)->Path;
                    if (x->Length() > 5) {
                           const char16 *suffix = x->End()-5;
                        if (suffix[0]=='.' && tolower(suffix[1])=='s' && tolower(suffix[2])=='m' && tolower(suffix[3])=='r' && tolower(suffix[4])=='d') {
                            int j = 3;
                            ++j;
                            try {
                                create_task(folders->GetAt(i)->GetFileAsync(L"notes.xml")).then([=](StorageFile^ file)
                                {
                                    String^ path = folders->GetAt(i)->Path;
                                    int x = 1;
                                    create_task(FileIO::ReadTextAsync(file)).then([file](task<String^> task)
                                    {
                                        try {
                                            String^ buffer = task.get();
                                        } catch (Exception^ e) {
                                            int x = 4;
                                            x = x+1;
                                        }
                                        int x = 3;
                                        x = x+1;
                                    });
                                });
                            } catch (Exception^ e) {
                                int x = 1;
                                x = x+1;
                            }
                            int y = 4;
                            y = y+1;
                        }
                    }
                }
            });
        } catch(Exception^ e) {
            int x = 5;
            x = x+1;
        }
    }

    One important note--in some cases, the file "notes.xml" may not exist and the GetFileAsync call will fail.  What happens when it fails?  I think I've got all the exceptions trapped.  Also, are the .then continuations executed on the same thread as the calling process?  Finally, you will note that multiple getFileAsync tasks are spawned of concurrently.  Do I need to rearrange the code so that the tasks are executed in sequence?


    - Ken


    • Edited by Ken Roe Thursday, August 30, 2012 9:08 PM
    Thursday, August 30, 2012 9:05 PM
  • I have a similar problem.  The following code works for the "Local" folder, but fails for the "Install" folder with the exception "invalid parameter was passed to a function that considers invalid parameters fatal":

    void MainPage::OnNavigatedTo(NavigationEventArgs^ e)
    {
    	(void) e;	// Unused parameter
    
       // app data folder
       ws::IStorageFolder^ localFolder = ws::ApplicationData::Current->LocalFolder;
       // app installation folder
       Windows::ApplicationModel::Package^ package = Windows::ApplicationModel::Package::Current;
       ws::IStorageFolder^ installFolder = package->InstalledLocation;
    
       ws::StorageFile^ localFolderFile = Tools::GetFile(localFolder, "foo.dat");
       ws::StorageFile^ installFolderFile = Tools::GetFile(localFolder, "foo.dat");
    }
    
    ws::StorageFile^ Tools::GetFile(ws::IStorageFolder^ folder, String^ fileName)
    {
       concurrency::task<ws::StorageFile^> getFileOperation(folder->GetFileAsync(fileName));
       try
       {
          getFileOperation.wait();
       }
       catch(Exception^ ex)
       {
          OutputDebugString(ex->Message->Begin());
          OutputDebugString(L"\n");
          return nullptr;
       }
       ws::StorageFile^ file = getFileOperation.get();
       return file;
    }

    Thursday, October 4, 2012 10:33 PM
  • If a file does not exist or opens error from GetFileAsync() there is an exception. I have met the similar problem. However I also can not trap the exception yet. That is a problem.

    Charlie Chang L

    Friday, October 5, 2012 1:38 AM
  • Catching the exception is not the problem.  The problem is that the function throws the exception and then shuts down the process.  I figured it out, based on what Rob was saying above.  Seems you cannot wait too much in the UI / Main thread, or else this happens.  The solution is to chain the async operations together as Rob said.

    Friday, October 5, 2012 3:30 AM
  • I am wondering how do you handle the case that your file does not exist. How do you catch the exception and avoid exit?  

    Charlie Chang L

    Friday, October 5, 2012 2:14 PM