c - writefileex - writefile用法




異步WriteFile僅適用於逐步調試 (2)

我正在使用使用同步和異步I / O操作的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
}

我找不到這個奇怪的行為的任何解釋。 這真的讓我瘋狂。

可能是什麼問題呢 ?

謝謝你的理解。

編輯1:我運行在Windows XP SP3 32位客戶Linux Mint 18 64位VMware工作站12和使用Mingw-W64 i686-7.1.0-release-win32-sjlj-rt_v5-rev0Code :: Blocks IDE

EDIT2:我使用一個稱為libserialportC串行端口庫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");
    }

第三方庫按設計工作,但是您使用不正確。

libserialport庫是跨平台的,提供了非阻塞I / O語義而不是異步I / O語義。 這些是完全不同的。 發出非阻塞式寫入時,只能寫入可以立即發送的那部分數據。

這意味著您有責任跟踪已經寫入了多少字節(如果有),並在稍後的時間點為其餘的數據發出另一個寫入操作。 目前,你的代碼完全忽略了返回值,告訴你寫了多少字節,所以一些或全部數據可能會丟失並不奇怪。

在這種情況下,在寫入操作之後立即調用sp_drain 。 這會阻塞,直到寫入完成,使得使用非阻塞寫入毫無意義。 所以解決你的問題最簡單的方法是調用sp_blocking_write而不是sp_nonblocking_write

這個庫很可能提供了阻塞的I / O語義,因為這是Linux程序員最熟悉的方法。 在Windows中模擬阻塞語義也許比在Linux中模擬異步I / O語義要容易一些。

如果您更熟悉異步I / O,並且您的代碼不打算跨平台,則可能更願意查找專門為Windows設計的另一個第三方庫。


事實證明,我必須弄髒自己的手指,重新實施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