COMMTIMEOUT 和线程在串口读函数上没有超时

COMMTIMEOUT and Thread not timming out on serial port Read function

我正在调用一个通过串行端口操作 I/o 开发板的函数,以检查它是否正在我的主 class.
实例中进行通信 我知道这是有风险的,但不幸的是,这是一段已经使用了一段时间的旧代码,因此在我被要求改进功能时我无法更改它。

如果没有通信问题,应用程序将启动,使用该功能并继续没有问题。
当 I/o 板出现通信故障时会出现问题,我发现读取功能挂起并在大多数时间阻止应用程序启动。有时应用程序会加载并报告存在通信故障。

我想要实现的是每次出现通信故障时应用程序都能成功加载。

comport 最初是用 COMMTIMEOUTs 设置的,我预计当没有任何内容可读时端口会超时。我试图改变时间但无济于事。

我也尝试过为读取功能使用一个线程,这样它就不会阻止启动但它仍然挂起。

目前正在同步设置端口。

有人有什么建议吗?如果需要,我可以放一些代码示例。

Main.cpp

extern COMPort comPort;
BOOL Main::InitInstance()
{
 int i = comPort.DoorLatchOff();
 If(i<0) printf("Error checksum. COM port?\n");
 else printf("checksum ok.\n");
}

COMPort.h

class CCOMPort    
{  
  public:  
   CCOMPort (COM_PORT port = NULL_COM, DCB * state = NULL);
   BOOL SetPortNumber (COM_PORT port = NULL_COM, DCB * state = NULL);
   void Read(BYTE* buff, int count); 
   int DoorLatchOff(void); 
  protected:  
   HANDLE    m_hPort;  
};   
static HANDLE    m_hPortThreaded;
typedef struct readParam{BYTE* readBuff;int readCount;}RP, *PRP;

DWORD WINAPI ThreadedRead( LPVOID lpParam );

COMPort.cpp

CCOMPort::CCOMPort (COM_PORT port, DCB * state) : m_portNum (port), m_hPort(INVALID_HANDLE_VALUE)
{
 SetPortNumber (port, state);
}
BOOL CCOMPort::SetPortNumber (COM_PORT port, DCB * state)
{
  if (m_hPort != INVALID_HANDLE_VALUE){
    ::CloseHandle (m_hPort);
    m_hPort = INVALID_HANDLE_VALUE;
  }

   m_portNum     = port;
   m_currState   = m_defState;
   m_originState = m_defState;

  if (m_portNum != NULL_COM){
  stringstream ssPortName;
  ssPortName << "COM" << (m_portNum + 1) << ":" << flush;

  m_hPort = ::CreateFile (ssPortName.str().c_str(), 
                         GENERIC_READ | GENERIC_WRITE, 
                         FILE_SHARE_READ | FILE_SHARE_WRITE,
                          NULL, 
                          OPEN_EXISTING, 
                          FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, 
                          NULL);

  if (m_hPort == INVALID_HANDLE_VALUE)
    return FALSE;
  else
    {
      GetState (& m_originState);

     if (state)
      m_currState = * state;
     SetState (& m_currState);

     GetCommTimeouts(m_hPort, &timeouts);

     timeouts.ReadIntervalTimeout = 75; //15
     timeouts.ReadTotalTimeoutMultiplier = 5;       //1
     timeouts.ReadTotalTimeoutConstant = 1250;      //250
     timeouts.WriteTotalTimeoutMultiplier = 5;      //1
     timeouts.WriteTotalTimeoutConstant = 1250;     //250
     SetCommTimeouts(m_hPort, &timeouts);
     FlushOutput ();
     FlushInput ();
     PurgeOutput ();
     PurgeInput ();
    }
  }

  return TRUE;
}
void CCOMPort::Read(BYTE* buff, int count)
{
  PRP pReadArray[1];
  DWORD dwThreadArray[1];
  HANDLE hThreadArray[1];
  m_hPortThreaded = m_hPort;

  pReadArray[0] = (PRP) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RP));

  if(pReadArray[0] == NULL){
    ExitProcess(2);
  }

  pReadArray[0]->readBuff = buff;
  pReadArray[0]->readCount = count;
  hThreadArray[0] = CreateThread(NULL,
                                    0,
                         ThreadedRead,
                        pReadArray[0],
                                    0,
                   &dwThreadArray[0]);

  if(hThreadArray[0] == NULL){
    ExitProcess(3);
  }

  WaitForSingleObject(hThreadArray[0],500/*INFINITE*/);
  CloseHandle(hThreadArray[0]);

  if(pReadArray[0] != NULL){
    HeapFree(GetProcessHeap(), 0, pReadArray[0]);
    pReadArray[0] = NULL;    
  }
}

DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
  BOOL bDone = FALSE, bResult;
  //int buff_idx = 0;
  DWORD dwCommModemStatus;
  DWORD dwBytesTransfered;

  PRP pReadArray;
  pReadArray = (PRP)lpParam;
  SetCommMask(m_hPortThreaded, EV_RXCHAR);
  while(!bDone){
    WaitCommEvent(m_hPortThreaded, &dwCommModemStatus, 0);
    if(dwCommModemStatus == 0){
    bDone = TRUE;
    break;
    }
    if(dwCommModemStatus & EV_RXCHAR){
      bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);
      bDone = TRUE;
    }
  }
  return(bResult);

}
int COMPort::DoorLatchOff(void)
{
  unsigned char comm_str[10];
  int chksum, chksum1;
  DWORD count = 6;

  WriteComm(21, 7, 0);
  comm.Read(comm_str, count);

  chksum = comm_str[0] + comm_str[2] + comm_str[3];
  chksum1 = comm_str[4];
  chksum1 = (chksum1 << 8) | comm_str[5];

  if(chksum == chksum1)
   return(0);
  else
   return(-1);
}

处理硬件 I/O 时,最佳做法是将应用程序 (GUI) 线程与命令执行线程分离。
如果您正在开发 C++ Win32 应用程序,您可以使用 SerialLib。它是一个古老但稳定的Win32事件驱动串行库。

请问,您能否尝试从 ThreadedRead 中删除 WaitCommEvent 函数,看看它是否仍然挂起?

DWORD WINAPI ThreadedRead(LPVOID lpParam)
{
    BOOL bResult;
    DWORD dwBytesTransfered = 0;

    PRP pReadArray;
    pReadArray = (PRP)lpParam;

    while (dwBytesTransfered == 0) {
        bResult = ReadFile(m_hPortThreaded, pReadArray[0].readBuff, pReadArray[0].readCount, &dwBytesTransfered, 0);

        Sleep(250);
    }

    return(bResult);
}

最近遇到同样的问题,不过已经解决了。 有两种方式:

  1. 在论坛上,有些人建议将 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 都设置为 MAXDWORD,如 MSDN 文档中 REMARKS 部分所推荐的那样。但在这种情况下,函数 returns 每次输入缓冲区中至少有一个字符时。

  2. 我发现的最可靠的决定是将 ReadIntervalTimeout 和 ReadTotalTimeoutMultiplier 设置为 0,并将 ReadTotalTimeoutConstant 设置为您的超时值,如下所示。对我来说效果很好。

    COMMTIMEOUTS        commtimeouts;
    
    GetCommTimeouts (hCommFile, &commtimeouts);
    
    commtimeouts.ReadIntervalTimeout         = 0;
    commtimeouts.ReadTotalTimeoutMultiplier  = 0;
    commtimeouts.ReadTotalTimeoutConstant    = timeout;
    commtimeouts.WriteTotalTimeoutMultiplier = 0;
    commtimeouts.WriteTotalTimeoutConstant   = 0;
    
    SetCommTimeouts (hCommFile, &commtimeouts);