
简介ComPort Library V4.14 是一套面向 Delphi 与 C Builder 开发者的经典串口通信控件适用于需要在 Windows 桌面程序中快速集成串口收发、参数配置与设备调试功能的场景覆盖从入门到中高级的串口编程需求。压缩包共 192 个文件约 1.88MB包含 38 个 dpk 与 8 个 bpk 安装包、12 个 pas 与 16 个 cpp 源码、10 个 dfm 窗体、7 个 hpp 头文件及 7 个 dcu 编译单元另有 res 资源、hlp 帮助、示例工程等便于按版本选择安装与二次开发。资源兼容 Delphi 5 至 XE7、XE10 及 C Builder 3 至 XE7XE7 之后的版本可沿用 XE7 的编译安装方式。目前已有 1845 人学习下载读者可获取完整源码、安装包与示例工程快速掌握串口控件集成、事件处理与常见通信问题排查思路。1. 从一次现场调试翻车说起这个串口控件到底能干什么去年帮一个做工业称重设备的朋友排查问题现场一台工控机跑着 Delphi XE7 写的上位机通过 RS232 读电子秤数据。现象很玄学软件刚启动时能正常收数跑上两三个小时就丢包重启软件又好了。他一开始怀疑是秤的问题换了三台秤都一样又怀疑线缆换了屏蔽线还是丢。最后定位到串口读写线程里——用的就是系统自带的 TComPort 老版本接收缓冲区溢出后没有正确复位事件回调里又做了耗时操作把主线程堵死了。那次之后我一直在找一个能稳定跑在 Delphi XE7 到 10.x 上的串口控件。系统自带的那个太老XE 之后 Unicode 字符串处理有变化很多老控件直接编译不过。后来在一个老外的开源项目里翻到了 ComPort Library V4.14试了一圈下来它在 XE7、XE8、10 Seattle、10.2 Tokyo、10.3 Rio、10.4 Sydney 上都能直接装、直接用源码结构清晰事件模型也合理。这篇就把它拆开讲清楚它是什么、怎么装、关键参数怎么设、坑在哪。ComPort Library 本质上是一套封装 Windows 串口 APICreateFile、SetCommState、ReadFile/WriteFile、WaitCommEvent的 Delphi 组件库对外暴露成 TComPort、TComDataPacket、TComComboBox 等几个组件。它不依赖 .NET、不依赖第三方 DLL纯 Pascal 源码编译进 EXE 就能跑。适合做工业采集、仪器控制、PLC 通讯、条码枪读取这类需要长时间稳定收发串口数据的场景。如果你只是偶尔读个 GPS 模块系统自带控件也能凑合但只要涉及多串口并发、大数据量连续接收、需要精确控制超时和缓冲区这套库的优势就出来了。2. 安装与工程集成从源码包到能编译的第一个工程2.1 源码包结构与编译顺序拿到 ComPort Library V4.14 的源码包解压后通常能看到这几个目录Source放核心单元Packages放各版本 Delphi 的包工程Demos放示例Docs放帮助文件。核心单元就那么几个ComPort.pas、ComPortThread.pas、ComDataPacket.pas、ComComboBox.pas、ComPortUtils.pas。别小看这几个文件它们之间的依赖关系决定了编译顺序。我一般不会一上来就装包而是先把Source目录下的单元按依赖顺序理一遍。ComPortUtils.pas最底层只依赖 Windows 单元ComPortThread.pas依赖 UtilsComPort.pas依赖前两个ComDataPacket.pas依赖 ComPortComComboBox.pas依赖 ComPort 和 VCL 控件。如果你在工程里直接引用源码而不是装包就要保证搜索路径里这几个单元都能找到且编译顺序正确。提示源码包里如果有.dpk包工程优先用包安装比手动加搜索路径省事得多尤其是多版本 Delphi 共存的时候。2.2 在 XE7 到 10.x 上安装包以 XE7 为例打开Packages目录下对应版本的包工程通常命名里带版本号比如ComPort_XE7.dpk。打开后先别急着 Compile检查两处一是Requires里引用的 rtl、vcl 包名是否和你当前 Delphi 版本匹配二是Contains里列出的单元路径是否指向正确的Source目录。确认无误后右键包工程选 Install。安装成功后组件面板上会出现一个 ComPort 标签页里面躺着 TComPort、TComDataPacket 等组件。10.x 系列Seattle 到 Sydney的安装流程基本一样但要注意 10.4 Sydney 之后 IDE 对包签名的要求变严了如果提示加载失败检查一下包的 Output 目录是否在系统 PATH 里或者干脆把编译好的.bpl手动拷到 Delphi 的 bin 目录下。我遇到过几次装完组件面板不显示的情况九成是.bpl没放到正确位置或者 IDE 的 Known Packages 列表里没注册。2.3 最小可运行工程打开串口收数据装好之后新建一个 VCL Forms Application从组件面板拖一个 TComPort 和一个 TComMemo或者普通的 TMemo到窗体上。下面这段代码是我常用的最小接收框架procedure TForm1.FormCreate(Sender: TObject); begin // 设置串口参数COM396008 数据位无校验1 停止位 ComPort1.Port : COM3; ComPort1.BaudRate : br9600; ComPort1.DataBits : dbEight; ComPort1.Parity.Bits : prNone; ComPort1.StopBits : sbOneStopBit; // 设置接收缓冲区大小默认 1024工业场景建议加大 ComPort1.BufferSize : 4096; // 打开串口 ComPort1.Open; end; procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer); var Str: string; begin // 读取接收到的数据Count 是当前缓冲区里的字节数 ComPort1.ReadStr(Str, Count); Memo1.Lines.Add(Str); end;这段代码的逻辑很直白FormCreate里配置参数并打开串口OnRxChar事件在串口收到数据时触发Count告诉你这次有多少字节可读ReadStr把它们读成字符串。参数方面BufferSize是驱动层接收缓冲区大小默认值偏小长时间高速接收容易溢出我一般设成 4096 或 8192。BaudRate、DataBits、Parity、StopBits必须和从设备手册上查到的一致错一个就收不到正确数据。注意OnRxChar是在串口监控线程里触发的不是主线程。如果你在这个事件里直接操作 VCL 控件必须用TThread.Synchronize或TThread.Queue切回主线程否则轻则界面卡顿重则随机崩溃。上面代码为了简洁直接写了 Memo实际项目里一定要改。3. 核心参数与事件模型把收发时序控制住3.1 串口参数配置的对应关系ComPort Library 把 Windows DCB 结构里的字段映射成了 Delphi 属性用起来比直接调 API 舒服但映射关系得心里有数。下面这张表是我从源码里对着 DCB 结构整理出来的配参数时直接查组件属性DCB 字段常用取值说明BaudRateBaudRatebr9600 / br115200波特率必须与从设备一致DataBitsByteSizedbEight / dbSeven数据位多数设备 8 位Parity.BitsParityprNone / prEven / prOdd校验位无校验最常见StopBitsStopBitssbOneStopBit / sbTwoStopBits停止位多数 1 位FlowControlfOutxCtsFlow 等fcNone / fcHardware流控长距离传输建议硬件流控BufferSize驱动缓冲区4096 / 8192接收缓冲区工业场景加大FlowControl这个属性容易被忽略。短距离、低波特率下用fcNone没问题但只要线缆超过几米或者波特率上到 115200建议开fcHardware让 RTS/CTS 参与流控否则接收端处理不过来时数据就丢了。我那个称重设备的项目后来就是把流控改成硬件流控配合加大缓冲区才彻底解决丢包。3.2 OnRxChar 与 OnTxEmpty 的配合串口通讯本质上是异步的你调WriteStr把数据丢进发送缓冲区就返回了实际什么时候发完不知道。ComPort Library 提供了OnTxEmpty事件在发送缓冲区空时触发。如果你做的是「发一条命令等设备回复」这种问答式通讯正确的时序是在OnTxEmpty里启动一个超时定时器然后在OnRxChar里收到完整回复后停掉定时器。这样能避免命令还没发完就开始等回复或者回复已经来了定时器还在跑。procedure TForm1.SendCommand(const Cmd: string); begin FWaitingReply : True; ComPort1.WriteStr(Cmd); // 启动 500ms 超时定时器超时未收到回复则重试或报错 TimerReply.Interval : 500; TimerReply.Enabled : True; end; procedure TForm1.ComPort1TxEmpty(Sender: TObject); begin // 发送完成可以在这里做状态标记但不要在这里等回复 FTxDone : True; end; procedure TForm1.ComPort1RxChar(Sender: TObject; Count: Integer); begin if FWaitingReply then begin TimerReply.Enabled : False; // 收到回复停掉超时 FWaitingReply : False; // 处理回复数据 end; end;这里的关键是FWaitingReply这个状态标记和超时定时器的配合。没有超时机制的话设备一旦不回复程序就永远卡在等待状态。超时时间设多少取决于设备响应速度一般 200 到 1000 毫秒我习惯先设 500 毫秒实测后再调。3.3 TComDataPacket 做粘包拆包串口收数据最头疼的问题之一就是粘包设备连续发三帧数据驱动一次性全给你OnRxChar只触发一次Count是三帧的总长度。如果你按固定长度解析就会错位。ComPort Library 里的 TComDataPacket 组件就是干这个的你告诉它帧头、帧尾、最大长度它自动帮你把数据流切成一个个完整的包通过OnPacket事件给你。procedure TForm1.FormCreate(Sender: TObject); begin ComDataPacket1.ComPort : ComPort1; // 帧头为 STX0x02帧尾为 ETX0x03 ComDataPacket1.StartString : #$02; ComDataPacket1.StopString : #$03; ComDataPacket1.MaxBufferSize : 1024; ComDataPacket1.OnPacket : ComDataPacket1Packet; end; procedure TForm1.ComDataPacket1Packet(Sender: TObject; const Str: string); begin // 这里拿到的 Str 就是一个完整帧不含帧头帧尾 ProcessFrame(Str); end;StartString和StopString支持多字符如果你的协议帧头是两个字节直接写两个字符就行。MaxBufferSize是保护机制防止帧尾一直不出现导致缓冲区无限增长。实际项目里我还会在OnPacket里加校验比如 CRC16校验不过就丢弃避免脏数据进入业务逻辑。4. 避坑与排查那些让我熬夜的串口问题4.1 打开串口报「拒绝访问」现象调ComPort1.Open时抛异常提示 Access Denied。原因通常有三个一是串口已被其他程序占用比如调试助手没关二是 COM 口号写错了系统里根本不存在这个口三是权限问题某些工控机上普通用户没有串口访问权限。解决顺序先在设备管理器里确认 COM 口号再关掉所有可能占用串口的程序最后检查当前用户是否在 Administrators 组里。如果还不行用CreateFile直接试一下看具体错误码。4.2 数据收着收着就断了现象程序跑一段时间后OnRxChar不再触发但设备还在发数据。原因多半是接收缓冲区溢出后驱动没有正确复位或者OnRxChar里做了耗时操作导致监控线程被阻塞。解决办法把BufferSize加大到 8192OnRxChar里只做数据读取和入队把解析和处理放到单独的消费者线程里。另外检查OnRxChar里有没有调用Application.ProcessMessages这个在串口事件里是禁忌会导致重入问题。4.3 中文乱码现象收到的数据里中文显示成乱码。原因是 Delphi XE 之后默认字符串是 UnicodeString而串口传的是字节流。ReadStr会按当前代码页把字节转成字符串如果设备发的是 GBK 编码而系统代码页是 UTF-8就乱了。解决办法用Read读成字节数组然后手动用TEncoding.GetEncoding(936)解码或者干脆全程用字节数组处理只在显示时转码。4.4 多串口同时工作时相互干扰现象一个程序开两个 TComPort一个正常一个丢数据。原因是每个 TComPort 内部都有自己的监控线程如果两个口的OnRxChar都往同一个 UI 控件写数据线程同步没做好就会互相阻塞。解决办法每个串口配一个独立的接收队列UI 刷新用定时器批量拉取不要在OnRxChar里直接操作界面。另外确认两个串口的BufferSize都够大避免一个口的线程长时间占用 CPU 导致另一个口的事件延迟。4.5 关闭程序时线程没退出现象程序关闭后进程还在任务管理器里或者下次打开串口报占用。原因是ComPort1.Close没有正确终止监控线程或者OnRxChar里还有未处理完的数据。解决办法在FormClose里先ComPort1.Close再Application.ProcessMessages一下让线程消息处理完最后确保所有自定义线程都已Terminate并WaitFor。我一般还会在Close之后加个短延时给驱动一点时间释放句柄。5. 进阶技巧用 TComComboBox 做设备热插拔与参数持久化TComComboBox 是这套库里容易被低估的组件。它继承自 TComboBox但会自动枚举系统当前可用的串口并在设备插拔时刷新列表。做现场调试工具时这个特性很省事用户不用去设备管理器查 COM 号下拉框里直接选。procedure TForm1.FormCreate(Sender: TObject); begin // 绑定到 TComPort选中项自动同步到 ComPort1.Port ComComboBox1.ComPort : ComPort1; // 设置波特率列表 ComComboBox1.BaudRates : [br9600, br19200, br38400, br115200]; // 启用自动刷新设备插拔时列表自动更新 ComComboBox1.AutoRefresh : True; end;AutoRefresh打开后TComComboBox 内部会监听系统设备变更消息有新串口插入或拔出时自动重新枚举。这个功能在 Windows 上依赖WM_DEVICECHANGE消息如果你的程序主窗体消息循环被阻塞刷新会延迟。所以还是那句话别在主线程里做耗时操作。参数持久化方面我习惯把用户选的串口参数存到 INI 文件里下次启动时读回来。ComPort Library 本身不提供持久化功能但它的属性都是 published 的可以用 RTTI 批量读写。下面这个思路我用了很多次procedure TForm1.SaveSettings; var Ini: TIniFile; begin Ini : TIniFile.Create(ChangeFileExt(Application.ExeName, .ini)); try Ini.WriteString(Serial, Port, ComPort1.Port); Ini.WriteInteger(Serial, BaudRate, Ord(ComPort1.BaudRate)); Ini.WriteInteger(Serial, DataBits, Ord(ComPort1.DataBits)); Ini.WriteInteger(Serial, Parity, Ord(ComPort1.Parity.Bits)); Ini.WriteInteger(Serial, StopBits, Ord(ComPort1.StopBits)); finally Ini.Free; end; end;读回来的时候反向操作注意枚举类型转换时要做范围检查防止 INI 文件被手动改坏导致程序异常。这套组合拳打下来一个能自动识别设备、记住参数、稳定收发的串口工具就成型了。从那以后我每次集成串口功能都强制走一遍「加大缓冲区 → 事件里只入队 → 独立线程解析 → 超时兜底」这四步再也没出现过跑几小时就丢包的情况。希望帮到你。本文还有配套的精品资源点击获取