多线程和MessageDlgPos

时间:2016-01-30 23:05:09

标签: multithreading delphi

您好我正在做一个代码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)

如何解决此问题?

2 个答案:

答案 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

相关问题