Конференция "Сети" » Обработка исключений Indy
 
  • Nilman © (27.08.09 18:00) [0]
    Помогите пож правильно обработать неудачные попытки соединения в Indy. Есть функция, которая проверяет доступность сервиса, к которому планируется дальнейшее обращение:

    function NetAvail:boolean;
    var
     HTTP: TIdHTTP;
     FServAnsw: WideString;
     IdProxyConnectionInfo: TIdProxyConnectionInfo;
    begin
     Result:=False;
     HTTP:=TIdHTTP.Create(MainForm);
     IdProxyConnectionInfo:=TIdProxyConnectionInfo.Create;
     try
       HTTP.ReadTimeout:=30000;

       if MainForm.AppSettings.UseProxy then
       begin
         IdProxyConnectionInfo.ProxyServer:=MainForm.AppSettings.ProxyHost;
         IdProxyConnectionInfo.ProxyPort:=StrToInt(MainForm.AppSettings.ProxyPort);
         IdProxyConnectionInfo.ProxyUsername:=MainForm.AppSettings.ProxyLogin;
         IdProxyConnectionInfo.ProxyPassword:=MainForm.AppSettings.ProxyPassw;
         HTTP.ProxyParams:=IdProxyConnectionInfo;
       end;
       try
         FServAnsw:=Http.Get('http://'+MHost+'/isavail.txt');
       except
         on e: Exception do
           begin
             //ShowMessage(e.Message);
           end;
       end;
     finally
       HTTP.Free;
       IdProxyConnectionInfo.Free;
     end;

     if Pos(UnicodeString('ok'),UnicodeString(FServAnsw))>0 then Result:=True;
    end;

    ...........................

     while not NetAvail do
     begin
       Synchronize(VCL_ShowNoInetMsg);
       if InfoForm.ModalResult<>mrOK then Terminate;
     end;


    Когда не использую прокси, но хост недоступен, всё проходит нормально, выдаётся сообщение проверить интернет или повторить позднее. При использовании рабочего прокси, тоже всё вроде ровно. Когда подставляю заведомо нерабочий прокси, получаю сначала ошибку 10061 от сокета, которую могу контролировать, потом вылетает Invalid pointer operation. Судя по всему летит оно откуда то из недр инди, т.к. вылетает она на строке Http.Get... Подскажите пож, это ошибка инди или же я её как то неправильно ловлю?.. И вообще бывают ли не обрабатываемые исключения, которые нельзя по тихому обработать и не пугать юзера?
  • Сергей М. © (27.08.09 20:11) [1]

    > доступность сервиса, к которому планируется дальнейшее обращение


    Можно подумать, что при "дальнейшем обращении" ранее проверенный на доступность сервис просто обязан быть доступным..


    > это ошибка инди


    Хрен с ним, с Инди ..

    Смущает другое - Syncronize() и следом же несинхронизированное образение к VCL-объекту ..

    Что еще за чудеса у тебя там творятся в доп.потоке - пока известно тебе лишь одному)
  • Nilman © (28.08.09 20:37) [2]

    > Можно подумать, что при "дальнейшем обращении" ранее проверенный
    > на доступность сервис просто обязан быть доступным..

    считаем что за пол секунды сервис отвалится не может..

    > Смущает другое - Syncronize() и следом же несинхронизированное
    > образение к VCL-объекту ..

    а зачем синхронизация при чтении значения свойства окна?.. и вообще эта строчка там собсно не нужна, считаем что её там нет

    > Что еще за чудеса у тебя там творятся в доп.потоке - пока
    > известно тебе лишь одному)

    там всё просто, показывается сообщение что сервис недоступен, две кнопки, продолжить и отменить, никаких премудростей

    procedure TRegisterThread.VCL_ShowNoInetMsg;
    begin
       if InfoForm.ShowRetryCancelInfo('You are need an active Internet connection to continue registration. Connect to the Internet and click Retry.')<>mrOK then
       begin
         Success:=False;
         Terminate;
         Exit;
       end;
    end

  • Сергей М. © (28.08.09 22:13) [3]

    > считаем что за пол секунды сервис отвалится не может


    С чего бы вдруг считаем ?


    > зачем синхронизация при чтении значения свойства окна?


    Затем же самым, зачем строчку

    HTTP:=TIdHTTP.Create(MainForm);

    ты ОБЯЗАН был синхронизировать.

    А ты не сподобился.
    Тем самым продемонстрировал непонимание необходимости и необязательности синхронизации.

    И таких "чудес" у тебя, видимо, еще немало, судя по многоточию и всяким там "считаем что есть", "считаем что нет"
  • Nil (29.08.09 00:38) [4]

    > С чего бы вдруг считаем ?

    с того что вероятность такого исхода практически равна нулю! это всё равно что  ставить обработку исключения например на выключение света на котором работает программа! это невозможно и моего изначального вопроса никак не касается!

    > HTTP:=TIdHTTP.Create(MainForm);

    за тем же самым не ответ, поясни если много знаешь! если бы я всё это знал на отлично, я бы не создавал этот топик. но я знаю точно что чтение значения InfoForm.ModalResult не вызывает в VCL никаких событий и определённо точно могу сказать что синхронизировать его не нужно. точно так же как и создание TIdHTTP не инициирует никаких событий связанных с VCL. если я в чём то ошибаюсь, разубеди! только обоснованно!

    > Тем самым продемонстрировал непонимание необходимости и
    > необязательности синхронизации.

    в данном случае, синхронизацию я представляю себе чётко и сделал всё правильно

    и вообще всё это не по теме моего вопроса, то о чём мы сейчас флеймим никак не касается проблем работы инди через прокси
  • CrytoGen (29.08.09 08:31) [5]
    Молодец, ты всё правильно сделал. Значит всё уже должно работать.
  • Сергей М. © (29.08.09 10:03) [6]
    Передача существующего компонента-фладельца Owner параметром в конструктор создаваемого компонента ведет к неявному вызову метода Owner.InsertComponent, который НЕпотокобезопасен.


    > это всё равно что  ставить обработку исключения например
    > на выключение света на котором работает программа


    К чему эти глупости ?
    И вообзще зачем нужна циклическая проверка доступности сервиса ?
    Придет время, когда сервис реально понадобится, ты обратишься к нему, получишь исключение и сообщишь юзеру о таких-то проблемах с сервисом.
    А не получишь - работай дальше с сервисом спокойно.
    Да и нет никакой гарантии, что сервис окажется доступным и работоспособным на всем протяжении работы в сеансе подключения к нему.


    > вылетает Invalid pointer operation. Судя по всему летит оно
    > откуда то из недр инди


    Судя по ЧЕМУ конкретно ?
  • Nilman © (31.08.09 13:16) [7]

    > Передача существующего компонента-фладельца Owner параметром
    > в конструктор создаваемого компонента ведет к неявному вызову
    > метода Owner.InsertComponent, который НЕпотокобезопасен.
    >
    >

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

    > К чему эти глупости ?
    > И вообзще зачем нужна циклическая проверка доступности сервиса
    > ?
    > Придет время, когда сервис реально понадобится, ты обратишься
    > к нему, получишь исключение и сообщишь юзеру о таких-то
    > проблемах с сервисом.
    > А не получишь - работай дальше с сервисом спокойно.
    > Да и нет никакой гарантии, что сервис окажется доступным
    > и работоспособным на всем протяжении работы в сеансе подключения
    > к нему.
    >

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

    > Судя по ЧЕМУ конкретно ?

    судя по этому коду:

    unit Unit1;

    interface

    uses
     Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
     Dialogs, StdCtrls;

    type
     TForm1 = class(TForm)
       Button1: TButton;
       procedure Button1Click(Sender: TObject);
     private
       { Private declarations }
     public
       { Public declarations }
     end;

    var
     Form1: TForm1;

    implementation

    uses IdHTTP, IdExceptionCore, IdHTTPHeaderInfo;

    {$R *.dfm}

    function NetAvail:boolean;
    var
     HTTP: TIdHTTP;
     FServAnsw: WideString;
     IdProxyConnectionInfo: TIdProxyConnectionInfo;
    begin
     Result:=False;
     HTTP:=TIdHTTP.Create(Form1);
     IdProxyConnectionInfo:=TIdProxyConnectionInfo.Create;
     try
       HTTP.ReadTimeout:=30000;

         IdProxyConnectionInfo.ProxyServer:='localhost';
         IdProxyConnectionInfo.ProxyPort:=8080;
         IdProxyConnectionInfo.ProxyUsername:='';
         IdProxyConnectionInfo.ProxyPassword:='';
         HTTP.ProxyParams:=IdProxyConnectionInfo;

    //      218.248.28.173:3128

       try
         FServAnsw:=Http.Get('http://'+MHost+'/isavail.txt');
       except
         on e: Exception do
           begin
             ShowMessage(e.Message);
           end;
       end;
     finally
       HTTP.Free;
       IdProxyConnectionInfo.Free;
     end;

     if Pos(UnicodeString('ok'),UnicodeString(FServAnsw))>0 then Result:=True;
    end;

    procedure TForm1.Button1Click(Sender: TObject);
    begin
     if NetAvail then ShowMessage('1') else ShowMessage('2');
    end;

    end.

  • Nilman © (31.08.09 13:23) [8]
    пробовал этот код и в d2009 и в d2007, результат тот же.. судя по всему что-то я не так делаю..
  • Сергей М. © (31.08.09 13:47) [9]

    > в каких то случаях это будет не безопасно


    Не в "каких-то", а во всех случаях, когда возможны вызовы метода InsertComponent одновременно более чем из одного потока.


    > главная форма перестанет быть только в том случае когда
    > все его потомки будут уничтожены


    Какие такие "потомки" ?
    Я веду речь о форме как компоненте-владельце компонентов.
    Компоненты, которыми владеет форма, размещаются в списке Components. Список этот НЕ допускает несинхронизированных обращений к нему. Занесение в список - это InsertComponent, удаление из списка - RemoveComponent.


    > какие именно события может вызвать InsertComponent которые
    > могут расстроить поток?


    Любые события, влекущие изменение состояния списка Components компонента-владельца и потенциально могущие произойти одновременно более чем в одном потоке процесса VCL-приложения.

    Например, ты в своем доп потоке создаешь некий компонент

    MyComp := TMyComp.Create(форма-владелец); //здесь происходит неявный вызов метода форма-владелец.InsertComponent, добавляющий твой компонент в список компонентов, которыми владеет форма-владелец.
    ..
    MyComp.Free; //а здесь, соотв-но, происходит неявный вызов метода форма-владелец.RemoveComponent, который удалит твой компонент из списка компонентов, которыми владеет форма-владелец.

    При этом нет никакой гарантии, что те же самые действия в те же моменты времени не пытается осуществить какой-либо другой поток процесса, например, основной.

    Такая ситуация может привести к непредсказуемым последствиям, вплоть до краха процесса.


    > судя по этому коду


    Я спросил какая конкретно строчка этого кода вызывает исключение..
  • Nilman © (31.08.09 14:33) [10]

    >
    > Любые события, влекущие изменение состояния списка Components
    > компонента-владельца и потенциально могущие произойти одновременно
    > более чем в одном потоке процесса VCL-приложения.
    >
    > Например, ты в своем доп потоке создаешь некий компонент
    >
    > MyComp := TMyComp.Create(форма-владелец); //здесь происходит
    > неявный вызов метода форма-владелец.InsertComponent, добавляющий
    > твой компонент в список компонентов, которыми владеет форма-
    > владелец.
    > ..
    > MyComp.Free; //а здесь, соотв-но, происходит неявный вызов
    > метода форма-владелец.RemoveComponent, который удалит твой
    > компонент из списка компонентов, которыми владеет форма-
    > владелец.
    >
    > При этом нет никакой гарантии, что те же самые действия
    > в те же моменты времени не пытается осуществить какой-либо
    > другой поток процесса, например, основной.
    >
    > Такая ситуация может привести к непредсказуемым последствиям,
    >  вплоть до краха процесса.

    понял, спасибо за разъяснения! не подумал что RemoveComponent могут вызвать одновременно два компонента..


    > Я спросил какая конкретно строчка этого кода вызывает исключение.
    > .

    ок, тогда попробую максимально подробно. вот здесь вываливается ошибка
    FServAnsw:=Http.Get('http://'+MHost+'/isavail.txt');
    трасировка довела меня до попытки соединения, CheckAndConnect так по моему называется функция в инди, где возникает исключение, on e: Exception do begin ShowMessage(e.Message); end; выдаёт ошибку сокета, connection refused, а после эксепшена вылетает Invalid pointer operation. Причём это не исключение, а просто ошибка. на сообщение об ошибке только эта фраза и всё. голословность этого сообщение и натолкнуло на мысль что это какая то не обрабатываемая ошибка инди..
  • Сергей М. © (31.08.09 14:52) [11]

    > это какая то не обрабатываемая ошибка инди


    Так а причем здесь Инди, если то исключение, что он возбудил в ходе исполнения метода Get(), ты успешно перехватил и убедился в его вменяемости


    > после эксепшена вылетает Invalid pointer operation


    Что значит "после" ?

    try
     .. Get ..
    except
     on E:Exception do ...
    // <-- "после" - это здесь что ли ?
    end;



    И я что-то не пойму - то ты демонстрируешь код для исполнения в доп.потоке, то теперь для исполнения в основном ..

    Т.е. Invalid pointer operation возникает вне зависимости от того, в каком потоке тобой вызывается ф-ция NewAvail() ?

    И какая версия Инди используется ?
  • Nilman © (31.08.09 22:17) [12]

    > Так а причем здесь Инди, если то исключение, что он возбудил
    > в ходе исполнения метода Get(), ты успешно перехватил и
    > убедился в его вменяемости

    вполне вероятно что какой то деструктор исключения инди с ошибкой отваливается..

    > Что значит "после" ?
    >


      try
        FServAnsw:=Http.Get('http://ixitools.com/isavail.txt');
      except
        on e: Exception do
          begin
            ShowMessage(e.Message);
            a:=1;
          end;
      end;


    на строчку a:=1 ставлю брэкпоинт, трасировка доходит до ShowMessage, который успешно показывает сообщение об исключении и дальше вываливается Invalid pointer operation.. при этом трасировка до a:=1 не доходит

    > И я что-то не пойму - то ты демонстрируешь код для исполнения
    > в доп.потоке, то теперь для исполнения в основном ..
    >

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

    > И какая версия Инди используется ?

    10
  • Сергей М. © (01.09.09 10:13) [13]
    Т.е. ты утверждаешь, что этот код

    try
       FServAnsw:=Http.Get('http://ixitools.com/isavail.txt');
     except
       on e: Exception do
         begin
           ShowMessage(e.Message);
           a:=1;
         end;
     end;



    исполняется гарантированно в основном потоке, и при этом ты наблюдаешь ту же самую картину с ошибкой Invalid pointer operation, возникающей при выполнении ShowMessage ?

    Ну а не приходило в голову

    try
       FServAnsw:=Http.Get('http://ixitools.com/isavail.txt');
     except
     end;


    ?
  • Nilman © (03.09.09 13:11) [14]
    всё таки оказалась кривота не в инди а в моём коде.. странно что про класс TIdProxyConnectionInfo в хелпе видимо забыли или просто порешили что в хелпе ему не место..

    проблема была в создании TIdProxyConnectionInfo
    так можно:
     HTTP.ProxyParams:=TIdProxyConnectionInfo.Create;
     HTTP.ProxyParams.ProxyServer:=MainForm.AppSettings.ProxyHost;
     ...
    так нельзя:
     IdProxyConnectionInfo:=TIdProxyConnectionInfo.Create;
     IdProxyConnectionInfo.ProxyServer:=MainForm.AppSettings.ProxyHost;
     ...
     HTTP.ProxyParams:=IdProxyConnectionInfo;

    благодарю за содействие в решении проблемы!

    пс
    нужно ли руками освобождать idHTTP.ProxyParams? или idHTTP сам сделает это, если ProxyParams не nil?
  • CrytoGen (03.09.09 13:42) [15]
    это легко проверить :) убей idHTTP и попробуй получить воспользоваться ProxyParams.
  • Сергей М. © (03.09.09 13:52) [16]
    Создавать собственный экз-р TIdProxyConnectionInfo вовсе не обязательно.

    Его создает сам компонент  TIdHTTP при своей самоинициализации (см. TIdCustomHTTP.InitComponent) и сам же уничтожает его при своем разрушении (см. TIdCustomHTTP.Destroy)

    Но если ты все же создал его, то строка
    HTTP.ProxyParams:=IdProxyConnectionInfo;


    недопустима, ибо она приведет к утечке памяти: компонент TIdHTTP заместит в поле FProxyParameters ссылку на созданный им самим экз-р TIdProxyConnectionInfo ссылкой на твой экз-р БЕЗО всяких проверок (думаю, что это серьезная недоработка разработчиков Инди)

    Поступать в таком случае следует так:

    IdProxyConnectionInfo:=TIdProxyConnectionInfo.Create;
    try
    ...
      HTTP.ProxyParams.Assign(IdProxyConnectionInfo);
    ...
    finally
      IdProxyConnectionInfo.Free;
    end;
  • Nilman © (03.09.09 15:18) [17]
    я уже подзапутался.. изначально пробовал так HTTP.ProxyParams.Host:='..' но получал от компилятора ругань, точно какую не помню, но смысл такой что напрямую не могу обращаться к Host. нашёл какой то кусок кода в интернете который говорил что нужно делать так
    IdProxyConnectionInfo:=TIdProxyConnectionInfo.Create;
    try
    ...
     HTTP.ProxyParams:=IdProxyConnectionInfo;
    ...
    finally
     IdProxyConnectionInfo.Free;
    end;
    как я уже говорил тут, что если прокси рабочий то всё проходило нормально, а если же появлялся любой exception сразу получал Invalid pointer operation после exception..
    сейчас всё вернулось на свои места, как изначально и предполагалось что должно работать:

     HTTP:=TIdHTTP.Create(MainForm);
     try
       HTTP.ReadTimeout:=30000;

       if MainForm.AppSettings.UseProxy then
       begin
         HTTP.ProxyParams.ProxyServer:=MainForm.AppSettings.ProxyHost;
         HTTP.ProxyParams.ProxyPort:=StrToInt(MainForm.AppSettings.ProxyPort);
         HTTP.ProxyParams.ProxyUsername:=MainForm.AppSettings.ProxyLogin;
         HTTP.ProxyParams.ProxyPassword:=MainForm.AppSettings.ProxyPassw;
    //      218.248.28.173:3128
       end;
       try
         FServAnsw:=Http.Get('http://'+MHost+'/isavail.txt');
       except
    //      on e: Exception do
    //        begin
    //          ShowMessage(e.Message);
    //        end;
       end;
     finally
       HTTP.Free;
     end;

    почему решил попробовать именно так
    HTTP.ProxyParams:=TIdProxyConnectionInfo.Create;
    потому что раньше встречал где то в стронних компонентах такой подход, что компонент содержит только указатель на объект при этом все действия по инициализации и разрушению оставались на плечах программера.. а вот сразу заглянуть на внутренности TIdCustomHTTP как то и не подумал.

    а как на самом деле в данной ситуации происходит утечка? насколько понимаю, выделяется память в размере SizeOf(TIdProxyConnectionInfo), указатель на этот кусок помещается в FProxyParameters, потом я получается меняю этот указатель на указатель для созданного мной экземпляр TIdProxyConnectionInfo, а тот кусок памяти который выделил под него TIdCustomHTTP.InitComponent получается теперь без указателя. но этот кусок же в адресном пространстве программы? и насколько понимаю, при закрытии программы вся память выделенная программе освобождается, включая этот мой экземпляр TIdProxyConnectionInfo про который никто не знает? чем и кому этот подвисший кусок может критически навредить?
  • Сергей М. © (03.09.09 17:00) [18]

    > при закрытии программы вся память выделенная программе освобождается,
    >  включая этот мой экземпляр TIdProxyConnectionInfo про который
    > никто не знает?


    Да.


    > чем и кому этот подвисший кусок может критически навредить?


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

    Но в ходе работы программы, если создание экз-ра без его последующего уничтожения осуществляется много раз, например, в бесконечном цикле, это рано или поздно при очередной попытке создания экз-ра обязательно приведет к отказу, связанному с отсутствием свободной виртуальной памяти в адресном пространстве твоего процесса.
  • Nil (04.09.09 23:14) [19]
    благодарю!
 
Конференция "Сети" » Обработка исключений Indy
Есть новые Нет новых   [134437   +29][b:0][p:0.004]