串行I / O重叠/非重叠Windows / Windows CE [英] Serial I/O Overlapped/Non-Overlapped with Windows/Windows CE

查看:175
本文介绍了串行I / O重叠/非重叠Windows / Windows CE的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对不起,这不是一个问题,而是帮助人们解决这些特殊问题。我正在处理的问题需要使用串行I / O,但主要是在Windows CE 6.0下运行。但是,最近我被问到应用程序是否也可以在Windows下工作,所以我开始着手解决这个问题。我确实花了很多时间环顾四周,看看是否有人得到了我正在寻找的答案,所有这些都是在很多错误信息和在某些情况下基本上都是错误的事情。所以解决了这个问题后,我想我会和所有人分享我的发现,所以任何遇到这些困难的人都会得到答案。

I'm sorry this isn't much of a question, but more of to help people having problems with these particular things. The problem I'm working on requires the use of Serial I/O, but is primarily running under Windows CE 6.0. However, I was recently asked if the application could also be made to work under Windows too, so I set about solving this problem. I did spend quite a lot of time looking around to see if anyone had the answers I was looking for and it all came across as a lot of misinformation and things that were just basically wrong in some instances. So having solved this problem, I thought I'd share my findings with everyone so anyone encountering these difficulties would have answers.

在Windows CE下,OVERLAPPED I / O是 NOT 支持。这意味着通过串口的双向通信可能非常麻烦。主要的问题是,当您等待来自串行端口的数据时,您无法发送数据,因为这样做会导致主线程阻塞,直到读取操作完成或超时(取决于您是否设置了超时)

Under Windows CE, OVERLAPPED I/O is NOT supported. This means that bi-directional communication through the serial port can be quite troublesome. The main problem being that when you are waiting on data from the serial port, you cannot send data because doing so will cause your main thread to block until the read operation completes or timeouts (depending on whether you've set timeouts up)

与大多数人进行串行I / O一样,我设置了一个读取器串行线程来读取串口,该串口使用带有EV_RXCHAR掩码的WaitCommEvent()来等待串行数据。现在,这就是Windows和Windows CE出现问题的地方。

Like most people doing serial I/O, I had a reader serial thread set up for reading the serial port, which used WaitCommEvent() with an EV_RXCHAR mask to wait for serial data. Now this is where the difficulty arises with Windows and Windows CE.

如果我有一个像这样的简单读者线程,作为一个例子: -

If I have a simple reader thread like this, as an example:-

UINT SimpleReaderThread(LPVOID thParam)
{
    DWORD eMask;
    WaitCommEvent(thParam, &eMask, NULL);
    MessageBox(NULL, TEXT("Thread Exited"), TEXT("Hello"), MB_OK);
}

显然在上面的例子中,我不是从序列中读取数据端口或任何东西,我假设thParam包含通信端口的打开句柄等。现在,问题是在你的线程执行并点击WaitCommEvent()时,你的读者线程将进入休眠状态等待串口数据。好的,这很好,应该如此,但是...你如何结束这个线程并让MessageBox()出现?好吧,事实证明,它实际上并不那么容易,并且它在执行其串行I / O方面是Windows CE和Windows之间的根本区别。

Obviously in the above example, I'm not reading the data from the serial port or anything and I'm assuming that thParam contains the opened handle to the comm port etc. Now, the problem is under Windows when your thread executes and hits the WaitCommEvent(), your reader thread will go to sleep waiting for serial port data. Okay, that's fine and as it should be, but... how do you end this thread and get the MessageBox() to appear? Well, as it turns out, it's not actually that easy and is a fundamental difference between Windows CE and Windows in the way it does its Serial 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下你可以做一件事来结束WaitCommEvent(),那就是调用CancelSynchronousIo()函数,这将结束你的WaitCommEvent(),但要注意这可能是设备依赖的。 CancelSynchronousIo()的主要问题是Windows CE也不支持它,所以你运气不好用于解决这个问题!

Under Windows CE, you can do a couple of things to make the WaitCommEvent() fall through, such as SetCommMask(COMMPORT_HANDLE, 0) or even CloseHandle(COMMPORT_HANDLE). This will allow you to properly terminate your thread and therefore release the serial port for you to start sending data again. However neither of these things will work under Windows and both will cause the thread you call them from to sleep waiting on the completion of the WaitCommEvent(). So, how do you end the WaitCommEvent() under Windows? Well, ordinarily you'd use OVERLAPPED I/O and the thread blocking wouldn't be an issue, but since the solution has to be compatible with Windows CE as well, OVERLAPPED I/O isn't an option. There is one thing you can do under Windows to end the WaitCommEvent() and that is to call the CancelSynchronousIo() function and this will end your WaitCommEvent(), but be aware this can be device dependent. The main problem with CancelSynchronousIo() is that it isn't supported by Windows CE either, so you're out of luck using that for this problem!

那么怎么做你做吧?事实上,要解决此问题,您根本无法使用WaitCommEvent(),因为无法在Windows CE支持的Windows上终止此功能。然后,您将使用ReadFile(),它会在读取NON OVERLAPPED I / O时再次阻塞,而 WILL 可以使用Comm Timeouts。

So how do you do it? The fact is, to solve this problem, you simply can't use WaitCommEvent() as there is no way to terminate this function on Windows that is supported by Windows CE. That then leaves you with ReadFile() which again will block whilst it is reading NON OVERLAPPED I/O and this WILL work with Comm Timeouts.

使用ReadFile()和COMMTIMEOUTS结构意味着您必须有一个紧凑的循环等待您的串行数据,但如果您没有收到大量的串行数据,它应该不是问题。此外,以小超时结束循环的事件还将确保将资源传递回系统,并且您不会将处理器置于100%负载下。以下是我提出的解决方案,如果您认为可以改进,我会很感激您的反馈。

Using ReadFile() and a COMMTIMEOUTS structure does mean that you will have to have a tight loop waiting for your serial data, but if you're not receiving large amount of serial data, it shouldn't be a problem. Also an event for ending your loop with a small timeout will also ensure that resources are passed back to the system and you're not hammering the processor at 100% load. Below is the solution I came up with and would appreciate some feedback, if you think it could be improved.

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上正常工作。

So to end the thread you just signal the event in the event_handle and that allow you to exit the thread properly and clean up resources and works correctly on Windows and Windows CE.

希望能帮助我见过的所有人都遇到此问题。

Hope that helps everyone who I've seen has had difficulty with this problem.

推荐答案

由于我认为上面的评论存在误解,所以这里有两个可能的解决方案的更多细节,这些解决方案不使用紧密循环。请注意,这些使用运行时确定,因此在两个操作系统下都很好(尽管你必须分别为每个目标编译),并且因为它们都不使用 #ifdef 它不太可能结束在没有立即注意到的情况下打破编译器的一方或另一方。

Since I think there was a misunderstanding in my comment above, here's more detail on two possible solutions that don't use a tight loop. Note that these use runtime determination and aretherefore fine under both OSes (though you have to compile for each target separately anyway) and since neither use an #ifdef it's less likely to end up breaking the compiler on one side or the other without you noticing immediately.

首先,您可以动态加载CancelSynchonousIo并在操作系统中使用它时使用它。甚至可选择做某事而不是取消CE(比如关闭句柄?);

First, you could dynamically load CancelSynchonousIo and use it when present in the OS. Even optionally doing something instead of the Cancel for CE (like maybe closing the handle?);

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 ++,那么对于基于平台的单独类来说这将是一个很好的例子)将确定平台并在桌面上重叠使用:

The other option, which takes a bit more work as you're going to likely have different threading models (though if you're using C++, it would be an excellent case for separate classes based on platform anyway) would be to determine the platform and use overlapped on the desktop:

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);
}

这篇关于串行I / O重叠/非重叠Windows / Windows CE的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆