Ошибка WaitCommEvent из-за недопустимого параметра при втором проходе

В моем приложении используется последовательный ввод-вывод с перекрывающимися событиями. По какой-то причине ::WaitCommEvent постоянно терпит неудачу при втором проходе цикла с ERROR_INVALID_PARAMETER. Если кто-нибудь может объяснить, что мне нужно делать по-другому, это было бы очень признательно. Мой код инициализации/открытия последовательного порта и функция потока следуют. Следует отметить, что код инициализации/открытия выполняется после запуска функции потока, для чего и вызывается ::WaitForSingleObject.

Кроме того, мне было интересно, будет ли что-то вроде ::WaitForSingleObject( pobjSerialPort->m_hSerialPort, INFINITE ); допустимым в качестве неблокирующего средства определения, когда последовательный порт открыт.

Инициализация последовательного порта:

DWORD CSerialPort::Open( const wchar_t * portName )
{
    DCB dcb = {0};
    DWORD dwError = ERROR_SUCCESS;

    do
    {
        if ( this->IsOpen() != FALSE )
        {
            TRACE(_T("CSerialPort::Open : Warning: Attempted to re-open serial port that is already open.\r\n"));
            continue;
        }

        // Overwrite port name if specified.
        if ( portName != NULL )
        {
            this->m_pwcPortName.clear();
            this->m_pwcPortName.append( SP_NAME_PREFIX );
            this->m_pwcPortName.append( portName );
        }

        ASSERT(this->m_pwcPortName.length() > 0);

        // Open the serial port.
        if ( (this->m_hSerialPort = ::CreateFile(
            m_pwcPortName.c_str(),          // Formatted serial port name
            GENERIC_READ | GENERIC_WRITE,   // Access: Read and write
            0,                              // Share: No sharing
            NULL,                           // Security: None
            OPEN_EXISTING,                  // COM port already exists
            FILE_FLAG_OVERLAPPED,           // Asynchronous I/O
            NULL                            // No template file for COM port
            )) == INVALID_HANDLE_VALUE )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to get the handle to the serial port.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Initialize the DCB structure with COM port parameters.
        if ( !::BuildCommDCB( _T("baud=38400 parity=N data=8 stop=1"), &dcb ) )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to build the DCB structure.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Set the serial port communications events mask.
        if ( !::SetCommMask( this->m_hSerialPort, SP_COMM_MASK ) )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to set comm. events to be monitored.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Set serial port parameters.
        if ( !::SetCommState( this->m_hSerialPort, &dcb ) )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to set the comm state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Set the serial port communications timeouts.
        this->m_ct.ReadIntervalTimeout = MAXDWORD;
        this->m_ct.ReadTotalTimeoutMultiplier = 0;
        this->m_ct.ReadTotalTimeoutConstant = 0;
        this->m_ct.WriteTotalTimeoutMultiplier = 0;
        this->m_ct.WriteTotalTimeoutConstant = 0;

        if ( !::SetCommTimeouts( this->m_hSerialPort, &(this->m_ct) ) )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to set the comm timeout values.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Create thread to receive data.
        if ( (this->m_hSpRxThread = CreateThread(
            NULL,                           // No security attributes.
            0,                              // Use default initial stack size.
            reinterpret_cast<LPTHREAD_START_ROUTINE>(SerialPortRxThreadFn), // Function to execute in new thread.
            this,                           // Thread parameters.
            0,                              // Use default creation settings.
            NULL                            // Thread ID is not needed.
            )) == NULL )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to create serial port receive thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Create thread to transmit data.
        if ( (this->m_hSpTxThread = CreateThread(
            NULL,                           // No security attributes.
            0,                              // Use default initial stack size.
            reinterpret_cast<LPTHREAD_START_ROUTINE>(SerialPortTxThreadFn), // Function to execute in new thread.
            this,                           // Thread parameters.
            0,                              // Use default creation settings.
            NULL                            // Thread ID is not needed.
            )) == NULL )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to create serial port transmit thread.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        if ( !::SetEvent( this->m_hSpPortOpenEvent ) )
        {
            dwError = ::GetLastError();
            TRACE(_T("CSerialPort::Open : Failed to set event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }
    }
    while ( 0 );

    return dwError;
}

Тема событий связи с последовательным портом:

static DWORD SerialPortCommEvtsThreadFn( void * pParam )
{
    CSerialPort * pobjSerialPort = NULL;
    BOOL blContinue = TRUE;
    DWORD dwError = ERROR_SUCCESS;
    DWORD dwEventMask = 0;
    DWORD dwObjectWaitState;
    OVERLAPPED ovComm = { 0 };
    int i = 0;
    static HANDLE pHandles[SPCM_MAX_EVENTS + SP_ONE_ITEM]; // +1 for overlapped event

    // Validate parameters.
    if ( pParam == NULL )
    {
        dwError = ERROR_INVALID_PARAMETER;
        TRACE(_T("SerialPortTxThreadFn : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
        return dwError;
    }

    pobjSerialPort = (CSerialPort *)pParam;

    // Load event handles.
    pHandles[i++] = pobjSerialPort->GetCommHandle( SPCM_THREAD_EXIT_EVT_ID );
    pHandles[i++] = pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID );
    pHandles[i++] = pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID );

    while ( (blContinue != FALSE) && (dwError == ERROR_SUCCESS) )
    {
        // Wait for serial port to open.
        if ( (dwObjectWaitState = ::WaitForSingleObject( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ), INFINITE )) != WAIT_OBJECT_0 )
        {
            dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
            TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        if ( ovComm.hEvent == NULL )
        {
            // Create event object for serial port communications events OVERLAPPED structure.
            if ( (ovComm.hEvent = ::CreateEvent(
                NULL,                           // No security
                TRUE,                           // Create a manual-reset event object
                FALSE,                          // Initial state is non-signaled
                NULL                            // No name specified
                )) == NULL )
            {
                dwError = ::GetLastError();
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed to create event object.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }

            pHandles[i++] = ovComm.hEvent;
        }
        else
        {
            i++;
        }

        // Wait for a communications event.
        if ( !::WaitCommEvent( pobjSerialPort->m_hSerialPort, &dwEventMask, &ovComm ) )
        {
            if ( (dwError = ::GetLastError()) != ERROR_IO_PENDING )
            {
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
            else
            {
                dwError = ERROR_SUCCESS;
            }
        }
        else
        {
            if ( (dwError = HandleCommOvEvent( pobjSerialPort, dwEventMask )) != ERROR_SUCCESS )
            {
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed handling communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }

            continue;
        }

        dwObjectWaitState = ::WaitForMultipleObjects( i--, pHandles, FALSE, INFINITE );

        switch ( dwObjectWaitState )
        {
        case WAIT_OBJECT_0 + SPCM_THREAD_EXIT_EVT_ID:
            blContinue = FALSE;
            break;

        case WAIT_OBJECT_0 + SPCM_PORT_OPEN_EVT_ID:
            if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID ) ) )
            {
                dwError = ::GetLastError();
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
            break;

        case WAIT_OBJECT_0 + SPCM_PORT_CLOSED_EVT_ID:
            if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ) ) )
            {
                dwError = ::GetLastError();
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
            break;

        case WAIT_OBJECT_0 + SPCM_MAX_EVENTS:
            if ( (dwError = HandleCommOvEvent( pobjSerialPort, dwEventMask )) != ERROR_SUCCESS )
            {
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed handling communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }

            ::CloseHandle( ovComm.hEvent );
            ::memset( &ovComm, 0, sizeof(OVERLAPPED) );
            break;

        default:
            dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
            TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            break;
        }
    }

    return dwError;
}

Функция обработки событий связи

static DWORD HandleCommOvEvent( CSerialPort * pobjSerialPort, DWORD dwEvtMask )
{
    DWORD dwError = ERROR_SUCCESS;

    do
    {
        // Validate parameters.
        if ( pobjSerialPort == NULL )
        {
            dwError = ERROR_INVALID_PARAMETER;
            TRACE(_T("HandleCommEvent : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        // Handle the transmit complete event.
        if ( dwEvtMask & EV_TXEMPTY )
        {
            if ( (dwError = HandleTxDoneCommEvent( pobjSerialPort )) != ERROR_SUCCESS )
            {
                TRACE(_T("HandleCommEvent : Failed handling transmit complete event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
        }

        // Handle the received data event.
        if ( dwEvtMask & EV_RXCHAR )
        {
            if ( (dwError = HandleRxDataCommEvent( pobjSerialPort )) != ERROR_SUCCESS )
            {
                TRACE(_T("HandleCommEvent : Failed handling received data event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
        }
    }
    while ( 0 );

    return dwError;
}

Функция обработки события связи с полученными данными

static DWORD HandleRxDataCommEvent( CSerialPort * pobjSerialPort )
{
    DWORD dwError = ERROR_SUCCESS;

    do
    {
        // Validate parameters.
        if ( pobjSerialPort == NULL )
        {
            dwError = ERROR_INVALID_PARAMETER;
            TRACE(_T("HandleRxDataCommEvent : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }

        if ( !::SetEvent( pobjSerialPort->GetRxHandle( SPRX_RECEIVED_DATA_EVT_ID ) ) )
        {
            dwError = ::GetLastError();
            TRACE(_T("HandleRxDataCommEvent : Failed setting event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            continue;
        }
    }
    while ( 0 );

    return dwError;
}

Получить функцию потока

static DWORD SerialPortRxThreadFn( void * pParam )
{
    CSerialPort * pobjSerialPort = NULL;
    BOOL blContinue = TRUE;
    DWORD dwError = ERROR_SUCCESS;
    DWORD dwEventMask = 0;
    DWORD dwObjectWaitState;
    OVERLAPPED ovComm = { 0 };
    int i = 0;
    static BYTE pBuf[SP_RX_BUF_SIZE];
    static HANDLE pHandles[SPRX_MAX_EVENTS + SP_ONE_ITEM]; // +1 for overlapped event

    ASSERT(s_pobjRxBuffer != NULL);

    // Validate parameters.
    if ( pParam == NULL )
    {
        dwError = ERROR_INVALID_PARAMETER;
        TRACE(_T("SerialPortRxThreadFn : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
        return dwError;
    }

    pobjSerialPort = (CSerialPort *)pParam;

    // Load event handles.
    pHandles[i++] = pobjSerialPort->GetRxHandle( SPRX_THREAD_EXIT_EVT_ID );
    pHandles[i++] = pobjSerialPort->GetRxHandle( SPRX_RECEIVED_DATA_EVT_ID );

    while ( (blContinue != FALSE) && (dwError == ERROR_SUCCESS) )
    {
        if ( ovComm.hEvent == NULL )
        {
            // Create event object for serial port communications events OVERLAPPED structure.
            if ( (ovComm.hEvent = ::CreateEvent(
                NULL,                           // No security
                TRUE,                           // Create a manual-reset event object
                FALSE,                          // Initial state is non-signaled
                NULL                            // No name specified
                )) == NULL )
            {
                dwError = ::GetLastError();
                TRACE(_T("SerialPortRxThreadFn : Failed to create event object.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }

            pHandles[i++] = ovComm.hEvent;
        }
        else
        {
            i++;
        }

        dwObjectWaitState = ::WaitForMultipleObjects( i--, pHandles, FALSE, INFINITE );

        switch ( dwObjectWaitState )
        {
        case WAIT_OBJECT_0 + SPRX_THREAD_EXIT_EVT_ID:
            blContinue = FALSE;
            break;

        case WAIT_OBJECT_0 + SPRX_RECEIVED_DATA_EVT_ID:
            if ( !::ReadFile( pobjSerialPort->m_hSerialPort, pBuf, SP_RX_BUF_SIZE, NULL, &ovComm ) )
            {
                if ( (dwError = ::GetLastError()) != ERROR_IO_PENDING )
                {
                    TRACE(_T("SerialPortRxThreadFn : Failed reading data from serial port.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                    continue;
                }
            }
            else
            {
                if ( (dwError = HandleReceivedDataOvEvent( pobjSerialPort, &ovComm, pBuf )) != ERROR_SUCCESS )
                {
                    TRACE(_T("SerialPortRxThreadFn : Failed handling serial port received data event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                    continue;
                }
            }
            break;

        case WAIT_OBJECT_0 + SPRX_MAX_EVENTS:
            if ( (dwError = HandleReceivedDataOvEvent( pobjSerialPort, &ovComm, pBuf )) != ERROR_SUCCESS )
            {
                TRACE(_T("SerialPortRxThreadFn : Failed handling serial port received data event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }

            ::CloseHandle( ovComm.hEvent );
            ::memset( &ovComm, 0, sizeof(OVERLAPPED) );
            break;

        default:
            dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
            TRACE(_T("SerialPortRxThreadFn : There is a problem with the OVERLAPPED structure's event handle.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            break;
        }
    }

    return dwError;
}

person Jim Fell    schedule 16.05.2011    source источник


Ответы (3)


Похоже, вы пытаетесь использовать одну и ту же структуру OVERLAPPED для WaitCommEvent и ReadFile. Это должно вызывать у вас бесконечные проблемы.


Ссылка на документацию: При выполнении нескольких одновременных перекрывающихся операций в одном потоке вызывающий поток должен указать структура OVERLAPPED для каждой операции


Попытка исправить (не проверено, даже не скомпилировано). Вещи, которые необходимо заполнить для вашего проекта, помечены // TODO.

static DWORD SerialPortCommEvtsThreadFn( void * pParam )
{
    CSerialPort * pobjSerialPort = NULL;
    BOOL blContinue = TRUE;
    DWORD dwError = ERROR_SUCCESS;
    DWORD dwEventMask = 0;
    DWORD dwObjectWaitState;
    OVERLAPPED ovWaitComm = { 0 };
    OVERLAPPED ovRead = { 0 };
    const DWORD numHandles = SPCM_MAX_EVENTS + 2;
    HANDLE pHandles[numHandles];

    // Validate parameters.
    if ( pParam == NULL )
    {
        dwError = ERROR_INVALID_PARAMETER;
        TRACE(_T("SerialPortTxThreadFn : Invalid parameter.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
        return dwError;
    }
    pobjSerialPort = static_cast<CSerialPort *>(pParam);

    ovWaitComm.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    ovRead.hEvent = ::CreateEvent(NULL, FALSE, FALSE, NULL);
    if (!ovWaitComm.hEvent || !ovRead.hEvent) {
        dwError = ::GetLastError();
        TRACE(_T("SerialPortCommEvtsThreadFn : Failed to create event objects.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
        return dwError;
    }

    // Load event handles.
    pHandles[SPCM_THREAD_EXIT_EVT_ID ] = pobjSerialPort->GetCommHandle( SPCM_THREAD_EXIT_EVT_ID );
    pHandles[SPCM_PORT_OPEN_EVT_ID ] = pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID );
    pHandles[SPCM_PORT_CLOSED_EVT_ID ] = pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID );
    pHandles[numHandles - 2] = ovWaitComm.hEvent;
    pHandles[numHandles - 1] = ovRead.hEvent;

    // Wait for serial port to open.
    if ( (dwObjectWaitState = ::WaitForSingleObject( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ), INFINITE )) != WAIT_OBJECT_0 ) {
        dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
        TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
        return dwError;
    }

    // TODO: SetCommTimeouts

    // Wait for a communications event.
    if ( !::WaitCommEvent( pobjSerialPort->m_hSerialPort, &dwEventMask, &ovWaitComm ) {
        if ( (dwError = ::GetLastError()) != ERROR_IO_PENDING ) {
            TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            return dwError;
        }
    }

    // TODO: Here, call ReadFile, passing &ovRead

    dwError = ERROR_SUCCESS;
    while ( blContinue && (dwError == ERROR_SUCCESS) )
    {
        dwObjectWaitState = ::WaitForMultipleObjects( numHandles, pHandles, FALSE, INFINITE );
        switch ( dwObjectWaitState )
        {
        case WAIT_OBJECT_0 + SPCM_THREAD_EXIT_EVT_ID:
            blContinue = FALSE;
            break;

        case WAIT_OBJECT_0 + SPCM_PORT_OPEN_EVT_ID:
            if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_CLOSED_EVT_ID ) ) )
            {
                dwError = ::GetLastError();
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
            break;

        case WAIT_OBJECT_0 + SPCM_PORT_CLOSED_EVT_ID:
            if ( !::ResetEvent( pobjSerialPort->GetCommHandle( SPCM_PORT_OPEN_EVT_ID ) ) )
            {
                dwError = ::GetLastError();
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed to reset event object signal state.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }
            break;

        case WAIT_OBJECT_0 + numHandles - 2:
            if ( (dwError = HandleCommOvEvent( pobjSerialPort, dwEventMask )) != ERROR_SUCCESS )
            {
                TRACE(_T("SerialPortCommEvtsThreadFn : Failed handling communications event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
                continue;
            }

            // TODO: call WaitCommEvent again, if HandleCommOvEvent didn't already
            break;

        case WAIT_OBJECT_0 + numHandles - 1:
            // TODO: do something with the received data, it's now in the buffer supplied to ReadFile
            // TODO: call ReadFile again
            break;

        default:
            dwError = ( dwObjectWaitState == WAIT_FAILED ) ? ::GetLastError() : dwObjectWaitState;
            TRACE(_T("SerialPortCommEvtsThreadFn : Failed waiting for event.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), dwError, _T(__FILE__), __LINE__);
            break;
        }
    }

    return dwError;
}
person Ben Voigt    schedule 16.05.2011
comment
Не могли бы вы уточнить? Я думал, что они должны использовать одну и ту же структуру OVERLAPPED. - person Jim Fell; 16.05.2011
comment
@ Джим: О, абсолютно нет. Структура OVERLAPPED используется ядром для хранения хода операции. С момента запуска операции и до завершения операции (проверено с помощью GetOverlappedResult или WaitForSingleObject) буфер OVERLAPPED принадлежит ядру, и с ним нельзя ничего делать, особенно его нельзя ни освободить, ни передать каким-либо функциям API. (исключение: вы можете и должны передать его адрес GetOverlappedResult и CancelIoEx). OVERLAPPED является точным аналогом типа данных aiocb (блок управления асинхронным вводом-выводом) в Linux. - person Ben Voigt; 17.05.2011
comment
Это должно быть само собой разумеющимся, поскольку, если вы передали один и тот же OVERLAPPED буфер нескольким операциям, как GetOverlappedResult узнает, результат какой операции проверять? - person Ben Voigt; 17.05.2011
comment
Вызовы WaitCommEvent и ReadFile являются частью одной и той же операции. ReadFile используется для чтения данных из COM-порта, а WaitCommEvent используется для определения состояния ввода/вывода этого COM-порта. - person Jim Fell; 17.05.2011
comment
@Jim: Часть той же задачи в соответствии с тем, как вы об этом думаете, но не часть тех же операций, поскольку операция определяется API Win32. WaitCommEvent начинает операцию с перекрытием. Как и ReadFile. Для этих двух операций требуются два отдельных буфера OVERLAPPED. - person Ben Voigt; 17.05.2011
comment
Я очень не решаюсь переписать последовательный ввод-вывод для своего приложения, потому что кажется, что каждый раз, когда я даже его настраиваю, он ломается, и требуется пара дней, чтобы он снова заработал. Как обработать два события для двух структур OVERLAPPED? Если бы вы могли указать мне пример того, как это делается с асинхронным последовательным вводом-выводом, или предоставить некоторый псевдокод в своем ответе, это было бы очень полезно. - person Jim Fell; 17.05.2011
comment
@Jim: В вашем обновленном коде я вижу только одну операцию (WaitCommEvent), вызов ReadFile пропал. Вы хотите, чтобы я добавил это правильно? - person Ben Voigt; 19.05.2011
comment
@Ben Voigt: я разместил дополнительный код. В HandleCommOvEvent сообщается о событии, чтобы дать указание функции потока полученных данных считать данные из последовательного порта. Другая структура OVERLAPPED используется ::ReadFile. Тем не менее, это заходит так далеко. - person Jim Fell; 19.05.2011
comment
@Ben Voigt (продолжение): в первый раз в цикле в SerialPortCommEvtsThreadFn поток ожидает открытия последовательного порта, он создает событие ручного сброса для структуры OVERLAPPED, ::WaitCommEvent возвращает false с ERROR_IO_PENDING, а первое событие возвращается WaitForMultipleObjects равно SPCM_PORT_OPEN_EVT_ID, что ожидаемо, поскольку последовательный порт всегда будет открываться при первом проходе. - person Jim Fell; 19.05.2011
comment
@Ben Voigt (продолжение): во второй раз через цикл ::WaitForSingleObject возвращается немедленно (порт уже открыт), переменная i просто увеличивается (перекрывающееся событие уже создано), а ::WaitCommEvent возвращает false. Здесь ::GetLastError возвращает ERROR_INVALID_PARAMETER. - person Jim Fell; 19.05.2011
comment
@Ben Voigt (исправление): ПК никогда не доберется до ::ReadFile до того, как произойдет ERROR_INVALID_PARAMETER из ::WaitCommEvent. - person Jim Fell; 19.05.2011
comment
@Jim: Вы также не можете запускать две операции WaitCommEvent с использованием одного и того же буфера OVERLAPPED. Не вызывайте WaitCommEvent при каждом проходе цикла. Вызовите его, когда предыдущее WaitCommEvent завершится. Я показал это в своем ответе. - person Ben Voigt; 19.05.2011
comment
@Ben Voigt: Хорошо, исправление вызова ::WaitCommEvent, похоже, решило эту проблему. Спасибо. Сейчас я работаю над сглаживанием звонка ::WriteFile, о котором скоро опубликую. - person Jim Fell; 19.05.2011
comment
@Jim: Не уверен, что вы хотите, чтобы награда осталась невостребованной ... принятие ответа не присуждает награду. - person Ben Voigt; 19.05.2011
comment
@Ben Voigt: Это не позволит мне присудить награду до завтра. Вот ссылка на мой новый вопрос: stackoverflow.com/questions/6062529/ сбой записи последовательного порта - person Jim Fell; 19.05.2011

Похоже, вы вызываете GetLastError(), когда не проверили наличие ошибки.

person Jay    schedule 16.05.2011
comment
Что ты имеешь в виду? Насколько я понимаю, при перекрытом вводе-выводе, если WaitCommEvent возвращает false, то GetLastError должно возвращать ERROR_IO_PENDING, но я получаю ERROR_INVALID_PARAMETER. WaitCommEvent - это вводящее в заблуждение имя, согласно документации MSDN, оно более или менее возвращается немедленно. Я знаю, что возникла серьезная проблема, потому что код просто крутился, пытаясь обработать ошибку ERROR_INVALID_PARAMETER, которая мешала запуску других потоков в моем приложении. - person Jim Fell; 16.05.2011

Вы звоните

WaitCommEvent(pobjSerialPort->m_hSerialPort, &dwEventMask, &(pobjSerialPort->m_ovEvents))

1-й и 3-й параметры WaitCommEvent являются входными параметрами, однако вы не указали их код инициализации.

Как вы инициализируете m_hSerialPort? Вы вызываете CreateFile с допустимыми параметрами и проверяете, нет ли ошибки? Как вы инициализируете m_ovEvents? Вы вызываете CreateEvent с допустимыми параметрами и проверяете, нет ли ошибки?

Поскольку вы получаете ERROR_INVALID_PARAMETER, я думаю, что ваша проблема заключается в инициализации одного из этих параметров.

person Lior Kogan    schedule 16.05.2011