为什么在构造函数中调用类方法会导致程序挂起?

时间:2019-06-17 02:11:20

标签: c++ windows qt winapi

我正在尝试使用Qt创建GUI来将Rip Grep包装为一个项目,以使其更加熟悉Qt,C ++和Win32 API。我正在编写一个名为RunCommand的类,以包含运行命令行程序并将其输出捕获到字符串以供GUI使用的功能。在RunCommand中,我有一些方法封装了RunCommand必须完成的工作的不同部分。我试图让构造函数通过调用方法来完成所有工作。问题是,当我从构造函数调用方法时,程序会无限期挂起,但是当我将方法中的代码直接复制并粘贴到构造函数中时,事情将按预期进行。我在这里想念什么?

我注意到,如果将方法直接粘贴到构造函数中,则代码可以正常运行。当用户单击RunCommand时,将启动helloButton。我有一条消息要写入QTextEdit小部件进行调试,该调试是在该方法中的任何实际代码运行并立即返回之前,将其放置在构造函数调用的类方法中,并且仍然遇到挂起,所以我认为问题出在我调用该方法的方式上,而不是该方法试图执行的操作。如果我不从构造函数进行类方法调用,则可以将其他文本写入QTextEdit小部件。

easyrip.cpp

#include "easyrip.h"
#include "ui_easyrip.h"
#include "runcommand.h"
#include "synchapi.h"

using namespace std;

EasyRip::EasyRip(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::EasyRip)
{
    ui->setupUi(this);

    connect(ui->helloButton, &QPushButton::pressed, this, &EasyRip::testHello);
}

EasyRip::~EasyRip()
{
    delete ui;
}

void EasyRip::testHello()
{
    ui->cmdDisplay->setText("Running rg help command...");
    string cmdOutput = "";
    RunCommand(R"(C:\Users\Name\OneDrive\RipGrep\rg.exe --help)", cmdOutput);
    ui->cmdDisplay->setText(cmdOutput.c_str());
}

runco​​mmand.h

#ifndef CMDRUNNER_H
#define CMDRUNNER_H

#include <Windows.h> 
#include <tchar.h>
#include <stdio.h> 
#include <strsafe.h>
#include <string>

class RunCommand
{
public:
    RunCommand(const std::string cmd, std::string& cmdOutput);
    ~RunCommand();

private:
    void CreateChildProcess(const std::string cmd, std::string& cmdOutput);
    void ReadFromPipe(std::string& cmdOutput);

    HANDLE _hChildStd_OUT_Rd = nullptr;
    HANDLE _hChildStd_OUT_Wr = nullptr;
};

#endif

runco​​mmand.cpp

#include "runcommand.h"

constexpr int BUFSIZE = 4096;

using namespace std;

RunCommand::~RunCommand()
{
   CloseHandle(_hChildStd_OUT_Rd);
   CloseHandle(_hChildStd_OUT_Wr);
}

// Runs cmd and returns the command output on cmdOutput.
RunCommand::RunCommand(const string cmd, string& cmdOutput)
{
   // Set the bInheritHandle flag so pipe handles are inherited by the child process.
   SECURITY_ATTRIBUTES saAttr;
   saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
   saAttr.bInheritHandle = TRUE;
   saAttr.lpSecurityDescriptor = nullptr;

   // Create a pipe for the child process's STDOUT.
   if (!CreatePipe(&_hChildStd_OUT_Rd, &_hChildStd_OUT_Wr, &saAttr, 0))
   {
      cmdOutput.assign("Error: StdoutRd CreatePipe failed");
      return;
   }

   // Ensure the read handle to the pipe for STDOUT is not inherited.
   // We want the child process to only inherit the write end of the PIPE
   // we created above. Then it can write to the inherited write end of 
   // the PIPE, and we can read from the non-inherited read end.
   if (!SetHandleInformation(_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, FALSE))
   {
      cmdOutput.assign("Error: Stdout SetHandleInformation failed");
      return;
   }

   // If "Debug point" is assigned to cmdOutput here and returned, 
   // the text is successfully displayed in the QTextEdit widget,
   // and the code does not hang.

   // Create the child process.
   // I have also tried calling this as RunCommand::CreateChildProcess
   this->CreateChildProcess(cmd, cmdOutput);

   // Read the standard output from the child process.
   this->ReadFromPipe(cmdOutput);
}

// Create a child process that uses the previously created pipes for STDOUT.
void RunCommand::CreateChildProcess(const string cmd, string& cmdOutput)
{
   cmdOutput.assign("Debug point"); // Issue: Never reaches this line.
   return;

   ...
}

// Read output from the child process's pipe for STDOUT
// and copy it to the referenced std::string.
// Stop when there is no more data. 
void RunCommand::ReadFromPipe(std::string& cmdOutput)
{
   ...
}

预期结果:rg --help命令的内容被复制到cmdOutput字符串中,然后显示在QTextEdit小部件中。

实际结果:该程序无限期挂起,并且在RunCommand构造函数尝试调用类方法时需要强制关闭该程序。

感谢任何建议,谢谢。

1 个答案:

答案 0 :(得分:0)

作为后续,我最终发现问题不是由调用构造函数中的方法引起的。即使不建议这样做,从技术上讲也很好。

真正的问题发生在ReadFromPipe中。由于某种原因,子进程在运行后没有退出,这导致正在读取的逻辑永远挂起,以等待某种文件结束信号。我从未在用户界面中看到"Debug point",因为即使在设置文本后从CreateChildProcess立即返回,我也忘记也从ReadFromPipe返回。 ReadFromPipe中挂起的代码意味着我从未在UI中看到调试文本。

感谢所有有关如何改进的有用建议,它们对于帮助我找出问题所在确实很有帮助。