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

问题描述:

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

I currently have this 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运行批处理文件,但是我不确定如何获得结果.

And I would like to run it in a standard C++ Qt windows application and store the percentage in a variable. I can see you can run batch files using QProcess, but I'm not sure how to get the result.

在此先感谢您的帮助.

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

There are multiple solutions to this task (standard C++, Qt, WinAPI, etc.), I will list a few of those. I have tested and verified all the listed ones (in case I did not make a mistake, they should work fine).

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

However all the solutions listed below requires you to change one line in the batch script you provided. The reason for that is the last line "echo %Cpusage0%" only prints the result value to the command prompt instead of returning it.

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

来自 echo %Cpusage0%

exit %Cpusage0%

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

This returns the value in the variable Cpusage0 from the command prompt.

解决方案:

通常:批处理文件需要通过命令解释器运行( cmd.exe -Windows上的命令提示符).另外,如果您希望仅在解释器中运行一个命令然后终止该命令,则需要在cmd.exe中指定/C选项(在您的情况下确实如此).

In general: the batch file needs to be run through the command interpreter (cmd.exe - command prompt on Windows). In addition you need to specify the /C option to the cmd.exe if you wish to run only one command in the interpreter and then terminate it (that's true in your case).

因此,您希望执行的命令是:

So the command you wish to execute is:

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

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

Note: if you change the Command std::string the previously obtained c_str() pointers become invalid. So either

  • 在运行std :: system()时不要更改std :: string
  • 使用本地变量存储命令(就像我在示例中所做的那样)
  • 将命令复制到C字符串([const] char*)指针中,或 最好放入智能指针字符数组 ( std::unique_ptr<char[]> ).
  • don't change the std::string while the std::system() call is running,
  • use a local variable for storing the command (like i did in the examples),
  • copy the command into a C-string ([const] char*) pointer or preferably into a smart pointer character array (std::unique_ptr<char[]>).

如果使用C字符串,请不要忘记删除它.

In case of using C-string, don't forget to delete it.

摘要:

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

A,正在等待 std :: system(...)一个>完成.这会阻止调用线程.

A, Waiting for std::system(...) to finish. This blocks the calling thread.

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

B, Running std::system(...) in a different thread with std::thread. This does not block the calling thread.

C,正在运行 std :: system(...)在具有 std :: async 的不同线程中进行.这不会阻止调用线程.

C, Running std::system(...) in a different thread with std::async. This does not block the calling thread.

Qt- QProcess

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

A, Waiting for QProcess to finish. This blocks the calling thread.

B,使用 QProcess :: finished()发出信号通知插槽>.这不会阻止调用线程.

B, Signaling a slot with QProcess::finished(). This does not block the calling thread.

WinAPI- CreateProcess(...)

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

A, Waiting for CreateProcess(...) to finish. This blocks the calling thread.

B,启动新线程( CreateProcess(...)完成.这不会阻止调用线程.

B, Starting a new thread (CreateThread(...)) to wait for CreateProcess(...) to finish. This does not block the calling thread.

替代:我想提到在问题评论中建议的 @wOxxOm 相同的内容-您可以获取CPU使用率直接在C ++中.之前已经在StackOverflow上问过几次,下面是一些示例:

Alternative: I would like to mention the same thing @wOxxOm advised in a comment on the question - you can get the CPU usage directly in C++. This has been asked on StackOverflow a couple of times before, here are some examples:

如何确定CPU和内存消耗从流程内部进行?

使用C ++检索Windows中的CPU负载百分比总计

注意:我尚未验证直接来自C ++的cpu用法"的答案,但是其中一个被强烈反对,另一个被接受为答案.

Note: I haven't verified the "cpu usage directly from C++" answers myself, but one is heavily upvoted and the other one is accepted as an answer.

注意:这些是具有最少错误检查功能的简约解决方案.如果使用多线程解决方案",请不要忘记为共享资源添加适当的保护(例如,使用std::atomic<int>或std :: mutex保护共享变量).

Note: These are minimalistic solutions with minimal error checking. In case of using a "multi-threaded solution" do not forget to add proper protection for the shared resources (for example using std::atomic<int> or std::mutex to protect the shared variable).

您可以在当前线程中执行批处理文件,并通过调用

You can execute the batch file in the current thread and wait for the result by calling std::system(...) with the Command and storing the result in an int variable (the percentage value).

A, 普通阻止 这将阻止调用线程.

This blocks the calling thread.

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

        return std::system(Command.c_str());
    }

您可以使用 std :: thread std :: async (...).

B, std :: thread . ( std :: promise 这不会阻止调用线程.

This does not block the calling thread.

    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个调用.

The following basically wraps the 1/B solution into 1 call.

C std :: async(...)

这不会阻止调用线程.

This does not block the calling thread.

    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 线程完成执行并获得结果.

2. Qt - QProcess

Similarly to the standard c++ solutions you can wait for the QProcess thread to finish the execution and obtain the result.

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 .

With Qt another possibility is to signal an appropriate slot function for receiving the result of the 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 -

3. WinAPI - CreateProcess(...)

There is a variation for the blocking wait with WinAPI as well.

A 这将阻止调用线程.

This blocks the calling thread.

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 相同的操作,但是创建一个新线程以等待批处理文件完成.

Or you can do the same as 3/A, but create a new thread to wait for the batch file to finish.

B CreateProcess()

这不会阻止调用线程.

This does not block the calling thread.

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;
}