windows - Windows/Windows CE的串行I/O重叠/不重叠

标签 windows io serial-port windows-ce

很抱歉,这不是一个大问题,而是更多的帮助人们解决这些特定问题的方法。我正在解决的问题要求使用串行I/O,但主要在Windows CE 6.0下运行。但是,最近有人问我是否也可以在Windows下运行该应用程序,因此我着手解决此问题。我确实花了很多时间环顾四周,看看是否有人能得到我所需要的答案,而这一切都是由于很多错误信息和在某些情况下基本上是错误的事情引起的。因此,解决了这个问题后,我想与大家分享我的发现,以便遇到这些困难的任何人都能得到答案。

在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()?好吧,通常您会使用OVERIAPPED I/O,而线程阻塞不会成为问题,但是由于该解决方案也必须与Windows CE兼容,因此OVERLAPPED I/O并不是一个选择。在Windows下,您可以完成一件事来结束WaitCommEvent(),即调用CancelSynchronousIo()函数,这将结束您的WaitCommEvent(),但请注意,这可能与设备有关。 CancelSynchronousIo()的主要问题是Windows CE也不支持它,因此使用它来解决此问题是您不走运!

你是怎么做到的?事实是,要解决此问题,您根本不能使用WaitCommEvent(),因为没有方法可以在Windows CE支持的Windows上终止此功能。这样,您就剩下ReadFile()了,它将在读取NON-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上正常工作。

希望能帮助我见过的每个人都遇到这个问题。

最佳答案

由于我认为上面的评论有误解,因此,这里有两个不使用紧密循环的可能解决方案的详细信息。请注意,它们使用运行时确定,因此在两种操作系统下都很好(尽管无论如何您必须分别为每个目标进行编译),并且由于都不使用#ifdef,因此不太可能最终在没有立即注意到的情况下破坏一侧或另一侧的编译器。

首先,您可以动态加载CancelSynchonousIo并在操作系统中使用它时使用它。甚至可以选择执行某些操作来代替CE的Cancel(例如,关闭句柄?);

typedef BOOL (WINAPI *CancelIo)(HANDLE hThread);

HANDLE hPort;

BOOL CancelStub(HANDLE h)
{
    // stub for WinCE
    CloseHandle(hPort);
}

void IoWithCancel()
{
    CancelIo cancelFcn;

    cancelFcn = (CancelIo)GetProcAddress(
        GetModuleHandle(_T("kernel32.dll")), 
        _T("CancelSynchronousIo"));

    // if for some reason you want something to happen in CE
    if(cancelFcn == NULL)
    {
        cancelFcn = (CancelIo)CancelStub;
    }

    hPort = CreateFile( /* blah, blah */);

    // do my I/O

    if(cancelFcn != NULL)
    {
        cancelFcn(hPort);
    }
}

另一个选择会花费更多的工作,因为您可能会有不同的线程模型(尽管如果您使用的是C++,无论如何,基于平台的单独类都是一个很好的例子)将确定平台并在桌面上重叠使用:
HANDLE hPort;

void IoWithOverlapped()
{
    DWORD overlapped = 0;
    OSVERSIONINFO version;

    GetVersionEx(&version);
    version.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
    if((version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
        || (version.dwPlatformId == VER_PLATFORM_WIN32_NT))
    {
        overlapped = FILE_FLAG_OVERLAPPED;
    }
    else
    {
        // create a receive thread
    }

    hPort = CreateFile(
        _T("COM1:"), 
        GENERIC_READ | GENERIC_WRITE, 
        FILE_SHARE_READ | FILE_SHARE_WRITE, 
        NULL, 
        OPEN_EXISTING, 
        overlapped,
        NULL);
}

关于windows - Windows/Windows CE的串行I/O重叠/不重叠,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14956541/

相关文章:

windows - 为 Windows 7 构建自定义凭证提供程序

c++ - 如何将命名管道的流转换为套接字流? (在 Windows 上使用 C++)

java - 未读取第一个和最后一个数据项。 (JAVA)

c - 我怎样才能让 program1 从 promram2 获取输入,而 program2 同时从终端获取输入?

c# - 以编程方式获取友好的端口名称

c# - 在不输入 "mono filename.exe"的情况下在 Windows 上运行 Mono 应用程序?

windows - 在 Windows 中生成随机文件

c++ - 尝试将 ofstream 传递给对象时出错 (C++)

c - 通过串口读写二进制数据

serial-port - 尝试从 SMT32 读取串行端口时没有消息显示