WaitCommEvent 返回87 原因分析

今天终于将串口通讯的接收端勉强实现了。 基于学习简单, 我是利用Win32的串口通讯API实现了一个控制台程序。

首先附上代码

#include <stdio.h>
#include <windows.h>
#include <assert.h>

//Global variable
HANDLE m_hCommPort;
DWORD WINAPI ComThreadFunc();
void main()
{	
	DCB dcb;
	char input;
	printf("This is a main thread!\n");
	printf("Do you want to open COM6(Yes/no)?\n");
	scanf("%c",&input);
	if('y'==input)
	{
		/*Open a file! Here is comm!*/
		m_hCommPort= ::CreateFile("COM6",
			GENERIC_READ|GENERIC_WRITE, 
			0,
			NULL,
			OPEN_EXISTING,
			FILE_FLAG_OVERLAPPED,
			0);
		
		if(INVALID_HANDLE_VALUE==m_hCommPort)
		{
			printf("invalid handle!\n");
		}
		/*Set comm state!*/
		
		if(!SetupComm(m_hCommPort,1024,1024))	// set the inqueue&outqueue buffer
		{
			printf("Setup Comm input buffer and output buffer failed!\n");
		}
		
		if (!GetCommState(m_hCommPort,&dcb)) {
			// Handle the error.
			printf ("GetCommState failed with error %d.\n", GetLastError());
			return;
		}
		
		
		
		dcb.BaudRate = CBR_115200;     // set the baud rate
		dcb.ByteSize = 8;             // data size, xmit, and rcv
		dcb.Parity = NOPARITY;        // no parity bit
		dcb.StopBits = ONESTOPBIT;    // one stop bit		
		
		if (!SetCommState(m_hCommPort, &dcb)) {
			// Handle the error.
			printf ("SetCommState failed with error %d.\n", GetLastError());
			return;
		}
		
		
		printf("set com6 with baudrate 115200 seccussfully!\n");
		
		/*Set comm mask*/
		if(!SetCommMask(m_hCommPort,EV_RXCHAR|EV_RXFLAG))
		{
			printf("Set comm mask failed!");
		}
		/*Create thread!*/
		HANDLE hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)ComThreadFunc,NULL,0,NULL);
		
		CloseHandle(hThread);
		
		while(1)
		{
			//main thread
		}
	}
	else if('n'==input)
	{
		return;
	}
	else
	{
		printf("Please just input yes or no!\n");
		Sleep(5000);
		return;
	}
}

DWORD WINAPI ComThreadFunc()
{
	char buff[1024]={0};
	DWORD res,factbyte;
	COMSTAT rst;
	int fError,commEvError;
	DWORD dwEvtMask,CommResult,readResult;
	BOOL bRet;
	BOOL fWaitingOnStat=FALSE;	// indicate if wait comm event is pending.
	OVERLAPPED osComm = {0};	// overlapped structure for comm event operations
	OVERLAPPED osReader = {0};  // overlapped structure for read operations
	printf("I am the comm thread!\n");
	
	osComm.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
	if (osComm.hEvent == NULL)
		printf("Create comm Event failed!\n");
	osReader.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
	if (osReader.hEvent == NULL)
		printf("Create reader Event failed!\n");
	
	
	while(1)
	{
		if(!fWaitingOnStat)
		{
			bRet=WaitCommEvent(m_hCommPort,&dwEvtMask,&osComm);
			if(!bRet)
			{
				commEvError=GetLastError();
				assert(commEvError==ERROR_IO_PENDING);
				fWaitingOnStat=TRUE;
			}
			else
			{
				//WaitCommEvent always will return FALSE for overlapped I/O
			}
		}
		/*wait for pending operations to complete*/
		if(fWaitingOnStat)
		{
			CommResult=WaitForSingleObject(osComm.hEvent,500);//Now it can't get the event???Block for ever!!
			
			switch(CommResult)
			{
			case WAIT_OBJECT_0:
				ClearCommError(m_hCommPort,&res,&rst);
				if(!ReadFile(m_hCommPort,buff,rst.cbInQue,&factbyte,&osReader))
				{
					fError=GetLastError();
					assert(fError==ERROR_IO_PENDING);
					readResult=WaitForSingleObject(osReader.hEvent,500);
					switch(readResult)
					{
					case WAIT_OBJECT_0:
						printf("%s",buff);
						memset(buff,0,1024);
						fWaitingOnStat=FALSE;
					case WAIT_TIMEOUT:
						//ClearCommError(m_hCommPort,&res,&rst);
						//SetCommMask(m_hCommPort,EV_RXCHAR|EV_RXFLAG);
						break;
					default:
						//do nothing!
						break;
					}
				}
				else
				{
					buff[rst.cbInQue]=0;
					printf("%s",buff);
					memset(buff,0,1024);
					fWaitingOnStat=FALSE;
				}
				break;
			case WAIT_TIMEOUT:
				//ClearCommError(m_hCommPort,&res,&rst);
				//SetCommMask(m_hCommPort,EV_RXCHAR|EV_RXFLAG);
				break;
			default:
				//do nothing!
				break;
			}
		}
	}
	return 0L;
}



现在修改WaitForSingleObject的time-out interval 为500(原来是用INFINITE一直阻塞的),目前简单实现了类似超级终端的接收端功能, 待以后完善并且利用MFC制作GUI界面的程序。

 

另外对标题的WaitCommEvent返回87的原因, 参考以下说明:

Take a look at <winerror.h>. You'll see that the value 87 corresponds to 
ERROR_INVALID_PARAMETER.

The issue is that WaitCommEvent is being invoked with
the same OVERLAPPED structure before the system has
completed the previous overlapped I/O.

In the case of ERROR_IO_PENDING, the code should use
a WaitForSingleObject, WaitForMultipleObjects, or
GetOverlappedResult so that the OVERLAPPED object
hEvent member becomes signalled before invoking
WaitCommEvent again.

 

WaitCommEvent在重叠I/O模式下不会阻塞, 而是立即返回的。

但通常都会失败,报ERROR_IO_PENDING错误。

在Pending状态下, 我们必须使用WaitForSingleObject, WaitForMultipleObjects或者GetOverlappedResult 等待Event产生,

在这个等待过程中,我们不能再去调用WaitCommEvent函数, 否则会产生87---ERROR_INVALID_PARAMETER错误。

只有当上述 wait function等待到Event,并将Event置位为有信号状态下,下次再去调用WaitCommEvent才不会报87错误。

 

故对87错误有如下几种解决方法:

1. 按上述代码, 在WaitCommEvent前加一个判断, 判断当前的Pending状态是否处理完,如果没处理完, 就不再调用WaitCommEvent函数。

2. WaitForSingleObjects的time-out interval 使用INFINITE,这样就和非重叠的效果是一样的了, 阻塞住了。

3. 我们可以对WAIT_TIMEOUT的case下对Event进行重新设定, 调用SetCommMask, 由于SetCommMask后WaitCommEvent立即返回,故下次调用时不会报87的错误。

参考MSDN的解释

If a process attempts to change the device handle's event mask by using the SetCommMask function while an overlapped WaitCommEvent operation is in progress, WaitCommEvent returns immediately. The variable pointed to by the lpEvtMask parameter is set to zero.

当然第三种方法可能不太正确, 但确实也能解决87这个问题。

  • 0
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WaitCommEvent 是一个用于等待串口事件的函数,可以等待串口收到数据或者数据发送完毕等事件的发生。下面是一个使用 WaitCommEvent 函数的示例: ```c++ #include <Windows.h> #include <iostream> int main() { HANDLE hComm; DWORD dwEventMask; bool success; // 打开串口 hComm = CreateFile("COM1", GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); if (hComm == INVALID_HANDLE_VALUE) { std::cout << "Failed to open COM1\n"; return 1; } // 设置串口参数 DCB dcbSerialParams = { 0 }; dcbSerialParams.DCBlength = sizeof(DCB); success = GetCommState(hComm, &dcbSerialParams); if (!success) { std::cout << "Failed to get serial parameter\n"; CloseHandle(hComm); return 1; } dcbSerialParams.BaudRate = CBR_9600; dcbSerialParams.ByteSize = 8; dcbSerialParams.StopBits = ONESTOPBIT; dcbSerialParams.Parity = NOPARITY; success = SetCommState(hComm, &dcbSerialParams); if (!success) { std::cout << "Failed to set serial parameter\n"; CloseHandle(hComm); return 1; } // 设置串口事件 success = SetCommMask(hComm, EV_RXCHAR); if (!success) { std::cout << "Failed to set comm mask\n"; CloseHandle(hComm); return 1; } // 等待串口事件 success = WaitCommEvent(hComm, &dwEventMask, NULL); if (!success) { std::cout << "Failed to wait comm event\n"; CloseHandle(hComm); return 1; } else { if (dwEventMask & EV_RXCHAR) { std::cout << "Received data from COM1\n"; } } // 关闭串口 CloseHandle(hComm); return 0; } ``` 这个例子中,首先打开串口 "COM1",然后设置串口参数,接着设置串口事件为 EV_RXCHAR,即等待串口收到数据事件的发生。最后调用 WaitCommEvent 函数等待事件的发生,如果收到 EV_RXCHAR 事件,就输出 "Received data from COM1"。最后关闭串口
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值