简体   繁体   English

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

[英]Qt Creator: How to run a windows batch file and get result

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%

And I would like to run it in a standard C++ Qt windows application and store the percentage in a variable. 我想在标准的C ++ Qt Windows应用程序中运行它,并将百分比存储在变量中。 I can see you can run batch files using QProcess , but I'm not sure how to get the result. 我可以看到您可以使用QProcess运行批处理文件,但是我不确定如何获得结果。

Thanks in advance for any help. 在此先感谢您的帮助。

There are multiple solutions to this task (standard C++, Qt, WinAPI, etc.), I will list a few of those. 此任务有多种解决方案(标准C ++,Qt,WinAPI等),我将列举其中一些。 I have tested and verified all the listed ones (in case I did not make a mistake, they should work fine). 我已经测试并验证了所有列出的内容(如果我没有记错的话,它们应该可以正常工作)。

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% ”仅将结果值打印到命令提示符,而不返回它。

For that reason I changed the last line of the batch file 因此,我更改了批处理文件的最后一行

from echo %Cpusage0% 来自 echo %Cpusage0%

to exit %Cpusage0% exit %Cpusage0%

This returns the value in the variable Cpusage0 from the command prompt. 这将从命令提示符处返回变量Cpusage0的值。

Solutions: 解决方案:

In general: the batch file needs to be run through the command interpreter ( cmd.exe - command prompt on Windows). 通常,批处理文件需要通过命令解释器运行( cmd.exe -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). 另外,如果您只想在解释器中运行一个命令然后终止它,则需要在cmd.exe中指定/ C选项 (在您的情况下确实如此)。

So the command you wish to execute is: 因此,您希望执行的命令是:

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

Note: if you change the Command std::string the previously obtained c_str() pointers become invalid. 注意:如果更改Command std :: string,则先前获得的c_str()指针将变为无效。 So either 所以要么

  • don't change the std::string while the std::system() call is running, 在执行std :: system()调用时,请勿更改std :: string,
  • 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字符串( [const] char* )指针,或最好复制到智能指针字符数组( std::unique_ptr<char[]> )。

In case of using C-string, don't forget to delete it. 如果使用C字符串,请不要忘记删除它。

Summary: 摘要:

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

    A, Waiting for std::system(...) to finish. A,等待std :: system(...)完成。 This blocks the calling thread. 这将阻止调用线程。

    B, Running std::system(...) in a different thread with std::thread . B,在与std :: thread不同的线程中运行std :: system(...) This does not block the calling thread. 不会阻止调用线程。

    C, Running std::system(...) in a different thread with std::async . C,使用std :: async在另一个线程中运行std :: system(...) This does not block the calling thread. 不会阻止调用线程。

  2. Qt - QProcess Qt- QProcess

    A, Waiting for QProcess to finish. A,等待QProcess完成。 This blocks the calling thread. 这将阻止调用线程。

    B, Signaling a slot with QProcess::finished() . B,用QProcess :: finished()通知插槽。 This does not block the calling thread. 不会阻止调用线程。

  3. WinAPI - CreateProcess(...) WinAPI- CreateProcess(...)

    A, Waiting for CreateProcess(...) to finish. A,等待CreateProcess(...)完成。 This blocks the calling thread. 这将阻止调用线程。

    B, Starting a new thread ( CreateThread(...) ) to wait for CreateProcess(...) to finish. B,启动一个新线程( CreateThread(...) )以等待CreateProcess(...)完成。 This does not block the calling thread. 不会阻止调用线程。

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++. 替代方法 :我想在问题评论中提到@wOxxOm所建议的相同内容-您可以直接在C ++中获得CPU使用率。 This has been asked on StackOverflow a couple of times before, here are some examples: 之前已经在StackOverflow上问过几次,下面是一些示例:

How to determine CPU and memory consumption from inside a process? 如何从进程内部确定CPU和内存消耗?

Retrieving CPU Load Percent total in Windows with C++ 使用C ++在Windows中检索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. 注意 :我自己尚未验证“直接来自C ++的cpu用法”的答案,但是其中一个被强烈反对,另一个被接受为答案。


In details: 详细说明:

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). 如果使用“多线程解决方案”,请不要忘记为共享资源添加适当的保护(例如,使用std::atomic<int>或std :: mutex保护共享变量)。

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

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

A, Plain blocking std::system(...) call. A,普通阻止std :: system(...)调用。

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

You can do the same in another thread (and continue to do other things while waiting for the result) by using either std::thread or std::async(...) . 您可以使用std :: threadstd :: async(...)在另一个线程中执行相同的操作(并在等待结果时继续执行其他操作

B, std::thread . B, std :: thread ( std::promise , std::future ) std :: promisestd :: future

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

The following basically wraps the 1/B solution into 1 call. 以下基本上将1 / B解决方案包装为1个调用。

C, std::async(...) 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 2. Qt - QProcess

Similarly to the standard c++ solutions you can wait for the QProcess thread to finish the execution and obtain the result. 与标准c ++解决方案类似,您可以等待QProcess线程完成执行并获取结果。

A, QProcess::waitForFinished(-1) A, QProcess :: waitForFinished(-1)

This blocks the calling thread. 这将阻止调用线程。

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

With Qt another possibility is to signal an appropriate slot function for receiving the result of the QProcess . 使用Qt的另一种可能性是发信号通知适当的时隙功能以接收QProcess的结果。

B, QProcess::finished() B, QProcess :: finished()

This does not block the calling thread. 这不会阻止调用线程。

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(...) 3. WinAPI - CreateProcess(...)

There is a variation for the blocking wait with WinAPI as well. WinAPI的阻塞等待也有所不同。

A, CreateProcess(...) A, CreateProcess(...)

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

Or you can do the same as 3/A , but create a new thread to wait for the batch file to finish. 或者,您可以执行与3 / A相同的操作 ,但是创建一个新线程以等待批处理文件完成。

B, CreateThread() , CreateProcess() B, CreateThread()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;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM