From 11f9bf9fd9ecae6d192c2b31afb4e2517de239a9 Mon Sep 17 00:00:00 2001 From: gmipf Date: Mon, 26 Oct 2026 14:46:10 +0100 Subject: [PATCH] Stop writing after the last file of a cabinet folder (#107) * Stop writing after the last file of a cabinet folder When the last data block of a folder held the end of two or more files, WriteData still wrote the rest of the block after EndFile had closed the last file, which threw an ObjectDisposedException. It now returns there, as it already does when the first file in a block ends the folder. Co-Authored-By: Claude Opus 5.5 * Simplify the console check in the cabinet extraction test Extract catches the ObjectDisposedException and only writes it to Console.Error, so the test still reads the console, but without the try/finally. It now fails with the whole exception as its message. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- .../MicrosoftCabinetTests.cs | 28 ++++++++++++++++++ .../TestData/MicrosoftCabinet/mszip.cab | Bin 0 -> 113 bytes .../MicrosoftCabinet.Extraction.cs | 2 +- 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 SabreTools.Wrappers.Test/TestData/MicrosoftCabinet/mszip.cab diff --git a/SabreTools.Wrappers.Test/MicrosoftCabinetTests.cs b/SabreTools.Wrappers.Test/MicrosoftCabinetTests.cs index dc325dc5..0e07e7f4 100644 --- a/SabreTools.Wrappers.Test/MicrosoftCabinetTests.cs +++ b/SabreTools.Wrappers.Test/MicrosoftCabinetTests.cs @@ -1,3 +1,4 @@ +using System; using System.IO; using System.Linq; using Xunit; @@ -56,5 +57,32 @@ namespace SabreTools.Wrappers.Test var actual = MicrosoftCabinet.Create(data); Assert.Null(actual); } + + #region Extract + + [Fact] + public void Extract_LastBlockEndsTwoFiles() + { + // One MS-ZIP block that holds both files + string path = Path.Combine(Environment.CurrentDirectory, "TestData", "MicrosoftCabinet", "mszip.cab"); + var cabinet = MicrosoftCabinet.Create(File.ReadAllBytes(path), 0); + Assert.NotNull(cabinet); + + // Extract catches the exception and only writes it to the console + string outputDirectory = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var originalError = Console.Error; + var error = new StringWriter(); + Console.SetError(error); + cabinet.Extract(outputDirectory, includeDebug: true); + Console.SetError(originalError); + + // Assert.True shows the whole exception, Assert.Empty cuts it off + Assert.True(error.ToString().Length == 0, error.ToString()); + Assert.Equal("Hello, ", File.ReadAllText(Path.Combine(outputDirectory, "a.txt"))); + Assert.Equal("World!", File.ReadAllText(Path.Combine(outputDirectory, "b.txt"))); + Directory.Delete(outputDirectory, recursive: true); + } + + #endregion } } diff --git a/SabreTools.Wrappers.Test/TestData/MicrosoftCabinet/mszip.cab b/SabreTools.Wrappers.Test/TestData/MicrosoftCabinet/mszip.cab new file mode 100644 index 0000000000000000000000000000000000000000..c5d8257af008fa5a6bae0c448e09e9e4b0401563 GIT binary patch literal 113 zcmeYbc6MW6U|=W&5ey6rI#8OKk&%Ikfq}t9h=C!3fq{XMfsuh7D#yT}7{!pFz>ug{ pQc=Rd#=roTfruu7ML|jh8F(3-y+3=LJ$dqaAjf(AQ=Wk=3;;hq5HkP( literal 0 HcmV?d00001 diff --git a/SabreTools.Wrappers/MicrosoftCabinet.Extraction.cs b/SabreTools.Wrappers/MicrosoftCabinet.Extraction.cs index 5e4de53d..e0e93e0b 100644 --- a/SabreTools.Wrappers/MicrosoftCabinet.Extraction.cs +++ b/SabreTools.Wrappers/MicrosoftCabinet.Extraction.cs @@ -548,7 +548,7 @@ namespace SabreTools.Wrappers _fileStream.Write(data, (int)tempBytesLeft, (int)_bytesLeft); tempBytesLeft += _bytesLeft; if (EndFile(outputDirectory)) - break; + return; } _fileStream.Write(data, (int)tempBytesLeft, data.Length - (int)tempBytesLeft);