Странное поведение функции ClearCommError

Некоторое время назад при работе над одной программой я столкнулся с необычным поведением функции ClearCommError. Задача была довольно простой: есть прибор, от которого по COM порту принимаются данные. Вроде бы ничего сложного, но меня ждал подвох.

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

COMSTAT comstat;
unsigned long temp;
//Определяем размер поступивших данных
ClearCommError(hCOM, &temp, &comstat);
if(comstat.cbInQue > size_buffer)
{
   //Если нужно увеличиваем размер буфера под данные
   pbuffer = realloc(pbuffer, comstat.cbInQue); 
   size_buffer = comstat.cbInQue;
}
//Читаем данные
DWORD fact_size = 0;
ReadFile(hCOM, pbuffer, comstat.cbInQue, &fact_size, &overlapped);
//Обрабатываем данные
PerformData(fact_size);

Какого же было мое удивление, когда я обнаружил, что он не работает. Точнее работает, но крайне нестабильно. Почему? во время отладки выяснилось, что проблема в функции ClearCommError, которая неправильно заполняла структуру COMSTAT. Точнее она неправильно определяла размер данных, поступивших в порт. Прибор, от которого я принимал данные, работал с известной частотой, размер данных отправляемых им за один раз также известен. Поэтому оценить сколько должно приходить — не проблема. Функция ClearCommError на разных итерациях определяла размер принятых байт то в 4096, то в 2, то в 3 байта. Неслабый разброс. Может я делаю что-то не так?

Согласно документации функция ClearCommError предназначена для сброса ошибки COM порта. Может быть для определения размера полученных данных нужно использовать какую-то другую функцию? Я полез в Интернет. Но ничего не нашел. Во всех найденных мною примерах размер полученных данных определялся именно этой функцией. Других функций нет. Так в чем дело?

Поиск в Интернете все-таки дал свои плоды. Оказывается я не первый кто столкнулся с такой проблемой. ее обсуждение можно найти на форуме vingrad (ссылка). К сожалению, красивого решения найти не удалось. Поэтому пришлось использовать функцию ClearCommError в цикле. Пример кода:

COMSTAT comstat;
unsigned long temp;
//Определяем размер поступивших данных
ClearCommError(hCOM, &temp, &comstat);
while(comstat.cbInQue)
{
   //Определяем размер чтобы уместиться в буфер
   unsigned long size;
   size = (comstat.cbInQue < size_buffer) ? comstat.cbInQue : size_buffer;
   //Читаем данные
   unsigned long fact_size = 0;   
   ReadFile(hCOM, pbuffer, size, &fact_size, &overlapped);
   //Обрабатываем данные 
   PerformData(fact_size);
   //Проверяем все ли мы прочитали из COM порта
   ClearCommError(hCOM, &temp, &comstat);
}

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *