Invalid content after updating empty password-protected archive.
Workaround: Instead of creating an empty archive, add a dummy file to it that can be removed later.
The attached gif file illustrates the inability to open the newly added file with the password:
Sub Main()
Dim sZipFilePath As String = "..\..\test.zip"
File.Delete(sZipFilePath)
CreateArchive(sZipFilePath)
Dim decryptionSettings As DecryptionSettings = EncryptionSettings.CreateDecryptionSettings()
AddHandler decryptionSettings.PasswordRequired, AddressOf DecryptionSettings_PasswordRequired
Dim compressionSettings As CompressionSettings = Nothing
Dim encoding As Encoding = Nothing
Using oFS As FileStream = File.Open(sZipFilePath, FileMode.OpenOrCreate)
Using oArchive As ZipArchive = ZipArchive.Update(oFS, encoding, compressionSettings, decryptionSettings)
Using entry As ZipArchiveEntry = oArchive.CreateEntry("newText.txt")
Dim writer As StreamWriter = New StreamWriter(entry.Open())
writer.WriteLine("Hello world!")
writer.Flush()
End Using
End Using
End Using
End Sub
Private Sub CreateArchive(sZipFilePath As String)
Using stream As Stream = File.Open(sZipFilePath, FileMode.Create)
Dim _encryptionSettings As PasswordEncryptionSettings = EncryptionSettings.CreatePkzipPasswordEncryptionSettings()
_encryptionSettings.Password = "telerik"
Dim compressionSettings As CompressionSettings = Nothing
Dim encoding As Encoding = Nothing
Using archive As ZipArchive = ZipArchive.Create(stream, encoding, compressionSettings, _encryptionSettings)
Using entry As ZipArchiveEntry = archive.CreateEntry("text.txt")
Dim writer As StreamWriter = New StreamWriter(entry.Open())
writer.WriteLine("Hello world!")
writer.Flush()
End Using
End Using
End Using
End Sub
Private Sub DecryptionSettings_PasswordRequired(ByVal sender As Object, ByVal e As PasswordRequiredEventArgs)
e.Password = "telerik"
End Sub
Workaround: Until this bug is fixed the obsolete API could be used instead:
ZipArchive archive = new ZipArchive(stream, ZipArchiveMode.Create, true, encoding, compressionSettings, encryptionSettings)
ZipArchive oArchive = new ZipArchive(stream, ZipArchiveMode.Update, true, encoding, compressionSettings, decryptionSettings)
Steps to reproduce:
1.Create a zip archive for a txt file with password protection
2. Try updating the protected file and insert a new text line for example
The settings used for decrypting the file don't seem to be used encrypting again after the update operation. Here is a sample code snippet for reproducing the error message:
Sub Main()
Dim sZipFilePath As String = "..\..\test.zip"
File.Delete(sZipFilePath)
CreateArchive(sZipFilePath)
Dim decryptionSettings As DecryptionSettings = EncryptionSettings.CreateDecryptionSettings()
AddHandler decryptionSettings.PasswordRequired, AddressOf DecryptionSettings_PasswordRequired
Dim compressionSettings As CompressionSettings = Nothing
Dim encoding As Encoding = Nothing
Using oFS As FileStream = File.Open(sZipFilePath, FileMode.OpenOrCreate)
Using oArchive As ZipArchive = ZipArchive.Update(oFS, encoding, compressionSettings, decryptionSettings)
For Each entry As ZipArchiveEntry In oArchive.Entries
Using entryStream As Stream = entry.Open()
Dim reader As New StreamReader(entryStream)
Dim content As String = reader.ReadToEnd()
entryStream.Seek(0, SeekOrigin.End)
Dim writer As New StreamWriter(entryStream)
writer.WriteLine("Updated line.")
writer.Flush()
End Using
Next
End Using
End Using
End Sub
Private Sub CreateArchive(sZipFilePath As String)
Using stream As Stream = File.Open(sZipFilePath, FileMode.Create)
Dim encryptionSettings As PasswordEncryptionSettings = encryptionSettings.CreatePkzipPasswordEncryptionSettings()
encryptionSettings.Password = "MyPassword"
Dim compressionSettings As CompressionSettings = Nothing
Dim encoding As Encoding = Nothing
Using archive As ZipArchive = ZipArchive.Create(stream, encoding, compressionSettings, encryptionSettings)
Using entry As ZipArchiveEntry = archive.CreateEntry("text.txt")
Dim writer As StreamWriter = New StreamWriter(entry.Open())
writer.WriteLine("Hello world!")
writer.Flush()
End Using
End Using
End Using
End Sub
Private Sub DecryptionSettings_PasswordRequired(ByVal sender As Object, ByVal e As PasswordRequiredEventArgs)
e.Password = "MyPassword"
End Sub
The stream that is internally created by the CreateFromDirectory method remains opened and the user can't close it:
This is error message observed when using the below code snippet:
The process cannot access the file 'C:\Users\dyordano\OneDrive - Progress Software Corporation\MyProjects\1640071ZipFileCreateFromDirectory\myzip.zip' because it is being used by another process.
static void Main(string[] args)
{
string archiveFileName = @"..\..\myzip.zip";
string sourceDirectoryName = @"..\..\Folder";
File.Delete(archiveFileName);
Telerik.Windows.Zip.Extensions.ZipFile.CreateFromDirectory(sourceDirectoryName, archiveFileName, Telerik.Windows.Zip.CompressionLevel.Optimal, true);
using (Stream stream = File.Open(archiveFileName, FileMode.Open))
{
}
}
Workaround:
static void Main(string[] args)
{
string archiveFileName = @"..\..\myzip.zip";
string sourceDirectoryName = @"..\..\Folder";
File.Delete(archiveFileName);
// Telerik.Windows.Zip.Extensions.ZipFile.CreateFromDirectory(sourceDirectoryName, archiveFileName, Telerik.Windows.Zip.CompressionLevel.Optimal, true);
CreateFromDirectory(sourceDirectoryName, archiveFileName, Telerik.Windows.Zip.CompressionLevel.Optimal, true);
using (Stream stream = File.Open(archiveFileName, FileMode.Open))
{
}
}
private static void CreateFromDirectory(string sourceDirectoryName, string destinationArchiveFileName, Telerik.Windows.Zip.CompressionLevel compressionLevel, bool includeBaseDirectory)
{
Encoding entryNameEncoding = null;
DeflateSettings compressionSettings = new DeflateSettings()
{
CompressionLevel = compressionLevel,
HeaderType = CompressedStreamHeader.None
};
char[] directorySeparatorChar = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar };
sourceDirectoryName = Path.GetFullPath(sourceDirectoryName);
destinationArchiveFileName = Path.GetFullPath(destinationArchiveFileName);
FileStream fileStream = File.Open(destinationArchiveFileName, FileMode.CreateNew, FileAccess.Write, FileShare.None); ;
using (ZipArchive zipArchive = new ZipArchive(fileStream, ZipArchiveMode.Create,false, entryNameEncoding))
{
bool empty = true;
DirectoryInfo directoryInfo = new DirectoryInfo(sourceDirectoryName);
string fullName = directoryInfo.FullName;
if (includeBaseDirectory && directoryInfo.Parent != null)
{
fullName = directoryInfo.Parent.FullName;
}
foreach (FileSystemInfo fileSystemInfo in directoryInfo.EnumerateFileSystemInfos("*", SearchOption.AllDirectories))
{
empty = false;
int length = fileSystemInfo.FullName.Length - fullName.Length;
string entryName = fileSystemInfo.FullName.Substring(fullName.Length, length);
entryName = entryName.TrimStart(directorySeparatorChar);
DirectoryInfo subfolder = fileSystemInfo as DirectoryInfo;
if (subfolder != null)
{
if (IsDirectoryEmpty(subfolder))
{
zipArchive.CreateEntry(string.Concat(entryName, Path.DirectorySeparatorChar));
}
}
else
{
ZipFile.CreateEntryFromFile(zipArchive, fileSystemInfo.FullName, entryName, compressionSettings);
}
}
if (includeBaseDirectory && empty)
{
zipArchive.CreateEntry(string.Concat(directoryInfo.Name, Path.DirectorySeparatorChar));
}
}
}
private static bool IsDirectoryEmpty(DirectoryInfo directoryInfo)
{
bool empty = true;
using (IEnumerator<FileSystemInfo> enumerator = directoryInfo.EnumerateFileSystemInfos("*", SearchOption.AllDirectories).GetEnumerator())
{
if (enumerator.MoveNext())
{
empty = false;
}
}
return empty;
}
Note: We should ensure that all of the obsolete API that used to use the "leave open" flag don't keep the stream locked.
Add support for extraction of AES-encrypted archives.
There is a related feature request for the creation of such archives: ZipLibrary: Add support for creation of AES-encrypted archives.
I'm trying to implement the IXmlSerialization for class SerializableDictionary. When starts the serialization of class to xml, I'm catching the System.NotSupportedException: 'Can't flush final block twice'. The problem occurs when second serializer trying to call Serialize method on same XmlWriter.
using (var archive = new ZipArchive(stream, ZipArchiveMode.Create, false, null))
{
foreach (var item in array)
{
using (var entry = archive.CreateEntry($"{item.Name}.reg-ext"))
{
XmlInOut<RegulationItem>.SaveToStream(entry.Open(), item);
}
}
}
Could it be bug with the ArchiveEntry Stream?
I've placed a solution into a github repo. Also I tried to replace the stream of entry with the MemoryStream, and it helps. But I would to do it directly, if it's possible.
Add support for the creation of AES-encrypted archives.
There is a related feature request for the extraction of such archives: ZipLibrary: Add support for extraction of AES-encrypted archives.