Qt Creator:如何运行Windows批处理文件并获得结果

时间:2015-11-21 15:31:30

标签: c++ windows qt batch-file

我目前有这个批处理文件:

@echo off

setlocal enabledelayedexpansion

set Times=0

for /f "skip=1" %%p in ('wmic cpu get loadpercentage') do (

set Cpusage!Times!=%%p

set /A Times=!Times! + 1

)

echo %Cpusage0%

我想在标准的C ++ Qt windows应用程序中运行它并将百分比存储在变量中。我可以看到您可以使用QProcess运行批处理文件,但我不确定如何获得结果。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

此任务有多种解决方案(标准C ++,Qt,WinAPI等),我将列出其中的一些。我已经测试并验证了所有列出的(如果我没有犯错,他们应该工作正常)。

但是,下面列出的所有解决方案都要求您更改所提供的批处理脚本中的一行。 原因是最后一行" echo %Cpusage0%"只有结果值打印到命令提示符而不是返回

因此我更改了批处理文件的最后一行

来自 echo %Cpusage0%

exit %Cpusage0%

这将从命令提示符返回变量Cpusage0中的值。

<强>解决方案:

通常:批处理文件需要通过命令解释器(cmd.exe - Windows上的命令提示符)运行。此外,如果您希望在解释器中只运行一个命令然后终止它,那么您需要为cmd.exe指定 / C选项。(在您的情况下,这是真的)。 / p>

所以你想要执行的命令是:

std::string BatchFile = "..."; // Access path to the batch file.
std::string Command = "cmd.exe /C " + BatchFile;

注意:如果更改Command std :: string,则先前获得的c_str()指针将变为无效。 所以要么

  • 在std :: system()调用运行时不要更改std :: string,
  • 使用局部变量来存储命令(就像我在示例中所做的那样),
  • 将命令复制到C字符串([const] char*)指针或 最好是成智能指针字符数组 (std::unique_ptr<char[]>)。

如果使用C-string,请不要忘记将其删除。

<强>要点:

  1. 标准C ++ - std::system(...)

    A,等待std::system(...)完成。这阻止调用线程。

    B,在std::system(...)的其他主题中运行std::thread不会阻止调用线程。

    C,在std::system(...)的其他主题中运行std::async不阻止调用线程。

  2. Qt - QProcess

    A,等待QProcess完成。这阻止调用线程。

    B,使用QProcess::finished()发信号通知一个广告位。 不阻止调用线程。

  3. WinAPI - CreateProcess(...)

    A,等待CreateProcess(...)完成。这阻止调用线程。

    B,开始一个新线程(CreateThread(...))等待CreateProcess(...)完成。 不阻止调用线程。

  4. 替代:我想在问题评论中提到同样的事情 @wOxxOm - 您可以获得CPU使用率直接用C ++编写。之前已经在StackOverflow上问了几次,这里有一些例子:

    How to determine CPU and memory consumption from inside a process?

    Retrieving CPU Load Percent total in Windows with C++

    注意:我还没有直接从C ++&#34;验证&#34; cpu的使用情况。我自己回答,但其中一个被大量投票,另一个被接受为答案。

    详细信息:

    注意:这些是简约的解决方案,只需最少的错误检查。如果使用&#34;多线程解决方案&#34;不要忘记为共享资源添加适当的保护(例如使用std::atomic<int>或std :: mutex来保护共享变量)。

    1. 标准C ++ - std::system(...)

    您可以在当前线程中执行批处理文件,并通过使用 命令 调用std::system(...)并将结果存储在int变量中来等待结果(百分比值)。

    A, 普通阻止std::system(...)来电。

    这会阻止调用线程。

        auto runBatchSTDSystemWaited(const std::string& BatchFile) -> int {
            auto Command = std::string("cmd.exe /C " + BatchFile);
    
            return std::system(Command.c_str());
        }
    

    您可以使用std::threadstd::async(...)在另一个帖子中执行相同操作(并在等待结果时继续执行其他操作)。

    B, std::thread。 (std::promisestd::future

    这不会阻止调用线程。

        auto runBatchSTDSystemThread(const std::string& BatchFile, std::shared_ptr<std::promise<int>> Promise) -> std::future<int> {
        // Note: the Promise object must exist until the last call to either the promise or the future objects.
    
            auto Command = std::string("cmd.exe /C " + BatchFile);
            auto Future = Promise->get_future();
    
            std::thread Thread([](decltype(Command) STDSystemCommand, decltype(Promise) ResultPromise) -> void {
                ResultPromise->set_value_at_thread_exit(std::system(STDSystemCommand.c_str()));
            }, Command, Promise);
            Thread.detach();
    
            return Future;
            // Note: You can access the CPU usage value by calling the std::future::get() function of the returned future object.
        }
    

    以下基本上将 1 / B 解决方案包含在1个电话中。

    C, std::async(...)

    这不会阻止调用线程。

        auto runBatchSTDSystemAsync(const std::string& BatchFile) -> std::future<int> {
            auto Command = std::string("cmd.exe /C " + BatchFile);
    
            // std::async can be forced to create new thread with std::launch::async launch policy.
            // Make sure that the Command string exists until the new thread ends (reason for string copy).
            // The lambda-function is required to copy the command string to the new thread.
            auto Future = std::future<int>(std::async(std::launch::async, [](decltype(Command) STDSystemCommand) -> int {
                                               return std::system(STDSystemCommand.c_str());
                                           }, Command));
    
            return Future;
            // Note: You can access the CPU usage value by calling the std::future::get() function of the returned future object.
        }
    

    2。 Qt - QProcess

    与标准c ++解决方案类似,您可以等待QProcess线程完成执行并获得结果。

    A, QProcess::waitForFinished(-1)

    这会阻止调用线程。

    auto runBatchQtQProcessWaited(const std::string& BatchFile) -> int {
        QProcess Process;
        auto Command = QString("cmd.exe");
        auto Arguments = QStringList{
                QString("/C"),
                QString::fromStdString(BatchFile)
        };
    
        Process.start(Command, Arguments);
        Process.waitForFinished(-1);
    
        return Process.exitCode();
    }
    

    使用Qt,另一种可能性是发出适当的时隙函数来接收QProcess的结果。

    B, QProcess::finished()

    这不会阻止调用线程。

    class SlotClass : public QObject {
        Q_OBJECT
    
    public:
        SlotClass(std::shared_ptr<QProcess> Process);
    
        auto getResult() const -> int;
    
    public slots:
        /*auto*/ void onPostFinishQtQProcess(int ExitCode, QProcess::ExitStatus ExitStatus) /*-> void*/; 
        // Seems like Qt 5.5 moc compiler fails to correctly recognize auto declared slots. (Throws error when compiling at ":".)
    
    private:
        std::shared_ptr<QProcess> Process;
        int Result;
    };
    
    SlotClass::SlotClass(std::shared_ptr<QProcess> Process) :
        Process(Process),
        Result(-1) {}
    
    
    auto SlotClass::getResult() const -> int {
        return this->Result;
    }
    
    /*auto*/ void SlotClass::onPostFinishQtQProcess(int ExitCode, QProcess::ExitStatus ExitStatus) /*-> void*/ {
        if (ExitStatus == QProcess::CrashExit)
            throw std::runtime_error("Batch process crashed.");
    
        this->Result = ExitCode;
    }
    
    auto runBatchQtQProcessSignaled(const std::string& BatchFile, const SlotClass& SlotObject) -> void {
        auto Command = QString("cmd.exe");
        auto Arguments = QStringList{
                QString("/C"),
                QString::fromStdString(BatchFile)
        };
    
        QObject::connect(SlotObject.getProcess().get(), SIGNAL(finished(int, QProcess::ExitStatus)),
                         &SlotObject, SLOT(onPostFinishQtQProcess(int, QProcess::ExitStatus)));
    
        SlotObject.getProcess()->start(Command, Arguments);
    }
    

    3。 WinAPI - CreateProcess(...)

    使用WinAPI的阻塞等待也有变化。

    A, CreateProcess(...)

    这会阻止调用线程。

    auto runBatchWinAPICreateProcessWaited(const std::string& BatchFile) -> int {
        auto Command = "cmd.exe /C " + BatchFile;
    
        // Creates wide string array from the narrow command string.
        auto WideStringConverter = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>();
        auto WideCommand = WideStringConverter.from_bytes(Command);
        auto WideCommandArray = std::make_unique<wchar_t[]>(WideCommand.length() + 1);
        std::wcscpy(WideCommandArray.get(), WideCommand.c_str());
    
        // Initializes necessary structures.
        STARTUPINFO BatchStartupInformation;
        std::memset(&BatchStartupInformation, 0, sizeof(BatchStartupInformation));
        BatchStartupInformation.cb = sizeof(BatchStartupInformation);
    
        PROCESS_INFORMATION BatchProcessInformation;
        std::memset(&BatchProcessInformation, 0, sizeof(BatchProcessInformation));
    
        // Creates a new command prompt process with no window and executes the given batch file.
        if (!CreateProcess(nullptr, WideCommandArray.get(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW,
                       nullptr, nullptr, &BatchStartupInformation, &BatchProcessInformation))
            throw std::exception(("Could not create process for running the batch file. Error code: " + std::to_string(GetLastError())).c_str());
    
        // Waits until the created process has already finished.
        auto WaitResult = WaitForSingleObject(BatchProcessInformation.hProcess, INFINITE);
        if (WAIT_FAILED == WaitResult)
            throw std::runtime_error(("Waiting for batch process failed. Error code: " + std::to_string(GetLastError())).c_str());
        //else if (WAIT_TIMEOUT == WaitResult)
        //    ;   //...
    
        auto ProcessResult = 0ul;
        if (!GetExitCodeProcess(BatchProcessInformation.hProcess, &ProcessResult))
            throw std::exception(("Could not retrieve process exit code after running batch file. Exit code: " + std::to_string(GetLastError())).c_str());
    
        CloseHandle(BatchProcessInformation.hProcess);
        CloseHandle(BatchProcessInformation.hThread);
    
        return ProcessResult;
    }
    

    或者你可以像 3 / A 那样做,但是创建一个新线程来等待批处理文件完成。

    B, CreateThread()CreateProcess()

    这不会阻止调用线程。

    auto runBatchWinAPICreateProcessEvent(const std::string& BatchFile) -> void {
        auto Command = "cmd.exe /C " + BatchFile;
    
        // Creates wide string array from the narrow command string.
        auto WideStringConverter = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>();
        auto WideCommand = WideStringConverter.from_bytes(Command);
        auto WideCommandArray = std::make_unique<wchar_t[]>(WideCommand.length() + 1);
        std::wcscpy(WideCommandArray.get(), WideCommand.c_str());
    
        // Initializes necessary structures.
        STARTUPINFO BatchStartupInformation;
        std::memset(&BatchStartupInformation, 0, sizeof(BatchStartupInformation));
        BatchStartupInformation.cb = sizeof(BatchStartupInformation);
    
        PROCESS_INFORMATION BatchProcessInformation;
        std::memset(&BatchProcessInformation, 0, sizeof(BatchProcessInformation));
    
        // Creates a new command prompt process with no window and executes the given batch file.
        if (!CreateProcess(nullptr, WideCommandArray.get(), nullptr, nullptr, FALSE, CREATE_NO_WINDOW,
                       nullptr, nullptr, &BatchStartupInformation, &BatchProcessInformation))
            throw std::exception(("Could not create process for running the batch file. Error code: " + std::to_string(GetLastError())).c_str());
    
        if (!CreateThread(nullptr, 0, &waitForWinAPICreateProcessResult, new PROCESS_INFORMATION(BatchProcessInformation), 0, nullptr))
            throw std::exception(("Could not create process for retrieving the result of the batch file. Error code: " + std::to_string(GetLastError())).c_str());
    }
    
    auto WINAPI waitForWinAPICreateProcessResult(LPVOID ThreadParameter) -> DWORD {
        auto BatchProcessInformation = std::unique_ptr<PROCESS_INFORMATION>(reinterpret_cast<PROCESS_INFORMATION*>(ThreadParameter));
    
        // Waits until the created process has already finished.
        auto WaitResult = WaitForSingleObject(BatchProcessInformation->hProcess, INFINITE);
        if (WAIT_FAILED == WaitResult)
            throw std::runtime_error(("Waiting for batch process failed. Error code: " + std::to_string(GetLastError())).c_str());
        //else if (WAIT_TIMEOUT == WaitResult)
        //    ;   //...
    
        auto ProcessResult = 0ul;
        if (!GetExitCodeProcess(BatchProcessInformation->hProcess, &ProcessResult))
            throw std::exception(("Could not retrieve process exit code after running batch file. Exit code: " + std::to_string(GetLastError())).c_str());
    
        // You have the result in the ProcessResult variable.
    
        CloseHandle(BatchProcessInformation->hProcess);
        CloseHandle(BatchProcessInformation->hThread);
    
        return 0;
    }