Hi,
I hope someone can help me out, since I've spent a lot of time already
solving the problem.
I am using a TComPort component that uses a TComThread instance for
monitoring the comport status and acting according to this status. This
TComThread is created in the constructor of the TComPort object.
The problem occurs when I try to stop the thread and right after that
closing the handle to the com port. Sometimes I get a blue screen with a
exception 0E message.
This is what happens:
1 Open the com port with TComPort.Open. In this procedure the thread is
created. The constructor of the thread sets the StopEvent to non-signalled.
With Resume the thread starts running. StopEvent is used to stop the thread.
2 When the TComPort object is destroyed, TComPort.Close is called in the
destructor of TComPort. This procedure frees the TComThread object by
calling free. Next, in the destructor of TComThread, Stop is called. Stop
sets the StopEvent to signalled.
3 Because StopEvent is signalled, I assume that Execute is terminated,
because StopEvent is the event on which the repeat-until loop is ended.
(However, I've got the impression that it doesn't).
4 Then, getting back to the Close procedure of TComPort, DestroyHandle is
called in which the handle to the comport is Closed. This is exactly the
point where I get the blue screen.
If I manually free the thread and then after some time close the com port,
DestroyHandle doesn't give a blue screen.
What could be wrong here? I think it has something to do with the Execute
procedure which is still running when the handle to the comport is close. Is
this possible ?
Thanks a lot for spending time on this problem!
Roy
*********************
* Thread Class
*********************
TComThread = class(TThread)
private
Owner: TComPort;
Mask: DWORD;
StopEvent: THandle;
protected
procedure Execute; override;
procedure DoEvents;
procedure Stop;
public
constructor Create(AOwner: TComPort);
destructor Destroy; override;
end;
constructor TComThread.Create(AOwner: TComPort);
var
AMask: Integer;
begin
inherited Create(True);
StopEvent := CreateEvent(nil, True, False, nil);
Owner := AOwner;
AMask := 0;
if evRxChar in Owner.FEvents then
AMask := AMask or EV_RXCHAR;
if evRxFlag in Owner.FEvents then
AMask := AMask or EV_RXFLAG;
SetCommMask(Owner.ComHandle, AMask);
Resume;
end;
procedure TComThread.Execute;
var
EventHandles: Array[0..1] of THandle;
Overlapped: TOverlapped;
dwSignaled, BytesTrans: DWORD;
begin
FillChar(Overlapped, SizeOf(Overlapped), 0);
Overlapped.hEvent := CreateEvent(nil, True, True, nil);
EventHandles[0] := StopEvent;
EventHandles[1] := Overlapped.hEvent;
repeat
WaitCommEvent(Owner.ComHandle, Mask, @Overlapped);
dwSignaled := WaitForMultipleObjects(2, @EventHandles, False, INFINITE);
case dwSignaled of
WAIT_OBJECT_0:
Break;
WAIT_OBJECT_0 + 1:
if GetOverlappedResult(Owner.ComHandle, Overlapped,
BytesTrans, False) then Synchronize(DoEvents);
else
Break;
end;
until False;
Owner.PurgeIn;
Owner.PurgeOut;
CloseHandle(Overlapped.hEvent);
CloseHandle(StopEvent);
end;
procedure TComThread.Stop;
begin
SetEvent(StopEvent);
end;
destructor TComThread.Destroy;
begin
Stop;
inherited Destroy;
end;
*********************
* TComPort Class
*********************
constructor TComPort.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
.... Default property settings
ComHandle := INVALID_HANDLE_VALUE;
end;
destructor TComPort.Destroy;
begin
Close;
inherited Destroy;
end;
procedure TComPort.CreateHandle;
begin
ComHandle := CreateFile(
PChar(ComString),
GENERIC_READ or GENERIC_WRITE,
0,
nil,
OPEN_EXISTING,
FILE_FLAG_OVERLAPPED,
0);
if not ValidHandle then
raise EComHandle.Create('TComPort.CreateHandle: Unable to open com
port');
end;
procedure TComPort.DestroyHandle;
begin
if ValidHandle then
CloseHandle(ComHandle);
end;
function TComPort.ValidHandle: Boolean;
begin
if ComHandle = INVALID_HANDLE_VALUE then
Result := False
else
Result := True;
end;
procedure TComPort.Open;
begin
Close;
CreateHandle;
SetupState;
EventThread := TComThread.Create(Self);
FConnected := True;
if Assigned(FOnOpen) then
FOnOpen(Self);
end;
procedure TComPort.Close;
begin
if FConnected then
begin
EventThread.Free;
EventThread := nil
DestroyHandle;
FConnected := False;
if Assigned(FOnClose) then
FOnClose(Self);
end;
end;