抱歉,这不是一个问题,而是为了帮助遇到以下问题的人们。我正在解决需要使用串行输入/输出(Serial I/O)的问题,但主要是在Windows CE 6.0下运行。然而,最近有人问我是否可以将应用程序也制作成能够在Windows下运行,所以我开始解决这个问题。我花了很多时间寻找答案,但发现许多信息都是错误的或不准确的。因此,在解决了这个问题之后,我决定与大家分享我的研究结果,以便任何遇到这些困难的人都能得到答案。
在Windows CE下,OVERLAPPED I/O不受支持。这意味着通过串口进行双向通信可能会非常麻烦。主要问题是当您正在等待从串口接收数据时,您不能发送数据,因为这样做会导致您的主线程阻塞,直到读取操作完成或超时(取决于您是否设置了超时)。
像大多数进行串行I/O的人一样,我设置了一个读取器串行线程来读取串口,它使用带有EV_RXCHAR掩码的WaitCommEvent()等待串行数据。现在,这就是Windows和Windows CE的困难所在。
如果我有一个简单的读取器线程,例如:
显然,在上面的示例中,我没有从串口读取数据或其他任何操作,并且假设thParam包含已打开的comm端口的句柄等。现在,问题是在Windows下,当您的线程执行并命中WaitCommEvent()时,您的读取器线程将进入睡眠状态,等待串口数据。好吧,这很好,应该是这样的,但是...如何结束此线程并使MessageBox()出现?嗯,事实证明,这实际上并不容易,并且是Windows CE和Windows之间在其串行I/O方面处理方式上的根本差异。
在Windows CE下,您可以做一些事情来使WaitCommEvent()失效,例如SetCommMask(COMMPORT_HANDLE, 0)甚至CloseHandle(COMMPORT_HANDLE)。这将允许您正确终止线程,因此释放串行端口以便开始发送数据。但是,在Windows下,这两种方法都无法工作,并且都会导致您调用它们的线程进入睡眠状态,等待WaitCommEvent()完成。那么,在Windows下如何结束WaitCommEvent()呢?通常,您会使用OVERLAPPED I/O,并且线程阻塞不会成为问题,但由于解决方案必须与Windows CE兼容,因此OVERLAPPED I/O不是一种选择。在Windows下,有一件事可以做,就是调用CancelSynchronousIo()函数,这将结束您的WaitCommEvent(),但请注意,这可能与设备相关。 CancelSynchronousIo()的主要问题是Windows CE也不支持它,因此对于这个问题,您无法使用它!
那么你该怎么做呢?事实上,为了解决这个问题,您不能使用WaitCommEvent(),因为没有一种方式可以在Windows上终止此函数,而这种方式又得到Windows CE的支持。那么,只剩下ReadFile(),它将在读取非OVERLAPPED I/O时阻塞,并且这将与Comm Timeouts一起工作。
使用ReadFile()和COMMTIMEOUTS结构意味着您必须有一个紧密的循环等待串行数据,但如果您没有接收大量串行数据,则不应该成为问题。还可以使用小超时事件来结束循环,以确保资源传递回系统,并且您不会以100%的负载攻击处理器。以下是我提出的解决方案,如果您认为它可以改进,请给予反馈。
在Windows CE下,OVERLAPPED I/O不受支持。这意味着通过串口进行双向通信可能会非常麻烦。主要问题是当您正在等待从串口接收数据时,您不能发送数据,因为这样做会导致您的主线程阻塞,直到读取操作完成或超时(取决于您是否设置了超时)。
像大多数进行串行I/O的人一样,我设置了一个读取器串行线程来读取串口,它使用带有EV_RXCHAR掩码的WaitCommEvent()等待串行数据。现在,这就是Windows和Windows CE的困难所在。
如果我有一个简单的读取器线程,例如:
UINT SimpleReaderThread(LPVOID thParam)
{
DWORD eMask;
WaitCommEvent(thParam, &eMask, NULL);
MessageBox(NULL, TEXT("Thread Exited"), TEXT("Hello"), MB_OK);
}
显然,在上面的示例中,我没有从串口读取数据或其他任何操作,并且假设thParam包含已打开的comm端口的句柄等。现在,问题是在Windows下,当您的线程执行并命中WaitCommEvent()时,您的读取器线程将进入睡眠状态,等待串口数据。好吧,这很好,应该是这样的,但是...如何结束此线程并使MessageBox()出现?嗯,事实证明,这实际上并不容易,并且是Windows CE和Windows之间在其串行I/O方面处理方式上的根本差异。
在Windows CE下,您可以做一些事情来使WaitCommEvent()失效,例如SetCommMask(COMMPORT_HANDLE, 0)甚至CloseHandle(COMMPORT_HANDLE)。这将允许您正确终止线程,因此释放串行端口以便开始发送数据。但是,在Windows下,这两种方法都无法工作,并且都会导致您调用它们的线程进入睡眠状态,等待WaitCommEvent()完成。那么,在Windows下如何结束WaitCommEvent()呢?通常,您会使用OVERLAPPED I/O,并且线程阻塞不会成为问题,但由于解决方案必须与Windows CE兼容,因此OVERLAPPED I/O不是一种选择。在Windows下,有一件事可以做,就是调用CancelSynchronousIo()函数,这将结束您的WaitCommEvent(),但请注意,这可能与设备相关。 CancelSynchronousIo()的主要问题是Windows CE也不支持它,因此对于这个问题,您无法使用它!
那么你该怎么做呢?事实上,为了解决这个问题,您不能使用WaitCommEvent(),因为没有一种方式可以在Windows上终止此函数,而这种方式又得到Windows CE的支持。那么,只剩下ReadFile(),它将在读取非OVERLAPPED I/O时阻塞,并且这将与Comm Timeouts一起工作。
使用ReadFile()和COMMTIMEOUTS结构意味着您必须有一个紧密的循环等待串行数据,但如果您没有接收大量串行数据,则不应该成为问题。还可以使用小超时事件来结束循环,以确保资源传递回系统,并且您不会以100%的负载攻击处理器。以下是我提出的解决方案,如果您认为它可以改进,请给予反馈。
typedef struct
{
UINT8 sync;
UINT8 op
UINT8 dev;
UINT8 node;
UINT8 data;
UINT8 csum;
} COMMDAT;
COMSTAT cs = {0};
DWORD byte_count;
COMMDAT cd;
ZeroMemory(&cd, sizeof(COMMDAT));
bool recv = false;
do
{
ClearCommError(comm_handle, 0, &cs);
if (cs.cbInQue == sizeof(COMMDAT))
{
ReadFile(comm_handle, &cd, sizeof(COMMDAT), &byte_count, NULL);
recv = true;
}
} while ((WaitForSingleObject(event_handle, 2) != WAIT_OBJECT_0) && !recv);
ThreadExit(recv ? cd.data : 0xFF);
因此,要结束线程,只需在event_handle中发送信号即可,这将使您能够正确退出线程并清理资源,并且在Windows和Windows CE上工作正常。
希望这能帮助所有遇到这个问题的人。
MAXDWORD, 0,0,0,0
吗? - AaA