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.
摘要:
-
标准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:
注意:我尚未验证直接来自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.
这不会阻止调用线程.
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.
这不会阻止调用线程.
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.
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.
这不会阻止调用线程.
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;
}
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;
}
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;
}