0

这会执行 sane_read (使用SANE)并转换为 java 应用程序。

expected_bytes可能不是 100% 准确,因此使用向量<>。

出现问题是因为生成的图像是错误的。如果我env->SetByteArrayRegion直接使用它就可以了。

因此,错误出现在 vector<> 上下文中。

我究竟做错了什么?

    long expected_bytes = pars.bytes_per_line * pars.lines *
                            ((pars.format == SANE_FRAME_RGB
                              || pars.format == SANE_FRAME_GRAY) ? 1 : 3);

    //fprintf (stderr, "Expected bytes: %ld\n", expected_bytes);

    SANE_Byte buffer[2048];
    SANE_Int bytes_read;
    vector<SANE_Byte *> data;
    data.reserve(expected_bytes);

    do {
        status = sane_read((SANE_Handle *)hnd, buffer, sizeof(buffer), &bytes_read);

        if (status != SANE_STATUS_GOOD) {
            if (status == SANE_STATUS_EOF) {
                break;
            }

            throwEx(env, getError(status));
            break;
        }

        if (bytes_read > 0) {
            data.insert(data.end(), &buffer, (&buffer) + bytes_read);
        }
    }
    while(1);

    jbyteArray bytes = env->NewByteArray(data.size());
    env->SetByteArrayRegion(bytes, 0, data.size(), (const jbyte *) &data[0]);
4

1 回答 1

2

第一个问题是您已经声明dataSANE_Byte*(即指针)的向量而不是向量SANE_Byte(原始字符)。当您调用 时SetByteArrayRegion(),您希望传递一个指向字符数据的指针,但实际上您传递的是一个指向指针数组的指针;那些不会产生所需的图像数据。

第二个问题是将数据插入向量的方式:

SANE_Byte buffer[2048];
...
data.insert(data.end(), &buffer, (&buffer) + bytes_read);

由于buffer具有类型SANE_Byte[2048](2048 字节数组),这意味着&buffer具有类型SANE_Byte(*)[2048](指向 2048 字节数组的指针)。数组衰减为指向其第一个元素的指针,因此在大多数表达式上下文中,两者都buffer具有&buffer等效,但它们具有非常不同的类型,并且在与指针算术一起使用时表现不同。

假设buffer分配在地址 0x01000000。 buffer+1衰减到 0x01000001,并buffer+2047衰减到数组的最后一个元素 0x010007FF。那里没有惊喜。 &buffer也是 0x01000000,但如果考虑表达式(&buffer) + 1,就会发生不同的事情:它指向缓冲区开始后 2048 个字节

这是一个内存图:

地址 +0 +1 ... +0x7FF
           +--------+--------+- -+------------+
0x01000000 | 缓冲区[0] | 缓冲区[1] | ... | 缓冲区[2047] | // 这一行是 &buffer
           +--------+--------+- -+------------+
0x01000800 | ? | ? | ... | ? | // 这一行是 (&buffer) + 1
           +--------+--------+- -+------------+
0x01001000 | ? | ? | ... | ? | // 这一行是 (&buffer) + 2
           +--------+--------+- -+------------+
……
           +--------+--------+- -+------------+
0x013FF800 | ? | ? | ... | ? | // 这一行是 (&buffer) + 2047
           +--------+--------+- -+------------+
0x01400000 | ? | ? | ... | ? | // 这一行是 (&buffer) + 2048
           +--------+--------+- -+------------+

因此,如果sane_read()返回 2048 字节的完整缓冲区,则插入向量的内存范围[&buffer, (&buffer) + bytes_read)是 4 MB 的字符数据,从 开始的内存区域读取buffer,这是一个巨大的缓冲区溢出。然后,调用data.insert()将这 4 MB 解释为指针数组;如果您在 64 位系统上,那将有超过 100 万个指针,其内容未定义。老实说,我很惊讶这并没有立即崩溃。

构造向量的正确方法是声明它的类型vector<SANE_Byte>(字符,而不是指针),并在每次循环迭代时将每个缓冲区复制到其中。引用正确字节范围的buffer方法不是使用&bufferto (&buffer) + bytes_read,而是使用bufferto buffer + bytes_read,或等效&buffer[0]&buffer[bytes_read]

vector<SANE_Byte> data;
...
data.insert(data.end(), buffer, buffer + bytes_read);
于 2021-03-20T01:34:09.117 回答