在 Windows 中从 QProcess 读取二进制数据
Read binary data from QProcess in Windows
我有一些 .exe
文件(比如 some.exe
)写入标准输出 binary 数据。我没有这个程序的来源。我需要从我的 C++/Qt
应用程序 运行 some.exe
并读取我创建的进程的标准输出。当我尝试使用 QProcess::readAll
执行此操作时,有人将字节 \n
(0x0d
) 替换为 \r\n
(0x0a 0x0d
).
这是一个代码:
QProcess some;
some.start( "some.exe", QStringList() << "-I" << "inp.txt" );
// some.setTextModeEnabled( false ); // no effect at all
some.waitForFinished();
QByteArray output = some.readAll();
我在 cmd.exe
中尝试将输出重定向到文件,如下所示:
some.exe -I inp.txt > out.bin
并且用hexedit
查看了out.bin
,在应该0d
的地方出现了0a 0d
。
编辑:
这是一个模拟 some.exe
行为的简单程序:
#include <stdio.h>
int main() {
char buf[] = { 0x00, 0x11, 0x0a, 0x33 };
fwrite( buf, sizeof( buf[ 0 ] ), sizeof( buf ), stdout );
}
运行:
a.exe > out.bin
//out.bin
00 11 0d 0a 33
请注意,我无法修改 some.exe
,这就是为什么我不应该像 _setmode( _fileno( stdout, BINARY ) )
那样修改我的示例
问题是:我怎么能对 QProcess
或 Windows
或安慰 不要 将 CR
更改为 LF CR
?
OS: Windows 7
Qt: 5.6.2
不幸的是,它与 QProcess
或 Windows
或控制台无关。这都是关于 CRT
。 printf
或 fwrite
等函数正在考虑 _O_TEXT
标志以添加额外的 0x0D
(仅适用于 Windows
)。所以唯一的解决办法是用WriteProcessMemory
修改some.exe
的stdout
字段,或者在[=19=的地址space中调用_setmode
] 使用 DLL 注入技术或修补 lib。但这是一项棘手的工作。
how can I say to QProcess or to Windows or to console do not change CR with LF CR?
他们没有改变任何东西。 some.exe
坏了。就这样。它输出错误的东西。以文本模式输出二进制数据的人把事情搞砸了。
不过,有办法恢复。您必须实现一个解码器来修复 some.exe
的损坏输出。您知道每个 0a
前面都必须有 0d
。所以你必须解析输出,如果你找到一个 0a
,并且它前面有 0d
,删除 0d
,然后继续。或者,如果 0a
前面没有 0d
,您可以中止 - some.exe
不应产生此类输出,因为它已损坏。
appendBinFix
函数获取损坏的数据并将修复后的版本附加到缓冲区。
// https://github.com/KubaO/Whosebugn/tree/master/questions/process-fix-binary-crlf-51519654
#include <QtCore>
#include <algorithm>
bool appendBinFix(QByteArray &buf, const char *src, int size) {
bool okData = true;
if (!size) return okData;
constexpr char CR = '\x0d';
constexpr char LF = '\x0a';
bool hasCR = buf.endsWith(CR);
buf.resize(buf.size() + size);
char *dst = buf.end() - size;
const char *lastSrc = src;
for (const char *const end = src + size; src != end; src++) {
char const c = *src;
if (c == LF) {
if (hasCR) {
std::copy(lastSrc, src, dst);
dst += (src - lastSrc);
dst[-1] = LF;
lastSrc = src + 1;
} else
okData = false;
}
hasCR = (c == CR);
}
dst = std::copy(lastSrc, src, dst);
buf.resize(dst - buf.constData());
return okData;
}
bool appendBinFix(QByteArray &buf, const QByteArray &src) {
return appendBinFix(buf, src.data(), src.size());
}
以下测试工具确保它做正确的事情,包括模拟 some.exe
(本身)的输出:
#include <QtTest>
#include <cstdio>
#ifdef Q_OS_WIN
#include <fcntl.h>
#include <io.h>
#endif
const auto dataFixed = QByteArrayLiteral("\x00\x11\x0d\x0a\x33");
const auto data = QByteArrayLiteral("\x00\x11\x0d\x0d\x0a\x33");
int writeOutput() {
#ifdef Q_OS_WIN
_setmode(_fileno(stdout), _O_BINARY);
#endif
auto size = fwrite(data.data(), 1, data.size(), stdout);
qDebug() << size << data.size();
return (size == data.size()) ? 0 : 1;
}
class AppendTest : public QObject {
Q_OBJECT
struct Result {
QByteArray d;
bool ok;
bool operator==(const Result &o) const { return ok == o.ok && d == o.d; }
};
static Result getFixed(const QByteArray &src, int split) {
Result f;
f.ok = appendBinFix(f.d, src.data(), split);
f.ok = appendBinFix(f.d, src.data() + split, src.size() - split) && f.ok;
return f;
}
Q_SLOT void worksWithLFCR() {
const auto lf_cr = QByteArrayLiteral("\x00\x11\x0a\x0d\x33");
for (int i = 0; i < lf_cr.size(); ++i)
QCOMPARE(getFixed(lf_cr, i), (Result{lf_cr, false}));
}
Q_SLOT void worksWithCRLF() {
const auto cr_lf = QByteArrayLiteral("\x00\x11\x0d\x0a\x33");
const auto cr_lf_fixed = QByteArrayLiteral("\x00\x11\x0a\x33");
for (int i = 0; i < cr_lf.size(); ++i)
QCOMPARE(getFixed(cr_lf, i), (Result{cr_lf_fixed, true}));
}
Q_SLOT void worksWithCRCRLF() {
for (int i = 0; i < data.size(); ++i) QCOMPARE(getFixed(data, i).d, dataFixed);
}
Q_SLOT void worksWithQProcess() {
QProcess proc;
proc.start(QCoreApplication::applicationFilePath(), {"output"},
QIODevice::ReadOnly);
proc.waitForFinished(5000);
QCOMPARE(proc.exitCode(), 0);
QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
QByteArray out = proc.readAllStandardOutput();
QByteArray fixed;
appendBinFix(fixed, out);
QCOMPARE(out, data);
QCOMPARE(fixed, dataFixed);
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
if (app.arguments().size() > 1) return writeOutput();
AppendTest test;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&test, argc, argv);
}
#include "main.moc"
我有一些 .exe
文件(比如 some.exe
)写入标准输出 binary 数据。我没有这个程序的来源。我需要从我的 C++/Qt
应用程序 运行 some.exe
并读取我创建的进程的标准输出。当我尝试使用 QProcess::readAll
执行此操作时,有人将字节 \n
(0x0d
) 替换为 \r\n
(0x0a 0x0d
).
这是一个代码:
QProcess some;
some.start( "some.exe", QStringList() << "-I" << "inp.txt" );
// some.setTextModeEnabled( false ); // no effect at all
some.waitForFinished();
QByteArray output = some.readAll();
我在 cmd.exe
中尝试将输出重定向到文件,如下所示:
some.exe -I inp.txt > out.bin
并且用hexedit
查看了out.bin
,在应该0d
的地方出现了0a 0d
。
编辑:
这是一个模拟 some.exe
行为的简单程序:
#include <stdio.h>
int main() {
char buf[] = { 0x00, 0x11, 0x0a, 0x33 };
fwrite( buf, sizeof( buf[ 0 ] ), sizeof( buf ), stdout );
}
运行:
a.exe > out.bin
//out.bin
00 11 0d 0a 33
请注意,我无法修改 some.exe
,这就是为什么我不应该像 _setmode( _fileno( stdout, BINARY ) )
问题是:我怎么能对 QProcess
或 Windows
或安慰 不要 将 CR
更改为 LF CR
?
OS: Windows 7
Qt: 5.6.2
不幸的是,它与 QProcess
或 Windows
或控制台无关。这都是关于 CRT
。 printf
或 fwrite
等函数正在考虑 _O_TEXT
标志以添加额外的 0x0D
(仅适用于 Windows
)。所以唯一的解决办法是用WriteProcessMemory
修改some.exe
的stdout
字段,或者在[=19=的地址space中调用_setmode
] 使用 DLL 注入技术或修补 lib。但这是一项棘手的工作。
how can I say to QProcess or to Windows or to console do not change CR with LF CR?
他们没有改变任何东西。 some.exe
坏了。就这样。它输出错误的东西。以文本模式输出二进制数据的人把事情搞砸了。
不过,有办法恢复。您必须实现一个解码器来修复 some.exe
的损坏输出。您知道每个 0a
前面都必须有 0d
。所以你必须解析输出,如果你找到一个 0a
,并且它前面有 0d
,删除 0d
,然后继续。或者,如果 0a
前面没有 0d
,您可以中止 - some.exe
不应产生此类输出,因为它已损坏。
appendBinFix
函数获取损坏的数据并将修复后的版本附加到缓冲区。
// https://github.com/KubaO/Whosebugn/tree/master/questions/process-fix-binary-crlf-51519654
#include <QtCore>
#include <algorithm>
bool appendBinFix(QByteArray &buf, const char *src, int size) {
bool okData = true;
if (!size) return okData;
constexpr char CR = '\x0d';
constexpr char LF = '\x0a';
bool hasCR = buf.endsWith(CR);
buf.resize(buf.size() + size);
char *dst = buf.end() - size;
const char *lastSrc = src;
for (const char *const end = src + size; src != end; src++) {
char const c = *src;
if (c == LF) {
if (hasCR) {
std::copy(lastSrc, src, dst);
dst += (src - lastSrc);
dst[-1] = LF;
lastSrc = src + 1;
} else
okData = false;
}
hasCR = (c == CR);
}
dst = std::copy(lastSrc, src, dst);
buf.resize(dst - buf.constData());
return okData;
}
bool appendBinFix(QByteArray &buf, const QByteArray &src) {
return appendBinFix(buf, src.data(), src.size());
}
以下测试工具确保它做正确的事情,包括模拟 some.exe
(本身)的输出:
#include <QtTest>
#include <cstdio>
#ifdef Q_OS_WIN
#include <fcntl.h>
#include <io.h>
#endif
const auto dataFixed = QByteArrayLiteral("\x00\x11\x0d\x0a\x33");
const auto data = QByteArrayLiteral("\x00\x11\x0d\x0d\x0a\x33");
int writeOutput() {
#ifdef Q_OS_WIN
_setmode(_fileno(stdout), _O_BINARY);
#endif
auto size = fwrite(data.data(), 1, data.size(), stdout);
qDebug() << size << data.size();
return (size == data.size()) ? 0 : 1;
}
class AppendTest : public QObject {
Q_OBJECT
struct Result {
QByteArray d;
bool ok;
bool operator==(const Result &o) const { return ok == o.ok && d == o.d; }
};
static Result getFixed(const QByteArray &src, int split) {
Result f;
f.ok = appendBinFix(f.d, src.data(), split);
f.ok = appendBinFix(f.d, src.data() + split, src.size() - split) && f.ok;
return f;
}
Q_SLOT void worksWithLFCR() {
const auto lf_cr = QByteArrayLiteral("\x00\x11\x0a\x0d\x33");
for (int i = 0; i < lf_cr.size(); ++i)
QCOMPARE(getFixed(lf_cr, i), (Result{lf_cr, false}));
}
Q_SLOT void worksWithCRLF() {
const auto cr_lf = QByteArrayLiteral("\x00\x11\x0d\x0a\x33");
const auto cr_lf_fixed = QByteArrayLiteral("\x00\x11\x0a\x33");
for (int i = 0; i < cr_lf.size(); ++i)
QCOMPARE(getFixed(cr_lf, i), (Result{cr_lf_fixed, true}));
}
Q_SLOT void worksWithCRCRLF() {
for (int i = 0; i < data.size(); ++i) QCOMPARE(getFixed(data, i).d, dataFixed);
}
Q_SLOT void worksWithQProcess() {
QProcess proc;
proc.start(QCoreApplication::applicationFilePath(), {"output"},
QIODevice::ReadOnly);
proc.waitForFinished(5000);
QCOMPARE(proc.exitCode(), 0);
QCOMPARE(proc.exitStatus(), QProcess::NormalExit);
QByteArray out = proc.readAllStandardOutput();
QByteArray fixed;
appendBinFix(fixed, out);
QCOMPARE(out, data);
QCOMPARE(fixed, dataFixed);
}
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
if (app.arguments().size() > 1) return writeOutput();
AppendTest test;
QTEST_SET_MAIN_SOURCE_PATH
return QTest::qExec(&test, argc, argv);
}
#include "main.moc"