1

Basically the following code copies an entire folder from the app installation into the LocalCacheFolder so they can be manipulated/updated. In this case the contents of the folder called 'Data'

This code works fine on mobile, desktop and on Xbox in Dev Mode, but this line fails on Xbox in Retail Mode:

await file.CopyAsync(destinationFolder, file.Name, NameCollisionOption.FailIfExists);

This is also on a fresh install so I know the file(s) don't already exist.

Is there a different way to achieve this in Retail Mode, although in theory all UWP code should work across devices.

private async Task setupdatabase()
{
    StorageFolder destinationContainer = Windows.Storage.ApplicationData.Current.LocalCacheFolder;
    string root = Windows.ApplicationModel.Package.Current.InstalledLocation.Path;
    string path = root + @"\Data";

    StorageFolder sfolder = await StorageFolder.GetFolderFromPathAsync(path);

    await CopyFolderAsync(sfolder, destinationContainer);
}

public static async Task CopyFolderAsync(StorageFolder source, StorageFolder destinationContainer, string desiredName = null)
{
    StorageFolder destinationFolder = null;
    destinationFolder = await destinationContainer.CreateFolderAsync(desiredName ?? source.Name, CreationCollisionOption.OpenIfExists);

    foreach (var file in await source.GetFilesAsync())
    {
         await file.CopyAsync(destinationFolder, file.Name, NameCollisionOption.FailIfExists);
    }

    foreach (var folder in await source.GetFoldersAsync())
    {
         await CopyFolderAsync(folder, destinationFolder);
    }
}
Romasz
  • 29,662
  • 13
  • 79
  • 154
Stu Ayton
  • 489
  • 3
  • 15
  • Fails how? Does it throw an exception, or what? – Clockwork-Muse Feb 14 '17 at 07:07
  • that's the problem, as it only fails in Retail Mode, there's no way to connect a debugger to see what's going on. I have to make edits, submit to the store, wait for certification, install on a retail box and go through the entire time consuming process again. I've placed a textbox on screen and updated it's value after every command so I know where it's no longer working and tried catching any exception with try{}catch(Exception e){} blocks but it just skips the code in the catch – Stu Ayton Feb 14 '17 at 08:07

2 Answers2

2

Seems like a bug with CopyAsync on the Xbox in Retail Mode

Replacing:

await file.CopyAsync(destinationFolder, file.Name, NameCollisionOption.FailIfExists);

with:

StorageFile sourcefile = null;
string sourcefiletext = null;
            try
            {
                sourcefile = await source.GetFileAsync(file.Name);
                sourcefiletext = await FileIO.ReadTextAsync(sourcefile);
            }
            catch (Exception e)
            {
                Debug.WriteLine "Read source error:" + e.ToString();
            }

            try
            {

                StorageFile destfile = await destinationFolder.CreateFileAsync(file.Name, CreationCollisionOption.FailIfExists);
                await Windows.Storage.FileIO.WriteTextAsync(destfile, sourcefiletext);
            }
            catch (Exception e)
            {
                Debug.WriteLine "Write dest error:" + e.ToString();
            }

Basically breaking it into 2 separate operations fixed the problem and my app now runs normally. This is now being submitted as a bug report

Update: not quite a bug, but a feature, from Microsoft:

The issue here is that the package install folder is encrypted on Xbox retail mode.  A package has permission to read it's own files which is why the ReadTextAsync+WriteTextAsync works. CopyAsync on the other hand attempts to copy the file with all attributes associated with the file (including encryption).

Stu Ayton
  • 489
  • 3
  • 15
0

I'm not sure if that will be the case, as you code looks ok, but once I've stepped in a situation when running the app locally granted it some privileges. Maybe in this case also there are different privileges (for device/retail?) - thus, can you try to access the folder not via its path but directly with StorageFolder? Like this:

private async Task setupdatabase()
{
    StorageFolder sfolder = await Windows.ApplicationModel.Package.Current.InstalledLocation.GetFolderAsync("Data");
    await CopyFolderAsync(sfolder, ApplicationData.Current.LocalCacheFolder);
}

public static async Task CopyFolderAsync(StorageFolder source, StorageFolder destinationContainer, string desiredName = null)
{
    StorageFolder destinationFolder = await destinationContainer.CreateFolderAsync(desiredName ?? source.Name, CreationCollisionOption.OpenIfExists);

    var existingItems = await destinationFolder.GetFilesAsync(); // to check if files are already there
    foreach (var file in (await source.GetFilesAsync()).Where(x => !existingItems.Any(y => y.Name == x.Name)))
    {
        await file.CopyAsync(destinationFolder, file.Name);
    }

    foreach (var folder in await source.GetFoldersAsync())
    {
        await CopyFolderAsync(folder, destinationFolder);
    }
}

In second method I've changed FailIfExists attribute for a check-up of existing items - in case a problem is there.

Community
  • 1
  • 1
Romasz
  • 29,662
  • 13
  • 79
  • 154
  • Thanks @Romasz, that works fine locally, I've now compiled the app and submitted to the store so I can see if this works in retails mode, will report back as soon as I know – Stu Ayton Feb 14 '17 at 08:17
  • @StuAyton Have you also thought/tried using *LocalFolder* instead of *LocalCacheFolder*? – Romasz Feb 14 '17 at 10:53
  • Going by the documentation at https://learn.microsoft.com/en-us/uwp/api/Windows.Storage.ApplicationData#Windows_Storage_ApplicationData_LocalFolder LocalFolder data gets backed up to the cloud which I didn't want, it says Therefore if you want to persist data and keep it local to the device you should consider using LocalCacheFolder. – Stu Ayton Feb 14 '17 at 12:09
  • I've now been able to test your code in Retail Mode and unfortunately I get the same problem with the file not being copied – Stu Ayton Feb 14 '17 at 12:11
  • Weird, generally the code should work. I wonder if its a problem only of *LocalCacheFolder* or something deeper - can you check if you use *LocalFolder*, the same problem appears? – Romasz Feb 14 '17 at 12:33
  • It think I may have to go down that route, I've now managed to get an exception out of CopyAsync: System.UnauthorizedAccessException: Access is denied. (Excep_FromHResult 0x80070005) at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at SharedLibrary!+0x4511ee at SharedLibrary!+0x451168 at SharedLibrary!+0x699af7 at dimensions!+0x5d6d62 – Stu Ayton Feb 14 '17 at 13:08
  • @StuAyton I'm not sure about this case. As it works on other devices, maybe you can consider posting this as a bug/probem on user voice? – Romasz Feb 14 '17 at 13:18
  • 1
    I'm currently in discussions with Microsoft on this using one of my paid support requests, maybe if it's actually an Xbox bug they'll reimburse me for it lol – Stu Ayton Feb 14 '17 at 13:51
  • @StuAyton Give a sign/answer once you know something. – Romasz Feb 14 '17 at 13:51