I am trying to make a custom installer to extract a zip file from a fixed location into the users preferred location I have visited and found many sources, all of which do not work. The issue that the application freezes while unzipping the package and does not update the progress bar until its 100% completed ( not very useful in my opinion )
This is what I have so far
void Install()
{
using (Ionic.Zip.ZipFile zip = Ionic.Zip.ZipFile.Read(Constants.UpdateZipPath))
{
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(Zip_ExtractProgress);
zip.ExtractAll(installDir, ExtractExistingFileAction.OverwriteSilently);
}
}
void Zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
if (e.TotalBytesToTransfer > 0)
{
ProgressBar.Value = Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer);
}
}
This is one of the sources I found and does not work Extract ZipFile Using C# With Progress Report
When I try an use Task.Factory.StartNew(() => Install());
I get this Error
Exception thrown: 'System.InvalidOperationException' in WindowsBase.dll
Exception thrown: 'System.InvalidOperationException' in DotNetZip.dll
I am using Ionic.Zip.ZipFile
and when I use it outside of the main thread it doesnt work
I did get it to work with what Axel Kemper
had, but I had to start a thread just before I started unzipping, here is the code I came up with!
public class MyClass
{
public bool InstallIsCompleted = false;
public string CurrentFileBeingExtracted = "";
public int TotalNumberOfFiles = 1;
public int NumberOfFilesTransferred = 1;
public void MyFunction()
{
new Thread(Update).Start();
Task.Factory.StartNew(() => Install());
}
void Update()
{
while (!InstallIsCompleted)
{
Dispatcher.Invoke(() =>
{
// updates UI text elements including the display for which file
// is being extracted and the total progress of the total extraction
DisplayCurrentFile.Text = CurrentFileBeingExtracted;
float Percentage = (NumberOfFilesTransferred*100) / TotalNumberOfFiles;
InstallationProgressBar.Value = Percentage + (SecondProgressBar.Value * (1 / TotalNumberOfFiles));
});
}
//Install Completed
/* other code here */
}
void Install(string ZipPath, string TargetPath)
{
using (ZipFile zip = ZipFile.Read(ZipPath))
{
// initial setup before extraction
TotalNumberOfFiles= zip.Entries.Count;
zip.ExtractProgress += new EventHandler<ExtractProgressEventArgs>(Zip_ExtractProgress);
// actual extraction process
zip.ExtractAll(TargetPath, ExtractExistingFileAction.OverwriteSilently);
// since the boolean below is in the same "thread" the extraction must
// complete for the boolean to be set to true
InstallIsCompleted = true;
}
}
void Zip_ExtractProgress(object sender, ExtractProgressEventArgs e)
{
// must be above 0 to prevent *divide by 0 error
if (e.TotalBytesToTransfer > 0)
{
// If file has completed transfer, update the number of files transferred
if (Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer) >= 100)
{
NumberOfFilesTransferred++;
}
// updates the current file being exracted
CurrentFileBeingExtracted = e.CurrentEntry.FileName.Replace("zip::", "");
// updates the progress
ProgressBarExtensions.SetProgressValue(InstallationProgressBar, Convert.ToInt32(100 * e.BytesTransferred / e.TotalBytesToTransfer));
}
}
}
public static class ProgressBarExtensions
{
// this extension method can be used for any Control which supports
// InvokeRequired() and BeginInvoke()
public static void EnsureInvokeAsync(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.BeginInvoke(action);
}
else
{
action();
}
}
public static void SetProgressValue(this ProgressBar progressBar, int progressValue)
{
// https://stackoverflow.com/a/21969844/1911064
// the lambda will capture the argument in a closure
// the compiler does all the hard work for you
progressBar.EnsureInvokeAsync(() => progressBar.Value = progressValue);
}
}
This worked 100% like I wanted it to for me, although the progress calculation is not 100% accurate because it divides the total size by the number of files evenly, so a 10kb file out of a 1mb zip might be account for 25% completion if there are only 4 files.
For some reason e.TotalBytesToTransfer
only accounts for each file, and I'm not sure there is a solid way to calculate the extraction size before extraction, so in this instance its only possible to get the extraction progress per file, and calculate how many files there are