Асинхронный последовательный порт связи в Windows в C - PullRequest
2 голосов
/ 04 июня 2009

Я получаю сообщение об ошибке при попытке запустить файл c, который выполняет некоторые основные операции записи в последовательный порт. Я пытаюсь запустить его асинхронно, потому что записи иногда занимают много времени для передачи. Моя оригинальная версия работала синхронно с командами WriteFile (), которые работали нормально. Я новичок в использовании OVERLAPPED и был бы признателен и отзыв о нем.

Я получаю ошибку:

Debug Assertion Failed! 
<path to dbgheap.c> 
Line: 1317 
Expression: _CrtIsValidHeapPointer(pUserData)

когда вызывается вторая функция записи.

В основном:

    {
        //initialized port (with overlapped), DBC, and timeouts

        result = write_port(outPortHandle, 128);
        result = write_port(outPortHandle, 131);
    }




static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped) {
        //write completed. check for errors? if so throw an exception maybe?
        printf("write completed--and made it to callback function\n");
    }


int write_port(HANDLE hComm,BYTE* lpBuf) {

   OVERLAPPED osWrite = {0};

   // Create this write operation's OVERLAPPED structure's hEvent.
   osWrite.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
   if (osWrite.hEvent == NULL)
      // error creating overlapped event handle
      return 0;

   // Issue write.
   if (!WriteFileEx(hComm, &lpBuf, 1, &osWrite, &write_compl )) {
      if (GetLastError() != ERROR_IO_PENDING) { 
         // WriteFile failed, but isn't delayed. Report error and abort.
          printf("last error: %ld",GetLastError());
          return 0; //failed, return false;
      }
      else {
         // Write is pending.
         WaitForSingleObjectEx(osWrite.hEvent, 50, TRUE);   //50 ms timeout

        return -1; //pending
      }
   }
   else {
        return 1; //finished
   }
}

Это был не полный код, извините. Я также использовал массив байтов, а не констант. Но системные («пауза») приводили к ошибкам моего утверждения отладки, и после тщательного просмотра моего кода, когда WriteFileEx () был успешным, он никогда не устанавливал предупреждение / тайм-аут на событие в перекрывающейся структуре, функция обратного вызова никогда не будет вызвана. Я исправил эти проблемы, хотя.

Мне просто нужна помощь в обработке / доступе к одному байту в структуре, которая выделяется при вызове функции ReadFileEx () (для хранения прочитанного байта, чтобы его можно было обработать). Мне нужно знать, как получить доступ к этому хранилищу BYTE, используя смещение и сделать перекрывающуюся структуру нулевой. Будет ли сделать перекрывающуюся структуру нулевой так же просто, как установить для нее дескриптор INVALID_HANDLE_VALUE?

Ответы [ 2 ]

2 голосов
/ 04 июня 2009

Я думаю, у вас есть пара вопросов:


Вы передаете целое число в качестве указателя (ваш компилятор должен предупредить об этом или предпочтительно отказаться от компиляции кода):

result = write_port (outPortHandle, 128);

Сравните это с определением write_port:

int write_port (HANDLE hComm, BYTE * lpBuf) {

Вышеуказанные утверждения не совпадают. Затем вы передаете указатель на указатель lpBuf в функцию WriteFileEx, беря адрес BYTE * -> "& lpBuf". Это не приведет к тому, что, по вашему мнению, будет.


Даже если вы исправите это, у вас все равно будут потенциальные проблемы со временем жизни, когда запись будет успешно поставлена ​​в очередь, но не будет завершена в течение 50 мс времени ожидания.

При использовании перекрывающегося ввода-вывода необходимо убедиться, что буфер чтения / записи и перекрывающаяся структура остаются действительными до тех пор, пока ввод-вывод не будет завершен, отменен или соответствующее устройство не будет закрыто. В приведенном выше коде вы используете указатель на структуру OVERLAPPED, которая находится в стеке при вызове WriteFileEx. Если WriteFileEx не завершится в течение 50 мс, ожидающий ввод-вывод будет иметь ссылку на несуществующую структуру OVERLAPPED, и у вас (надеюсь) будет нарушение доступа (или, что еще хуже, незаметно поврежденные данные стека где-то в вашем приложении).

Канонический способ решения этих проблем времени жизни (если производительность не является большой проблемой) заключается в использовании пользовательской структуры, которая включает в себя структуру OVERLAPPED и некоторое хранилище для данных для чтения / записи. Выделите структуру при публикации записи и освободите структуру из процедуры завершения ввода-вывода. Передайте адрес включенной структуры OVERLAPPED в WriteFileEx и используйте, например, offsetof для получения адреса пользовательской структуры из адреса OVERLAPPED в процедуре завершения.

Также обратите внимание, что WriteFileEx фактически не использует член hEvent, IIRC.


РЕДАКТИРОВАТЬ: Добавлен пример кода, обратите внимание:

  1. На самом деле я не пытался скомпилировать код, возможны опечатки или другие проблемы с кодом.
  2. Это не самый эффективный способ отправки данных (выделение / освобождение блока памяти для каждого отправляемого байта). Это должно быть легко улучшить, хотя.
    #include <stddef.h>
    #include <assert.h>
    #include <windows.h>

    // ...
    typedef struct _MYOVERLAPPED
    {
        OVERLAPPED ol;
        BYTE buffer;
    } MYOVERLAPPED, *LPMYOVERLAPPED;
    // ...

    static void CALLBACK write_compl(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, LPOVERLAPPED lpOverlapped)
    {
        if (NULL == lpOverlapped)
        {
            assert(!"Should never happen");
            return;
        }

        LPBYTE pOlAsBytes = (LPBYTE)lpOverlapped;
        LPBYTE pMyOlAsBytes = pOlAsBytes - offsetof(MYOVERLAPPED, ol);
        LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)pOlAsBytes;

        if ((ERROR_SUCCESS == dwErrorCode) && 
            (sizeof(BYTE) == dwNumberOfBytesTransfered))
        {
            printf("written %uc\n", pMyOl->buffer);
        }
        else
        {
            // handle error
        }

        free(pMyOl);
    }


    int write_port(HANDLE hComm, BYTE byte) {

       LPMYOVERLAPPED pMyOl = (LPMYOVERLAPPED)malloc(sizeof(MYOVERLAPPED));

       ZeroMemory(pMyOl, sizeof(MYOVERLAPPED));
       pMyOl->buffer = byte;

       // Issue write.
       if (!WriteFileEx(hComm, &pMyOl->buffer, sizeof(BYTE), pMyOl, &write_compl )) {
          if (GetLastError() != ERROR_IO_PENDING) { 
             // WriteFile failed, but isn't delayed. Report error and abort.
              free(pMyOl);
              printf("last error: %ld",GetLastError());
              return 0; //failed, return false;
          }
          else {
            return -1; //pending
          }
       }
       else {
            free(pMyOl);
            return 1; //finished
       }
    }
0 голосов
/ 04 июня 2009
    result = write_port(outPortHandle, 128);
    result = write_port(outPortHandle, 131);

Аргументом lpBuf должны быть указатели на буферы, а не константы.

, например

char buffer;
buffer = 128;
result = write_port(outPortHandle, &buffer);
buffer = 131;
result = write_port(outPortHandle, &buffer);

Что вы действительно хотите сделать, так это передать длину буфера.

, например

    char buffer[]  = { 128, 131 };
    result = write_port(outPortHandle, &buffer, sizeof(buffer));

int write_port(HANDLE hComm,BYTE* lpBuf, size_t length) {

   ...

   // Issue write.
   if (!WriteFileEx(hComm, &lpBuf, length, &osWrite, &write_compl )) {
   ...
...