From 257abe23445adedafbc8f656d49e104bf2388900 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rune=20A=2E=20Juel=20M=C3=B8nnike?= Date: Tue, 23 Aug 2022 13:10:10 +0200 Subject: [PATCH 1/2] #55: First attempt at adding a progress callback. Unit tests failing for everything but 7zip format. --- SevenZipExtractor.Tests/Test7Zip.cs | 12 +++ SevenZipExtractor.Tests/TestArj.cs | 12 +++ SevenZipExtractor.Tests/TestBase.cs | 83 +++++++++++++++++++ SevenZipExtractor.Tests/TestLzh.cs | 12 +++ SevenZipExtractor.Tests/TestRar.cs | 12 +++ SevenZipExtractor.Tests/TestZip.cs | 12 +++ .../ArchiveExtractionProgressEventArgs.cs | 27 ++++++ SevenZipExtractor/ArchiveFile.cs | 8 +- SevenZipExtractor/ArchiveStreamCallback.cs | 22 ++++- SevenZipExtractor/ArchiveStreamsCallback.cs | 37 ++++++++- SevenZipExtractor/Entry.cs | 9 +- .../EntryExtractionProgressEventArgs.cs | 23 +++++ 12 files changed, 256 insertions(+), 13 deletions(-) create mode 100644 SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs create mode 100644 SevenZipExtractor/EntryExtractionProgressEventArgs.cs diff --git a/SevenZipExtractor.Tests/Test7Zip.cs b/SevenZipExtractor.Tests/Test7Zip.cs index 97cccfd..4649b7d 100644 --- a/SevenZipExtractor.Tests/Test7Zip.cs +++ b/SevenZipExtractor.Tests/Test7Zip.cs @@ -19,5 +19,17 @@ public void TestKnownFormatAndExtractToStream_OK() { this.TestExtractToStream(Resources.TestFiles.SevenZip, this.TestEntriesWithoutFolder, SevenZipFormat.SevenZip); } + + [TestMethod] + public void TestProgressWithArchiveExtraction_OK() + { + this.TestExtractArchiveWithProgress(Resources.TestFiles.SevenZip, SevenZipFormat.SevenZip); + } + + [TestMethod] + public void TestProgressWithEntryExtraction_OK() + { + this.TestExtractEntriesWithProgress(Resources.TestFiles.SevenZip, SevenZipFormat.SevenZip); + } } } \ No newline at end of file diff --git a/SevenZipExtractor.Tests/TestArj.cs b/SevenZipExtractor.Tests/TestArj.cs index 0af195f..ae372a9 100644 --- a/SevenZipExtractor.Tests/TestArj.cs +++ b/SevenZipExtractor.Tests/TestArj.cs @@ -21,5 +21,17 @@ public void Text_UnboxAndCast_OK() this.TestExtractToStream(Resources.TestFiles.ansimate_arj, testEntries, SevenZipFormat.Arj); } + + [TestMethod] + public void TestProgressWithArchiveExtraction_OK() + { + this.TestExtractArchiveWithProgress(Resources.TestFiles.ansimate_arj, SevenZipFormat.Arj); + } + + [TestMethod] + public void TestProgressWithEntryExtraction_OK() + { + this.TestExtractEntriesWithProgress(Resources.TestFiles.ansimate_arj, SevenZipFormat.Arj); + } } } \ No newline at end of file diff --git a/SevenZipExtractor.Tests/TestBase.cs b/SevenZipExtractor.Tests/TestBase.cs index 00bb63c..8b81934 100644 --- a/SevenZipExtractor.Tests/TestBase.cs +++ b/SevenZipExtractor.Tests/TestBase.cs @@ -56,7 +56,90 @@ protected void TestExtractToStream(byte[] archiveBytes, IList exp } } } + } + protected void TestExtractEntriesWithProgress(byte[] archiveBytes, SevenZipFormat? sevenZipFormat = null) + { + MemoryStream memoryStream = new MemoryStream(archiveBytes); + + using (ArchiveFile archiveFile = new ArchiveFile(memoryStream, sevenZipFormat)) + { + foreach (var entry in archiveFile.Entries) + { + if (entry.IsFolder) + { + continue; + } + + using (MemoryStream entryMemoryStream = new MemoryStream()) + { + bool progressCalledAtBeginning = false; + bool progressCalledAtEnd = false; + + entry.Extract(entryMemoryStream, (s, e) => + { + if (e.CurrentFileTotal > 0) + { + if (e.CurrentFileCompleted == 0) + { + progressCalledAtBeginning = true; + } + else if (e.CurrentFileCompleted == e.CurrentFileTotal) + { + progressCalledAtEnd = true; + } + } + }); + + Assert.IsTrue(progressCalledAtBeginning, $"Progress callback was not called at the beginning of extracting file {entry.FileName}."); + Assert.IsTrue(progressCalledAtEnd, $"Progress callback was not called at the end of extracting file {entry.FileName}."); + } + } + } + } + + protected void TestExtractArchiveWithProgress(byte[] archiveBytes, SevenZipFormat? sevenZipFormat = null) + { + MemoryStream memoryStream = new MemoryStream(archiveBytes); + + string tempPath = Path.Combine(Path.GetTempPath(), "SevenZipExtractorUnitTests"); + Directory.CreateDirectory(tempPath); + + try + { + using (ArchiveFile archiveFile = new ArchiveFile(memoryStream, sevenZipFormat)) + { + int progressCalledAtBeginning = 0; + int progressCalledAtEnd = 0; + HashSet progressCalledForIndex = new HashSet(); + + archiveFile.Extract(tempPath, true, (s, e) => + { + Assert.AreEqual(archiveFile.Entries.Count, e.TotalFileCount, "Incorrect total file count in progress callback."); + progressCalledForIndex.Add(e.CurrentFileNumber); + + if (e.CurrentFileTotal > 0) + { + if (e.CurrentFileCompleted == 0) + { + progressCalledAtBeginning++; + } + else if (e.CurrentFileCompleted == e.CurrentFileTotal) + { + progressCalledAtEnd++; + } + } + }); + + Assert.AreEqual(archiveFile.Entries.Count, progressCalledForIndex.Count, "Progress callback was not called at all for one or more files."); + Assert.IsTrue(archiveFile.Entries.Count <= progressCalledAtBeginning, "Progress callback was not called at the beginning of extracting each file."); + Assert.IsTrue(progressCalledAtEnd > 0, "Progress callback was not called at the end of extracting files."); + } + } + finally + { + Directory.Delete(tempPath, true); + } } } } \ No newline at end of file diff --git a/SevenZipExtractor.Tests/TestLzh.cs b/SevenZipExtractor.Tests/TestLzh.cs index 0fe0f23..3e299f2 100644 --- a/SevenZipExtractor.Tests/TestLzh.cs +++ b/SevenZipExtractor.Tests/TestLzh.cs @@ -21,5 +21,17 @@ public void TestKnownFormatAndExtractToStream_OK() { this.TestExtractToStream(Resources.TestFiles.lzh, this.TestEntriesWithoutFolder, SevenZipFormat.Lzh); } + + [TestMethod] + public void TestProgressWithArchiveExtraction_OK() + { + this.TestExtractArchiveWithProgress(Resources.TestFiles.lzh, SevenZipFormat.Lzh); + } + + [TestMethod] + public void TestProgressWithEntryExtraction_OK() + { + this.TestExtractEntriesWithProgress(Resources.TestFiles.lzh, SevenZipFormat.Lzh); + } } } \ No newline at end of file diff --git a/SevenZipExtractor.Tests/TestRar.cs b/SevenZipExtractor.Tests/TestRar.cs index 4ba0242..548850e 100644 --- a/SevenZipExtractor.Tests/TestRar.cs +++ b/SevenZipExtractor.Tests/TestRar.cs @@ -15,6 +15,18 @@ public void TestGuessAndExtractToStream_OK() public void TestKnownFormatAndExtractToStream_OK() { this.TestExtractToStream(Resources.TestFiles.rar, this.TestEntriesWithFolder, SevenZipFormat.Rar5); + } + + [TestMethod] + public void TestProgressWithArchiveExtraction_OK() + { + this.TestExtractArchiveWithProgress(Resources.TestFiles.rar, SevenZipFormat.Rar5); + } + + [TestMethod] + public void TestProgressWithEntryExtraction_OK() + { + this.TestExtractEntriesWithProgress(Resources.TestFiles.rar, SevenZipFormat.Rar5); } } } \ No newline at end of file diff --git a/SevenZipExtractor.Tests/TestZip.cs b/SevenZipExtractor.Tests/TestZip.cs index de71bf3..d7d505e 100644 --- a/SevenZipExtractor.Tests/TestZip.cs +++ b/SevenZipExtractor.Tests/TestZip.cs @@ -15,6 +15,18 @@ public void TestGuessAndExtractToStream_OK() public void TestKnownFormatAndExtractToStream_OK() { this.TestExtractToStream(Resources.TestFiles.zip, this.TestEntriesWithFolder, SevenZipFormat.Zip); + } + + [TestMethod] + public void TestProgressWithArchiveExtraction_OK() + { + this.TestExtractArchiveWithProgress(Resources.TestFiles.zip, SevenZipFormat.Zip); + } + + [TestMethod] + public void TestProgressWithEntryExtraction_OK() + { + this.TestExtractEntriesWithProgress(Resources.TestFiles.zip, SevenZipFormat.Zip); } } } \ No newline at end of file diff --git a/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs b/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs new file mode 100644 index 0000000..75dee36 --- /dev/null +++ b/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace SevenZipExtractor +{ + public class ArchiveExtractionProgressEventArgs : EntryExtractionProgressEventArgs + { + /// + /// The index of the file currently being extracted. + /// + public uint CurrentFileNumber {get; } + + /// + /// The total number of files that will be extracted. + /// + public int TotalFileCount {get; } + + internal ArchiveExtractionProgressEventArgs(uint currentFileNumber, int totalFileCount, ulong currentFileCompleted, ulong currentFileTotal) : base(currentFileCompleted, currentFileTotal) + { + CurrentFileNumber = currentFileNumber; + TotalFileCount = totalFileCount; + } + } +} diff --git a/SevenZipExtractor/ArchiveFile.cs b/SevenZipExtractor/ArchiveFile.cs index 8ccf66b..a976f19 100644 --- a/SevenZipExtractor/ArchiveFile.cs +++ b/SevenZipExtractor/ArchiveFile.cs @@ -75,7 +75,7 @@ public ArchiveFile(Stream archiveStream, SevenZipFormat? format = null, string l this.archiveStream = new InStreamWrapper(archiveStream); } - public void Extract(string outputFolder, bool overwrite = false) + public void Extract(string outputFolder, bool overwrite = false, EventHandler progressEventHandler = null) { this.Extract(entry => { @@ -92,10 +92,10 @@ public void Extract(string outputFolder, bool overwrite = false) } return null; - }); + }, progressEventHandler); } - public void Extract(Func getOutputPath) + public void Extract(Func getOutputPath, EventHandler progressEventHandler = null) { IList fileStreams = new List(); @@ -128,7 +128,7 @@ public void Extract(Func getOutputPath) fileStreams.Add(File.Create(outputPath)); } - this.archive.Extract(null, 0xFFFFFFFF, 0, new ArchiveStreamsCallback(fileStreams)); + this.archive.Extract(null, 0xFFFFFFFF, 0, new ArchiveStreamsCallback(fileStreams, progressEventHandler)); } finally { diff --git a/SevenZipExtractor/ArchiveStreamCallback.cs b/SevenZipExtractor/ArchiveStreamCallback.cs index e71e225..8877286 100644 --- a/SevenZipExtractor/ArchiveStreamCallback.cs +++ b/SevenZipExtractor/ArchiveStreamCallback.cs @@ -1,4 +1,5 @@ -using System.IO; +using System; +using System.IO; namespace SevenZipExtractor { @@ -6,19 +7,28 @@ internal class ArchiveStreamCallback : IArchiveExtractCallback { private readonly uint fileNumber; private readonly Stream stream; + private readonly EventHandler progressEventHandler; + + private ulong currentCompleteValue; + private ulong currentTotal; - public ArchiveStreamCallback(uint fileNumber, Stream stream) + public ArchiveStreamCallback(uint fileNumber, Stream stream, EventHandler progressEventHandler) { this.fileNumber = fileNumber; this.stream = stream; + this.progressEventHandler = progressEventHandler; } public void SetTotal(ulong total) { + this.currentTotal = total; + InvokeProgressCallback(); } public void SetCompleted(ref ulong completeValue) { + this.currentCompleteValue = completeValue; + InvokeProgressCallback(); } public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) @@ -41,5 +51,13 @@ public void PrepareOperation(AskMode askExtractMode) public void SetOperationResult(OperationResult resultEOperationResult) { } + + private void InvokeProgressCallback() + { + progressEventHandler?.Invoke( + this, + new EntryExtractionProgressEventArgs(this.currentCompleteValue, this.currentTotal) + ); + } } } \ No newline at end of file diff --git a/SevenZipExtractor/ArchiveStreamsCallback.cs b/SevenZipExtractor/ArchiveStreamsCallback.cs index b8f1fca..cb8ed87 100644 --- a/SevenZipExtractor/ArchiveStreamsCallback.cs +++ b/SevenZipExtractor/ArchiveStreamsCallback.cs @@ -1,27 +1,50 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.IO; +using System.Linq; namespace SevenZipExtractor { internal class ArchiveStreamsCallback : IArchiveExtractCallback { private readonly IList streams; + private readonly int streamCount; + private readonly EventHandler progressEventHandler; - public ArchiveStreamsCallback(IList streams) + private uint currentIndex; + private ulong currentTotal; + private ulong currentCompleteValue; + + public ArchiveStreamsCallback(IList streams, EventHandler progressEventHandler) { this.streams = streams; + this.streamCount = streams.Where(s => s != null).Count(); + this.progressEventHandler = progressEventHandler; } public void SetTotal(ulong total) { + this.currentTotal = total; } public void SetCompleted(ref ulong completeValue) { + this.currentCompleteValue = completeValue; + + // If completeValue is 0, currentIndex has not yet been set correctly, since GetStream is initially called after SetCompleted. + if (completeValue > 0) + { + InvokeProgressCallback(); + } } public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) { + this.currentIndex = index; + + // SetTotal and SetCompleted are called before GetStream, so now that currentIndex is correct, we invoke the progress callback. + InvokeProgressCallback(); + if (askExtractMode != AskMode.kExtract) { outStream = null; @@ -34,7 +57,7 @@ public int GetStream(uint index, out ISequentialOutStream outStream, AskMode ask return 0; } - Stream stream = this.streams[(int) index]; + Stream stream = this.streams[(int)index]; if (stream == null) { @@ -54,5 +77,13 @@ public void PrepareOperation(AskMode askExtractMode) public void SetOperationResult(OperationResult resultEOperationResult) { } + + private void InvokeProgressCallback() + { + progressEventHandler?.Invoke( + this, + new ArchiveExtractionProgressEventArgs(this.currentIndex, this.streamCount, this.currentCompleteValue, this.currentTotal) + ); + } } } \ No newline at end of file diff --git a/SevenZipExtractor/Entry.cs b/SevenZipExtractor/Entry.cs index 13220b0..d27a03a 100644 --- a/SevenZipExtractor/Entry.cs +++ b/SevenZipExtractor/Entry.cs @@ -86,7 +86,7 @@ internal Entry(IInArchive archive, uint index) /// public bool IsSplitAfter { get; set; } - public void Extract(string fileName, bool preserveTimestamp = true) + public void Extract(string fileName, bool preserveTimestamp = true, EventHandler progressEventHandler = null) { if (this.IsFolder) { @@ -103,7 +103,7 @@ public void Extract(string fileName, bool preserveTimestamp = true) using (FileStream fileStream = File.Create(fileName)) { - this.Extract(fileStream); + this.Extract(fileStream, progressEventHandler); } if (preserveTimestamp) @@ -111,9 +111,10 @@ public void Extract(string fileName, bool preserveTimestamp = true) File.SetLastWriteTime(fileName, this.LastWriteTime); } } - public void Extract(Stream stream) + + public void Extract(Stream stream, EventHandler progressEventHandler = null) { - this.archive.Extract(new[] { this.index }, 1, 0, new ArchiveStreamCallback(this.index, stream)); + this.archive.Extract(new[] { this.index }, 1, 0, new ArchiveStreamCallback(this.index, stream, progressEventHandler)); } } } diff --git a/SevenZipExtractor/EntryExtractionProgressEventArgs.cs b/SevenZipExtractor/EntryExtractionProgressEventArgs.cs new file mode 100644 index 0000000..f8b1ff0 --- /dev/null +++ b/SevenZipExtractor/EntryExtractionProgressEventArgs.cs @@ -0,0 +1,23 @@ +using System; + +namespace SevenZipExtractor +{ + public class EntryExtractionProgressEventArgs : EventArgs + { + /// + /// Number of bytes completed for file currently being extracted. + /// + public ulong CurrentFileCompleted { get; } + + /// + /// The total number of bytes to extract for the current file. + /// + public ulong CurrentFileTotal { get; } + + internal EntryExtractionProgressEventArgs(ulong currentFileCompleted, ulong currentFileTotal) + { + CurrentFileCompleted = currentFileCompleted; + CurrentFileTotal = currentFileTotal; + } + } +} \ No newline at end of file From e80b98fc50a40e9bad7f2727a18d26fa79740f9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Rune=20A=2E=20Juel=20M=C3=B8nnike?= Date: Wed, 24 Aug 2022 09:16:10 +0200 Subject: [PATCH 2/2] #55: Fixed progress reporting for formats other than 7z. --- SevenZipExtractor.Tests/TestBase.cs | 27 ++++++++++-------- .../ArchiveExtractionProgressEventArgs.cs | 14 +++++----- SevenZipExtractor/ArchiveFile.cs | 4 ++- SevenZipExtractor/ArchiveStreamCallback.cs | 19 +++++++++++-- SevenZipExtractor/ArchiveStreamsCallback.cs | 28 +++++++++++++++---- SevenZipExtractor/Entry.cs | 4 ++- .../EntryExtractionProgressEventArgs.cs | 14 +++++----- 7 files changed, 76 insertions(+), 34 deletions(-) diff --git a/SevenZipExtractor.Tests/TestBase.cs b/SevenZipExtractor.Tests/TestBase.cs index 8b81934..3441856 100644 --- a/SevenZipExtractor.Tests/TestBase.cs +++ b/SevenZipExtractor.Tests/TestBase.cs @@ -78,13 +78,13 @@ protected void TestExtractEntriesWithProgress(byte[] archiveBytes, SevenZipForma entry.Extract(entryMemoryStream, (s, e) => { - if (e.CurrentFileTotal > 0) + if (e.Total > 0) { - if (e.CurrentFileCompleted == 0) + if (e.Completed == 0) { progressCalledAtBeginning = true; } - else if (e.CurrentFileCompleted == e.CurrentFileTotal) + else if (e.Completed == e.Total) { progressCalledAtEnd = true; } @@ -112,28 +112,33 @@ protected void TestExtractArchiveWithProgress(byte[] archiveBytes, SevenZipForma int progressCalledAtBeginning = 0; int progressCalledAtEnd = 0; HashSet progressCalledForIndex = new HashSet(); + HashSet reportedTotalEntryCounts = new HashSet(); archiveFile.Extract(tempPath, true, (s, e) => { - Assert.AreEqual(archiveFile.Entries.Count, e.TotalFileCount, "Incorrect total file count in progress callback."); - progressCalledForIndex.Add(e.CurrentFileNumber); + reportedTotalEntryCounts.Add(e.EntryCount); + progressCalledForIndex.Add(e.EntryIndex); - if (e.CurrentFileTotal > 0) + if (e.Total > 0) { - if (e.CurrentFileCompleted == 0) + if (e.Completed == 0) { progressCalledAtBeginning++; } - else if (e.CurrentFileCompleted == e.CurrentFileTotal) + else if (e.Completed == e.Total) { progressCalledAtEnd++; } } }); - Assert.AreEqual(archiveFile.Entries.Count, progressCalledForIndex.Count, "Progress callback was not called at all for one or more files."); - Assert.IsTrue(archiveFile.Entries.Count <= progressCalledAtBeginning, "Progress callback was not called at the beginning of extracting each file."); - Assert.IsTrue(progressCalledAtEnd > 0, "Progress callback was not called at the end of extracting files."); + Assert.AreEqual(1, reportedTotalEntryCounts.Count, "None or more than one total file count reported in progress callback."); + + var entryCount = reportedTotalEntryCounts.First(); + Assert.IsTrue(entryCount > 0, "No entries to extract in test archive, or progress callback never called, or progress callback called with zero total entry count."); + Assert.AreEqual(entryCount, progressCalledForIndex.Count, "Progress callback was not called at all for one or more entries or was called more than expected."); + Assert.IsTrue(progressCalledAtBeginning > 0, "Progress callback was not called at the beginning of extraction."); + Assert.IsTrue(progressCalledAtEnd > 0, "Progress callback was not called at the end of extraction."); } } finally diff --git a/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs b/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs index 75dee36..0c90c31 100644 --- a/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs +++ b/SevenZipExtractor/ArchiveExtractionProgressEventArgs.cs @@ -9,19 +9,19 @@ namespace SevenZipExtractor public class ArchiveExtractionProgressEventArgs : EntryExtractionProgressEventArgs { /// - /// The index of the file currently being extracted. + /// The index of the entry currently being extracted. /// - public uint CurrentFileNumber {get; } + public uint EntryIndex {get; } /// - /// The total number of files that will be extracted. + /// The total number of entries that will be extracted. /// - public int TotalFileCount {get; } + public int EntryCount {get; } - internal ArchiveExtractionProgressEventArgs(uint currentFileNumber, int totalFileCount, ulong currentFileCompleted, ulong currentFileTotal) : base(currentFileCompleted, currentFileTotal) + internal ArchiveExtractionProgressEventArgs(uint entryIndex, int entryCount, ulong completed, ulong total) : base(completed, total) { - CurrentFileNumber = currentFileNumber; - TotalFileCount = totalFileCount; + EntryIndex = entryIndex; + EntryCount = entryCount; } } } diff --git a/SevenZipExtractor/ArchiveFile.cs b/SevenZipExtractor/ArchiveFile.cs index a976f19..80d393a 100644 --- a/SevenZipExtractor/ArchiveFile.cs +++ b/SevenZipExtractor/ArchiveFile.cs @@ -128,7 +128,9 @@ public void Extract(Func getOutputPath, EventHandler progressEventHandler) { @@ -22,13 +23,13 @@ public ArchiveStreamCallback(uint fileNumber, Stream stream, EventHandler streams, EventHandler progressEventHandler) { @@ -32,18 +34,16 @@ public void SetCompleted(ref ulong completeValue) this.currentCompleteValue = completeValue; // If completeValue is 0, currentIndex has not yet been set correctly, since GetStream is initially called after SetCompleted. - if (completeValue > 0) + if (completeValue > 0 && this.isCurrentValidForProgress) { - InvokeProgressCallback(); + this.InvokeProgressCallback(); } } public int GetStream(uint index, out ISequentialOutStream outStream, AskMode askExtractMode) { this.currentIndex = index; - - // SetTotal and SetCompleted are called before GetStream, so now that currentIndex is correct, we invoke the progress callback. - InvokeProgressCallback(); + this.isCurrentValidForProgress = false; if (askExtractMode != AskMode.kExtract) { @@ -65,6 +65,10 @@ public int GetStream(uint index, out ISequentialOutStream outStream, AskMode ask return 0; } + // SetTotal and SetCompleted are called before GetStream, so now that currentIndex is correct, we invoke the progress callback. + this.isCurrentValidForProgress = true; + this.InvokeProgressCallback(); + outStream = new OutStreamWrapper(stream); return 0; @@ -77,6 +81,15 @@ public void PrepareOperation(AskMode askExtractMode) public void SetOperationResult(OperationResult resultEOperationResult) { } + + public void InvokeFinalProgressCallback() + { + if (!this.finalProgressReported) + { + // 7z doesn't invoke SetCompleted for all formats when an entry is fully extracted, so we fake it. + this.SetCompleted(ref this.currentTotal); + } + } private void InvokeProgressCallback() { @@ -84,6 +97,11 @@ private void InvokeProgressCallback() this, new ArchiveExtractionProgressEventArgs(this.currentIndex, this.streamCount, this.currentCompleteValue, this.currentTotal) ); + + if (this.currentCompleteValue == this.currentTotal) + { + this.finalProgressReported = true; + } } } } \ No newline at end of file diff --git a/SevenZipExtractor/Entry.cs b/SevenZipExtractor/Entry.cs index d27a03a..d5cf8ce 100644 --- a/SevenZipExtractor/Entry.cs +++ b/SevenZipExtractor/Entry.cs @@ -114,7 +114,9 @@ public void Extract(string fileName, bool preserveTimestamp = true, EventHandler public void Extract(Stream stream, EventHandler progressEventHandler = null) { - this.archive.Extract(new[] { this.index }, 1, 0, new ArchiveStreamCallback(this.index, stream, progressEventHandler)); + ArchiveStreamCallback extractCallback = new ArchiveStreamCallback(this.index, stream, progressEventHandler); + this.archive.Extract(new[] { this.index }, 1, 0, extractCallback); + extractCallback.InvokeFinalProgressCallback(); } } } diff --git a/SevenZipExtractor/EntryExtractionProgressEventArgs.cs b/SevenZipExtractor/EntryExtractionProgressEventArgs.cs index f8b1ff0..b94443d 100644 --- a/SevenZipExtractor/EntryExtractionProgressEventArgs.cs +++ b/SevenZipExtractor/EntryExtractionProgressEventArgs.cs @@ -5,19 +5,19 @@ namespace SevenZipExtractor public class EntryExtractionProgressEventArgs : EventArgs { /// - /// Number of bytes completed for file currently being extracted. + /// Number of bytes completed. Can be packed or unpacked size depending on format. /// - public ulong CurrentFileCompleted { get; } + public ulong Completed { get; } /// - /// The total number of bytes to extract for the current file. + /// The total number of bytes to extract. Not set for some formats. /// - public ulong CurrentFileTotal { get; } + public ulong Total { get; } - internal EntryExtractionProgressEventArgs(ulong currentFileCompleted, ulong currentFileTotal) + internal EntryExtractionProgressEventArgs(ulong completed, ulong total) { - CurrentFileCompleted = currentFileCompleted; - CurrentFileTotal = currentFileTotal; + Completed = completed; + Total = total; } } } \ No newline at end of file