我希望构建一个例程,负责通过过程(Tproc)使用任务执行任何操作,同时显示加载屏幕,而不会冻结系统的主窗体。一个额外的细节是,它还能够捕获操作可能产生的可能异常。
我构建了下面的代码,它工作得很好,但有时会出错,加载屏幕最终无法关闭,始终存在于屏幕上。
任何建议都欢迎。谢谢。
type
TLoadingClass= class
strict private
class var FForm: TForm;
public
class procedure ActionAndWait(Action: Tproc);
end;
class procedure TLoadingClass.ActionAndWait(Action: Tproc);
var
aTask: ITask;
vException: Pointer;
begin
vException := nil;
FForm := TLoadingForm.Create(nil);
try
aTask := TTask.Run(procedure
begin
try
try
Action; {Run Action}
except on E: Exception do
vException := @E {Capture Exception}
end
finally
while not FForm.Showing do {Wait for the form to be created, if the action is very quick.}
Sleep(1);
TLoadingForm(FForm).Hide;
TLoadingForm(FForm).Close;
end;
end);
TLoadingForm(FForm).ShowModal; {Show the loading form}
finally
TTask.WaitForAll(aTask);
FreeAndNil(FForm);
if Assigned(vException) then
raise Exception(@vException);
end;
end;
字符串
调用示例
TLoadingClass.ActionAndWait(
procedure
begin
try
Sleep(5000);
raise Exception.Create('Test');
except on E: Exception do
ShowMessage(E.Message);
end;
end);
型
1条答案
按热度按时间y0u0uwnf1#
你的代码有几个问题。
首先,你从后台线程访问UI(表单),这是你永远不应该做的。这样的代码总是需要与主线程同步。
接下来,你没有正确处理任务异常。异常对象是由编译器自动处理的,你不能只是抓取一个指向异常对象的指针并在以后使用它。整个
try...except
在任务方法中是无用的。如果任务中有未处理的异常,Wait
或WaitForAll
将引发EAggregatedException
,您可以在那里捕获该异常并处理其内部异常。如果有多个任务引发异常,则会有多个内部异常。接下来,您已经在传递给
ActionAndWait
的匿名方法中捕获了异常,因此捕获的异常不会作为EAgreggatedException
传播。ActionAndWait
中的ShowMessage
将在后台线程的上下文中运行,如果您想从那里使用UI,它还需要与主线程同步。您在
TLoadingClass
中将FForm
声明为字段。最好删除该字段并使用本地变量,就像您在任务中使用的那样。当您调用Hide
和Close
时,也没有必要将FForm
类型转换为TLoadingForm
。此外,调用Close
就足够了,因为它也会隐藏表单。经过清理和更正的代码看起来像这样:
字符串
调用示例:
型