我正在尝试使用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());
}
runcommand.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
runcommand.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构造函数尝试调用类方法时需要强制关闭该程序。
感谢任何建议,谢谢。
答案 0 :(得分:0)
作为后续,我最终发现问题不是由调用构造函数中的方法引起的。即使不建议这样做,从技术上讲也很好。
真正的问题发生在ReadFromPipe
中。由于某种原因,子进程在运行后没有退出,这导致正在读取的逻辑永远挂起,以等待某种文件结束信号。我从未在用户界面中看到"Debug point"
,因为即使在设置文本后从CreateChildProcess
立即返回,我也忘记也从ReadFromPipe
返回。 ReadFromPipe
中挂起的代码意味着我从未在UI中看到调试文本。
感谢所有有关如何改进的有用建议,它们对于帮助我找出问题所在确实很有帮助。