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的主要内容,如果未能解决你的问题,请参考以下文章

我怎样才能简化这个逻辑

我怎样才能让 QtCreator 打破异常?

C# BackGroundWorker backgroundWorker1_DoWork中,按钮不能按的问题

BackgroundWorker UI不会更新Windows Phone 7中的进度

如何在点击触发的backgroundWorker中添加一段代码?

成功运行BackgroundWorker() 后程序的其余部分不执行