Почему вызов метода класса в конструкторе вызывает зависание программы? - PullRequest
0 голосов
/ 17 июня 2019

Я пытаюсь использовать Qt для создания графического интерфейса, чтобы обернуть Rip Grep как проект, чтобы лучше познакомиться с Qt, C ++ и Win32 API.У меня есть класс с именем RunCommand, который я пишу, чтобы содержать функциональность запуска программы командной строки и захвата ее вывода в строку для использования в графическом интерфейсе.В 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 пытается вызвать методы класса.

Любые предложения приветствуются, спасибо.

1 Ответ

0 голосов
/ 01 июля 2019

Чтобы продолжить, я в конце концов выяснил, что проблема не была вызвана вызовом методов в конструкторе. Это технически нормально, даже если это не рекомендуемый подход.

Реальная проблема произошла в ReadFromPipe. По какой-то причине дочерний процесс не завершался после выполнения, что приводило к зависанию считываемой логики, ожидающей какого-то сигнала конца файла. Я никогда не видел "Debug point" в пользовательском интерфейсе, потому что, хотя я сразу же вернулся с CreateChildProcess после установки текста, я забыл также сразу же вернуться с ReadFromPipe. Код висит в ReadFromPipe означает, что я никогда не видел текст отладки в пользовательском интерфейсе.

Спасибо за все полезные предложения о том, как улучшить, они были действительно полезны, помогая мне понять, что идет не так.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...