This commit is contained in:
f1iwq2
2024-08-24 09:12:51 +02:00
parent ede3d91c4d
commit 7d2c4bd591
43 changed files with 32873 additions and 14550 deletions

563
AwWin32.pas Normal file
View File

@@ -0,0 +1,563 @@
(***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is TurboPower Async Professional
*
* The Initial Developer of the Original Code is
* TurboPower Software
*
* Portions created by the Initial Developer are Copyright (C) 1991-2002
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Sulaiman Mah
* Sean B. Durkin
* Sebastian Zierer
*
* ***** END LICENSE BLOCK ***** *)
{*********************************************************}
{* AWWIN32.PAS 5.00 *}
{*********************************************************}
{* Win32 serial device layer and dispatcher *}
{*********************************************************}
{
Along with AwUser.pas, this unit defines/implements the dreaded Windows
serial port dispatcher. This unit provides the interface to the Win32
serial port drivers, the threading code is in AwUser.pas.
Be extrememly cautious when making changes here or in AwUser. The multi-
threaded nature, and very strict timing requirements, can lead to very
unpredictable results. Things as simple as adding doing a writeln to a
console window can dramatically change the results.
}
{Global defines potentially affecting this unit}
{$I AWDEFINE.INC}
{Options required for this unit}
{$X+,F+,K+,B-}
unit AwWin32;
{-Device layer for standard Win32 communications API}
interface
uses
Windows,
Classes,
SysUtils,
AdWUtil,
AdSocket,
OoMisc,
awUser;
type
TApdWin32Dispatcher = class(TApdBaseDispatcher)
protected
ReadOL : TOverLapped;
WriteOL : TOverLapped;
function EscapeComFunction(Func : Integer) : Integer; override;
function FlushCom(Queue : Integer) : Integer; override;
function GetComError(var Stat : TComStat) : Integer; override;
function GetComEventMask(EvtMask : Integer) : Cardinal; override;
function GetComState(var DCB: TDCB): Integer; override;
function SetComState(var DCB : TDCB) : Integer; override;
function ReadCom(Buf : PAnsiChar; Size: Integer) : Integer; override;
function WriteCom(Buf : PAnsiChar; Size: Integer) : Integer; override;
function SetupCom(InSize, OutSize : Integer) : Boolean; override;
procedure StartDispatcher; override;
procedure StopDispatcher; override;
function WaitComEvent(var EvtMask : DWORD;
lpOverlapped : POverlapped) : Boolean; override;
function OutBufUsed: Cardinal; override; // SWB
public
function CloseCom : Integer; override;
function OpenCom(ComName: PChar; InQueue,
OutQueue : Cardinal) : Integer; override;
function ProcessCommunications : Integer; override;
function CheckPort(ComName: PChar): Boolean; override;
end;
TApdTAPI32Dispatcher = class(TApdWin32Dispatcher)
public
constructor Create(Owner : TObject; InCid : Integer);
function OpenCom(ComName: PChar; InQueue,
OutQueue : Cardinal) : Integer; override;
end;
implementation
uses
StrUtils;
function TApdWin32Dispatcher.CheckPort(ComName: PChar): Boolean; //SZ
// Returns true if a port exists
var
Tmp: string;
CC: PCommConfig;
Len: Cardinal;
begin
Tmp := ComName;
if AnsiStartsText('\\.\', Tmp) then
Delete(Tmp, 1, 4);
New(CC);
try
FillChar(CC^, SizeOf(CC^), 0);
CC^.dwSize := SizeOf(CC^);
Len := SizeOf(CC^);
Result := GetDefaultCommConfig(PChar(Tmp), CC^, Len);
finally
Dispose(CC);
end;
if (not Result) and (GetLastError = ERROR_INSUFFICIENT_BUFFER) then
begin
GetMem(CC, Len);
try
FillChar(CC^, SizeOf(CC^), 0);
CC^.dwSize := SizeOf(CC^);
Result := GetDefaultCommConfig(PChar(Tmp), CC^, Len);
finally
FreeMem(CC);
end;
end;
end;
function TApdWin32Dispatcher.CloseCom : Integer;
{-Close the comport and cleanup}
begin
// Under certain circumstances, it is possible that CloseCom can be called
// recursively. In that event, we don't want to be re-executing this code.
// So, set a flag to show that we are inside this method and check it
// every time we enter. If it is already set, just exit and do nothing.
// This used to be accomplished by acquiring the DataSection critical section
// but this lead to occasional deadlocks.
EnterCriticalSection(DataSection); // SWB
if (CloseComActive) then // SWB
begin // SWB
LeaveCriticalSection(DataSection); // SWB
Result := 0; // SWB
Exit; // SWB
end; // SWB
CloseComActive := True; // SWB
LeaveCriticalSection(DataSection); // SWB
try // SWB
{Release the events}
if ReadOL.hEvent <> 0 then begin
CloseHandle(ReadOL.hEvent);
ReadOL.hEvent := 0;
end;
if WriteOL.hEvent <> 0 then begin
CloseHandle(WriteOL.hEvent);
WriteOL.hEvent := 0;
end;
if DispActive then begin
KillThreads := True;
{Force the comm thread to wake...}
SetCommMask(CidEx, 0);
SetEvent(ReadyEvent);
ResetEvent(GeneralEvent);
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(ComKill));
{$ENDIF}
end;
{Close the comport}
if CloseHandle(CidEx) then begin
Result := 0;
CidEx := -1;
end else
Result := -1;
finally // SWB
CloseComActive := False; // SWB
end; // SWB
end;
function TApdWin32Dispatcher.EscapeComFunction(Func: Integer): Integer;
{-Perform the extended comm function Func}
begin
EscapeCommFunction(CidEx, Func);
Result := 0;
end;
function TApdWin32Dispatcher.FlushCom(Queue: Integer): Integer;
{-Flush the input or output buffer}
begin
if (Queue = 0) and (OutThread <> nil) then begin
{Flush our own output buffer...}
SetEvent(OutFlushEvent);
{ this can cause a hang when using an IR port that does not have a }
{ connection (the IR receiver is not in range), the port drivers }
{ will not flush the buffers, so we'd wait forever }
WaitForSingleObject(GeneralEvent, 5000);{INFINITE);} {!!.02}
{...XMit thread has acknowledged our request, so flush it}
EnterCriticalSection(OutputSection);
try
OBufFull := False;
OBufHead := 0;
OBufTail := 0;
Result := Integer(PurgeComm(CidEx,
PURGE_TXABORT or PURGE_TXCLEAR));
finally
LeaveCriticalSection(OutputSection);
end;
end else
Result := Integer(PurgeComm(CidEx, PURGE_RXABORT or PURGE_RXCLEAR));
if Result = 1 then
Result := 0
else
Result := -Integer(GetLastError);
end;
function TApdWin32Dispatcher.GetComError(var Stat: TComStat): Integer;
{-Get the current error and update Stat}
var
Errors : DWORD;
begin
if ClearCommError(CidEx, Errors, @Stat) then
Result := Errors
else
Result := 0;
{Replace information about Windows output buffer with our own}
Stat.cbOutQue := OutBufUsed; // SWB
end;
function TApdWin32Dispatcher.GetComEventMask(EvtMask: Integer): Cardinal;
{-Set the communications event mask}
begin
Result := 0;
end;
function TApdWin32Dispatcher.GetComState(var DCB: TDCB): Integer;
{-Fill in DCB with the current communications state}
begin
if Integer(GetCommState(CidEx, DCB)) = 1 then
Result := 0
else
Result := -1;
end;
function TApdWin32Dispatcher.OpenCom(ComName: PChar; InQueue, OutQueue: Cardinal): Integer;
{-Open the comport specified by ComName}
begin
{Open the device}
Result := CreateFile(ComName, {name}
GENERIC_READ or GENERIC_WRITE, {access attributes}
0, {no sharing}
nil, {no security}
OPEN_EXISTING, {creation action}
FILE_ATTRIBUTE_NORMAL or
FILE_FLAG_OVERLAPPED, {attributes}
0); {no template}
if Result <> Integer(INVALID_HANDLE_VALUE) then begin
CidEx := Result;
{Create port data structure}
ReadOL.hEvent := CreateEvent(nil, True, False, nil);
WriteOL.hEvent := CreateEvent(nil, True, False, nil);
if (ReadOL.hEvent = 0) or (WriteOL.hEvent = 0) then begin
{Failed to create events, get rid of everything}
CloseHandle(ReadOL.hEvent);
CloseHandle(WriteOL.hEvent);
CloseHandle(Result);
Result := ecOutOfMemory;
Exit;
end;
end else
{Failed to open port, just return error signal, caller will
call GetLastError to get actual error code}
Result := -1;
end;
function TApdWin32Dispatcher.ReadCom(Buf: PAnsiChar; Size: Integer): Integer;
{-Read Size bytes from the comport specified by Cid}
var
OK : Bool;
Temp : DWORD;
begin
{Post a read request...}
OK := ReadFile(CidEx, {handle}
Buf^, {buffer}
Size, {bytes to read}
Temp, {bytes read}
@ReadOL); {overlap record}
{...and see what happened}
if not OK then begin
if GetLastError = ERROR_IO_PENDING then begin
{Waiting for data}
if GetOverLappedResult(CidEx, {handle}
ReadOL, {overlapped structure}
Temp, {bytes written}
True) then begin {wait for completion}
{Read complete, reset event}
ResetEvent(ReadOL.hEvent);
end;
end;
end;
Result := Integer(Temp);
end;
function TApdWin32Dispatcher.SetComState(var DCB: TDCB): Integer;
{-Set the a new communications device state from DCB}
begin
if SetCommState(CidEx, DCB) then
Result := 0
else
Result := -Integer(GetLastError);
end;
function TApdWin32Dispatcher.WriteCom(Buf: PAnsiChar; Size: Integer): Integer;
{-Write data to the comport}
type
PBArray = ^TBArray;
TBArray = array[0..pred(High(Integer))] of Byte;
var
SizeAtEnd : Integer;
LeftOver : Integer;
begin
{Add the data to the output queue}
EnterCriticalSection(OutputSection);
try
{we already know at this point that there is enough room for the block}
SizeAtEnd := OutQue - OBufHead;
if SizeAtEnd >= Size then begin
{can move data to output queue in one block}
Move(Buf^, OBuffer^[OBufHead], Size);
if SizeAtEnd = Size then
OBufHead := 0
else
Inc(OBufHead, Size);
end else begin
{ need to use two moves }
Move(Buf^, OBuffer^[OBufHead], SizeAtEnd);
LeftOver := Size - SizeAtEnd;
Move(PBArray(Buf)^[SizeAtEnd], OBuffer^, LeftOver);
OBufHead := LeftOver;
end;
finally
LeaveCriticalSection(OutputSection);
end;
{...finally, wake up the output thread to send the data}
SetEvent(OutputEvent);
Result := Size; {report all was sent}
end;
function TApdWin32Dispatcher.SetupCom(InSize, OutSize : Integer) : Boolean;
{-Set new in/out buffer sizes}
begin
Result := SetupComm(CidEx, InSize, OutSize);
end;
function TApdWin32Dispatcher.WaitComEvent(var EvtMask : DWORD;
lpOverlapped : POverlapped) : Boolean;
begin
Result := WaitCommEvent(CidEx, EvtMask, lpOverlapped);
end;
procedure TApdWin32Dispatcher.StartDispatcher;
begin
EnterCriticalSection(DataSection);
try
{See if we're already active}
if DispActive then
raise Exception.Create('Dispatcher already started');
DispActive := True;
{Create the com events thread}
KillThreads := False;
ComThread := TComThread.Create(Self);
{Wait for it to start...}
WaitForSingleObject(GeneralEvent, ThreadStartWait);
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(ComStart));
{$ENDIF}
{Create the dispatcher thread}
fDispThread := TDispThread.Create(Self);
{Wait for it to start...}
WaitForSingleObject(GeneralEvent, ThreadStartWait);
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(DispStart));
{$ENDIF}
{Create the output thread}
OutThread := TOutThread.Create(Self);
{Wait for it to start...}
WaitForSingleObject(GeneralEvent, ThreadStartWait);
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(OutStart));
{$ENDIF}
finally
LeaveCriticalSection(DataSection);
end;
end;
procedure TApdWin32Dispatcher.StopDispatcher;
var
ET : EventTimer;
begin
if not DispActive then
Exit;
{ Set the flag to kill the threads next time they wake, or after }
{ their current processing }
KillThreads := True;
if Assigned(OutThread) then begin
{Force the output thread to wake...}
SetEvent(OutFlushEvent);
{...and wait for it to die}
while (OutThread <> nil) do
SafeYield;
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(OutKill));
{$ENDIF}
end;
if Assigned(ComThread) then begin
{Force the comm thread to wake...}
SetCommMask(CidEx, 0);
SetEvent(ReadyEvent);
{... and wait for it to die}
ResetEvent(GeneralEvent);
while (ComThread <> nil) do
SafeYield;
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(ComKill));
{$ENDIF}
end;
{Now kill the timer}
KillTimer(0, TimerID);
if Assigned(DispThread) then begin
KillThreads := True;
{Wait for it to die}
NewTimer(ET, 36); { start a 2-second timer to prevent blocks }
while (DispThread <> nil) and not(TimerExpired(ET)) do
SafeYield;
if DispThread <> nil then begin
{$IFDEF DebugThreadConsole}
WriteLn('DispThread<>nil');
{$ENDIF}
{ thread didn't die, reset the event }
SetEvent(ComEvent);
{Wait for it to die yet again}
NewTimer(ET, 36); { start a 2-second timer to prevent blocks }
while (DispThread <> nil) and not(TimerExpired(ET)) do
SafeYield;
if DispThread <> nil then
{ disp thread is not responding, brutally terminate it }
DispThread.Free;
end;
{$IFDEF DebugThreadConsole}
Writeln(ThreadStatus(DispKill));
{$ENDIF}
end;
if ComEvent <> INVALID_HANDLE_VALUE then begin
if CloseHandle(ComEvent) then
ComEvent := INVALID_HANDLE_VALUE;
end;
if ReadyEvent <> INVALID_HANDLE_VALUE then begin
if CloseHandle(ReadyEvent) then
ReadyEvent := INVALID_HANDLE_VALUE;
end;
if GeneralEvent <> INVALID_HANDLE_VALUE then begin
if CloseHandle(GeneralEvent) then
GeneralEvent := INVALID_HANDLE_VALUE;
end;
if OutputEvent <> INVALID_HANDLE_VALUE then begin
if CloseHandle(OutputEvent) then
OutputEvent := INVALID_HANDLE_VALUE;
end;
if SentEvent <> INVALID_HANDLE_VALUE then begin
if CloseHandle(SentEvent) then
SentEvent := INVALID_HANDLE_VALUE;
end;
if OutFlushEvent <> INVALID_HANDLE_VALUE then begin
if CloseHandle(OutFlushEvent) then
OutFlushEvent := INVALID_HANDLE_VALUE;
end;
end;
function TApdWin32Dispatcher.ProcessCommunications : Integer;
{-Communications are running in separate threads -- give them a chance}
begin
Sleep(0);
Result := 0;
end;
constructor TApdTAPI32Dispatcher.Create(Owner : TObject; InCid : Integer);
begin
CidEx := InCid;
inherited Create(Owner);
end;
function TApdTAPI32Dispatcher.OpenCom(ComName: PChar; InQueue, OutQueue : Cardinal) : Integer;
begin
ReadOL.hEvent := CreateEvent(nil, True, False, nil);
WriteOL.hEvent := CreateEvent(nil, True, False, nil);
if (ReadOL.hEvent = 0) or (WriteOL.hEvent = 0) then begin
CloseCom;
Result := -1;
Exit;
end;
if CidEx <> 0 then
Result := CidEx
else begin
Result := ecCommNotOpen;
SetLastError(-Result);
end;
end;
// Added by SWB
function TApdWin32Dispatcher.OutBufUsed: Cardinal;
begin
EnterCriticalSection(OutputSection);
try
Result := 0;
if OBufFull then
Result := OutQue
else if OBufHead > OBufTail then
{Buffer is not wrapped}
Result := OBufHead - OBufTail
else if OBufHead < OBufTail then
{Buffer is wrapped}
Result := OBufHead + (OutQue - OBufTail);
finally
LeaveCriticalSection(OutputSection);
end;
end;
end.