Я новичок в C# и Windows Forms.
Я использую библиотеку syncfusion C# для шифрования/дешифрования файлов Word.
Я использую задачи Async/Await для массового расшифрования файлов.
У меня нет проблем зашифровать/расшифровать один файл или зашифровать несколько файлов.
Я столкнулся с проблемой при попытке расшифровать несколько файлов слов или массово.
При расшифровке одного файла с неверным паролем получаю исключение и все работает нормально.
Но во время массового дешифрования файлов с использованием await Task.WhenAll(tasks) внутри блока try-catch я не получаю никаких исключений, в то время как из первой задачи (первого файла для расшифровки) я должен получить исключение, потому что пароль неверен.
В течение всей недели я везде пробовал большинство предложенных решений, но безуспешно и не знаю, что мне делать.
Большое спасибо!
Код должен генерировать исключение, если первая задача не выполнена, но это не так.
GetPathOrExtention gte = new GetPathOrExtention();
private void DecryptDOCXFiles(string fileName)
{
FileStream inputStream = new FileStream(fileName, FileMode.Open, FileAccess.Read);
WordDocument document = new WordDocument(inputStream, FormatType.Docx, TbDecPwd1.Text);
FileStream outputStream = new FileStream(gte.GetDirPath(fileName) + "\\" + gte.GetfileName(fileName).Replace("Encrypted-", ""), FileMode.Create, FileAccess.Write);
document.Save(outputStream, FormatType.Docx);
inputStream.Close();
outputStream.Close();
}
private async Task DecryptWordFiles()
{
OpenFileDialog ofd = new OpenFileDialog();
ofd.Filter = "docx files (*.docx)|*.docx|All files (*.*)|*.*";
ofd.Multiselect = true;
var tasks = new List<Task>();
if (TbDecPwd1.Text != "" && ofd.ShowDialog() == DialogResult.OK)
{
foreach (string file in ofd.FileNames)
{
if (gte.GetFileExtension(file) != ".docx")
{
LblProceeding.ResetText();
MessageBox.Show("Files version is not supported!" + '\n' + '\n' + "Only Word version 2019 and above are supported!", "P-GEN", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
if (gte.GetFileExtension(file) == ".docx")
{
LblProceeding.Text = "Proceeding... Do not exit the software!";
var task = Task.Run(() => DecryptDOCXFiles(file)).ContinueWith(p => progressBar1.PerformStep());
tasks.Add(task);
}
}
}
try
{
await Task.WhenAll(tasks);
LblProceeding.Text = "Done!";
MessageBox.Show("Files Successfully Decrypted!", "P-GEN", MessageBoxButtons.OK, MessageBoxIcon.Information);
LblProceeding.ResetText();
progressBar1.Visible = false;
}
catch (AggregateException)
{
LblProceeding.ResetText();
MessageBox.Show("Ensure the password is correct!" + '\n' + '\n' + "Ensure the files you want to decrypt are not opened in another software!", "P-GEN", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
catch (Exception)
{
LblProceeding.ResetText();
MessageBox.Show("Ensure the password is correct!" + '\n' + '\n' + "Ensure the files you want to decrypt are not opened in another software!", "P-GEN", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
}
private void BtnWordFilesDec_Click(object sender, EventArgs e)
{
_ = DecryptWordFiles();
}
(Вы уверены, что проблема не в том, что вы используете Task.ContinueWith и продолжение не является ошибочным, даже если исходная задача ошибочна?)
Да, я уверен, что Task.ContinueWith не является проблемой, потому что он работает нормально, когда я ввожу правильный пароль.
Это не значит, что это не проблема, которая мешает вам увидеть ошибки в исходных задачах. Пробовали без звонка ContinueWith?
Теперь я предоставил консольное приложение, которое довольно убедительно доказывает, что проблема в Task.ContinueWith.





Проблема в том, что задачи, которые вы ожидаете, являются результатом звонка Task.ContinueWith, и все они выполняются без ошибок. Вас не волнует статус продолжения — вас волнуют статусы исходных задач.
Вот консольное приложение, демонстрирующее то же поведение:
var tasks = Enumerable.Range(0, 10)
.Select(ComputeDivision)
.Select(t => t.ContinueWith(t => Console.WriteLine($"Task status: {t.Status}")))
.ToList();
await Task.WhenAll(tasks);
Console.WriteLine("All tasks completed");
async Task ComputeDivision(int index)
{
await Task.Delay(TimeSpan.FromSeconds(index + 1));
Console.WriteLine($"Index: {index}; Division: {100 / index}");
}
Как написано, это завершает и печатает «Все задачи выполнены». Если вместо этого вы закомментируете вызов ContinueWith, вы увидите ошибку. (т. е. он будет печатать завернутый DivideByZeroException, а не «Все задачи выполнены».)
Аналогично, если вы удалите вызов ContinueWith, вы будете ждать завершения исходных задач, и результирующая задача в конечном итоге окажется ошибочной, если какая-либо из исходных задач окажется ошибочной.
Вы можете изменить продолжение на неудачное, если предыдущая задача не удалась, вызвав для него Wait - поэтому в вашем коде это будет:
.ContinueWith(p => { progressBar1.PerformStep(); p.Wait(); })
По общему признанию, это несколько некрасиво... но и использование Task.Run здесь, по моему мнению, тоже. (Я бы для начала все сделал с асинхронным вводом-выводом, сделав DecryptDOCXFiles асинхронным, если возможно.)
Возможно, существует лучший способ заставить продолжение распространять исходный статус задачи, но я не знаю его наизусть.
Большое спасибо, Джон!! Пусть Бог предложит вам всю необходимую помощь!!
Пожалуйста, предоставьте минимальный воспроизводимый пример, который вообще не требует выполнения каких-либо операций ввода-вывода или использования пользовательского интерфейса. Просто консольное приложение с одной задачей с ошибкой и другими задачами без ошибок, показывающее
Tasks.WhenAllвозврат задачи, которая в конечном итоге не привела к ошибке.