问题描述
我正在尝试制作一个自定义安装程序,以将zip文件从固定位置提取到我访问过的用户首选的位置,并找到了许多资源,但所有这些资源均无效。 应用程序在解压缩软件包时冻结,并且直到进度条完成100%时才更新进度条(在我看来不是很有用)
这是我到目前为止所拥有的
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);
}
}
这是我发现的来源之一,Extract ZipFile Using C# With Progress Report无效
当我尝试使用Task.Factory.StartNew(() => Install());
时
我收到此错误
Exception thrown: 'system.invalidOperationException' in WindowsBase.dll
Exception thrown: 'system.invalidOperationException' in DotNetZip.dll
我正在使用Ionic.Zip.ZipFile
,当我在主线程之外使用它时不起作用
解决方法
除非您的代码在主线程中运行,否则不应直接更新ProgressBar
值。
以下extension methods调用必要的帮助程序来规避此限制:
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);
}
}
示例调用:
progressBar1.SetProgressValue(myValue);
,
我确实可以将其与Axel Kemper
一起使用,但是我不得不在开始解压缩之前就启动了一个线程,这是我想到的代码!
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);
}
}
虽然进度计算不是100%准确的,因为它会将总大小除以文件数,所以它可以100%准确地完成工作,因此1mb zip中可能有10kb的文件如果只有4个文件,则完成率为25%。
由于某种原因,e.TotalBytesToTransfer
仅占每个文件的数量,我不确定在提取之前有可靠的方法来计算提取大小,因此在这种情况下,只能获取每个文件的提取进度,并计算出有多少文件