3
votes

For a Windows 8 Application in C/XAML I have to store some data to the LocalState folder. I have some trouble using async/await operator I think.

When I am saving datas while the application is running, everything works fine, but when I try to save the data during the OnSuspending method, it seems that my application doesn't wait for my saving method to be finished to suspend the application.

The stranger thing is that when I'm debugging and doing all the operation step by step slowly everything works fine, but when I don't put any breakpoints, the application is closed before the data are saved.

Here is my code :

    private async void OnSuspending(object sender, SuspendingEventArgs e)
    {
        var deferral = e.SuspendingOperation.GetDeferral();
        await  api.BeforeClosing(true);
        deferral.Complete();
    }

    public async Task BeforeClosing(Boolean toTombstoning)
    {
            SaveItem<LoadingProgress>(fileNameLoadingProgress, InitLoading);
    }


    public static async void SaveItem<T>(String fileName, T data, Boolean crossThreadSecure = false) where T : IBinarySerializable
    {

            await CreateNewStorageFile(fileName);

            StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
            StorageFolder dataFolder = await localFolder.GetFolderAsync("Data");

            StorageFile file = await dataFolder.GetFileAsync(fileName);

            using (var stream = await file.OpenStreamForWriteAsync())
            {
                using (var writer = new BinaryWriter(stream))
                {
                    writer.Write<T>(data);
                }
            }
    }

    public async static Task CreateNewStorageFile(string filename)
    {
        StorageFolder localFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
        StorageFolder dataFolder = await localFolder.CreateFolderAsync("Data", CreationCollisionOption.OpenIfExists);
        storage = await dataFolder.CreateFileAsync(filename, CreationCollisionOption.OpenIfExists);
    }

Am I doing something wrong with the await operator? Or is there a maximum amount of time when we can do some operations before suspending the application?

Thank you

EDIT : I made some new tests : If I remove deferral.Complete(); from the OnSuspending method, it saves the data well (but never close the app obviously...). I really think the problem is that I run out of time when the application is suspending. It really looks like the problem on this thread : StorageFolder.CreateFileAsync crashes when called from App.OnSuspending. But I have tried all the solutions of this question and I still have the problem...

2

2 Answers

10
votes

Your problem is that you're calling Complete before all your async operations have completed.

The simplest solution is to make SaveItem an async Task method instead of async void, and then await it in BeforeClosing.

As a general rule, async methods should always return Task/Task<T> unless they have to return void (e.g., event handlers).

7
votes

The short and surprising answer is that OnSuspending is an unreliable place for any application critical persistence logic. For some reason Microsoft made the decision to delay the OnSuspending event for 5-10 seconds after an application has been closed. If the application is re-launched prior to this event a separate instance of the application is spawned and it is unaware of the one that is still in the process of suspending.

I came to this conclusion after having problems following what seemed like very simple examples in the WinRT Application Data Sample project: http://code.msdn.microsoft.com/windowsapps/ApplicationData-sample-fb043eb2. After some frustration I found a thread on MSDN that discusses OnSuspending as a trigger for saving: http://social.msdn.microsoft.com/Forums/en-US/winappswithcsharp/thread/43da2212-9910-461d-816a-94b479ce9885/. The gist of this thread is that you should save as often and frequently as possible because OnSuspending is unreliable.