BackgroundWorker,我怎样才能显示真正的进步? C# Winforms
Posted
技术标签:
【中文标题】BackgroundWorker,我怎样才能显示真正的进步? C# Winforms【英文标题】:BackgroundWorker, how Could i show real progress? C# Winforms 【发布时间】:2021-11-16 15:50:28 【问题描述】:我创建了使用 AES 加密/解密文件的应用程序,但是当我需要处理大文件时,我需要等待几秒钟,因此我的 UI 没有响应。我知道如何使用 BackgroundWorker 来显示 progressBar 或其他东西的进度,但我知道如何显示 REAL!例如解密文件的进度。
长话短说,我想在progressBar中显示文件解密的实际进度,这样用户就会知道他需要等待多少时间才能结束。
我使用此代码解密具有特定 AES KEY(密码)的特定文件, 我在另一个班级做这件事,但在 UI 工作的同一个线程中。
public void FileDecrypt(string inputFile, string password)
byte[] passwordBytes = System.Text.Encoding.UTF8.GetBytes(password);
byte[] salt = new byte[32];
FileStream fsCrypt = new FileStream(inputFile, FileMode.Open);
fsCrypt.Read(salt, 0, salt.Length);
RijndaelManaged AES = new RijndaelManaged();
AES.KeySize = 256;
AES.BlockSize = 128;
var key = new Rfc2898DeriveBytes(passwordBytes, salt, 50000);
AES.Key = key.GetBytes(AES.KeySize / 8);
AES.IV = key.GetBytes(AES.BlockSize / 8);
AES.Padding = PaddingMode.PKCS7;
AES.Mode = CipherMode.CFB;
CryptoStream cs = new CryptoStream(fsCrypt, AES.CreateDecryptor(), CryptoStreamMode.Read);
string getFilePath = Path.Combine(Path.GetDirectoryName(inputFile), Path.GetFileNameWithoutExtension(inputFile));
string fileDirectoryPath = Path.GetDirectoryName(getFilePath);
string fileName = Path.GetFileNameWithoutExtension(getFilePath);
fileName += "_decrypted";
string fileExtenstion = Path.GetExtension(getFilePath);
string finalFileNameFinal = fileDirectoryPath + "\\" + fileName + fileExtenstion;
FileStream fsOut = new FileStream(finalFileNameFinal, FileMode.Create);
int read;
byte[] buffer = new byte[1048576];
try
while ((read = cs.Read(buffer, 0, buffer.Length)) > 0)
Application.DoEvents();
fsOut.Write(buffer, 0, read);
catch (CryptographicException ex_CryptographicException)
Console.WriteLine("CryptographicException error: " + ex_CryptographicException.Message);
catch (Exception ex)
Console.WriteLine("Error: " + ex.Message);
try
cs.Close();
catch (Exception ex)
Console.WriteLine("Error by closing CryptoStream: " + ex.Message);
finally
fsOut.Close();
fsCrypt.Close();
【问题讨论】:
加密/解密功能是否会通知您正在进行的进度,或者它是一个阻塞调用,仅在完整文件完成后返回?如果它只是返回,它会消耗什么参数?如果它是一个流,您可以编写一个代理流来通知文件的多少字节已被读取或写入。 如果你阻塞了 UI 线程,那么你就没有正确使用BackgroundWorker
。 this回答有帮助吗?
Real Progress
是主观的,它完全取决于您使用什么作为衡量标准,例如,如果您使用目录/文件夹中正在解密的文件计数,您可以报告每个单独文件的进度,这将给出一些剩余任务的指示。但就时间估计而言,它们永远不会是 100% real
,因为每个文件都可以有不同的文件大小,并且需要不同的时间来执行针对它们的操作。您的 UI 没有响应的原因是您在 UI 线程中而不是在后台进程中工作。
@Oliver 我将编辑我的帖子以向您展示一些代码。
继我之前的评论之后,要更新您的 UI 线程,我建议创建一个新方法,其中包含以下 if 语句条件:private void UpdateProgressOnUi(int value) if (this.InvokeRequired) this.Invoke(new Action(() => this.UpdateProgress(value))); return; this.UpdateProgress(value);
【参考方案1】:
你写道:
但是我不知道如何显示真实!例如解密文件的进度。
答案取决于您对“真实!”的定义。进步。这是自您开始解密文件以来的秒数,还是您期望完成的秒数。或者可能是一个百分比:经过的秒数除以总预期持续时间?
还有“例如”。显然,对于需要一些时间的其他过程,您也希望这样做。
唉,所有这些可能性都没有共同的答案。即使是解密文件,也无法测量解密文件其余部分所需的时间。解密时,不能保证解密前 1000 个字节与解密后 1000 个字节所用的时间相同。
但是,如果您将以下内容定义为“REAL!进步”,您可以尝试一下:
定义:解密流时,REAL!解密过程的进度定义为已解密的字节数相对于要解密的流的总长度。
换句话说:如果您的输入流是 1000k,并且您已经解密了 250k,那么您的 REAL!进度是 250k/1000k = 0.25,即 25%。
唉,这不是一个描述解密文件其余部分需要多少时间的值,但至少它给了操作员一个指示。
顺便说一句:考虑更频繁地使用using
,这样您就不必手动Close
/ Dispose
流。
using (var inputStream = new FileStream(inputFile, FileMode.Open))
long totalInputLength = inputStream.Length;
using (var cryptoStream = new CryptoStream(inputStream,
AES.CreateDecryptor(), CryptoStreamMode.Read))
using (var outputStream = new FileStream(finalFileNameFinal, FileMode.Create))
long totalNumberOfBytesRead = 0;
var numberOfBytesRead = cryptoStream.Read(buffer, 0, buffer.Length);
while (numberOfBytesRead > 0)
totalNumberOfBytesRead += numberOfBytesRead;
double realProgress = (double) totalNumberOfBytesRead /
(double) totalInputLength;
ReportProgress(realProgress);
outputStream.Write(buffer, 0, numberOfBytesRead);
catch (...) ...
注意:using 语句将处理流被刷新/关闭/处置,即使在异常之后。
【讨论】:
以上是关于BackgroundWorker,我怎样才能显示真正的进步? C# Winforms的主要内容,如果未能解决你的问题,请参考以下文章
C# BackGroundWorker backgroundWorker1_DoWork中,按钮不能按的问题
BackgroundWorker UI不会更新Windows Phone 7中的进度