您好我正在做一个代码MessageDlgPos同时运行五个线程,代码是这样的:
type
TMyThread = class(TThread)
protected
procedure Execute; override;
public
text: string;
property ReturnValue;
end;
procedure TMyThread.Execute;
begin
if Terminated then
Exit;
MessageDlgPos(text, mtInformation, [mbOk], 0, 100, 200);
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
LThread: TMyThread;
i: Integer;
begin
For i := 1 to 5 do
begin
LThread := TMyThread(Sender);
try
LThread.text := 'hi';
LThread.FreeOnTerminate := True;
except
LThread.Free;
raise;
end;
LThread.Resume;
end;
end;
问题是Delphi XE总是返回以下错误并且不执行任何操作:
第一次机会异常,价格为7524B727美元。在地址00D0B9AB处带有消息'访问冲突的异常类EAccessViolation。写下地址8CC38309'。处理tester.exe(6300)
如何解决此问题?
答案 0 :(得分:4)
正如David Heffernan指出的那样,MessageDlgPos()
无法安全地在主UI线程之外调用,并且您无法正确管理线程。您的代码需要看起来更像这样:
type
TMyThread = class(TThread)
protected
procedure Execute; override;
public
text: string;
property ReturnValue;
end;
procedure TMyThread.Execute;
begin
// no need to check Terminated here, TThread already
// does that before calling Execute()...
TThread.Synchronize(nil,
procedure
begin
MessageDlgPos(text, mtInformation, [mbOk], 0, 100, 200);
end
);
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
LThread: TMyThread;
i: Integer;
begin
For i := 1 to 5 do
begin
LThread := TMyThread.Create(True);
LThread.text := 'hi';
LThread.FreeOnTerminate := True;
LThread.Start;
end;
end;
我建议稍微不同的变体:
type
TMyThread = class(TThread)
private
fText: string;
protected
procedure Execute; override;
public
constructor Create(const aText: string); reintroduce;
property ReturnValue;
end;
constructor TMyThread.Create(const aText: string);
begin
inherited Create(False);
FreeOnTerminate := True;
fText := aText;
end;
procedure TMyThread.Execute;
begin
// no need to check Terminated here, TThread already
// does that before calling Execute()...
TThread.Synchronize(nil,
procedure
begin
MessageDlgPos(fText, mtInformation, [mbOk], 0, 100, 200);
end
);
end;
procedure TForm1.btnTestClick(Sender: TObject);
var
i: Integer;
begin
For i := 1 to 5 do
begin
TMyThread.Create('hi');
end;
end;
但无论如何,如果您不想使用TThread.Synchronize()
委托给主线程(因此一次只显示1个对话框),那么您根本不能使用MessageDlgPos()
,因为在主UI线程中调用是安全的。您可以使用Windows.MessageBox()
代替,可以在没有委派的情况下在工作线程中安全地调用(但之后您将失去指定其屏幕位置的能力,除非您使用线程本地直接访问其HWND
通过SetWindowsHookEx()
挂钩拦截对话框的创建并发现其HWND
):
procedure TMyThread.Execute;
begin
Windows.MessageBox(0, PChar(fText), PChar(Application.Title), MB_OK or MB_ICONINFORMATION);
);
end;
答案 1 :(得分:3)
有很多问题。最大的一个是:
LThread := TMyThread(Sender);
Sender
是一个按钮。转换为线程是完全错误的,也是异常的原因。将一个按钮转换为一个线程并不能实现。它仍然是一个按钮。
您可能想要创建一个线程。
LThread := TMyThread.Create(True);
您无法在主线程外显示VCL UI。对MessageDlgPos
的调用违反了该规则。如果您确实需要在此时显示UI,则需要使用TThread.Synchronize
让代码在主线程中执行。
你的异常处理程序对我没用。我想你应该把它删除。
Resume
已弃用。请改用Start
。