在Windows中从QProcess读取二进制数据

时间:2018-07-25 13:06:11

标签: windows qt console carriage-return linefeed

我有一些.exe文件(例如some.exe),它可以写入标准输出 binary 数据。我没有该程序的来源。我需要从some.exe应用程序中运行C++/Qt,并读取我创建的过程的标准输出。当我尝试使用QProcess::readAll执行此操作时,有人将字节\n0x0d)替换为\r\n0x0a 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 ) )

问题是:我该如何对QProcessWindows或要进行控制台操作不要CR更改LF CR?< / p>

操作系统:Windows 7
Qt:5.6.2

2 个答案:

答案 0 :(得分:1)

不幸的是,它与QProcessWindows或控制台无关。全部与CRT有关。像printffwrite之类的功能正在考虑使用_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"