📄 unidevice_builtin.pas
字号:
if OutCTSFlow then
DCB.Flags := DCB.Flags or dcb_OutxCTSFlow;
if OutDSRFlow then
DCB.Flags := DCB.Flags or dcb_OutxDSRFlow;
DCB.Flags := DCB.Flags or CControlDTR[ControlDTR]
or CControlRTS[ControlRTS];
if XonXoffOut then
DCB.Flags := DCB.Flags or dcb_OutX;
if XonXoffIn then
DCB.Flags := DCB.Flags or dcb_InX;
if DSRSensitivity then
DCB.Flags := DCB.Flags or dcb_DSRSensivity;
if TxContinueOnXoff then
DCB.Flags := DCB.Flags or dcb_TxContinueOnXoff;
end;
DCB.Parity := CParityBits[FParity.Bits];
DCB.StopBits := CStopBits[FStopBits];
if FBaudRate <> brCustom then
DCB.BaudRate := CBaudRate[FBaudRate]
else
DCB.BaudRate := FCustomBaudRate;
DCB.ByteSize := CDataBits[FDataBits];
if FParity.Check then
begin
DCB.Flags := DCB.Flags or dcb_Parity;
if FParity.Replace then
begin
DCB.Flags := DCB.Flags or dcb_ErrorChar;
DCB.ErrorChar := Char(FParity.ReplaceChar);
end;
end;
// apply settings
if not SetCommState(FHandle, DCB) then
raise EComPort.Create(CError_SetStateFailed, GetLastError);
end;
end;
// apply timeout properties
procedure TCustomSerialPort.ApplyTimeouts;
var
Timeouts: TCommTimeouts;
function GetTOValue(const Value: Integer): DWORD;
begin
if Value = -1 then
Result := MAXDWORD
else
Result := Value;
end;
begin
// if not connected or inside BeginUpdate/EndUpdate block, do nothing
if (FConnected) and (FUpdateCount = 0) and
(not ((csDesigning in ComponentState) or (csLoading in ComponentState))) then
begin
Timeouts.ReadIntervalTimeout := GetTOValue(FTimeouts.ReadInterval);
Timeouts.ReadTotalTimeoutMultiplier := GetTOValue(FTimeouts.ReadTotalMultiplier);
Timeouts.ReadTotalTimeoutConstant := GetTOValue(FTimeouts.ReadTotalConstant);
Timeouts.WriteTotalTimeoutMultiplier := GetTOValue(FTimeouts.WriteTotalMultiplier);
Timeouts.WriteTotalTimeoutConstant := GetTOValue(FTimeouts.WriteTotalConstant);
// apply settings
if not SetCommTimeouts(FHandle, Timeouts) then
raise EComPort.Create(CError_TimeoutsFailed, GetLastError);
end;
end;
// apply buffers
procedure TCustomSerialPort.ApplyBuffer;
begin
// if not connected or inside BeginUpdate/EndUpdate block, do nothing
if (FConnected) and (FUpdateCount = 0) and
(not ((csDesigning in ComponentState) or (csLoading in ComponentState))) then
//apply settings
if not SetupComm(FHandle, FBuffer.InputSize, FBuffer.OutputSize) then
raise EComPort.Create(CError_SetupComFailed, GetLastError);
end;
// initialize port
procedure TCustomSerialPort.SetupComPort;
begin
ApplyBuffer;
ApplyDCB;
ApplyTimeouts;
end;
// get number of bytes in input buffer
function TCustomSerialPort.InputCount: Integer;
var
Errors: DWORD;
ComStat: TComStat;
begin
if not ClearCommError(FHandle, Errors, @ComStat) then
raise EComPort.Create(CError_ClearComFailed, GetLastError);
Result := ComStat.cbInQue;
end;
// get number of bytes in output buffer
function TCustomSerialPort.OutputCount: Integer;
var
Errors: DWORD;
ComStat: TComStat;
begin
if not ClearCommError(FHandle, Errors, @ComStat) then
raise EComPort.Create(CError_ClearComFailed, GetLastError);
Result := ComStat.cbOutQue;
end;
// get signals which are in high state
function TCustomSerialPort.Signals: TComSignals;
var
Status: DWORD;
begin
if not GetCommModemStatus(FHandle, Status) then
raise EComPort.Create(CError_ModemStatFailed, GetLastError);
Result := [];
if (MS_CTS_ON and Status) <> 0 then
Result := Result + [csCTS];
if (MS_DSR_ON and Status) <> 0 then
Result := Result + [csDSR];
if (MS_RING_ON and Status) <> 0 then
Result := Result + [csRing];
if (MS_RLSD_ON and Status) <> 0 then
Result := Result + [csRLSD];
end;
// get port state flags
function TCustomSerialPort.StateFlags: TComStateFlags;
var
Errors: DWORD;
ComStat: TComStat;
begin
if not ClearCommError(FHandle, Errors, @ComStat) then
raise EComPort.Create(CError_ClearComFailed, GetLastError);
Result := ComStat.Flags;
end;
// set hardware line break
procedure TCustomSerialPort.SetBreak(OnOff: Boolean);
var
Act: Integer;
begin
if OnOff then
Act := Windows.SETBREAK
else
Act := Windows.CLRBREAK;
if not EscapeCommFunction(FHandle, Act) then
raise EComPort.Create(CError_EscapeComFailed, GetLastError);
end;
// set DTR signal
procedure TCustomSerialPort.SetDTR(OnOff: Boolean);
var
Act: DWORD;
begin
if OnOff then
Act := Windows.SETDTR
else
Act := Windows.CLRDTR;
if not EscapeCommFunction(FHandle, Act) then
raise EComPort.Create(CError_EscapeComFailed, GetLastError);
end;
// set RTS signals
procedure TCustomSerialPort.SetRTS(OnOff: Boolean);
var
Act: DWORD;
begin
if OnOff then
Act := Windows.SETRTS
else
Act := Windows.CLRRTS;
if not EscapeCommFunction(FHandle, Act) then
raise EComPort.Create(CError_EscapeComFailed, GetLastError);
end;
// set XonXoff state
procedure TCustomSerialPort.SetXonXoff(OnOff: Boolean);
var
Act: DWORD;
begin
if OnOff then
Act := Windows.SETXON
else
Act := Windows.SETXOFF;
if not EscapeCommFunction(FHandle, Act) then
raise EComPort.Create(CError_EscapeComFailed, GetLastError);
end;
// clear input and/or output buffer
procedure TCustomSerialPort.ClearBuffer(Input, Output: Boolean);
var
Flag: DWORD;
begin
Flag := 0;
if Input then
Flag := PURGE_RXCLEAR;
if Output then
Flag := Flag or PURGE_TXCLEAR;
if not PurgeComm(FHandle, Flag) then
raise EComPort.Create(CError_PurgeFailed, GetLastError);
end;
// return last errors on port
function TCustomSerialPort.LastErrors: TComErrors;
var
Errors: DWORD;
ComStat: TComStat;
begin
if not ClearCommError(FHandle, Errors, @ComStat) then
raise EComPort.Create(CError_ClearComFailed, GetLastError);
Result := [];
if (CE_FRAME and Errors) <> 0 then
Result := Result + [ceFrame];
if ((CE_RXPARITY and Errors) <> 0) and FParity.Check then // get around a bug
Result := Result + [ceRxParity];
if (CE_OVERRUN and Errors) <> 0 then
Result := Result + [ceOverrun];
if (CE_RXOVER and Errors) <> 0 then
Result := Result + [ceRxOver];
if (CE_TXFULL and Errors) <> 0 then
Result := Result + [ceTxFull];
if (CE_BREAK and Errors) <> 0 then
Result := Result + [ceBreak];
if (CE_IOE and Errors) <> 0 then
Result := Result + [ceIO];
if (CE_MODE and Errors) <> 0 then
Result := Result + [ceMode];
end;
// prepare PAsync variable for read/write operation
procedure PrepareAsync(AKind: TOperationKind; const Buffer;
Count: Integer; AsyncPtr: PAsync);
begin
with AsyncPtr^ do
begin
Kind := AKind;
if Data <> nil then
FreeMem(Data);
GetMem(Data, Count);
Move(Buffer, Data^, Count);
Size := Count;
end;
end;
// perform asynchronous write operation
procedure TCustomSerialPort.Lock;
begin
WaitForSingleObject(FSem, INFINITE);
end;
procedure TCustomSerialPort.UnLock;
begin
ReleaseSemaphore(FSem, 1, nil);
end;
function TCustomSerialPort.WriteAsync(const Buffer; Count: Integer; var AsyncPtr: PAsync): Integer;
var
Success: Boolean;
BytesTrans: DWORD;
begin
if AsyncPtr = nil then
raise EComPort.CreateNoWinCode(CError_InvalidAsync);
PrepareAsync(okWrite, Buffer, Count, AsyncPtr);
Success := WriteFile(FHandle, Buffer, Count, BytesTrans, @AsyncPtr^.Overlapped)
or (GetLastError = ERROR_IO_PENDING);
if not Success then
raise EComPort.Create(CError_WriteFailed, GetLastError);
Result := BytesTrans;
end;
// perform synchronous write operation
function TCustomSerialPort.Write(const Buffer; Count: Integer): Integer;
var
AsyncPtr: PAsync;
begin
InitAsync(AsyncPtr);
Lock;
try
WriteAsync(Buffer, Count, AsyncPtr);
Result := WaitForAsync(AsyncPtr);
finally
DoneAsync(AsyncPtr);
UnLock;
end;
end;
// perform asynchronous write operation
function TCustomSerialPort.WriteStrAsync(const Str: string; var AsyncPtr: PAsync): Integer;
begin
Result := WriteAsync(Str[1], Length(Str), AsyncPtr);
end;
// perform synchronous write operation
function TCustomSerialPort.WriteStr(const Str: string): Integer;
var
AsyncPtr: PAsync;
begin
InitAsync(AsyncPtr);
Lock;
try
WriteStrAsync(Str, AsyncPtr);
Result := WaitForAsync(AsyncPtr);
finally
DoneAsync(AsyncPtr);
UnLock;
end;
end;
// perform asynchronous read operation
function TCustomSerialPort.ReadAsync(var Buffer; Count: Integer; var AsyncPtr: PAsync): Integer;
var
Success: Boolean;
BytesTrans: DWORD;
begin
BytesTrans := 0;
if AsyncPtr = nil then
raise EComPort.CreateNoWinCode(CError_InvalidAsync);
AsyncPtr^.Kind := okRead;
Success := ReadFile(FHandle, Buffer, Count, BytesTrans, @AsyncPtr^.Overlapped)
or (GetLastError = ERROR_IO_PENDING);
if not Success then
begin
Debug(Port, 'ReadFile failed. LastError is ' + IntTostr(GetLastError));
raise EComPort.Create(CError_ReadFailed, GetLastError);
end;
Result := BytesTrans;
end;
// perform synchronous read operation
function TCustomSerialPort.Read(var Buffer; Count: Integer): Integer;
var
AsyncPtr: PAsync;
begin
InitAsync(AsyncPtr);
Lock;
try
ReadAsync(Buffer, Count, AsyncPtr);
Result := WaitForAsync(AsyncPtr);
finally
DoneAsync(AsyncPtr);
UnLock;
end;
end;
// perform asynchronous read operation
function TCustomSerialPort.ReadStrAsync(var Str: string; Count: Integer; var AsyncPtr: PAsync): Integer;
begin
SetLength(Str, Count);
Result := ReadAsync(Str[1], Count, AsyncPtr);
end;
// perform synchronous read operation
function TCustomSerialPort.ReadStr(var Str: string; Count: Integer): Integer;
var
AsyncPtr: PAsync;
begin
InitAsync(AsyncPtr);
Lock;
try
ReadStrAsync(Str, Count, AsyncPtr);
Result := WaitForAsync(AsyncPtr);
SetLength(Str, Result);
finally
DoneAsync(AsyncPtr);
UnLock;
end;
end;
function ErrorCode(AsyncPtr: PAsync): Integer;
begin
Result := 0;
case AsyncPtr^.Kind of
okWrite: Result := CError_WriteFailed;
okRead: Result := CError_ReadFailed;
end;
end;
// wait for asynchronous operation to end
function TCustomSerialPort.WaitForAsync(var AsyncPtr: PAsync): Integer;
var
BytesTrans, Signaled: DWORD;
Success: Boolean;
begin
if AsyncPtr = nil then
raise EComPort.CreateNoWinCode(CError_InvalidAsync);
Signaled := WaitForSingleObject(AsyncPtr^.Overlapped.hEvent, INFINITE);
Success := (Signaled = WAIT_OBJECT_0) and
(GetOverlappedResult(FHandle, AsyncPtr^.Overlapped, BytesTrans, False));
if not Success then
raise EComPort.Create(ErrorCode(AsyncPtr), GetLastError);
Result := BytesTrans;
end;
// abort all asynchronous operations
procedure TCustomSerialPort.AbortAllAsync;
begin
if not PurgeComm(FHandle, PURGE_TXABORT or PURGE_RXABORT) then
raise EComPort.Create(CError_PurgeFailed, GetLastError);
end;
// detect whether asynchronous operation is completed
function TCustomSerialPort.IsAsyncCompleted(AsyncPtr: PAsync): Boolean;
var
BytesTrans: DWORD;
begin
if AsyncPtr = nil then
raise EComPort.CreateNoWinCode(CError_InvalidAsync);
Result := GetOverlappedResult(FHandle, AsyncPtr^.Overlapped, BytesTrans, False);
if not Result then
if GetLastError <> ERROR_IO_PENDING then
raise EComPort.Create(CError_AsyncCheck, GetLastError);
end;
// waits for event to occur on serial port
procedure TCustomSerialPort.WaitForEvent(var Events: TComEvents;
StopEvent: THandle; Timeout: Integer);
var
Overlapped: TOverlapped;
Mask: DWORD;
Success: Boolean;
Signaled, EventHandleCount: Integer;
EventHandles: array[0..1] of THandle;
begin
// cannot call method if event thread is running
if FThreadCreated then
raise EComPort.CreateNoWinCode(CError_ThreadCreated);
FillChar(Overlapped, SizeOf(TOverlapped), 0);
Overlapped.hEvent := CreateEvent(nil, True, False, nil);
EventHandles[0] := Overlapped.hEvent;
if StopEvent <> 0 then
begin
EventHandles[1] := StopEvent;
EventHandleCount := 2;
end
else
EventHandleCount := 1;
try
SetCommMask(FHandle, EventsToInt(Events));
// let's wait for event or timeout
Success := WaitCommEvent(FHandle, Mask, @Overlapped);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -