Нарушение доступа, вызванное завершением компонента компонента Indy SSL (PPL / TTask)

Я использую версию Delphi (Delphi 10.2u2)

Моя цель - играть с веб-сервисом для обработки многопоточных команд GET, POST через инфраструктуру Indy, ничего сложного и все работает нормально, пока я не закрою приложение во время запроса.

Для этого я использую TidHTTPClient для обработки команд и TIdSSLIOHandlerSocketOpenSSL для поддержки шифрования трафика.

Для многопоточности я использую TTask из библиотеки параллельного программирования (PPL). «Я совершенно новый в использовании этой библиотеки с Delphi».

Вот пример кода:

procedure TForm3.Button1Click(Sender: TObject);
begin
TTask.Create(
              procedure
              var HTTP : TidHTTP;
                  SSL : TIdSSLIOHandlerSocketOpenSSL;
                  content : String;
              begin
                HTTP := TIdHTTP.Create(nil);

                SSL := TIdSSLIOHandlerSocketOpenSSL.create(nil);

                SSL.SSLOptions.Method := sslvSSLv23;

                HTTP.IOHandler       := SSL;
                HTTP.ConnectTimeout  := 20000;
                HTTP.ReadTimeout     := 60000;
                HTTP.HandleRedirects := true;

                try
                  {
                    If we close the application during the Get, it raise an exception.
                    This is because the SSL Library is freed before the get has the time to stop

                    finalization
                      UnLoadOpenSSLLibrary(); // called before get is over
                  }
                  Content := HTTP.Get('https://www.google.com/');

                  TThread.Synchronize(nil, procedure begin
                     memo1.text := Content;
                  end);
                except
                  // ...
                  on E : Exception do
                    TThread.Synchronize(nil, procedure begin
                      memo1.Text := 'Error'
                    end);
                end;

                SSL.Free;
                HTTP.Free;
  end).Start();
end;

Если вы компилируете и выполняете этот код, вы увидите, что все работает нормально, пока не решит закрыть программу во время запроса (GET, POST или что-то еще)

Ошибка может зависеть от точного момента закрытия приложения во время HTTP-запроса. Это будет нарушение доступа, ошибка Winsock2 (WSA ...) и т. Д. ...

Вот пример ошибки: Нарушение доступа

После небольшого поиска я обнаружил, что ошибка была вызвана тем фактом, что необходимые библиотеки освобождаются перед запущенными подписями.

Действительно, поскольку библиотеки SSL / Winsock (особенно) освобождаются во время незавершенного HTTP-запроса, непрерывность запроса вызывает некоторую недоступную функцию и вызывает исключение нулевого указателя, нарушение прав доступа и т. Д. ...

Прежде чем использовать TTasks, я использовал обычный TThread, и все просто отлично работало, потому что на приложении уничтожить событие я ждал, пока все мои TThread не закончили свою задачу, прежде чем позволить процессу уничтожения уйти далеко.

Насколько я знаю, и я, вероятно, ошибаюсь, Delphi управляет этой частью, но, похоже, делает это после завершения Units.

Лучшим решением для меня было бы управлять состоянием моих TTasks в случае уничтожения моей программы. Подождите, пока все мои TTasks не закончат свои задачи.

НО, когда я не могу найти способ сделать это без привлечения других исключений типа «Операция отменена»,

procedure TForm3.FormDestroy(Sender: TObject);
begin
  ATask.Cancel;
  repeat
  if ATask.Wait(500) = false then begin
     CheckSynchronize();
  end;
 until (ATask = nil);
end;

Выше пример не работает ...

Я уверен, что я делаю что-то неправильно, я не мог найти никакой помощи, связанной с этим вопросом в другом месте, Delphi PPL звучит много нового или моя проблема действительно необычна.

Большое спасибо заранее за вашу помощь.

Я действительно хочу использовать PPL для одного из моих самых больших проектов, я не могу развернуть приложение с такими сообщениями об ошибках ^ _ ^

С уважением,

Всего 1 ответ


Спасибо, что отправил меня на путь Реми. Вот полное решение проблемы.

procedure TForm3.FormDestroy(Sender: TObject);
begin
  while not TTask.WaitForAll(FTasks, 1000) do
    CheckSynchronize();
end;

Есть идеи?

10000