Search code examples
c#windowsonedrive

Cannot open file immediately after downloading from OneDrive


after downloading a JPG file from OneDrive successfully under Windows 8.1, I am not able to open it and use the stream as image source immediately. Instead, my app must run in a waiting loop to retry the opening until the exception "Error HRESULT E_FAIL has been returned from a call to a COM component" does not occur anymore. Or the app must inform the user to retry the action. After about one second or so, the file can be opened and the stream can be used as image source. This problem occurs if the JPG file was initially not available offline and my app downloads it. I want to know if anybody has a better solution for that problem. Here is some of my code:

private async void LoadFileButton_Click(object sender, RoutedEventArgs e)
    {
        FileOpenPicker picker = new FileOpenPicker();
        picker.SuggestedStartLocation = PickerLocationId.PicturesLibrary;
        picker.ViewMode = PickerViewMode.Thumbnail;
        picker.FileTypeFilter.Add(".png");
        picker.FileTypeFilter.Add(".jpe");
        picker.FileTypeFilter.Add(".jpeg");
        picker.FileTypeFilter.Add(".jpg");
        picker.FileTypeFilter.Add(".gif");

        Stream stream = null;
        StorageFile file = await picker.PickSingleFileAsync();
        if (file != null)
        {
            var basicProperties = await file.GetBasicPropertiesAsync();
            var props = await basicProperties.RetrievePropertiesAsync(
                              new string[] { "System.OfflineAvailability", "System.StorageProviderFileRemoteUri" });
            if (props.ContainsKey("System.OfflineAvailability"))
            {
                var offline = (uint)props["System.OfflineAvailability"];
                if (offline == 0)
                {
                    if (file.Provider.DisplayName == "OneDrive")
                    {
                        if (props.ContainsKey("System.StorageProviderFileRemoteUri"))
                        {
                            var uri = (string)props["System.StorageProviderFileRemoteUri"];
                            var res = await GameStorage.DownloadOneDriveFile(file, uri, _downloadProgressBar);
                            if (res is string)
                            {
                                await App.InformUserAsync(res.ToString(), _title.Text);
                                return;
                            }
                            stream = (Stream)res;
                        }
                    }
                    else
                    {
                        await App.InformUserAsync(String.Format(
                                App.GetString("MakeFileOfflinePrompt", "GameManagement"),
                                file.Path), _title.Text);
                        return;
                    }
                } 
            }
            if (stream == null)
            {
                stream = await file.OpenStreamForReadAsync();
            }
            await _photoClipper.SetDisplayImageStreamAsync(stream);
            _clipPhotoButton.IsEnabled = true;
        }
    }
internal static async Task<object> DownloadOneDriveFile(StorageFile file, string url, 
                                                            ProgressBar progressBar = null)
    {
        if (progressBar != null)
        {
            progressBar.Visibility = Visibility.Visible;
            progressBar.Value = 1;
        }
        if (__liveClient == null)
        {
            var msg = await ConnectLive();
            if (!String.IsNullOrEmpty(msg))
            {
                return msg;
            }
        }
        var uri = new Uri(WebUtility.UrlDecode(url));
        var pathElements = uri.LocalPath.Split(new char[] { '/' }, 
                                               StringSplitOptions.RemoveEmptyEntries);
        var parentId = "folder." + pathElements[0];
        IDictionary<string, object> props = null;

        for (var i = 1; i < pathElements.Length; i++)
        {
            props = await FindOneDrivePathElement(parentId, pathElements[i]);
            if (props == null)
            {
                return String.Format(App.GetString("OneDrivePathElementNotFound", 
                                     "GameManagement"), pathElements[i], uri);
            }
            parentId = props["id"].ToString();
            if (progressBar != null) progressBar.Value += 1;
        }
        try
        {
            var operation = await __liveClient.CreateBackgroundDownloadAsync(parentId + 
                                               "/content", file);
            LiveDownloadOperationResult result = null;

            if (progressBar != null)
            {
                progressBar.Value = 10;
                var progressHandler = new Progress<LiveOperationProgress>(
                    (progress) => { progressBar.Value = progress.ProgressPercentage; });
                var cts = new CancellationTokenSource();
                result = await operation.StartAsync(cts.Token, progressHandler);
            }
            else
            {
                result = await operation.StartAsync();
            }
            var trialsCount = 0;
            string openErr = null;
            Stream stream = null;

            while (trialsCount < 5)
            {
                try
                {
                    stream = await result.File.OpenStreamForReadAsync();
                    break;
                }
                catch (Exception ex)
                {
                    openErr = ex.Message; 
                }
                trialsCount += 1;
                await App.SuspendAsync(1000);
            }
            if (stream != null)
            {
                return stream;
            }
            return String.Format(App.GetString("OneDriveCannotOpenDownloadedFile", 
                                               "GameManagement"), file.Path, openErr);
        }
        catch (Exception ex)
        {
            return String.Format(App.GetString("OneDriveCannotDownloadFile",
                                               "GameManagement"), file.Path, ex.Message);
        }
        finally
        {
            if (progressBar != null)
            {
                progressBar.Visibility = Visibility.Collapsed;
            }
        }
    }
private static async Task<IDictionary<string, object>> FindOneDrivePathElement(
                                                           string parentId, string childName)
    {
        var res = await __liveClient.GetAsync(parentId + "/files");

        if (res.Result.ContainsKey("data"))
        {
            var items = (IList<object>)res.Result["data"];

            foreach (var item in items)
            {
                var props = (IDictionary<string, object>)item;
                if (props.ContainsKey("name"))
                {
                    var name = props["name"].ToString();
                    if (name == childName)
                    {
                        if (props.ContainsKey("id"))
                        {
                            return props;
                        }
                    }
                }
            }
        }
        return null;
    }

Solution

  • I have tried again to open a "available online-only" file and as by a miracle the exception "Error HRESULT E_FAIL" did not occur any more. For what ever reason, I don't know. But, I really do not need to handle that scenario by myself. Thanks.