[Solved] Running cmd.exe in QProcess without showing Command Prompt window?
-
Hi all,
not sure if it's my lacking Windows terminology that stops me from finding usable search results, but here goes:
Basically I just need to concatenate a number of files. Experience tells me that the OS is the right place to do this, and it works just fine in Mac OS using the cat command in bash. However, when trying to do the same stunt in Windows, along the lines of
cmd /C copy /B file1+file2 file3
it works fine, BUT I am seeing the Command Prompt window quickly opening and closing as the command is being executed via my QProcess object. Is there any way to avoid showing the Command Prompt window when executing a shell command in Windows?
-
Well then don't open the command prompt :)
To be more specific cmd is the command prompt app and you don't need it to call copy app.
Even more bluntly: -cmd /C- copy /B file1+file2 file3
The added benefit is that your QProcess will get an error code from the copy app, not from the cmd app.If you wan't to stay more portable there's also static "QFile::copy()":http://qt-project.org/doc/qt-5.0/qtcore/qfile.html#copy-2 that does more or less the same.
-
I wish it was that easy. There is (to the best of my knowledge) no binary called "copy", just as there's no binary called "dir". They're shell commands executed by cmd.exe.
Using QFile::copy() would be an obvious choice if I wanted to copy files, but that's not the case. I want to concatenate files, as I wrote in my first post.
-
Use "QDataStream":http://qt-project.org/doc/qt-4.8/qdatastream.html? I don't see the need to do this via shell when all the disk/file manipulation functionality is given by Qt.
Or actually you don't even need a data stream if you just want to cat files. Directly use QFile readData/writeData operations.
-
DerManu: I would agree, if not the Qt equivalent would be orders of magnitude slower than using the OS - at least according to several discussions I have read before choosing this approach. I'm potentially joining multi-GB files (which would sort of rule our readAll()/writeAll(), since these keep data in memory as far as I can tell).
But - if nothing else turns up, it will have to be my choice, at least on Windows.
-
readData/writeData allow reading/writing larger batches. So if you allocate, lets say 10 MiB in memory and then read/write continuous chunks of 10MiB (possibly asynchronously with threads, if that speeds things up, e.g. in the case of two disks), It should be fast. I assume Qt also just calls OS API, just like cmd.exe does.
In any case, don't believe rumors here, just try it yourself and benchmark performance. -
Alrightie then. I continued my search, ended up in solutions like .bat files called by a VBScript and decided that the chain of dependencies maybe was over the top to be worth the effort.
I did end up doing a "manual" read/write, appending to the target file via a buffer. Seeing the result suprised me a lot, and I must stand corrected regarding my ideas of which method is faster in this case - shell command vs. reading/writing files with QFile.
I am joining two files, totalling 144 MB on a quad qore iMac.
QFile read/write, 1 MB buffer: 455 ms
QFile read/write, 10 MB buffer: 431 ms
QProcess executing /bin/sh cat file1 file2 > file3: 480 msVery surprising for me. I don't know whether the increased time used by QProcess has to do with spawning a shell, but none the less, the total time is what matters is in this case.
Note also that the increased buffer size means very little.Anyways - thanks for kicking me in the right direction! :-p
(And just for the record - the very simple code):
@ joinedFile->open(QFile::Append);
long bufSize = 10485760;for (int i = 0; i < filePaths.length(); i++) { QFile partFile(filePaths[i]); partFile.open(QFile::ReadOnly); char *buf = new char[bufSize]; long readSize; while (!partFile.atEnd()) { readSize = partFile.read(buf, bufSize); joinedFile->write(buf, readSize); } delete buf; partFile.close(); }@
-
It's me again.
I nearly had a fit, when I noticed that another external process I had to execute via QProcess (FFmpeg) ALSO showed a Command Prompt window - this time called directly, ie. without cmd.exe.
I noticed that I executed the binary with QProcess::execute(), and tried with QProcess::start() instead. Voila, problem solved. But why is this? And why didn't anybody hint this before? :-p
-
You may be right, haha!
However, I have a collection of tuned FFmpeg parameters, and I don't feel like doing a bunch of experiments (again) figuring out the equivalent libavcodec parameters. Unless the parameters can be used interchangeably, but from what I know that isn't the case. Suggestions a much welcome, though.
I agree that a library is a much smoother solution in general than an external binary.
-
[quote author="janfaroe" date="1374079399"]I noticed that I executed the binary with QProcess::execute(), and tried with QProcess::start() instead. Voila, problem solved.[/quote]Congratulations :)
[quote]But why is this?[/quote]No idea. I had a look at the documentation, but it didn't describe any behavioural differences between execute() and start(). You can ask at the "Interest mailing list":http://lists.qt-project.org/mailman/listinfo/interest where Qt's engineers reside.
[quote]And why didn't anybody hint this before? :-p[/quote]Probably because nobody here knew that you were using execute()
(and/or nobody here knew about that there was a difference between the two functions)
-
Well, I'll just accept the reality now. My only experience with the mailing list has not been positive.
So, bottom line is:
-
At least in some cases, Qts file operations are faster than using a shell equivalent
-
QProcess::execute() shows a Command Prompt window, QProcess::start() doesn't.
I'll mark as solved. Thanks for the hints and input!
-
-
Hi, normally, for a console application of Windows OS, console window(Which you called Command Prompt) is need for INPUT/OUTPUT. And this is the default behavior of Windows api if the value dwCreationFlags is zero.
@
BOOL WINAPI CreateProcess(
In_opt LPCTSTR lpApplicationName,
Inout_opt LPTSTR lpCommandLine,
In_opt LPSECURITY_ATTRIBUTES lpProcessAttributes,
In_opt LPSECURITY_ATTRIBUTES lpThreadAttributes,
In BOOL bInheritHandles,
In DWORD dwCreationFlags,
In_opt LPVOID lpEnvironment,
In_opt LPCTSTR lpCurrentDirectory,
In LPSTARTUPINFO lpStartupInfo,
Out LPPROCESS_INFORMATION lpProcessInformation
);
@Let's see the manual of QProcess::execute()
bq. Starts the program program with the arguments arguments in a new process, waits for it to finish, and then returns the exit code of the process. Any data the new process writes to the console is forwarded to the calling process.
As you can see, output of the child-process will be forward to parent process, so there is no need to keep a Console window for the child-process any more.
That's why
@
CREATE_NO_WINDOW
@is passed to the CreateProcess() in this case.
--
BTW, there is no magic in the static member function QProcess::execute()
@
int QProcess::execute(const QString &program, const QStringList &arguments)
{
QProcess process;
process.setReadChannelMode(ForwardedChannels);
process.start(program, arguments);
if (!process.waitForFinished(-1))
return -2;
return process.exitStatus() == QProcess::NormalExit ? process.exitCode() : -1;
}
@ -
Thanks for your explanation!
I am a bit confused, though. From what you are writing, I understand it as when QProcess::execute() is called, CREATE_NO_WINDOW is passed. But in my case, I am experiencing the opposite, ie. that a console window is created when calling execute(), and not when calling start().
Am I misunderstanding something?
-
It's my fault. CREATE_NO_WINDOW is passed to the API when call QProcess::start() by default.
@
DWORD dwCreationFlags = (processChannelMode == QProcess::ForwardedChannels ? 0 : CREATE_NO_WINDOW);
@I am a bit confused too.
-
With a simple MainWindow application with a lineEdit and a plainTextEdit, I was able to get a command window to communicate. Not sure why the prompt is printed twice.
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); process = new QProcess(this); connect( process, SIGNAL(readyReadStandardOutput()), this, SLOT(on_StdoutAvailable()) ); connect(ui->lineEdit, SIGNAL(returnPressed()), this, SLOT(on_lineEdit_returnPressed())); process->start("cmd.exe"); } void MainWindow::on_lineEdit_returnPressed() { if (process->isOpen()) { QByteArray ba(ui->lineEdit->text().toUtf8() + '\n') ; char *ptr = ba.data(); int numBytes = process->write(ptr); } ui->lineEdit->clear(); } void MainWindow::on_StdoutAvailable() { QByteArray ba = process->readAllStandardOutput(); ui->plainTextEdit->appendPlainText(ba); }