⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 spcomm.pas.~8~

📁 其中包含XPMenu控件和SPComm控件
💻 ~8~
📖 第 1 页 / 共 5 页
字号:
    property StopBits: TStopBits read FStopBits write SetStopBits;
    property XonChar: AnsiChar read FXonChar write SetXonChar;
    property XoffChar: AnsiChar read FXoffChar write SetXoffChar;
    property ReplacedChar: AnsiChar read FReplacedChar write SetReplacedChar;

    property ReadIntervalTimeout: DWORD         read FReadIntervalTimeout         write SetReadIntervalTimeout;
    property ReadTotalTimeoutMultiplier: DWORD  read FReadTotalTimeoutMultiplier  write SetReadTotalTimeoutMultiplier;
    property ReadTotalTimeoutConstant: DWORD    read FReadTotalTimeoutConstant    write SetReadTotalTimeoutConstant;
    property WriteTotalTimeoutMultiplier: DWORD read FWriteTotalTimeoutMultiplier write SetWriteTotalTimeoutMultiplier;
    property WriteTotalTimeoutConstant: DWORD   read FWriteTotalTimeoutConstant   write SetWriteTotalTimeoutConstant;

    property OnReceiveData: TReceiveDataEvent
             read FOnReceiveData write FOnReceiveData;
    property OnReceiveError: TReceiveErrorEvent
             read FOnReceiveError write FOnReceiveError;
    property OnModemStateChange: TModemStateChangeEvent
             read FOnModemStateChange write FOnModemStateChange;
    property OnRequestHangup: TNotifyEvent
             read FOnRequestHangup write FOnRequestHangup;
    property OnSendDataEmpty: TSendDataEmptyEvent
             read FOnSendDataEmpty write FOnSendDataEmpty;
  end;

procedure Register;

implementation

(******************************************************************************)
//   TComm PUBLIC METHODS
(******************************************************************************)

constructor TComm.Create( AOwner: TComponent );
begin
  inherited Create( AOwner );

  ReadThread := nil;
  WriteThread := nil;
  hCommFile := 0;
  hCloseEvent := 0;
  FSendDataEmpty := True;

  FCommName := 'COM2';
  FBaudRate := 9600;
  FParityCheck := False;
  FOutx_CtsFlow := False;
  FOutx_DsrFlow := False;
  FDtrControl := DtrEnable;
  FDsrSensitivity := False;
  FTxContinueOnXoff := True;
  FOutx_XonXoffFlow := True;
  FInx_XonXoffFlow := True;
  FReplaceWhenParityError := False;
  FIgnoreNullChar := False;
  FRtsControl := RtsEnable;
  FXonLimit := 500;
  FXoffLimit := 500;
  FByteSize := _8;
  FParity := None;
  FStopBits := _1;
  FXonChar := chr($11);      // Ctrl-Q
  FXoffChar := chr($13);     // Ctrl-S
  FReplacedChar := chr(0);

  // 串口超时控制参数  TCommTimeouts 结构参数意义:
  //      ReadIntervalTimeout:两字符之间最大的延时,当读取串口数据时,
  //        一旦两个字符传输的时间差超过该时间,读取函数将返回现有的数据。
  //        设置为0表示该参数不起作用。
  //      ReadTotalTimeoutMultiplier:读取每字符间的超时。
  //      ReadTotalTimeoutConstant:一次读取串口数据的固定超时。
  //      WriteTotalTimeoutMultiplier:写入每字符间的超时。
  //      WriteTotalTimeoutConstant:一次写入串口数据的固定超时。
  //  所以:
  //     在一次读取串口的操作中,其超时为ReadTotalTimeoutMultiplier乘以读取的字节数
  //  再加上 ReadTotalTimeoutConstant。将ReadIntervalTimeout设置为MAXDWORD,并将
  //  ReadTotalTimeoutMultiplier 和ReadTotalTimeoutConstant设置为0,表示读取操作将
  //  立即返回存放在输入缓冲区的字符。
  //    在一次写入串口的操作中,其超时为WriteTotalTimeoutMultiplier乘以写入的字节数
  //    再加上 WriteTotalTimeoutConstant。
  FReadIntervalTimeout         := 100;  // 读字符间隔超时时间: 100 ms
  FReadTotalTimeoutMultiplier  := 1;    // 读操作时每字符间的超时时间: 1 ms (n个字符总共为n ms)
  FReadTotalTimeoutConstant    := 100;    // 基本的(额外的)读超时时间: 500 ms
  FWriteTotalTimeoutMultiplier := 1;    // 写操作时每字符间的超时时间: 1 ms (n个字符总共为n ms
  FWriteTotalTimeoutConstant   := 100;    // 基本的(额外的)写超时时间: 100 ms

  FInputLen     := 0; //默认是一次执行全部读取
  FUseRWThread  := False; //默认为异步操作
  OutputTimeout := 500;
  InputTimeout  := 500;

  if not (csDesigning in ComponentState) then
     FHWnd := AllocateHWnd(CommWndProc)
end;

destructor TComm.Destroy;
begin
  if not (csDesigning in ComponentState) then
     DeallocateHWnd(FHwnd);

  inherited Destroy;
end;

{****************************************************************************
** Procedure: StartComm** Author:    Administrator** Date:      23-七月-2007** Arguments: None** Result:    None** PURPOSE: Starts communications over the comm port.** PARAMETERS:
**   hNewCommFile - This is the COMM File handle to communicate with.
**                  This handle is obtained from TAPI.
** Output:
**   Successful: Startup the communications.
**   Failure: Raise a exception
** COMMENTS:
**   StartComm makes sure there isn't communication in progress already,
**   creates a Comm file, and creates the read and write threads.  It
**   also configures the hNewCommFile for the appropriate COMM settings.
**   If StartComm fails for any reason, it's up to the calling application
**   to close the Comm file handle.
****************************************************************************}procedure TComm.StartComm;
var
   hNewCommFile:   THandle;
   sCommName: string;
begin
  // Are we already doing comm?
  if (hCommFile <> 0) then
     raise ECommsError.Create( 'This serial port already opened' );

  //一般COM9以上的串口设备只能用"\\.\COM10",不能写成COM10, 还要注意大小写
  sCommName:= UpperCase(FCommName);
  if Length(FCommName)>4 then
    sCommName:=UpperCase('\\.\'+FCommName);

  hNewCommFile := CreateFile( PChar(sCommName),
                              GENERIC_READ or GENERIC_WRITE,
                              0, {not shared}
                              nil, {no security ??}
                              OPEN_EXISTING,
                              FILE_ATTRIBUTE_NORMAL or FILE_FLAG_OVERLAPPED,
                              0 {template} );

  if hNewCommFile = INVALID_HANDLE_VALUE then
     raise ECommsError.Create( 'Error opening serial port' );

  // Is this a valid comm handle?
  if GetFileType( hNewCommFile ) <> FILE_TYPE_CHAR then
  begin
       CloseHandle( hNewCommFile );
       raise ECommsError.Create( 'File handle is not a comm handle ' )
  end;

  if not SetupComm( hNewCommFile, 10000, 10000 ) then
  //if not SetupComm( hNewCommFile, 4096, 4096 ) then
  begin
       CloseHandle( hCommFile );
       raise ECommsError.Create( 'Cannot setup comm buffer' )
  end;

  // It is ok to continue.

  hCommFile := hNewCommFile;

  // purge any information in the buffer

  PurgeComm( hCommFile, PURGE_TXABORT or PURGE_RXABORT or
                        PURGE_TXCLEAR or PURGE_RXCLEAR ) ;
  FSendDataEmpty := True;

  // Setting the time-out value
  _SetCommTimeout;

  // Querying then setting the comm port configurations.
  _SetCommState;

  // Create the event that will signal the threads to close.
  hCloseEvent := CreateEvent( nil, True, False, nil );

  if hCloseEvent = 0 then
  begin
       CloseHandle( hCommFile );
       hCommFile := 0;
       raise ECommsError.Create( 'Unable to create event' )
  end;

  if FUseRWThread then
  begin
    CreateAndStartRWThread;
  end;
  // Everything was created ok.  Ready to go!
end; {TComm.StartComm}

{****************************************************************************
** Procedure: StopComm** Author:    Administrator** Date:      23-七月-2007** Arguments: None** Result:    None** PURPOSE: Stop and end all communication threads.** PARAMETERS:  none
** RETURN VALUE:  none
** COMMENTS:
**   Tries to gracefully signal all communication threads to
**   close, but terminates them if it has to.
****************************************************************************}procedure TComm.StopComm;
begin
  // No need to continue if we're not communicating.
  if hCommFile = 0 then
    Exit;

  // Close the threads.
  CloseRWThread;

  // Not needed anymore.
  CloseHandle( hCloseEvent );

  // Now close the comm port handle.
  CloseHandle( hCommFile );
  hCommFile := 0
end; {TComm.StopComm}

{****************************************************************************
** Procedure: WriteCommData** Author:    Administrator** Date:      23-七月-2007** Arguments: pDataToWrite: PChar; dwSizeofDataToWrite: Word** Result:    Boolean** PURPOSE: Send a String to the Write Thread to be written to the Comm.** PARAMETERS:
**   pszStringToWrite     - String to Write to Comm port.
**   nSizeofStringToWrite - length of pszStringToWrite.
**   bIsManualRead    -读缓冲区读取方式,默认False为事件方式,否则为读函数调用方式,
**                     由用户在写完数据后自己调用读函数获取读缓冲区数据
** RETURN VALUE:
**   Returns TRUE if the PostMessage is successful.
**   Returns FALSE if PostMessage fails or Write thread doesn't exist.
** COMMENTS:
**   This is a wrapper function so that other modules don't care that
**   Comm writing is done via PostMessage to a Write thread.  Note that
**   using PostMessage speeds up response to the UI (very little delay to
**   'write' a string) and provides a natural buffer if the comm is slow
**   (ie: the messages just pile up in the message queue).
** Note that it is assumed that pszStringToWrite is allocated with
**   LocalAlloc, and that if WriteCommData succeeds, its the job of the
**   Write thread to LocalFree it.  If WriteCommData fails, then its
**   the job of the calling function to free the string.
****************************************************************************}function TComm.WriteCommData( pDataToWrite: PChar; dwSizeofDataToWrite: Word): Boolean;
var
   Buffer: Pointer;
begin
  if (WriteThread <> nil) and (dwSizeofDataToWrite <> 0) then
  begin
    Buffer := Pointer(LocalAlloc( LPTR, dwSizeofDataToWrite+1 ));
    Move( pDataToWrite^, Buffer^, dwSizeofDataToWrite );
    if PostThreadMessage( WriteThread.ThreadID, PWM_COMMWRITE,
                          WPARAM(dwSizeofDataToWrite), LPARAM(Buffer) ) then
    begin
      FSendDataEmpty := False;
      Result := True;
      Exit;
    end
  end;
  Result := False
end; {TComm.WriteCommData}

(****************************************************************************
 * 函数:   WriteDataToComm(DataPtr: pchar; DataSize: DWORD; Timeout: DWORD=500);
 * 功能:   由通信端口送出字符串数据
 * 作者:   Administrator
 * 日期:   2007-09-25
 * 参数:   DataPtr: pchar; 待发送的字符串缓冲区
           DataSize: DWORD;  字符串长度
           Timeout: DWORD=500  写超时时间
 * 返回值: Boolean   是否发送成功
 * 描述:
 ****************************************************************************)
function TComm.WriteDataToComm(ADataPtr: PChar; ADataSize: DWORD; ATimeout: DWORD=500): Boolean;
var
  t1,
  dwLastError,
  dwHandleSignaled,
  dwNumberOfBytesWritten,
  dwWhereToStartWriting : DWORD;
  HandlesToWaitFor: array[0..1] of THandle;
  ovOverLapped:        TOverlapped; // Needed for overlapped I/O 
  buseRwThread: Boolean;
begin
  // Do nothing if port has not been opened
  Result := False;
  if hCommFile = 0 then Exit;

  buseRwThread:= UseRWThread;
  UseRWThread:=False;

  dwNumberOfBytesWritten := 0;
  dwWhereToStartWriting := 0; // Start at the beginning.

	if ADataSize < 1 then //空数据写
    Exit;

	// create event for overlapped I/O
	ovOverLapped.hEvent := CreateEvent(nil,  // pointer to security attributes
	                          FALSE,        // flag for manual-reset event
	                          FALSE,        // flag for initial state
	                          '');          // pointer to event-object name

  HandlesToWaitFor[0] := hCloseEvent;
  HandlesToWaitFor[1] := ovOverLapped.hEvent;

	if ovOverLapped.hEvent = INVALID_HANDLE_VALUE then
		// Handle the error. "WriteData(): CreateEvent() failed"
    Exit;
  try
    t1:= GetTickCount;  //获得Windows系统开始运行到现在所经历的时间,单位毫秒

    // Keep looping until all characters have been written.
    repeat
      if not WriteFile(hCommFile,   // handle to file to write to
                        ADataPtr[ dwWhereToStartWriting ],// pointer to data to write to file
                        ADataSize,      // number of bytes to write
                        dwNumberOfBytesWritten, // pointer to number of bytes written
                        @ovOverLapped) then   // pointer to structure needed for overlapped I/O
      begin
        // WriteFile failed.  Expected; lets handle it.
        dwLastError := GetLastError;

        // Its possible for this error to occur if the
        // service provider has closed the port.  Time to end.
        if dwLastError = ERROR_INVALID_HANDLE then
        begin
          Exit;
        end;

        // Unexpected error.  No idea what.
        if dwLastError <> ERROR_IO_PENDING then
        begin
          Exit;
        end;
        // This is the expected ERROR_IO_PENDING case.

        // Wait for either overlapped I/O completion,
        // or for the CloseEvent to get signaled. 将用于等待多个对象变为有信号状态
        //DWORD   WaitForMultipleObjects( DWORD   nCount,   //   等待的对象数量
        //CONST   HANDLE   *lpHandles, //   对象句柄数组指针
        //BOOL   fWaitAll,   //   等待方式,为TRUE表示等待全部对象都变为有信号状态才返回,为FALSE表示任何一个对象变为有信号状态则返回
        //DWORD   dwMilliseconds);  //   超时设置,以ms为单位,如果为INFINITE表示无限期的等待
        dwHandleSignaled := WaitForMultipleObjects(2, @HandlesToWaitFor,
                            False, ATimeout);

        case dwHandleSignaled of
          WAIT_OBJECT_0:     // CloseEvent signaled!
          begin
            // Time to exit.
            Exit;
          end;

          WAIT_OBJECT_0 + 1: // Wait finished.
          begin
            // Time to get the results of the WriteFile
            if not GetOverlappedResult(hCommFile,

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -