Qt installer script execute
-
I have partial success in executing command.
component.addOperation("Execute", "cmd", "/C", "PnPutil.exe", "-i", "-a", "@TargetDir@\\driver.inf");
It produces:
Execution failed(Unexpected exit code: 1): "cmd /C PnpUtil.exe -i -a D:\MyApp\driver.inf"
However, cmd /C PnpUtil.exe -i -a D:\MyApp\driver.inf run directly in command prompt produces exit code 0. Does somebody know why the exit codes differs?
I think I read the documentation and forum but command execution in Qt installer framework seems to be uncomprehensible to me. I don't have a clue why command execution mentioned here produces the errors, why I have to run pnputil.exe via cmd, etc.
-
Maybe try addElevatedOperation?
-
@Paul-Colby Thanks. I added
<RequiresAdminRights>true</RequiresAdminRights>
to my package.xml
and added elevated operation:
component.addElevatedOperation("Execute", "cmd", "/C", "PnPutil.exe", "-i", "-a", "@TargetDir@\\driver.inf");
Still the same - error code 1.
-
I discoved this works:
// launching python script in installer works component.addOperation("Execute", "C:/Python27/python.exe", "@TargetDir@/script.py", "workingdirectory=@TargetDir@");
This doesn't work:
// launching e.g. pnputil.exe or qmake.exe produces "no program defined" error component.addOperation("Execute", "C:/Windows/System32/pnputil.exe", "workingdirectory=@TargetDir@"); component.addOperation("Execute", "C:/Qt/5.6/mingw49_32/bin/qmake.exe", "workingdirectory=@TargetDir@");
Any idea why? (launching them in command line works of course)
EDIT: I found that "No program defined" defined error is defined in qprocess.cpp:
void QProcess::start(const QString &program, const QStringList &arguments, OpenMode mode) { Q_D(QProcess); if (d->processState != NotRunning) { qWarning("QProcess::start: Process is already running"); return; } if (program.isEmpty()) { Q_D(QProcess); d->processError = QProcess::FailedToStart; setErrorString(tr("No program defined")); emit error(d->processError); return; } d->program = program; d->arguments = arguments; d->start(mode); }
-
@MartinD said:
I discoved this works:
// launching python script in installer works component.addOperation("Execute", "C:/Python27/python.exe", "@TargetDir@/script.py", "workingdirectory=@TargetDir@");
This doesn't work:
// launching e.g. pnputil.exe or qmake.exe produces "no program defined" error component.addOperation("Execute", "C:/Windows/System32/pnputil.exe", "workingdirectory=@TargetDir@"); component.addOperation("Execute", "C:/Qt/5.6/mingw49_32/bin/qmake.exe", "workingdirectory=@TargetDir@");
Any idea why?
I suspect the reason is that the Python example has an argument (ignoring the
workerdirectory
bit) while the other two do not.The reason I suspect this, is that it looks (from trawling the code), that there's a bug in the Qt Installer Framework code specific to Q_OS_WIN and
args.count() == 1
.Let me explain...
I found that "No program defined" defined error is defined in qprocess.cpp:
That code / error indicates that QProcess::start is being called with an empty program name; ie the following check is being triggered.
if (program.isEmpty()) {
So, where is that coming from...
Looking into the QIF's code, we see that
ElevatedExecuteOperation
(which seems to handle allExecute
commands, not just the elevated one) uses QIF'sQProcessWrapper
class, which lightly wraps Qt'sQProcess
.That brings us to this bit of code, found in
ElevatedExecuteOperation::Private::run
:#ifdef Q_OS_WIN if (args.count() == 1) { process->setNativeArguments(args.front()); qDebug() << "ElevatedExecuteOperation setNativeArguments to start:" << args.front(); process->start(QString(), QStringList()); } else #endif { process->start(args.front(), args.mid(1)); }
See that on
Q_OS_WIN
, if the args count is 1, then:- the code uses
QProcess::setNativeArguments
(so, presumably, forward-to-back slash conversion is no longer in effect, etc) - this part is okay; then - invokes
QProcess::start
with a null string!
It's that second step which will always fail, as I read the code, since
QProcess::start
pays no attention to anything set viaQProcess::setNativeArguments
, so far as I can see, before checking the program name.The QProcess::setNativeArguments documentation is unclear, but numerous examples on github suggest that even when using it, the first argument to QProcess::start should still be the executable.
So I'd:
- try adding another argument to your latest example (to fail the
args.count() == 1
test); - report this as a (potential) bug on bugreports.qt.io to see what the QIF's devs think.
Cheers.
- the code uses
-
Hey,
old topic, but I've spent hours today on the same issue, and this is the only place I could google. And as someone new to Windows, it was not obvious at all.
The problem is with the Windows filesystem redirection:
https://msdn.microsoft.com/en-us/library/windows/desktop/aa384187.aspxSince the installer is 32-bit, Windows redirects calls to system32 into sysWOW64, which does not have pnputil. You need to use the sysnative directory instead, see the msdn article.
That's how I do it in my installer:
windir = installer.environmentVariable("windir"); console.log("WINDIR: " + windir); if (windir == "") { //Just in case. console.log("Undefined WINDIR? Assuming c:\\windows\\"); windir = "c:\\windows"; } pnputil = windir + "\\sysnative\\pnputil.exe"; console.log("pnputil = " + pnputil); component.addElevatedOperation("Execute", pnputil, "-i", "-a", "@TargetDir@\\driver.inf");
Works as expected!
-
Hi and welcome to devnet,
Thanks for sharing your findings !
Did you try to take a look at the bug report system ? If not, please do and if you find a related bug report, please update it too :)
-
The bug report is closed already:
https://bugreports.qt.io/browse/QTIFW-862
It seems that there was some unrelated problem, and it's been fixed in 2.1.0.
-
Thanks !
-
@xl0x in my case your solution produce error" "Execution failed(Unexpected exit code: 259): "C:\windows\sysnative\pnputil.exe -i -a C:\Program Files (x86)\My_app\driver\driver.inf"". Any soution for this?
I found the partial solution. I created bat file which installs driver:
start Pnputil -i -a PX218.inf
After that, I convert this bat file to exe using "Bat to Exe Convereter" application. Finally, I added this line to installscript.qs:
component.addOperation("Execute", "@TargetDir@/driver/install.exe", "workingdirectory=@TargetDir@/driver/");
and it work form me, but it's not convenient....