1

I have an application which performs the following:

  1. Initialises a connected Webcam

    private async Task<MediaCapture> InitialiseWebCam()
    {
        MediaCapture webCam = null;
        webCam = new MediaCapture();
    
        try
        {
            await webCam.InitializeAsync();
        }
        catch (Exception ex)
        {
            //Log error
        }
    
        return webCam;
    }
    
  2. Takes an image

    private async Task<StorageFile> TakePicture(MediaCapture webCam)
    {
        try
        {
            var image = await KnownFolders.PicturesLibrary.CreateFileAsync("TestImage.jpg", CreationCollisionOption.GenerateUniqueName);
    
            var imageEncodingProperties = ImageEncodingProperties.CreatePng();
            await webCam.CapturePhotoToStorageFileAsync(imageEncodingProperties, image);
    
            return image;
        }
        catch (Exception ex)
        {
            //Log error
        }
        return null;
    }
    
  3. Uploads image to Azure Blob Storage

    private async Task<StorageFile> UploadPictureToAzure(StorageFile image)
    {
    string Azure_StorageAccountName = "MYACCOUNTNAME";
    string Azure_ContainerName = "MYCONTAINERNAME";
    string Azure_AccessKey = "MYACCESSKEY";
    
        try
        {
            StorageCredentials creds = new StorageCredentials(Azure_StorageAccountName, Azure_AccessKey);
            CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);
            CloudBlobClient client = account.CreateCloudBlobClient();
            CloudBlobContainer sampleContainer = client.GetContainerReference(Azure_ContainerName);
    
            CloudBlockBlob blob = sampleContainer.GetBlockBlobReference(image.Name);
    
            await blob.UploadFromFileAsync(image);
    
            return image;
        }
        catch (Exception ex)
        {
            //Log
        }
    
        return image;
    }
    

The three methods are invoked as below:

public void Run(IBackgroundTaskInstance taskInstance)
{
    Monitor();
}

private void Monitor()
{
    var webCam = InitialiseWebCam().Result;
    var image = TakePicture(webCam).Result;

    var output = UploadPictureToAzure(image).Result;
}

At this point all is well and the image appears in my blob storage. I wanted this behaviour to run as a headless background application on Windows IoT, so I made the following adjustment:

public void Run(IBackgroundTaskInstance taskInstance)
{
    taskInstance.GetDeferral();

    timer = ThreadPoolTimer.CreatePeriodicTimer(Timer_Tick, TimeSpan.FromSeconds(60));
}

private void Timer_Tick(ThreadPoolTimer threadPoolTimer)
{
    Monitor();
}

With those changes in place the Monitor function is triggered as expected, however the output of UploadPictureToAzure(image).Result is null even though webCam and image are working as intended, and the application exits abruptly.

What is the reason for the behaviour above? I was expecting the image to be uploaded and the overall process continue to occur within the timer loop created.

EDIT:

I attempted to force synchronous behavior by adjusting my UploadPictureToAzure like so:

private void UploadPictureToAzure(StorageFile image)
{
    string Azure_StorageAccountName = "ACCOUNTNAME";
    string Azure_ContainerName = "CONTAINERNAME";
    string Azure_AccessKey = "ACCESSKEY";

    try
    {
         StorageCredentials creds = new StorageCredentials(Azure_StorageAccountName, Azure_AccessKey);
         CloudStorageAccount account = new CloudStorageAccount(creds, useHttps: true);
         CloudBlobClient client = account.CreateCloudBlobClient();
         CloudBlobContainer sampleContainer = client.GetContainerReference(Azure_ContainerName);

         CloudBlockBlob blob = sampleContainer.GetBlockBlobReference(image.Name);

         var response = blob.UploadFromFileAsync(image);
         response.AsTask().Wait();
     }
     catch (Exception ex)
     {
         //Log
     }
}

Now even with the call to .Wait(), execution returns immediately and the application exits.

If I remove usage of the ThreadPoolTimer.CreatePeriodicTimer, and replace it with a while loop then images upload without issue.

Jamie Keeling
  • 9,806
  • 17
  • 65
  • 102

2 Answers2

0

The thing with async methods is that they tend to crawl up your code and invade it all around :)...You have to use the await keyword instead of calling the Result property, otherwise you won't be sure the function actually finished executing before trying to grab the result. This applies to all async methods in your code.

Change your Monitor method to be async and return a Task, like so:

private async Task Monitor()
{
    var webCam = await InitialiseWebCam();
    var image = await TakePicture(webCam);
    var output = await UploadPictureToAzure(image);
}

I'm guessing Windows phone has an async API at the top level, so you can await up to the main thread, but if it doesn't, here are a couple of workarounds to call async methods synchronously

EDIT:

Per Quickstart: Create and register a background task (XAML) you can do the following on your Run method:

public async void Run(IBackgroundTaskInstance taskInstance)
{
    BackgroundTaskDeferral _deferral = taskInstance.GetDeferral();

    ThreadPoolTimer.CreatePeriodicTimer(async(timer) => { 
        await Monitor();
        _deferral.Complete();
    }, TimeSpan.FromSeconds(60));


}
Community
  • 1
  • 1
Andrei Dvoynos
  • 1,126
  • 1
  • 10
  • 32
  • With those changes in place the outcome is still the same - no image is uploaded to Azure and the application exits. – Jamie Keeling Sep 22 '15 at 21:37
  • Can you update your answer with that change? I have tried to change it myself but can't work out where to put the await within the line used to initialise the timer object. – Jamie Keeling Sep 22 '15 at 21:48
  • Surely if I make the change to my Run method then I am removing the looping behaviour that I need? – Jamie Keeling Sep 22 '15 at 22:06
  • I've added the function used by the Run method - apologies, I thought it was already there. – Jamie Keeling Sep 22 '15 at 22:11
  • Please try the Run method with the changes above and let me know how it goes – Andrei Dvoynos Sep 22 '15 at 22:30
  • The application now exits almost immediately. Putting a breakpoint on the first line within Monitor shows it is not being triggered - VS doesn't appear to be hiding any exceptions either. – Jamie Keeling Sep 22 '15 at 22:53
  • I've added a bit more to my initial question based on some of the investigation work I have done. – Jamie Keeling Sep 23 '15 at 12:49
  • Try the change that I put above and see if it works. It appears to be an issue with the implementation of the CreatePeriodicTimer method – Andrei Dvoynos Sep 23 '15 at 14:58
  • This will probably only work once though, I think what you need for periodic background tasks is the following: https://msdn.microsoft.com/en-us/library/windows/apps/xaml/hh977059.aspx – Andrei Dvoynos Sep 23 '15 at 15:04
0

In the end I was able to achieve what I need using the following code:

private readonly TimeSpan _delayDuration = TimeSpan.FromMinutes(20);

public void Run(IBackgroundTaskInstance taskInstance)
{
    taskInstance.GetDeferral();

    do
    {
        try
        {
            Monitor().Wait();
        }
        /* SNIP */
    }
    while (true);

private async Task Monitor()
{
    var webCam = await InitialiseWebCam();
    var image = await TakePicture(webCam);
    await UploadPictureToAzure(image);
    await Task.Delay(_delayDuration);
}


private async Task UploadPictureToAzure(StorageFile image)
    {
        var Azure_StorageAccountName = "accountname";
        var Azure_ContainerName = "container";
        var Azure_AccessKey = "key";
        try
        {
            StorageCredentials creds = new StorageCredentials(Azure_StorageAccountName, Azure_AccessKey);
            CloudStorageAccount account = new CloudStorageAccount(creds, true);
            CloudBlobClient client = account.CreateCloudBlobClient();
            CloudBlobContainer sampleContainer = client.GetContainerReference(Azure_ContainerName);
        CloudBlockBlob blob = sampleContainer.GetBlockBlobReference(image.Name);

        await blob.UploadFromFileAsync(image);
    }
    catch (Exception ex)
    {
        /* Logging */
        throw ex;
    }
}

private async Task<MediaCapture> InitialiseWebCam()
{
    var webCam = new MediaCapture();

    try
    {
        await webCam.InitializeAsync();
    }
    catch (Exception ex)
    {
       /* Logging */
       throw ex;
    }

    return webCam;
}

private async Task<StorageFile> TakePicture(MediaCapture webCam)
{
    try
    {
        var image = await KnownFolders.PicturesLibrary.CreateFileAsync("Image.png", CreationCollisionOption.ReplaceExisting);
        var imageEncodingProperties = ImageEncodingProperties.CreatePng();

        await webCam.CapturePhotoToStorageFileAsync(imageEncodingProperties, image);

        return image;
    }
    catch (Exception ex)
    {
        /* Logging */
        throw ex;
    }
}
Jamie Keeling
  • 9,806
  • 17
  • 65
  • 102