| Вступление |
Порядок запуска и работы "службы" (назовем все описываемое ниже так) Com-портов состоит из нескольких достаточно хорошо описанных шагов ( ):
| Чтение из Com-порта. |
Судя по контексту справки, касающейся функции CreateFile, для "отлова" момента поступления данных в Com-порт следует использовать функцию WaitCommEvent. Предварительно установив маску SetCommMask на то событие, которое хотелось бы отследить. Нужное событие наступает - вызываем функцию ReadFile для чтения поступающих данных.
Казалось бы все в порядке, но... Вызов функции WaitCommEvent насмерть тормозит приложение, пока какие-либо данные не поступят в Com-порт.
Можно конечно, просто взять и запустить в непрерывном цикле ReadFile, однако приложение хотя и будет как-то шевелиться, но это шевеление скорее всего будет напоминать предсмертные судороги.
Как выход из ситуации многие предлагают использовать потоки (thread), забывая при этом описать как это делать :)
Итак потоки.
В модуле Classes для потоков определен специальный класс TThread. Для создания потоков специалисты рекомендуют использовать именно его, а не создавать потоки используя BeginThread и EndThread, т.к. библиотека VCL не является защищенной для потоков в такой реализации. Следуя советам экспертов, для организации контроля поступающих данных в Com-порт и будем использовать готовый класс TThread.
В раздел interface определим тип переменных этого класса, переопределив только один метод класса - Execute, ну и дополнительно объявим свой метод, который и займется опросом Com-порта.
| Type //определим тип TComThread - наследника класса TThread TCommThread = class(TThread) private //процедура, занимающаяся опросом порта Procedure QueryPort; protected //переопределим метод запуска потока Procedure Execute; override; end; |
Далее в разделе глобальных переменных определим поток-переменную полученного выше типа CommThread:TCommThread; //наш поток, в котором будет работать процедура опроса порта Затем в разделе implementation начинаем ваять.
ВНИМАНИЕ!!!
К этому времени порт уже должен быть инициализирован функцией CreateFile.
| Procedure StartComThread; //инициализация нашего потока Begin {StartComThread} //пытаемся инициализировать поток CommThread:=TCommThread.Create(False); // проверяем получилось или нет If CommThread = Nil Then Begin {Nil} //ошибка, все выключаем и выходим SysErrorMessage(GetLastError); fmMain.btnStop.Click; Exit; End; {Nil} End; {StartComThread} |
| Procedure TCommThread.Execute; Begin {Execute} Repeat QueryPort;//процедура опроса порта будет производиться пока поток не будет прекращен Until Terminated; End; {Execute} |
| Procedure TCommThread.QueryPort; Var MyBuff:Array[0..1023] Of Char;//буфер для чтения данных ByteReaded:Integer; //количество считанных байт Str:String; //вспомогательная строка Status:DWord; //статус устройства (модема) Begin {QueryPort} //получим статус COM-порта устройства (модема) If Not GetCommModemStatus(hPort,Status) Then Begin {ошибка при получении статуса модема} //ошибка, все выключаем и выходим SysErrorMessage(GetLastError); fmMain.btnStop.Click; Exit; End; {ошибка при получении статуса модема} //Обработаем статус устройства (модема) и будем включать(выключать) лампочки //готовность устройства (модема) получать данные fmMain.imgCTSOn.Visible:=((Status AND MS_CTS_ON)=MS_CTS_ON); //готовность устройства (модема) к сеансу связи fmMain.imgDSROn.Visible:=((Status AND MS_DSR_ON)=MS_DSR_ON); //принимаются данные с линии сигнала fmMain.imgRLSDOn.Visible:=((Status AND MS_RLSD_ON)=MS_RLSD_ON); //входящий звонок fmMain.imgRingOn.Visible:=((Status AND MS_RING_ON)=MS_RING_ON); //читаем буфер из Com-порта FillChar(MyBuff,SizeOf(MyBuff),#0); If Not ReadFile(hPort,MyBuff,SizeOf(MyBuff),ByteReaded,Nil) Then Begin {ошибка при чтении данных} //ошибка, все закрываем и уходим SysErrorMessage(GetLastError); fmMain.btnStop.Click; Exit; End; {ошибка при чтении данных} //данные пришли If ByteReaded>0 Then Begin {ByteReaded>0} //посчитаем общее количество прочитанных байтов ReciveBytes:=ReciveBytes+ByteReaded; //преобразуем массив в строку Str:=String(MyBuff); //отправим строку на просмотр fmMain.Memo1.Text:=fmMain.Memo1.Text+ Str; //покажем количество считанных байтов fmMain.lbRecv.Caption:='recv: '+IntToStr(ReciveBytes)+' bytes...'; End; {ByteReaded>0} End; {QueryPort} |
| Прилагающийся пример |