Ошибка доступа при вызове функций диалоговых окон в многопоточной среде Delphi XE
Вопрос пользователя связан с возникновением ошибки доступа при попытке вызова функции MessageDlgPos в многопоточном приложении на Delphi XE. Проблема заключается в неправильной работе с потоками и попытке обращения к элементам пользовательского интерфейса из потока, отличного от основного.
Описание проблемы
Пользователь столкнулся с проблемой, что при запуске пяти потоков, каждый из которых должен был показать диалоговое окно, возникает ошибка доступа. Код, который используется для создания потоков и вызова диалогового окна, следующий:
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;
Ошибка возникает из-за неправильного обращения к объекту Sender, который является кнопкой, а не потоком. Также в коде присутствует вызов MessageDlgPos из потока, что не допускается, так как диалоговые окна VCL должны быть вызваны только из основного потока.
Решение проблемы
Для устранения ошибки необходимо исправить следующие моменты:
Создание потока вместо попытки приведения Sender к типу TMyThread.
Использование TThread.Synchronize для выполнения кода в основном потоке, если необходимо показать диалоговое окно.
Удаление лишнего обработчика исключений, который не имеет смысла в данном контексте.
Вот исправленный код:
type
TMyThread = class(TThread)
protected
procedure Execute; override;
public
text: string;
end;
procedure TMyThread.Execute;
begin
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;
end;
constructor TMyThread.Create(const aText: string);
begin
inherited Create(False);
FreeOnTerminate := True;
fText := aText;
end;
procedure TMyThread.Execute;
begin
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;
Если необходимо использовать диалоговые окна, которые могут быть вызваны из любого потока, можно использовать Windows.MessageBox, который не требует делегирования в основной поток.
Заключение
При работе с многопоточностью в Delphi важно помнить, что операции с пользовательским интерфейсом должны выполняться в основном потоке. Использование TThread.Synchronize позволяет безопасно вызвать код в основном потоке, что необходимо для отображения диалоговых окон.
Пользователь столкнулся с ошибкой доступа в многопоточном приложении Delphi XE при попытке вызова функции диалогового окна из вторичных потоков, что является недопустимым, так как элементы пользовательского интерфейса должны обрабатываться только в основ
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.