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