Respond to ReadEvent on Serial Port

Please discuss general Delphi programming topics here.

Respond to ReadEvent on Serial Port

Postby ratep2001 » June 11th, 2003, 9:02 am

Hi,
I have found many source codes all over the network about how to access the serial port in Delphi. Everybody uses handles with CreateFile (COMx,......) etc, then uses ReadFile, WriteFile. When it comes to receiving characters, almost everybody uses SetCommMask to set for EV_RXCHAR event, then uses WaitCommEvent to respond to this particular event and read the buffer.
But does it mean that I must use some kind of a loop and wait for the event, thus risking to stay in a loop forever and don't do anything else useful? How can I solve this problem more elegantly, like having an event handler for my COMx handle to respond to? I know that one can write an EventHandle for an object, but an event handler for a virtual file handler referring to COMx ??
I would like to stress that I don't want to use some third party components. This solution with having a handle to COMx and making different system calls like GetCommState, WaitCommEvent etc. seems really nice, but how to respond to the incoming characters immediately and empty the input buffer ??? And still not being in a loop and waiting for event, which might not happen at all or occur once in several minutes or so.
Thank you in advance.
ratep2001
Member
Member
 
Posts: 1
Joined: June 11th, 2003, 8:59 am

Postby Kambiz » June 14th, 2003, 2:53 pm

You have to add the FILE_FLAG_OVERLAPPED flag to the flags of CreateFile procedure. Then, to read (or write) data you have to defined a TOverlapped record and assign an event handle to its hEvent member. The other members of this record should be zero for communication devices.

The following function returns a handle to the specified COM number and initializes the Overlapped record:

Code: Select all
function OpenComPort(ComNum: Integer; var Overlapped: TOverlapped): Integer;
var
  ComName: String;
begin
  ComName := '\\.\COM' + IntToStr(ComNum);
  Result := CreateFile(PChar(ComName), GENERIC_READ or GENERIC_WRITE, 0,
    nil, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING or FILE_FLAG_OVERLAPPED, 0);
  if Result <> HFILE_ERROR then
  begin
    FillChar(Overlapped, SizeOf(TOverlapped), 0);
    Overlapped.hEvent := CreateEvent(nil, True, False, nil);
  end;
end;

to read the data from the port, you have to call the ReadFile as follow:

Code: Select all
ReadFile(ComHandle, YourBuffer, YourNumberOfBytesToRead, Dummy, Overlapped);

In the above call, the ComHandle and Overlapped arguments are supposed to be initialized by a previous call to OpenComPort function. The Dummy argument is the number of read bytes, which actually is meaningless in an overlapped operation and can be nil. However, because of a mistake in translation of Widnwos header files to Delphi, it is defained as var instead of pointer. The meaning of YourBuffer and YourNumberOfBytesToRead is more clear than others. But just keep in mind that the YourBuffer argument should be available in the scope of the overlapped operation.

Well, now you should wait for the Overlapped.hEvent event to be signalled. You can wait or check this event whatever you want, however it is better to do it in a separate thread. The following sample thread can be used as a pattern for an overlapped read operation:

Code: Select all
procedure TReadThread.Execute;
var
  NumberOfBytesRead: DWORD;
begin
  while not Terminated then
  begin
    // Waits for read completion
    WaitForSingleObject(Overlapped.hEvent, INFINITE);
    if not Terminated then
    begin
      // Resets the event for the next read
      ResetEvent(Overlapped.hEvent);
      // Get the number of bytes read
      GetOverlappedResult(ComHandle, Overlapped, NumberOfBytesRead, False);
      // Data in the read buffer is ready now
      // For example you can post a message to the main form to notify it
      // about the new data in the buffer.
    end;
  end;
end;

As you can see in the above code, we should reset the Overlapped.hEvent before any overlapped operation (read or write).

When the COM handle is no longer needed, as well as closing the port we should release the Overlapped.hEvent . For example the finalization code for the above thread can be as follow:

Code: Select all
ReadThread.Terminate;
SetEvent(Overlapped.hEvent);
ReadThread.WaitFor;
CloseHandle(Overlapped.hEvent);
CloseHandle(ComHandle);

Depends to your case, you may want two initialize and use two overlapped records, one for the read and the other for the write operation to have these operations simultaneously.

By the way, I have never used overlapped operations, so the code I've written here may have some bugs, as usual. ;)

Hope that it helps.

Greetings,
Kambiz
User avatar
Kambiz
Administrator
Administrator
 
Posts: 2408
Joined: March 7th, 2003, 7:10 pm

Use com port

Postby pcastrillon » November 29th, 2007, 12:08 pm

use Synaser

it is a delphi non-visua component. Easy to use to Delphi and Kylix (Win32, Linux)

Download it from http://synapse.ararat.cz/

Here sample code:

Code: Select all
FormCreate......

        ser[1]:=TBlockSerial.Create;
        ser[1].RaiseExcept:=FALSE;
   ser[1].Connect('COM1',9600,8,'N',0,false,false);


procedure Send(ser: TBlockSerial; Trama:string);
begin
   ser.RaiseExcept:=True;
   ser.DTR := False;
   ser.EnableSoftRTSToggle(true);
   
   if ser.CanWrite(100) = true then
   begin
      ser.SendString(caracter);
   
           memo1.Lines.Add(FormatDateTime('hh:mm:ss', Time) + ': (send) ' + Trama);
   
           ser.DTR := True;
           ser.EnableSoftRTSToggle(false);
   end;
end;


//put into timer to 200 milliseconds
Function ReadCOM: string;
begin
      if ser[i].CanRead(50) = true then
      begin
               ReadCOM := ser[i].RecvPacket(5); //5 ok
      end;
end


Pablo Castrillon
Pablo Castrillon
pcastrillon
Member
Member
 
Posts: 2
Joined: November 29th, 2007, 11:35 am


Return to Delphi Programming

Who is online

Users browsing this forum: No registered users and 1 guest