Нарушение прав доступа, вызванное финализацией SSL-компонента Indy (PPL / TTask) - PullRequest
0 голосов
/ 13 ноября 2018

Я использую версию 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 ...) и т. Д. *

Вот пример ошибки: Access Violation

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

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

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

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

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

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

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 Ответ

0 голосов
/ 14 ноября 2018

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

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