From 9c2b20cea528c67154641587e49a55bcc44a1f33 Mon Sep 17 00:00:00 2001 From: Robert Ribaya Date: Tue, 14 Apr 2026 17:39:56 +0800 Subject: [PATCH 1/4] Update ZipReportStorage.cs --- CS/ZipReportStorage.cs | 108 +++++++++++++++++++---------------------- 1 file changed, 50 insertions(+), 58 deletions(-) diff --git a/CS/ZipReportStorage.cs b/CS/ZipReportStorage.cs index 4e60d90..96ff326 100644 --- a/CS/ZipReportStorage.cs +++ b/CS/ZipReportStorage.cs @@ -8,30 +8,10 @@ using DevExpress.Data.Filtering; using DevExpress.XtraReports.UI; using DevExpress.XtraReports.Extensions; -using DevExpress.Utils.Zip; // ... namespace ReportStorageSample { class ZipReportStorage : ReportStorageExtension { - class ZipFilesHelper : IDisposable { - Stream stream; - InternalZipFileCollection zipFiles = new InternalZipFileCollection(); - public InternalZipFileCollection ZipFiles { - get { - return zipFiles; - } - } - public ZipFilesHelper(string path) { - if (File.Exists(path)) { - stream = File.OpenRead(path); - zipFiles = InternalZipArchive.Open(stream); - } - } - public virtual void Dispose() { - if (stream != null) - stream.Dispose(); - } - } const string fileName = "ReportStorage.zip"; public ZipReportStorage() { } @@ -50,29 +30,20 @@ public override bool IsValidUrl(string url) { } public override byte[] GetData(string url) { // Open ZIP archive. - using (ZipFilesHelper helper = new ZipFilesHelper(StoragePath)) { - // Read a file with a specified URL from the archive. - InternalZipFile zipFile = GetZipFile(helper.ZipFiles, url); - if (zipFile != null) - return GetBytes(zipFile); + if (!File.Exists(StoragePath)) return new byte[] { }; + using (var fileStream = File.OpenRead(StoragePath)) + using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Read)) { + var entry = archive.GetEntry(url); + if (entry == null) + return new byte[] { }; + using (var entryStream = entry.Open()) + using (var ms = new MemoryStream()) { + entryStream.CopyTo(ms); + return ms.ToArray(); + } } } - static byte[] GetBytes(InternalZipFile zipFile) { - return GetBytes(zipFile.FileDataStream, (int)zipFile.UncompressedSize); - } - static byte[] GetBytes(Stream stream, int length) { - byte[] result = new byte[length]; - DevExpress.Utils.Helpers.StreamHelper.FillBuffer(stream, result, 0, length); - return result; - } - static InternalZipFile GetZipFile(InternalZipFileCollection zipFiles, string url) { - foreach(InternalZipFile item in zipFiles) { - if (StringsEgual(item.FileName, url)) - return item; - } - return null; - } static bool StringsEgual(string a, string b) { return string.Equals(a, b, StringComparison.OrdinalIgnoreCase); } @@ -83,23 +54,41 @@ public override void SetData(XtraReport report, string url) { void SaveArchive(string url, byte[] buffer) { string tempPath = Path.ChangeExtension(StoragePath, "tmp"); // Create a new ZIP archive. - using(InternalZipArchive arch = new InternalZipArchive(tempPath)) { + using (var destStream = new FileStream(tempPath, FileMode.Create)) + using (var destArchive = new ZipArchive(destStream, ZipArchiveMode.Create)) { // Open a ZIP archive where report files are stored. - using (ZipFilesHelper helper = new ZipFilesHelper(StoragePath)) { - bool added = false; - // Copy all report files to a new archive. - // Update a file with a specified URL. - // If the file does not exist, create it. - foreach(InternalZipFile item in helper.ZipFiles) { - if (StringsEgual(item.FileName, url)) { - arch.Add(item.FileName, DateTime.Now, buffer); - added = true; + if (File.Exists(StoragePath)) { + using (var srcStream = File.OpenRead(StoragePath)) + using (var srcArchive = new ZipArchive(srcStream, ZipArchiveMode.Read)) { + bool added = false; + // Copy all report files to a new archive. + // Update a file with a specified URL. + // If the file does not exist, create it. + foreach (var item in srcArchive.Entries) { + if (StringsEgual(item.FullName, url)) { + var newEntry = destArchive.CreateEntry(item.FullName); + using (var newStream = newEntry.Open()) + newStream.Write(buffer, 0, buffer.Length); + added = true; + } + else { + var newEntry = destArchive.CreateEntry(item.FullName); + using (var entryStream = item.Open()) + using (var newStream = newEntry.Open()) + entryStream.CopyTo(newStream); + } + } + if (!added) { + var newEntry = destArchive.CreateEntry(url); + using (var newStream = newEntry.Open()) + newStream.Write(buffer, 0, buffer.Length); } - else - arch.Add(item.FileName, DateTime.Now, GetBytes(item)); } - if (!added) - arch.Add(url, DateTime.Now, buffer); + } + else { + var newEntry = destArchive.CreateEntry(url); + using (var newStream = newEntry.Open()) + newStream.Write(buffer, 0, buffer.Length); } } // Replace the old ZIP archive with the new one. @@ -170,12 +159,15 @@ string[] GetUrls() { } List GetUrlsCore(Predicate method) { List list = new List(); - using (ZipFilesHelper helper = new ZipFilesHelper(StoragePath)) { - foreach(InternalZipFile item in helper.ZipFiles) - if (method == null || method(item.FileName)) - list.Add(item.FileName); + if (!File.Exists(StoragePath)) return list; + using (var fileStream = File.OpenRead(StoragePath)) + using (var archive = new ZipArchive(fileStream, ZipArchiveMode.Read)) { + foreach (var entry in archive.Entries) + if (method == null || method(entry.FullName)) + list.Add(entry.FullName); } + return list; } } } From 54bd97ef56f38722c30d3e8a54adc375328d11dd Mon Sep 17 00:00:00 2001 From: Robert Ribaya Date: Tue, 14 Apr 2026 17:40:20 +0800 Subject: [PATCH 2/4] Update ZipReportStorage.vb --- VB/ZipReportStorage.vb | 360 +++++++++++++++++++++-------------------- 1 file changed, 185 insertions(+), 175 deletions(-) diff --git a/VB/ZipReportStorage.vb b/VB/ZipReportStorage.vb index 24cbcdc..3c1525d 100644 --- a/VB/ZipReportStorage.vb +++ b/VB/ZipReportStorage.vb @@ -9,182 +9,192 @@ Imports DevExpress.Xpo Imports DevExpress.Data.Filtering Imports DevExpress.XtraReports.UI Imports DevExpress.XtraReports.Extensions -Imports DevExpress.Utils.Zip ' ... Namespace ReportStorageSample - Friend Class ZipReportStorage - Inherits ReportStorageExtension - Private Class ZipFilesHelper - Implements IDisposable - Private stream As Stream - Private zipFiles_Renamed As New InternalZipFileCollection() - Public ReadOnly Property ZipFiles() As InternalZipFileCollection - Get - Return zipFiles_Renamed - End Get - End Property - Public Sub New(ByVal path As String) - If File.Exists(path) Then - stream = File.OpenRead(path) - zipFiles_Renamed = InternalZipArchive.Open(stream) - End If - End Sub - Public Overridable Sub Dispose() Implements IDisposable.Dispose - If stream IsNot Nothing Then - stream.Dispose() - End If - End Sub - End Class - Private Const fileName As String = "ReportStorage.zip" - Public Sub New() - End Sub - Private ReadOnly Property StoragePath() As String - Get - Dim dirName As String = Path.GetDirectoryName(Application.ExecutablePath) - Return Path.Combine(dirName, fileName) - End Get - End Property - Public Overrides Function CanSetData(ByVal url As String) As Boolean - ' Always return true to confirm that the SetData method is available. - Return True - End Function - Public Overrides Function IsValidUrl(ByVal url As String) As Boolean - Return Not String.IsNullOrEmpty(url) - End Function - Public Overrides Function GetData(ByVal url As String) As Byte() - ' Open ZIP archive. - Using helper As New ZipFilesHelper(StoragePath) - ' Read a file with a specified URL from the archive. - Dim zipFile As InternalZipFile = GetZipFile(helper.ZipFiles, url) - If zipFile IsNot Nothing Then - Return GetBytes(zipFile) - End If - Return New Byte() { } - End Using - End Function - Private Shared Function GetBytes(ByVal zipFile As InternalZipFile) As Byte() - Return GetBytes(zipFile.FileDataStream, CInt(Fix(zipFile.UncompressedSize))) - End Function - Private Shared Function GetBytes(ByVal stream As Stream, ByVal length As Integer) As Byte() - Dim result(length - 1) As Byte - stream.Read(result, 0, result.Length) - Return result - End Function - Private Shared Function GetZipFile(ByVal zipFiles As InternalZipFileCollection, ByVal url As String) As InternalZipFile - For Each item As InternalZipFile In zipFiles - If StringsEgual(item.FileName, url) Then - Return item - End If - Next item - Return Nothing - End Function - Private Shared Function StringsEgual(ByVal a As String, ByVal b As String) As Boolean - Return String.Equals(a, b, StringComparison.OrdinalIgnoreCase) - End Function - Public Overrides Sub SetData(ByVal report As XtraReport, ByVal url As String) - report.Extensions("StorageID") = url - SaveArchive(url, GetBuffer(report)) - End Sub - Private Sub SaveArchive(ByVal url As String, ByVal buffer() As Byte) - Dim tempPath As String = Path.ChangeExtension(StoragePath, "tmp") - ' Create a new ZIP archive. - Using arch As New InternalZipArchive(tempPath) - ' Open a ZIP archive where report files are stored. - Using helper As New ZipFilesHelper(StoragePath) - Dim added As Boolean = False - ' Copy all report files to a new archive. - ' Update a file with a specified URL. - ' If the file does not exist, create it. - For Each item As InternalZipFile In helper.ZipFiles - If StringsEgual(item.FileName, url) Then - arch.Add(item.FileName, DateTime.Now, buffer) - added = True - Else - arch.Add(item.FileName, DateTime.Now, GetBytes(item)) - End If - Next item - If (Not added) Then - arch.Add(url, DateTime.Now, buffer) - End If - End Using - End Using - ' Replace the old ZIP archive with the new one. - If File.Exists(StoragePath) Then - File.Delete(StoragePath) - End If - File.Move(tempPath, StoragePath) - End Sub - Private Function GetBuffer(ByVal report As XtraReport) As Byte() - Using stream As New MemoryStream() - report.SaveLayout(stream) - Return stream.ToArray() - End Using - End Function - Public Overrides Function GetNewUrl() As String - ' Show the report selection dialog and return a URL for a selected report. - Dim form As StorageEditorForm = CreateForm() - form.textBox1.Enabled = False - If form.ShowDialog() = System.Windows.Forms.DialogResult.OK Then - Return form.textBox1.Text - End If - Return String.Empty - End Function - Private Function CreateForm() As StorageEditorForm - Dim form As New StorageEditorForm() - For Each item As String In GetUrls() - form.listBox1.Items.Add(item) - Next item - Return form - End Function - Public Overrides Function SetNewData(ByVal report As XtraReport, ByVal defaultUrl As String) As String - Dim form As StorageEditorForm = CreateForm() - form.textBox1.Text = defaultUrl - form.listBox1.Enabled = False - ' Show the save dialog to get a URL for a new report. - If form.ShowDialog() = DialogResult.OK Then - Dim url As String = form.textBox1.Text - If (Not String.IsNullOrEmpty(url)) AndAlso (Not form.listBox1.Items.Contains(url)) Then - TypeDescriptor.GetProperties(GetType(XtraReport))("DisplayName").SetValue(report, url) - SetData(report, url) - Return url - Else - MessageBox.Show("Incorrect report name", "Error", MessageBoxButtons.OKCancel, MessageBoxIcon.Error) - End If - End If - Return String.Empty - End Function - Public Overrides Function GetStandardUrlsSupported(ByVal context As ITypeDescriptorContext) As Boolean - ' Always return true to confirm that the GetStandardUrls method is available. - Return True - End Function - Public Overrides Function GetStandardUrls(ByVal context As ITypeDescriptorContext) As String() - If context IsNot Nothing AndAlso TypeOf context.Instance Is XRSubreport Then - Dim xrSubreport As XRSubreport = TryCast(context.Instance, XRSubreport) - If xrSubreport.RootReport IsNot Nothing AndAlso xrSubreport.RootReport.Extensions.TryGetValue("StorageID", storageID) Then - Dim result As List(Of String) = GetUrlsCore(AddressOf CanPassId) - Return result.ToArray() - End If - End If - Return GetUrls() - End Function - Private storageID As String - Private Function CanPassId(ByVal id As String) As Boolean - Return id <> storageID - End Function - Private Function GetUrls() As String() - Return GetUrlsCore(Nothing).ToArray() - End Function - Private Function GetUrlsCore(ByVal method As Predicate(Of String)) As List(Of String) - Dim list As New List(Of String)() - Using helper As New ZipFilesHelper(StoragePath) - For Each item As InternalZipFile In helper.ZipFiles - If method Is Nothing OrElse method(item.FileName) Then - list.Add(item.FileName) - End If - Next item - Return list - End Using - End Function - End Class + Public Class ZipReportStorage + Inherits ReportStorageExtension + + Private Const fileName As String = "ReportStorage.zip" + Public Sub New() + End Sub + Private ReadOnly Property StoragePath As String + Get + Dim dirName As String = Path.GetDirectoryName(Application.ExecutablePath) + Return Path.Combine(dirName, fileName) + End Get + End Property + Public Overrides Function CanSetData(url As String) As Boolean + ' Always return true to confirm that the SetData method is available. + Return True + End Function + Public Overrides Function IsValidUrl(url As String) As Boolean + Return Not String.IsNullOrEmpty(url) + End Function + Public Overrides Function GetData(url As String) As Byte() + ' Open ZIP archive. + If Not File.Exists(StoragePath) Then + Return New Byte() {} + End If + + Using fileStream = File.OpenRead(StoragePath) + Using archive = New ZipArchive(fileStream, ZipArchiveMode.Read) + Dim entry = archive.GetEntry(url) + If entry Is Nothing Then + Return New Byte() {} + End If + + Using entryStream = entry.Open() + Using ms As New MemoryStream() + entryStream.CopyTo(ms) + Return ms.ToArray() + End Using + End Using + End Using + End Using + End Function + Private Shared Function StringsEgual(a As String, b As String) As Boolean + Return String.Equals(a, b, StringComparison.OrdinalIgnoreCase) + End Function + Public Overrides Sub SetData(report As XtraReport, url As String) + report.Extensions("StorageID") = url + SaveArchive(url, GetBuffer(report)) + End Sub + Private Sub SaveArchive(url As String, buffer As Byte()) + Dim tempPath As String = Path.ChangeExtension(StoragePath, "tmp") + ' Create a new ZIP archive. + Using destStream As New FileStream(tempPath, FileMode.Create) + Using destArchive As New ZipArchive(destStream, ZipArchiveMode.Create) + + ' Open existing archive if it exists + If File.Exists(StoragePath) Then + Using srcStream = File.OpenRead(StoragePath) + Using srcArchive As New ZipArchive(srcStream, ZipArchiveMode.Read) + Dim added As Boolean = False + + For Each item In srcArchive.Entries + Dim newEntry = destArchive.CreateEntry(item.FullName) + + If StringsEgual(item.FullName, url) Then + Using newStream = newEntry.Open() + newStream.Write(buffer, 0, buffer.Length) + End Using + added = True + Else + Using entryStream = item.Open() + Using newStream = newEntry.Open() + entryStream.CopyTo(newStream) + End Using + End Using + End If + Next + + If Not added Then + Dim newEntry = destArchive.CreateEntry(url) + Using newStream = newEntry.Open() + newStream.Write(buffer, 0, buffer.Length) + End Using + End If + End Using + End Using + Else + Dim newEntry = destArchive.CreateEntry(url) + Using newStream = newEntry.Open() + newStream.Write(buffer, 0, buffer.Length) + End Using + End If + End Using + End Using + ' Replace the old ZIP archive with the new one. + If File.Exists(StoragePath) Then + File.Delete(StoragePath) + End If + File.Move(tempPath, StoragePath) + End Sub + Private Function GetBuffer(report As XtraReport) As Byte() + Using stream As New MemoryStream() + report.SaveLayout(stream) + Return stream.ToArray() + End Using + End Function + Public Overrides Function GetNewUrl() As String + Dim form As StorageEditorForm = CreateForm() + form.textBox1.Enabled = False + + If form.ShowDialog() = DialogResult.OK Then + Return form.textBox1.Text + End If + Return String.Empty + End Function + Private Function CreateForm() As StorageEditorForm + Dim form As New StorageEditorForm() + For Each item As String In GetUrls() + form.listBox1.Items.Add(item) + Next + + Return form + End Function + Public Overrides Function SetNewData(report As XtraReport, defaultUrl As String) As String + Dim form As StorageEditorForm = CreateForm() + form.textBox1.Text = defaultUrl + form.listBox1.Enabled = False + + If form.ShowDialog() = DialogResult.OK Then + Dim url As String = form.textBox1.Text + + If Not String.IsNullOrEmpty(url) AndAlso Not form.listBox1.Items.Contains(url) Then + TypeDescriptor.GetProperties(GetType(XtraReport))("DisplayName").SetValue(report, url) + SetData(report, url) + Return url + Else + MessageBox.Show("Incorrect report name", "Error", + MessageBoxButtons.OKCancel, MessageBoxIcon.[Error]) + End If + End If + Return String.Empty + End Function + Public Overrides Function GetStandardUrlsSupported(context As ITypeDescriptorContext) As Boolean + ' Always return true to confirm that the GetStandardUrls method is available. + Return True + End Function + Public Overrides Function GetStandardUrls(context As ITypeDescriptorContext) As String() + If context IsNot Nothing AndAlso TypeOf context.Instance Is XRSubreport Then + Dim xrSubreport As XRSubreport = CType(context.Instance, XRSubreport) + + If xrSubreport.RootReport IsNot Nothing AndAlso + xrSubreport.RootReport.Extensions.TryGetValue("StorageID", storageID) Then + + Dim result As List(Of String) = GetUrlsCore(AddressOf CanPassId) + Return result.ToArray() + End If + End If + Return GetUrls() + End Function + Private storageID As String + Private Function CanPassId(id As String) As Boolean + Return id <> storageID + End Function + Private Function GetUrls() As String() + Return GetUrlsCore(Nothing).ToArray() + End Function + Private Function GetUrlsCore(method As Predicate(Of String)) As List(Of String) + Dim list As New List(Of String)() + + If Not File.Exists(StoragePath) Then + Return list + End If + + Using fileStream = File.OpenRead(StoragePath) + Using archive As New ZipArchive(fileStream, ZipArchiveMode.Read) + For Each entry In archive.Entries + If method Is Nothing OrElse method(entry.FullName) Then + list.Add(entry.FullName) + End If + Next + End Using + End Using + + Return list + End Function + End Class End Namespace From 32880bcb3b7cb57d47484f33668dc49bca56c129 Mon Sep 17 00:00:00 2001 From: Robert Ribaya Date: Tue, 14 Apr 2026 17:41:30 +0800 Subject: [PATCH 3/4] Use SaveLayoutToXml --- CS/ZipReportStorage.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CS/ZipReportStorage.cs b/CS/ZipReportStorage.cs index 96ff326..fd6957d 100644 --- a/CS/ZipReportStorage.cs +++ b/CS/ZipReportStorage.cs @@ -98,7 +98,7 @@ void SaveArchive(string url, byte[] buffer) { } byte[] GetBuffer(XtraReport report) { using (MemoryStream stream = new MemoryStream()) { - report.SaveLayout(stream); + report.SaveLayoutToXml(stream); return stream.ToArray(); } } From 5dad81f07722bd11c97572f2b381a362623cdc28 Mon Sep 17 00:00:00 2001 From: Robert Ribaya Date: Tue, 14 Apr 2026 17:41:56 +0800 Subject: [PATCH 4/4] Use SaveLayoutToXml --- VB/ZipReportStorage.vb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VB/ZipReportStorage.vb b/VB/ZipReportStorage.vb index 3c1525d..dc46f90 100644 --- a/VB/ZipReportStorage.vb +++ b/VB/ZipReportStorage.vb @@ -113,7 +113,7 @@ Namespace ReportStorageSample End Sub Private Function GetBuffer(report As XtraReport) As Byte() Using stream As New MemoryStream() - report.SaveLayout(stream) + report.SaveLayoutToXml(stream) Return stream.ToArray() End Using End Function