На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
! ПРАВИЛА РАЗДЕЛА · FAQ раздела Delphi · Книги по Delphi
Пожалуйста, выделяйте текст программы тегом [сode=pas] ... [/сode]. Для этого используйте кнопку [code=pas] в форме ответа или комбобокс, если нужно вставить код на языке, отличном от Дельфи/Паскаля.
Следующие вопросы задаются очень часто, подробно разобраны в FAQ и, поэтому, будут безжалостно удаляться:
1. Преобразовать переменную типа String в тип PChar (PAnsiChar)
2. Как "свернуть" программу в трей.
3. Как "скрыться" от Ctrl + Alt + Del (заблокировать их и т.п.)
4. Как прочитать список файлов, поддиректорий в директории?
5. Как запустить программу/файл?
... (продолжение следует) ...

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


Внимание
Попытки открытия обсуждений реализации вредоносного ПО, включая различные интерпретации спам-ботов, наказывается предупреждением на 30 дней.
Повторная попытка - 60 дней. Последующие попытки бан.
Мат в разделе - бан на три месяца...
Модераторы: jack128, D[u]fa, Shaggy, Rouse_
  
> Проблема с TComPort / WaitForSingleObject , вызываю всех гуру форума
    Доброго времени суток!

    Я достаточно много лет использую компонент TComPort by Dejan Crnila. Баловался с факс-модемами, мобилками, было несколько серьезных разработок. Никаких проблем не возникало. И вот однажды стали возникать проблемы, проявляющиеся в виде зависания наглухо программы, управляющей механическим оборудованием.

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

    Максимальный режим дебага ничего не показал, пришлось залазить в исходники компонента и модифицировав задуманные разработчиками возможности CALLTRACE (модифицировал OutputDebugString) получил следующие выводы:

    TCustomComPort.Read у меня в следующем виде:
    ExpandedWrap disabled
      // perform synchronous read operation
      function TCustomComPort.Read(Buffer:TCPortBytes; Count: Integer): Integer;
      var
        AsyncPtr: PCPortAsync;
      begin
      {$ifdef CPORT_CALLTRACE}
        OutputDebugString('TCustomComPort.Read '+inttostr(Count)+' bytes');
      {$endif}
        CportInitAsync(AsyncPtr);
        try
      {$ifdef CPORT_CALLTRACE}
        OutputDebugString('>> Before ReadAsync');
      {$endif}
          ReadAsync(Buffer, Count, AsyncPtr);
      {$ifdef CPORT_CALLTRACE}
        OutputDebugString('>> After ReadAsync');
      {$endif}
          Result := WaitForAsync(AsyncPtr);
      {$ifdef CPORT_CALLTRACE}
        OutputDebugString('>> After WaitForAsync');
      {$endif}
        finally
          CportDoneAsync(AsyncPtr);
        end;
      end;


    Из нее вызывается функция
    ExpandedWrap disabled
      // wait for asynchronous operation to end
      function TCustomComPort.WaitForAsync(var AsyncPtr: PCPortAsync): Integer;
      var
        BytesTrans, Signaled: DWORD;
        Success: Boolean;
      begin
        if AsyncPtr = nil then
          raise EComPort.Create(FPort,CError_InvalidAsync);
       
        {$ifdef CPORT_CALLTRACE}
         OutputDebugString(PChar('TCustomComPort.WaitForAsync WaitForSingleObject! '+      IntToHex(Integer(AsyncPtr^.Overlapped.hEvent),8)) );
        {$endif}
       
        //Signaled := WaitForSingleObject(AsyncPtr^.Overlapped.hEvent, INFINITE); //ORIGINAL
        Signaled := WaitForSingleObject(AsyncPtr^.Overlapped.hEvent, 5000);
       
        {$ifdef CPORT_CALLTRACE}
         OutputDebugString( PChar('TCustomComPort.WaitForAsync Signaled='+IntToStr(Signaled)) );
        {$endif}
       
        Success := (Signaled = WAIT_OBJECT_0) and
            (GetOverlappedResult(FHandle, AsyncPtr^.Overlapped, BytesTrans, False));
       
        if not Success then
          raise EComPortExt.Create(FPort, ErrorCode(AsyncPtr), GetLastError);
       
        if not FInputCountNotSupported then begin
       
          if (AsyncPtr^.Kind = okRead) and (InputCount = 0) then begin
          {$ifdef CPORT_CALLTRACE}
           OutputDebugString('TCustomComPort.WaitForAsync SendSignalToLink');
          {$endif}
            SendSignalToLink(leRx, False)
          end else
            if AsyncPtr^.Data <> nil then begin
              {$ifdef CPORT_CALLTRACE}
               OutputDebugString('TCustomComPort.WaitForAsync TxNotifyLink');
              {$endif}
              TxNotifyLink( TCPortBytes( AsyncPtr^.Data), AsyncPtr^.Size);
            end;
            
        end;
       
        {$ifdef CPORT_CALLTRACE}
         OutputDebugString( PChar('TCustomComPort.WaitForAsync BytesTrans='+IntToStr(BytesTrans)) );
        {$endif}
        Result := BytesTrans;
      end;


    В ней на WaitForSingleObject с параметром INFINITE (именно бесконечность была в оригинальном коде) все благополучно наглухо зависает (основной поток программы со всеми вытекающими) при этом какие-то фоновые потоки, не связанные с портами, продолжают работать.
    Я изменил время ожидания на 5 секунд. Существенно лучше не стало. Программа перестала зависать, но все последующие попытки чтения из порта заканчивались исключением. Это позволило только добавить код, позволяющий вызвать оператора для перезагрузки. Нет, не программы, компьютера, так как даже после выхода порт остается недоступен в операционной системе (консольная команда mode не отображает его в списке).

    Вот CALLTRACE для успешного запроса / ответа с моими комментариями
    Цитата
    12:23:14 TCustomComPort.WaitForAsync WaitForSingleObject! 000004C8
    12:23:14 TCustomComPort.WaitForAsync Signaled=0
    12:23:14 TCustomComPort.WaitForAsync TxNotifyLink
    12:23:14 TCustomComPort.WaitForAsync BytesTrans=5 //в порт ушло 5 байт данных
    12:23:14 TCustomComPort.DoneAsync
    12:23:14 TCustomComPort.Read 7 bytes //прочитать из порта 7 байт
    12:23:14 >> Before ReadAsync
    12:23:14 >> After ReadAsync
    12:23:14 TCustomComPort.WaitForAsync WaitForSingleObject! 000004C8
    12:23:14 TCustomComPort.WaitForAsync Signaled=0
    12:23:14 TCustomComPort.WaitForAsync SendSignalToLink
    12:23:14 TCustomComPort.WaitForAsync BytesTrans=7
    12:23:14 >> After WaitForAsync //прочитано 7 байт успешно

    Вот проблемный запрос:
    Цитата
    12:23:14 TCustomComPort.WaitForAsync WaitForSingleObject! 000004C8
    12:23:14 TCustomComPort.WaitForAsync Signaled=0
    12:23:14 TCustomComPort.WaitForAsync TxNotifyLink
    12:23:14 TCustomComPort.WaitForAsync BytesTrans=11 //в порт пошли 11 байт
    12:23:14 TCustomComPort.DoneAsync
    12:23:14 TCustomComPort.Read 3 bytes / TCustomComPort.Read //прочитать ответ 4 байта
    12:23:14 >> Before ReadAsync
    12:23:14 >> After ReadAsync
    12:23:14 TCustomComPort.WaitForAsync WaitForSingleObject! 000004C8 //ожидаем
    //тут мы 5 секунд висим
    12:23:19 TCustomComPort.WaitForAsync Signaled=258 //код ТАЙМАУТ

    Вот как выглядят все последующие команды:
    Цитата
    12:23:19 TCustomComPort.WaitForAsync WaitForSingleObject! 000004C8
    12:23:19 TCustomComPort.WaitForAsync Signaled=0
    12:23:19 TCustomComPort.WaitForAsync TxNotifyLink
    12:23:19 TCustomComPort.WaitForAsync BytesTrans=5 //ушло 5 байт
    12:23:19 TCustomComPort.DoneAsync
    12:23:19 TCustomComPort.Read 7 bytes //прочитать 7 байт
    12:23:19 >> Before ReadAsync
    12:23:19 >> After ReadAsync
    12:23:19 TCustomComPort.WaitForAsync WaitForSingleObject! 000004C8 //ожидаем
    12:23:19 TCustomComPort.WaitForAsync Signaled=0 //сразу возврат с кодом 0
    после чего где-то в коде компонента возникает исключение, которое мой код отрабатывает как ошибку


    Ну и вопросы - кто виноват и что делать?

    Дело не в длине пакета данных, так как в порт постоянно отправляются команды 5, 11 байт и другой длины.

    Возможно, это поведение операционной системы либо внешнего USB-RS232 адаптера (девайс качественный, не один год используется).

    А может это как-то связано с поведением контроллера, с которым ведется коммуникация (но после перезагрузки ПК и запуска программы заново контроллер отвечает нормально, перезагрузка по питанию не производится).

    Еще один момент который может влиять на вероятность возникновения проблемы (несколько раз в сутки при интенсивном использовании) это то что управление на самом деле производится двумя однотипными устройствами через однотипные контроллеры (разные только номера COM портов) и непосредственно перед зависанием в один и тот же контроллер уходит подряд 7 команд серией (7я - зависающая), хотя обычное поведение это одна команда в один порт, за ней тут же одна в другой порт или серии по несколько команд в каждый порт попеременно. Возможно что серия 7 команд в один порт (записать, прочитать) самая длинная во всем коде программы.

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

    Понимаю что проблема достаточно сложная, потому прошу гуру форума подсказать что касается работы TComPort с портом и вызовом WaitForSingleObject - почему может быть такое поведение?
      Найдите где вызывается функция ReadFile/WriteFile(hComPort,...,Overlapped) - там будет callback функция, которую система вызывает по окончании чтения/записи в порт. Эта функция должна установить состояние hEvent по окончании данных в буфере, на котором зависает WaitForSingleObject.
      Далее стоит искать там (в callback функции) причину зависания. Скорее всего эта функция будет вызываться из ReadAsync.
      Сообщение отредактировано: macomics -
        Ок, в Read() вызывается ReadAsync()
        ExpandedWrap disabled
          // perform asynchronous read operation
          function TCustomComPort.ReadAsync(var Buffer:TCPortBytes; Count: Integer; var AsyncPtr: PCPortAsync): Integer;
          var
            Success: Boolean;
            BytesTrans: DWORD;
            lastErr:DWORD;
          begin
            if AsyncPtr = nil then
              raise EComPort.Create(FPort,CError_InvalidAsync);
            AsyncPtr^.Kind := okRead;
           
            Buffer[0] := AnsiChar($FF);
            // Note the position of the caret (^). It compiles fine but doesn't work without "Buffer^"
            Success := ReadFile(FHandle, Buffer^, Count, BytesTrans, @AsyncPtr^.Overlapped)
              or (GetLastError = ERROR_IO_PENDING);
           
            if not Success then begin
                lastErr := GetLastError;
                if FReadAsyncExceptionsEnabled then begin
                    raise EComPortExt.Create(FPort, CError_ReadFailed, lastErr);
                end else begin
                    Inc(FReadAsyncErrorCount);
                    FReadAsyncLastError := lastErr;
                    result := 0;
                    exit;
                end;
            end;
           
            Result := BytesTrans;
          end;


        То есть если здесь not Success то следует анализировать GetLastError, я Вас правильно понял?
        Если внутри ReadAsync происходит ошибка (и функция возвращает 0) то какие мои дальнейшие действия - в коде не должна вызываться функция WaitForAsync(), в которой было бесконечное ожидание?
          Не совсем. При возникновении ошибки hEvent не устанавливается в состояние Signaled и система его не установит тоже никогда. Отсюда и будет зависание.
          Т.е. При Success = False либо надо вручную вызвать SetEvent или не вызывать функцию
          ExpandedWrap disabled
            Result := WaitForAsync(AsyncPtr);
          если предыдущий вызов
          ExpandedWrap disabled
            ReadAsync(Buffer, Count, AsyncPtr);
          вернет not Success.
          Логика должна быть такая. Но тут вызывается функция ожидания даже если при чтении из Com-порта произошла ошибка.

          Прочитайте про работу hEvent в структуре Overlapped

          Добавлено
          т.е. код стоит изменить на такой
          ExpandedWrap disabled
            function TCustomComPort.Read(Buffer:TCPortBytes; Count: Integer): Integer;
            var
              AsyncPtr: PCPortAsync;
              Success: Integer;
            begin
              Result := 0;
            {$ifdef CPORT_CALLTRACE}
              OutputDebugString('TCustomComPort.Read '+inttostr(Count)+' bytes');
            {$endif}
              CportInitAsync(AsyncPtr);
              try
            {$ifdef CPORT_CALLTRACE}
              OutputDebugString('>> Before ReadAsync');
            {$endif}
                Success := ReadAsync(Buffer, Count, AsyncPtr); // Сохраняем передаваемое количество байт
            {$ifdef CPORT_CALLTRACE}
              OutputDebugString('>> After ReadAsync');
            {$endif}
                if Success <> 0 then // Передаваемое количество байт не равно 0, тогда ожидаем завершения передачи
                    Result := WaitForAsync(AsyncPtr);
            {$ifdef CPORT_CALLTRACE}
              OutputDebugString('>> After WaitForAsync');
            {$endif}
              finally
                CportDoneAsync(AsyncPtr);
              end;
            end;
          и соответственно
          ExpandedWrap disabled
            function TCustomComPort.ReadAsync(var Buffer:TCPortBytes; Count: Integer; var AsyncPtr: PCPortAsync): Integer;
            var
              Success: Boolean;
              BytesTrans: DWORD;
              lastErr:DWORD;
            begin
              if AsyncPtr = nil then
                raise EComPort.Create(FPort,CError_InvalidAsync);
              AsyncPtr^.Kind := okRead;
             
              Buffer[0] := AnsiChar($FF);
              // Note the position of the caret (^). It compiles fine but doesn't work without "Buffer^"
              Success := ReadFile(FHandle, Buffer^, Count, BytesTrans, @AsyncPtr^.Overlapped)
                or (GetLastError = ERROR_IO_PENDING);
              if BytesTrans = 0 then BytesTrans := Count; // Если не будет ошибок возвращаем Count;
              if not Success then begin
                  BytesTrans := 0; // При ошибке возвращаем 0!
                  lastErr := GetLastError;
                  if FReadAsyncExceptionsEnabled then begin
                      raise EComPortExt.Create(FPort, CError_ReadFailed, lastErr);
                  end else begin
                      Inc(FReadAsyncErrorCount);
                      FReadAsyncLastError := lastErr;
                      result := 0;
                      exit;
                  end;
              end;
             
              Result := BytesTrans;
            end;
          А INFINITE можете вернуть обратно.
          Сообщение отредактировано: macomics -
            Вариант с вызовом WaitForAsync только если в ReadAsync не было ошибок пока не сработал, при инициализации первые несколько команд прошли, следующая крешанула программу. Буду разбираться днем в чем отличие.

            Удалось воспроизвести ошибку, приводившую к зависанию пока в предыдущем варианте кода, но с логом GetLastError и результата Success.
            Как ни странно,
            Цитата
            TCustomComPort.ReadAsync 0 bytes Success, lastErr = 997

            То есть BytesTrans равен 0, в MSDN написано
            Цитата
            ReadFile sets this value to zero before doing any work or error checking. Use NULL for this parameter if this is an asynchronous operation to avoid potentially erroneous results.
            То есть ноль это законно для lpOverlapped <> nil, причем написано даже что этот параметр тоже должен быть nil. А 997 это ERROR_IO_PENDING. То есть в ReadAsync все выполняется как задумано, какого ж тогда лешего происходит таймаут в WaitForSingleObject...


            Уточню. Программа шлет контроллеру пакет-запрос и получает пакет-ответ. Может ли подобная ситуация произойти если контроллер не ответил на запрос, на который согласно документации должен был ответить? Или тогда вернется 0 байт и зависаний быть не должно? Вообще в компоненте TComPort таймауты установлены, 2 секунды или что-то около того, потому если кабели не подключены программа ошибку выводит, а не зависает.
            Сообщение отредактировано: Виталь -
              Цитата Виталь @
              Удалось воспроизвести ошибку, приводившую к зависанию пока в предыдущем варианте кода, но с логом GetLastError и результата Success.

              При получении Success можете просто вызвать
              ExpandedWrap disabled
                SetEvent(AsyncPtr^.Overlapped.hEvent);
              Тогда
              ExpandedWrap disabled
                Signaled := WaitForSingleObject(AsyncPtr^.Overlapped.hEvent, INFINITE);
              не должна зависнуть. Проблема в том, что ReadFile закончила работу нормально (без необходимости ожидания).
              Цитата Виталь @
              Может ли подобная ситуация произойти если контроллер не ответил на запрос, на который согласно документации должен был ответить?

              Может.
              Цитата Виталь @
              Или тогда вернется 0 байт и зависаний быть не должно?

              Нет. По вашему алгоритму вы до посинения (или в течении 5 сек) ждете передачи от контроллера.
              Цитата Виталь @
              Вообще в компоненте TComPort таймауты установлены, 2 секунды или что-то около того, потому если кабели не подключены программа ошибку выводит, а не зависает.

              Это другие таймауты. В вашей функции таймаут - ждать до посинения (INFINITE) был в оригинале.
              Сообщение отредактировано: macomics -
                Спасибо что находите время отвечать среди ночи. Сейчас попробую SetEvent
                За это время обернул ReadAsync() и WaitForAsync() в общий try...except и на выходе получил
                Цитата
                Exception in Read: COM Port Error: Unknown Error #0 on COM3
                Да, эта гадость сегодня слишком часто воспроизводится.

                Вот этот код 0 передается в параметре AWinCode в EComPortExt.Create()
                ExpandedWrap disabled
                  inherited Create( 'COM Port Error: '+ComErrorMessage(AWinCode)+' on '+port);


                Но это очевидно лезет отсюда
                ExpandedWrap disabled
                    Success := (Signaled = WAIT_OBJECT_0) and
                        (GetOverlappedResult(FHandle, AsyncPtr^.Overlapped, BytesTrans, False));
                   
                    if not Success then
                      raise EComPortExt.Create(FPort, ErrorCode(AsyncPtr), GetLastError);

                И толком мне ничего не дает. Поскольку COM порт снова пропал из системы - перезагрузка, за это время допишу SetEvent. Почему порт пропадает я так и не понял, думал что как-то связано с тем что не вызывается CportDoneAsync(AsyncPtr), но не то.


                С SetEvent падает сразу, это в if Success then
                Цитата
                22:32:53 TCustomComPort.Read 5 bytes
                22:32:53 TCustomComPort.ReadAsync 0 bytes Success
                22:32:53 TCustomComPort.WaitForAsync WaitForSingleObject 000004D0
                22:32:53 TCustomComPort.WaitForAsync Signaled=0
                22:32:53 >> Exception in Read: COM Port Error: Unknown Error #996 on COM16
                (ERROR_IO_INCOMPLETE, Overlapped I/O event is not in a signaled state.)
                Сообщение отредактировано: Виталь -
                  При получении этой ошибки добавьте CancelIo(FHandle);
                  Должно отменить ошибочную операцию в/в. Это должно оживлять COM порт без необходимости перезагрузки.
                  ExpandedWrap disabled
                      if not Success then begin
                        CancelIo(FHandle);
                        raise EComPortExt.Create(FPort, ErrorCode(AsyncPtr), GetLastError);
                      end;
                  ExpandedWrap disabled
                        function TCustomComPort.ReadAsync(var Buffer:TCPortBytes; Count: Integer; var AsyncPtr: PCPortAsync): Integer;
                        var
                          Success: Boolean;
                          BytesTrans: DWORD;
                          lastErr:DWORD;
                        begin
                          if AsyncPtr = nil then
                            raise EComPort.Create(FPort,CError_InvalidAsync);
                          AsyncPtr^.Kind := okRead;
                        
                          Buffer[0] := AnsiChar($FF);
                          // Note the position of the caret (^). It compiles fine but doesn't work without "Buffer^"
                          Success := ReadFile(FHandle, Buffer^, Count, BytesTrans, @AsyncPtr^.Overlapped)
                            or (GetLastError = ERROR_IO_PENDING);
                          Result := Count;
                          if not Success then begin
                              lastErr := GetLastError;
                              if lastErr = SUCCESS then begin
                                  SetEvent(FPort);
                                  exit;
                              end else begin
                                  result := 0;
                                  if FReadAsyncExceptionsEnabled then begin
                                      raise EComPortExt.Create(FPort, CError_ReadFailed, lastErr);
                                  end else begin
                                      Inc(FReadAsyncErrorCount);
                                      FReadAsyncLastError := lastErr;
                                      exit;
                                  end;
                              end;
                          end;
                        end;
                  Так будет правильнее.

                  Добавлено
                  В общем как-то так должно сработать. Смысл в том, что при успешном начале чтения из COM или полностью считанном блоке из COM порта функция возвращает значение отличное от 0. Тогда должна быть задействована задержка. Хотя судя по всему при Success не стоит запускать задержку. Тогда стоит разделить значения переменной Result на 2. Флаг необходимости задержки и количество считанных байт. Еще можно проверять GetLastError = ERROR_IO_PENDING перед вызовом WaitForAsync.
                  ExpandedWrap disabled
                        function TCustomComPort.Read(Buffer:TCPortBytes; Count: Integer): Integer;
                        var
                          AsyncPtr: PCPortAsync;
                          Success: Integer;
                        begin
                          Result := 0;
                        {$ifdef CPORT_CALLTRACE}
                          OutputDebugString('TCustomComPort.Read '+inttostr(Count)+' bytes');
                        {$endif}
                          CportInitAsync(AsyncPtr);
                          try
                        {$ifdef CPORT_CALLTRACE}
                          OutputDebugString('>> Before ReadAsync');
                        {$endif}
                            Success := ReadAsync(Buffer, Count, AsyncPtr); // Сохраняем передаваемое количество байт
                        {$ifdef CPORT_CALLTRACE}
                          OutputDebugString('>> After ReadAsync');
                        {$endif}
                            if GetLastError = ERROR_IO_PENDING then // Требуется ожидание завершения асинхронной операции
                                Result := WaitForAsync(AsyncPtr)
                            else Result := Success;
                        {$ifdef CPORT_CALLTRACE}
                          OutputDebugString('>> After WaitForAsync');
                        {$endif}
                          finally
                            CportDoneAsync(AsyncPtr);
                          end;
                        end;
                  Сообщение отредактировано: macomics -
                    Сложно. Схему работы я понимаю так:
                    ReadAsync вызывает ReadFile с Overlapped, при этом в отличие от традиционного синхронного чтения из файла в буффер, в этот момент ничего не читается, BytesTrans возвращается 0, в Buffer^ ничего не пишется, и в моем случае Success всегда true (даже когда виснет).

                    Далее вызывается WaitForAsync, в ней WaitForSingleObject() возвращает результат как только получен ответ от устройства, GetOverlappedResult собственно считывает данные, ну и далее по тексту.

                    Если я вызову SetEvent где-то раньше WaitForSingleObject() - вызов WaitForSingleObject() вернется сразу, а GetOverlappedResult сразу вернет какую-то ошибку из-за чего появится Exception что я и увидел при прошлом эксперименте. Похожая ситуация если WaitForSingleObject 5 секунд возвращается с ошибкой таймаут.

                    Оно то будет правильно, но блок if not Success then у меня никогда еще не выполнялся и GetLastError у меня всегда ERROR_IO_PENDING (не видел SUCCESS).

                    CancelIO я закинул в Except...end. На удивление, пока не висло (но начинал с того что это процесс спонтанный, вообще счастье что ошибка происходила больше 2 раз за последний час. Очень сложно такие вещи дебажить.

                    Цитата
                    if GetLastError = ERROR_IO_PENDING then // Требуется ожидание завершения асинхронной операции
                    Result := WaitForAsync(AsyncPtr)
                    else Result := Success;

                    вот это сейчас сделаю, это правильно, а то предыдущий вариант if Success <> 0 then все обваливал.
                      Цитата Виталь @
                      ReadAsync вызывает ReadFile с Overlapped, при этом в отличие от традиционного синхронного чтения из файла в буффер, в этот момент ничего не читается, BytesTrans возвращается 0, в Buffer^ ничего не пишется, и в моем случае Success всегда true (даже когда виснет).

                      Далее вызывается WaitForAsync, в ней WaitForSingleObject() возвращает результат как только получен ответ от устройства, GetOverlappedResult собственно считывает данные, ну и далее по тексту.

                      Все так. Только надо еще учесть, что у устройства есть свой внутренний буфер приема. И если в этом буфере уже достаточно байт, то ReadFile может вернуть True, а GetLastError = SUCCESS. Тогда из буфера устройства требуемое количество байт будет скопировано в буфер сразу и асинхронная операция не понадобится. Вот на этом и виснет. Ждет окончания асинхронной операции, которая даже не начиналась и система соответственно не собирается сигнализировать о ее окончании. Поэтому WaitForSingleObject(..., INFINITE) зависает.

                      Но еще в операциях чтения/записи при возникновении ошибки хорошо было бы добавить CancelIo, чтобы не создавать бесконечных ожиданий на прием или передачу у драйвера устройства. Из-за этого и пропадает устройство. Его драйвер занят такой операцией в момент опроса дерева устройств (поэтому его даже не видно).
                        У меня короткие запросы и ответы, я не буду утверждать это со стопроцентной уверенностью, но возможно что пакетов больше 20 байт при управлении этим оборудованием не бывает. Если по какой-то причине в буфере накопятся ответы на несколько запросов - возможно. ОК, я учту Ваше замечание. К сожалению оказалось что компонент TComport далек от совершенства и его надо дорабатывать, хотя никогда раньше с подобными трудностями при работе с ним я не сталкивался.

                        В любом случае Вы сильно помогли. CancelIo в блоке except...end кажется решил проблему. Я вижу несколько Exception в логе по одному порту и одно в логе по второму за остаток вчерашнего вечера, ни одно из них не привело ни к зависанию, ни к постоянным ошибкам при отправке последующих команд и исчезновению порта из системы, как было раньше. Это уже серьезный прогресс. Необходимо будет провести еще несколько экспериментов после исправлений кода и убедиться что ошибка в одной команде не приводит к каким-либо значимым сбоям при работе оборудования.
                          Цитата Виталь @
                          Далее вызывается WaitForAsync, в ней WaitForSingleObject() возвращает результат как только получен ответ от устройства, GetOverlappedResult собственно считывает данные, ну и далее по тексту.

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

                          В ней на WaitForSingleObject с параметром INFINITE (именно бесконечность была в оригинальном коде) все благополучно наглухо зависает

                          Может тайм-ауты не спрограммированы ?
                          Как я подозреваю, смысл использования INFINITE (максимального ожидания) в этой wait - функции в том,
                          чтобы реальный тайм-аут операции ожидания определялся заданными тайм-аутами COMM-порта.
                          (Которые могут меняться от проекта к проекту.)
                          ---
                          Возникающая исключительная ситуация говорит от том, что где-то серьёзная
                          паталогия. При этом компонент TComPort может быть совершенно не виноват.
                          ---
                          Виталь, ты читаешь/пишешь данные из одного потока или из разных ?
                          Сообщение отредактировано: ЫукпШ -
                          0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                          0 пользователей:


                          Рейтинг@Mail.ru
                          [ Script execution time: 0,0745 ]   [ 16 queries used ]   [ Generated: 11.12.24, 03:12 GMT ]