0
votes

I have an UWP app (Universal Windows Platform). I'm trying to zip folder and its contents (sub-folders, images, text files).

It works well if the text file is not empty.

But it gives an exception when any text file is blank or empty (0KB size).

Following is the error:

{System.ArgumentException: The specified buffer index is not within the buffer capacity. at System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(IBuffer source, UInt32 sourceIndex, Int32 count) at System.Runtime.InteropServices.WindowsRuntime.WindowsRuntimeBufferExtensions.ToArray(IBuffer source) at SmartflowRuntimes.Service.d__54.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at SmartflowRuntimes.Service.d__54.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at SmartflowRuntimes.Service.d__53.MoveNext()}

This is my code:

public async Task<string> ZipHelper(string zipFileName)
{
    try
    {
        string appFolderPath = ApplicationData.Current.LocalFolder.Path;
        StorageFolder destnFolder = await StorageFolder.GetFolderFromPathAsync(appFolderPath + "\\destn");
        StorageFolder sourceFolder = await StorageFolder.GetFolderFromPathAsync(appFolderPath + "\\src");

        StorageFile zipFile = await destnFolder.CreateFileAsync(zipFileName, CreationCollisionOption.ReplaceExisting);
        Stream zipToCreate = await zipFile.OpenStreamForWriteAsync();
        ZipArchive archive = new ZipArchive(zipToCreate, ZipArchiveMode.Update);

        await ZipFolderContents(sourceFolder, archive, sourceFolder.Path);
        archive.Dispose();
        return "success";
    }
    catch (Exception ex)
    {
        return "fail";
    }
}

public async Task ZipFolderContents(StorageFolder sourceFolder, ZipArchive archive, string baseDirPath)
{
    IReadOnlyList<StorageFile> files = await sourceFolder.GetFilesAsync();

    foreach (StorageFile file in files)
    {
        ZipArchiveEntry readmeEntry = archive.CreateEntry(file.Path.Remove(0, baseDirPath.Length));
        byte[] buffer = WindowsRuntimeBufferExtensions.ToArray(await FileIO.ReadBufferAsync(file));
        using (Stream entryStream = readmeEntry.Open())
        {
            await entryStream.WriteAsync(buffer, 0, buffer.Length);
        }
    }

    IReadOnlyList<StorageFolder> subFolders = await sourceFolder.GetFoldersAsync();

    if (subFolders.Count() == 0) return;

    foreach (StorageFolder subfolder in subFolders)
        await ZipFolderContents(subfolder, archive, baseDirPath);
}

Are there any work around for this issue? Thanks.

1

1 Answers

1
votes

This System.ArgumentException is thrown from the WindowsRuntimeBufferExtensions.ToArray method, which expects that the IBuffer's size is larger than 0.

So to fix this, check for file size before calling this method, just like you may quite frequently check an object reference for null before calling its methods.

ZipArchiveEntry readmeEntry = archive.CreateEntry(file.Path.Remove(0, baseDirPath.Length));

ulong fileSize = (await file.GetBasicPropertiesAsync()).Size;                
byte[] buffer = fileSize > 0 ? (await FileIO.ReadBufferAsync(file)).ToArray() 
                : new byte[0];

using (Stream entryStream = readmeEntry.Open())
{
    await entryStream.WriteAsync(buffer, 0, buffer.Length);                    
}

By the way, WindowsRuntimeBufferExtensions.ToArray is an extension method, which enables you to call a nonexisting ToArray method on an IBuffer instance (so is called extension method). I also show how to call it in a more convenient way.