如何在VBA中读取串口前检查是否存在等待数据?
在Access中用串口仿真扫描仪时,避免无数据读取导致假死的解决方案
嘿,这个场景我太熟了——用Access做入库系统,搭配串口仿真的扫描仪,没数据时程序直接卡死,还得兼顾其他事务,确实闹心。咱们可以通过Windows API实现非阻塞式检查串口数据,完美解决这个问题,具体步骤如下:
1. 先在VBA模块中声明所需的API和常量
VBA本身没有直接检查串口缓冲区的函数,得借助系统提供的ClearCommError API,它能返回串口输入缓冲区里的待读字节数。把这段代码放在模块的最顶部(要在所有过程之外):
' 适配64位Access,32位的话去掉PtrSafe,把LongPtr换成Long Private Declare PtrSafe Function ClearCommError Lib "kernel32" ( _ ByVal hFile As LongPtr, _ lpErrors As Long, _ lpStatComms As COMSTAT _ ) As Long ' 存储串口状态的结构体,重点看cbInQue字段(待读字节数) Private Type COMSTAT fCtsHold As Long fDsrHold As Long fRlsdHold As Long fXoffHold As Long fXoffSent As Long fEof As Long fTxim As Long fReserved As Long cbInQue As Long cbOutQue As Long End Type
2. 非阻塞式读取的核心逻辑
打开串口后,每次读取前先调用ClearCommError检查缓冲区:如果cbInQue大于0,说明有数据,再执行读取;如果等于0,直接跳过,程序就不会卡住了。
给你个完整的示例代码(包含打开串口、检查数据、读取处理):
' 模块级变量,保存串口句柄,方便定时器调用 Private hCom As LongPtr Sub OpenSerialPort() ' 打开串口,参数要和你的扫描仪匹配:波特率9600、无校验、8位数据位、1停止位 Open "COM1:9600,N,8,1,CS,DSR" For Random As #1 Len = 1 ' 获取串口的文件句柄,传给API用 hCom = FileAttr(1, vbFileAttrHandle) ' 启动窗体定时器,每隔100ms检查一次数据(避免占用过多资源) Forms!你的窗体名.TimerInterval = 100 End Sub ' 窗体的Timer事件(右键窗体→事件→定时器→选择该过程) Private Sub Form_Timer() Dim comStat As COMSTAT Dim errors As Long Dim inputData As String Dim bytesAvailable As Long ' 确保串口已经打开 If hCom <> 0 Then ' 调用API获取串口状态 If ClearCommError(hCom, errors, comStat) <> 0 Then bytesAvailable = comStat.cbInQue If bytesAvailable > 0 Then ' 读取所有待读数据 inputData = String(bytesAvailable, Chr(0)) Get #1, , inputData ' 这里调用你的入库处理逻辑 ProcessInventory(inputData) End If End If End If End Sub Sub ProcessInventory(barcode As String) ' 你的入库记录逻辑,比如把barcode写入Access表 CurrentDb.Execute "INSERT INTO 入库记录表(条码, 入库时间) VALUES('" & barcode & "', Now())" MsgBox "条码" & barcode & "入库成功!" End Sub Sub CloseSerialPort() ' 关闭串口和定时器 If hCom <> 0 Then Close #1 hCom = 0 Forms!你的窗体名.TimerInterval = 0 End If End Sub
3. 关键注意事项
- 适配32/64位Access:如果是32位Access,要把代码里的
PtrSafe删掉,LongPtr替换成Long,不然会报错。 - 串口参数匹配:打开串口的参数(波特率、校验位等)必须和扫描仪的串口设置完全一致,不然读不到数据。
- 定时器间隔:建议设置100-200ms,既能及时响应扫描,又不会让程序过度占用CPU。
- 避免阻塞:不要用
Input或Line Input这类阻塞式读取方法,必须用Get配合API检查来实现非阻塞读取。
为什么原来的Open语句会卡死?因为默认的串口读取是阻塞模式——没有数据时程序会一直等待,直到有数据或超时,这就导致Access失去响应。用API先检查缓冲区,就能彻底避免这个问题。
内容的提问来源于stack exchange,提问作者Paul Stewart
相关产品推荐
相关产品推荐

