norains的专栏

只专注于WINCE开发

用户操作
[即时聊天] [发私信] [加为好友]
norainsID:norains
142873次访问,排名598,好友0人,关注者53人。
代码其实是一种乐趣
norains的文章
原创 189 篇
翻译 0 篇
转载 10 篇
评论 274 篇
norains的公告
联系方式请看置顶文章
最近评论
dfdf:讨厌MFC!我觉得MFC就是太乱了!看似无用的代码不要不行,MD微软啥都给我们做完了,原理性的东西我们却永远没法搞懂了!
ironox:有个地方 我觉得很别扭,不知道怎么办好

比如说 CReg reg(HKEY_CURRENT_USER,TEXT("ControlPanel\Volume"));
ControlPanel\Volume 有可能不存在呀,这个该怎么处理哦?对象虽然创建了,出错了也没提示
szterry:呵呵,果然工作狂技术狂,同感,一样的感觉……不过我才刚毕业一年……搞IT就是玩……
jinlking:这个botton的实现只是在主窗口画了一块区域,对于事件的处理还要放在主窗口的窗口处理函数之中,在对应的消息处理上调用CheckTap来判断是否是此“按钮”,问一下,这种方法与把按钮封装在子窗口中有什么区别,二者使用那个更好?
KUODY:博主真是好人
文章分类
收藏
    相册
    动漫
    文章图片
    程序交流
    xumercury的BLOG
    狗友们的博客
    清蒸石斑鱼
    美女如刀锋
    茁茁的BLOG
    魅力老姐的窝
    存档
    软件项目交易
    订阅我的博客
    XML聚合  FeedSky
    订阅到鲜果
    订阅到Google
    订阅到抓虾
    订阅到BlogLines
    订阅到Yahoo
    订阅到GouGou
    订阅到飞鸽
    订阅到Rojo
    订阅到newsgator
    订阅到netvibes

    原创 WinXP与WinCE串口的运行机制之比较收藏

    新一篇: 对《在C++类中实现Windows窗口的创建》一文的补充 | 旧一篇: 享受实况,品味生活

    //========================================================================
    //TITLE:
    //    WinXP与WinCE串口的运行机制之比较
    //AUTHOR:
    //    norains
    //DATE:
    //    Saturday  25-November-2006
    //Passed Environment:
    //    PC:WinXP+VC6.0
    //    CE:WinCE4.2+EVC4.0
    //========================================================================
        查看微软相关的串口通信文档,可以发现在桌面操作系统中,串口通信分为两种模式:同步和异步.而WinCE只有一种,但文档中却没标明归属哪种模式.实际上,WinCE的串口通信模式更像介于同步和异步之间.
        在此先简要地介绍何为同步和异步.所谓的同步,指得是对同一个设备或文件(在文中只的是串口COM1)的读或写操作必须要等待上一个操作完成才能进行.比如说,调用ReadFile()函数读取串口,但由于上一个WriteFile()操作没完成,ReadFile()的操作就被阻塞,直到WriteFile()完成后才能运行.而异步,则无论上一个操作是否完成,都会执行目前调用的操作.还是拿前面举的例子,在异步模式下,即使WriteFile()没有执行完成,ReadFile()也会立刻执行.
       
       
    1.CreateFile()参数的差异
        首先说明一下WinCE和WinXP打开串口时参数的差异.以打开串口COM1为例子,WinCE下的名字为"COM1:",而WinXP为"COM1",两者的唯一区别仅仅在于WinCE下多个分号.
        例如:
        HANDLE hd = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinCE
        HANDLE hd = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinXP
        在这稍微多说一下,在默认的环境下,TEXT宏在WinCE下会编译为双字节,而WinXP为单字节.换句话说,TEXT("COM1")在WinCE下相当于L"COM1",而WinXP则为"COM1".
       
       
    2.单线程比较
       还是用代码做例子来说明比较形象.这是一段单线程的代码,先对串口进行写操作,然后再读.对于WinXP来说,这是同步模式.(与主题无关的代码省略)  
       int WINAPI WinMain( HINSTANCE hInstance,
         HINSTANCE hPrevInstance,
         LPTSTR    lpCmdLine,
         int       nCmdShow)
       {
         ...     
          HANDLE hCom = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinCE
          //HANDLE Com = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinXP
         
          ...
          DWORD dwBytes;
          if(WriteFile(hCom,TEXT("COM1:"),5,&dwBytes,NULL) == FALSE) //WinCE
          //if(WriteFile(hCom,TEXT("COM1"),5,&dwBytes,NULL) == FALSE) //WinXP
          {
            return 0x05;
          }
         
          ...
          DWORD dwRead;
          char szBuf[MAX_READ_BUFFER];
          if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,NULL) == FALSE)
          {
            return 0x10;
          }
         
          ...
       }
        经过实验,可以发现这段代码在WinCE和WinXP下都能正常工作,并且其表现也相同,都是在WriteFile()函数返回后才执行ReadFile().
        由于异步模式在单线程中也能正常运作,唯一的不同只是在执行WriteFile()时可能也会执行ReadFile()(依WriteFile()函数执行的时间而定),所在此不表.
      
    3.多线程比较
        单线程两者表现相同,那多线程呢?下面这段代码采用多线程,先是创建一个读的线程,用来监控串口是否有新数据到达,然后在主线程中对串口写出数据.
        这里假设是这么一个情况,有两台设备,分别为A和B,下面的代码运行于设备A,设备B仅仅只是应答而已.换句话说,只有A向B发送数据,B才会返回应答信号.
        //主线程
        int WINAPI WinMain( HINSTANCE hInstance,
         HINSTANCE hPrevInstance,
         LPTSTR    lpCmdLine,
         int       nCmdShow)
        {
          ...
          CreateThread(NULL,0,ReadThread,0,0,&dwThrdID); //创建一个读的线程.
       
          ...     
          HANDLE hCom = CreateFile(TEXT("COM1:"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinCE
          //HANDLE Com = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,NULL); //WinXP
         
          ...
          DWORD dwBytes;
          if(WriteFile(hCom,"AT\r\n",4,&dwBytes,NULL) == FALSE) //WinCE
          //if(WriteFile(hCom,"AT\r\n",5,&dwBytes,NULL) == FALSE) //WinXP
          {
            return 0x05;
          }
         
          ...
       
        }
       
        //读线程
        DWORD WINAPI ReadThread()
        {
          ...
          SetCommMask(hCom),EV_RXCHAR);
          DWORD dwCommStatus = 0;
        if(WaitCommEvent(hCom),&dwCommStatus,NULL) == FALSE)
        {
         //Clear the error flag
         DWORD dwErrors;
         COMSTAT comStat;
         memset(&comStat,0,sizeof(comStat));
         ClearCommError(hCom,&dwErrors,&comStat);
         return 0x15;
        }
       
        ...
        char szBuf[MAX_READ_BUFFER]={0};
        DWORD dwRead;
        if(ReadFile(hCom),szBuf,MAX_READ_BUFFER,&dwRead,NULL) == FALSE || dwRead == 0)
       {
        return 0x20;
       }
       
       ...
        }
       
        这段代码在WinCE下运行完全正常,读线程在监听收到的数据的同时,主线程顺利地往外发数据.
        然而同样的代码,在WinXP下则根本无法完成工作.运行此代码,我们将发现CPU的占用率高达99%.通过单步调试,发现两个线程分别卡在WaitCommEvent()和WriteFile()函数中.因为根据同步模式的定义,当前对设备的操作必须要等待上一个操作完毕方可执行.在以上代码中,因为设备B没接到设备A的命令而不会向设备A发送应答,故WaitCommEvent()函数因为没有检测到接受数据而一直在占用串口;而WaitCommEvent()一直占据串口使得WriteFile()没有得到串口资源而处于阻塞状态,这就造成了死锁.
        而这种情况没有在WinCE上出现,只要WaitCommEvent()和WriteFile()不在同一个线程,就可以正常工作.这应该和系统的调度方式有关.
       
        如果要在PC上同时进行WaitCommEvent()和WriteFile()操作,需要把串口的模式改写为异步模式.
        更改后的代码如下:
       
        //主线程
        int WINAPI WinMain( HINSTANCE hInstance,
         HINSTANCE hPrevInstance,
         LPTSTR    lpCmdLine,
         int       nCmdShow)
        {
          ...
          CreateThread(NULL,0,ReadThread,0,0,&dwThrdID); //创建一个读的线程.
       
          ...     
          HANDLE Com = CreateFile(TEXT("COM1"),GENERIC_READ|GENERIC_WRITE,0,NULL,OPEN_EXISTING,0,FILE_FLAG_OVERLAPPED);
         
          ...
          OVERLAPPED olWrite;
         memset(&olWrite,0,sizeof(m_olWrite));
         olWrite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
     
          DWORD dwBytes; 
         if(WriteFile(hCom,"AT\r\n",4,&dwBytes,&olWrite) == FALSE)
         {
          if(GetLastError() != ERROR_IO_PENDING)
          {
           return 0x20;
          }
         }
         if(GetOverlappedResult(hCom,&olWrite,&dwBytes,TRUE) == FALSE)
         {
          return 0x25;
         }
          ...
       
        }
       
        //读线程
        DWORD WINAPI ReadThread()
        {
          ...     
         memset(&olWaite,0,sizeof(olWaite)); 
         olWaite.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);     
          SetCommMask(hCom),EV_RXCHAR);
          DWORD dwCommStatus = 0;
        WaitCommEvent(hCom,&dwCommStatus,olWaite);
        DWORD dwByte; //norains:It is only suitable for the GetOverlappedResult(),not undefined here.
        if(GetOverlappedResult(hCom,olWaite,&dwByte,TRUE) == FALSE)
        {
         if(GetLastError() != ERROR_IO_PENDING)
         {
          return 0x30;
         }
         //Clear the error flag
         DWORD dwErrors;
         COMSTAT comStat;
         memset(&comStat,0,sizeof(comStat));
         ClearCommError(hCom,&dwErrors,&comStat);
         return 0x35;
        }
       
        ...
        memset(&olRead,0,sizeof(olRead));
        olRead.hEvent = CreateEvent(NULL,TRUE,FALSE,NULL);
        char szBuf[MAX_READ_BUFFER]={0};
        DWORD dwRead;
        if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,olRead) ==FALSE)
        {
         if(GetLastError() != ERROR_IO_PENDING)
         {
          return 0x40;
         }
         if(GetOverlappedResult(hCom,olRead,&dwRead,TRUE) == FALSE)
         {
          return 0x45;
         }
         if(dwRead == 0)
         {
          return 0x50;
         }
        }
       
       ...
        }
       
        测试经过更改后的代码,可以发现在WinXP下终于可以同时调用WaitCommEvent()和WriteFile()而不造成死锁.
        在这里可以发现WinCE和WinXP的串口调度的差异性:单线程中,WinCE的串口工作方式和WinXP串口的同步工作模式相符;而多线程中,WinCE串口工作方式却又和WinXP的异步方式吻合.虽然无法确切比较WinCE的单一串口模式是否比WinXP的双模式更为优越,但可以确认的是,WinCE的这种串口调用方式给程序员带来了极大的便利.
       
       
    4.WinXP异步模式两种判断操作是否成功的方法
        因为在WinXP的异步模式中,WriteFile(),ReadFile()和WaitCommEvent()大部分情况下都是未操作完毕就返回,所以不能简单地判断返回值是否为TRUE或FALSE来判断.
        以ReadFile()函数做例子.
        一种是上文所用的方法:
        if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,olRead) ==FALSE)
        {
         if(GetLastError() != ERROR_IO_PENDING)
         {
          return 0x40;
         }
         if(GetOverlappedResult(hCom,olRead,&dwRead,TRUE) == FALSE)
         {
          return 0x45;
         }
         if(dwRead == 0)
         {
          return 0x50;
         }
        }
        如果ReadFile()返回为TRUE,则表明读文件已经完成.但这种情况几乎不会出现,因为对外设的读写相对于内存的读写来说非常慢,所以一般在ReadFile()函数还没执行完毕,程序已经执行到下一个语句.
        当ReadFile()返回为FALSE时,需要采用GetLastError()函数判断读操作是否在后台进行.如果在后台进行,则调用GetOverlappedResult()函数获取ReadFile()函数的结果.在这里要注意的是,GetOverlappedResult()函数的最后一个参数必须设置为TRUE,表明要等ReadFile()函数在后台运行完毕才返回.如果最后一个参数设置为FALSE,则即使ReadFile()还在后台执行,GetOverlappedResult()函数也会立刻返回,从而造成判断错误.
       
        另一种是调用WaitForSingleObject函数达到此目的:
        if(ReadFile(hCom,szBuf,MAX_READ_BUFFER,&dwRead,olRead) ==FALSE)
        {
         if(GetLastError() != ERROR_IO_PENDING)
         {
          return 0x40;
         }
         if(WaitForSingleObject(olRead.hEvent,INFINITE) != WAIT_OBJECT_0)
        {
         return 0x55;
        } 
         if(GetOverlappedResult(hCom,olRead,&dwRead,FALSE) == FALSE)
         {
          return 0x45;
         }
         if(dwRead == 0)
         {
          return 0x50;
         }
        }
        因为ReadFile()在后台执行完毕以后,会发送一个event,所以在这里可以调用WaitForSingleObject()等待ReadFile()执行完毕,然后再调用GetOverlappedResult()获取ReadFile()的最终结果.在这里需要注意的是,GetOverlappedResult()的最后一个参数一定要设置为FALSE,因为WaitForSingleObject()已经捕获了ReadFile()发出的event,再也没有event传递到GetOverlappedResult()函数.如果此时GetOverlappedResult()最后一个参数设置为TRUE,则线程会一直停留在GetOverlappedResult()函数而不往下执行. 

    发表于 @ 2006年11月25日 21:51:00|评论(loading...)|编辑

    新一篇: 对《在C++类中实现Windows窗口的创建》一文的补充 | 旧一篇: 享受实况,品味生活

    评论:没有评论。

    发表评论  


    登录
    Csdn Blog version 3.1a
    Copyright © norains