我有一些.exe
文件(例如some.exe
),它可以写入标准输出 binary 数据。我没有该程序的来源。我需要从some.exe
应用程序中运行C++/Qt
,并读取我创建的过程的标准输出。当我尝试使用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
并用out.bin
查看hexedit
,应该有0a 0d
的地方有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
?< / p>
操作系统:Windows 7
Qt:5.6.2
答案 0 :(得分:1)
不幸的是,它与QProcess
或Windows
或控制台无关。全部与CRT
有关。像printf
或fwrite
之类的功能正在考虑使用_O_TEXT
标志来添加额外的0x0D
(仅对Windows
适用)。因此,唯一的解决方案是使用stdout
来修改some.exe
的{{1}}的字段,或者使用DLL注入在WriteProcessMemory
的地址空间内调用_setmode
技术或修补库。但这是一项棘手的工作。
答案 1 :(得分:1)
我怎么对QProcess或Windows或控制台说不要用LF CR更改CR?
他们什么都没改变。 some.exe
已损坏。就这样。输出错误的东西。无论是谁使它以文本模式输出brinary数据,都糟透了。
不过,有一种恢复方法。您必须实现一个解码器,该解码器将修复some.exe
的损坏输出。您知道每个0a
的前面必须是0d
。因此,您必须解析输出,如果找到了0a
,并且前面有0d
,请删除0d
,然后继续。 (可选)如果0a
之前没有0d
-some.exe
不会产生这样的输出,因为它已损坏,您可以中止。
appendBinFix
函数获取损坏的数据,并将固定版本附加到缓冲区。
// https://github.com/KubaO/stackoverflown/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"