c open Асинхронный WriteFile работает только на отладке Step by Step




writefile winapi (2)

Сторонняя библиотека работает так, как она была разработана, но вы используете ее неправильно.

Библиотека libserialport является кросс-платформенной и обеспечивает неблокирующую семантику ввода-вывода, а не асинхронную семантику ввода-вывода. Это совсем другое. Когда вы выдаете неблокирующую запись, будет записана только та часть данных, которая может быть отправлена ​​немедленно.

Это означает, что вы несете ответственность за отслеживание того, сколько байтов (если они есть) были записаны, и выпустить еще одну операцию записи в более поздний момент времени для остальных данных. В настоящее время ваш код полностью игнорирует возвращаемое значение, сообщающее вам, сколько байтов было написано, поэтому совершенно не удивительно, что некоторые или все данные могут быть потеряны.

В этом конкретном случае вы вызываете sp_drain сразу после операции записи. Это блокируется до тех пор, пока запись не будет выполнена, что делает невозможным запись без блокировки. Таким образом, самым простым способом решить вашу проблему будет вызов sp_blocking_write вместо sp_nonblocking_write .

Вполне вероятно, что эта библиотека обеспечивает блокирование семантики ввода-вывода, потому что это тот подход, с которым наиболее знакомы программисты Linux. Также может быть несколько проще эмулировать блокирующую семантику в Windows, чем для эмуляции асинхронной семантики ввода-вывода в Linux.

Если вам удобнее работать с асинхронным вводом-выводом, и если ваш код не предназначен для кросс-платформенного, вы можете захотеть найти другую стороннюю библиотеку, разработанную специально для Windows.

Я использую библиотеку C, которая использует операции синхронного и асинхронного ввода-вывода.

Когда я пытаюсь использовать операцию асинхронной записи, функция, которая вызывает WriteFile , не гарантирует, что все байты будут записаны, если я не пошаговую отладку в нее (и это очень странно). вот функция записи:

SP_API enum sp_return sp_nonblocking_write(struct sp_port *port, const void *buf, size_t count)
{
    TRACE("%p, %p, %d", port, buf, count);

    CHECK_OPEN_PORT();

    if (!buf)
        RETURN_ERROR(SP_ERR_ARG, "Null buffer");

    DEBUG_FMT("Writing up to %d bytes to port %s", count, port->name);

    if (count == 0)
        RETURN_INT(0);

#ifdef _WIN32
    DWORD written = 0;
    BYTE *ptr = (BYTE *) buf;

    /* Check whether previous write is complete. */
    if (port->writing) {
        if (HasOverlappedIoCompleted(&port->write_ovl)) {
            DEBUG("Previous write completed");
            port->writing = 0;
        } else {
            DEBUG("Previous write not complete");
            /* Can't take a new write until the previous one finishes. */
            RETURN_INT(0);
        }
    }

    /* Set timeout. */
    if (port->timeouts.WriteTotalTimeoutConstant != 0) {
        port->timeouts.WriteTotalTimeoutConstant = 0;
        if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
            RETURN_FAIL("SetCommTimeouts() failed");
    }

    /*
     * Keep writing data until the OS has to actually start an async IO
     * for it. At that point we know the buffer is full.
     */
    while (written < count) {
        /* Copy first byte of user buffer. */
        port->pending_byte = *ptr++;

        /* Start asynchronous write. */
        if (WriteFile(port->hdl, &port->pending_byte, 1, NULL, &port->write_ovl) == 0) {
            if (GetLastError() == ERROR_IO_PENDING) {
                if (HasOverlappedIoCompleted(&port->write_ovl)) {
                    DEBUG("Asynchronous write completed immediately");
                    port->writing = 0;
                    written++;
                    continue;
                } else {
                    DEBUG("Asynchronous write running");
                    port->writing = 1;
                    RETURN_INT(++written);
                }
            } else {
                /* Actual failure of some kind. */
                RETURN_FAIL("WriteFile() failed");
            }
        } else {
            DEBUG("Single byte written immediately");
            written++;
        }
    }

    DEBUG("All bytes written immediately");

    RETURN_INT(written);
#else
    /* Returns the number of bytes written, or -1 upon failure. */
    ssize_t written = write(port->fd, buf, count);

    if (written < 0)
        RETURN_FAIL("write() failed");
    else
        RETURN_INT(written);
#endif
}

Я не мог найти никаких объяснений этому странному поведению. Это действительно сводит меня с ума.

В чем может быть проблема ?

Спасибо за Ваше понимание.

EDIT1: Я работаю под Windows XP SP3 32-битный гость на Linux Mint 18 64 бит под VMware Workstation 12 и с помощью Mingw-W64 i686-7.1.0-release-win32-sjlj-rt_v5-rev0 и Code :: Blocks IDE

EDIT2: Я использую библиотеку последовательного порта C, называемую libserialport https://sigrok.org/wiki/Libserialport , ее API-документы: http://sigrok.org/api/libserialport/unstable/index.html

моя часть кода:

printf("Opening port '%s' \n", desired_port);
    error = sp_get_port_by_name(desired_port,&port);
    if (error == SP_OK)
    {
        error = sp_open(port,SP_MODE_READ_WRITE);
        if (error == SP_OK)
        {
            sp_flush(port, SP_BUF_BOTH);

            char tosend[] = "AT+CIMI\r";
            error = sp_flush(port, SP_BUF_BOTH);
            error = sp_nonblocking_write(port, tosend, strlen(tosend));
            sp_drain(port);

            wxSleep(3);

            char byte_buff[128];
            memset(byte_buff, 0, sizeof(byte_buff));
            int byte_num = 0;
            byte_num = sp_blocking_read(port,byte_buff, 127, 100);
            error = sp_input_waiting(port);
            wxMessageBox(byte_buff);

            sp_close(port);
        }
        else
        {
            printf("Error opening serial device\n");
        }
    }
    else
    {
        printf("Error finding serial device\n");
    }

Оказывается, мне приходится загрязнять мои пальцы и повторно запускать sp_nonblocking_write самостоятельно, чтобы исправить проблему. вот моя повторная реализация, которая работает как шарм:

SP_API enum sp_return sp_nonblocking_write(struct sp_port *port,
                                           const void *buf, size_t count)
{
    TRACE("%p, %p, %d", port, buf, count);

    CHECK_OPEN_PORT();

    if (!buf)
        RETURN_ERROR(SP_ERR_ARG, "Null buffer");

    DEBUG_FMT("Writing up to %d bytes to port %s", count, port->name);

    if (count == 0)
        RETURN_INT(0);

#ifdef _WIN32
    DWORD bytes_written = 0;

    /* Check whether previous write is complete. */
    if (port->writing) {
        if (GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, FALSE) == 0) {
            if (GetLastError() == ERROR_IO_INCOMPLETE) {
                DEBUG("Previous write incomplete");
                /* Can't take a new write until the previous one finishes. */
                RETURN_INT(0);
            } else
                RETURN_FAIL("GetOverlappedResult() failed");
        } else {
            DEBUG("Previous write completed");
            port->writing = 0;
        }
    }

    /* Set timeout. */
    if (port->timeouts.WriteTotalTimeoutConstant != 0) {
        port->timeouts.WriteTotalTimeoutConstant = 0;
        if (SetCommTimeouts(port->hdl, &port->timeouts) == 0)
            RETURN_FAIL("SetCommTimeouts() failed");
    }

    /* Do write. */
    if (WriteFile(port->hdl, buf, count, NULL, &port->write_ovl) == 0)
        if (GetLastError() != ERROR_IO_PENDING)
            RETURN_FAIL("WriteFile() failed");

    /* Get number of bytes written. */
    if (GetOverlappedResult(port->hdl, &port->write_ovl, &bytes_written, FALSE) == 0) {
        if (GetLastError() == ERROR_IO_INCOMPLETE) {
            DEBUG("Asynchronous write running");
            port->writing = 1;
        }
        else /* GetLastError() != ERROR_IO_INCOMPLETE */
            RETURN_FAIL("GetOverlappedResult() failed");
    }
    else {
        DEBUG("Asynchronous write completed immediately");
        port->writing = 0;
    }

    DEBUG_FMT("%d bytes written immediately", bytes_written);

    RETURN_INT(bytes_written);
#else
    ssize_t bytes_written;

    /* Returns the number of bytes written, or -1 upon failure. */
    if ((bytes_written = write(port->fd, buf, count)) < 0) {
        if (errno == EAGAIN)
            /* No bytes written. */
            bytes_written = 0;
        else
            /* This is an actual failure. */
            RETURN_FAIL("write() failed");
    }
    RETURN_INT(bytes_written);
#endif
}

Спасибо, ребята, за поддержку и вклад.





writefile