I have a QT desktop aplication and now I want that when it starts it automatically calls openvpn to start too.
First try I've made it with a simple system() call, and it works, but it leaves cmd window in screen. I don't like it, but it works:
system("openvpn-gui --connect m2smart.ovpn")
But now I want to launch directly without needing the cmd window, and if I do it without arguments it works, this:
QProcess openvpn;
QString cmd("openvpn-gui.exe");
openvpn.startDetached(cmd);
Now, the only thing that I need is to indicate which config.ovpn to connect, like in the first example, I've searched how to do it and it seems to be like that:
QProcess openvpn;
QString cmd("openvpn-gui.exe");
QStringList args;
args << "--connect m2smart.ovpn";
openvpn.startDetached(cmd, args);
But it shows an openvpn window that says:
"Options error: unrecognized option or missing parameter(s): --connect m2smart.ovpn"
But if I open a cmd and put "openvpn-gui --connect m2smart.ovpn it works, independently of my working directory.
So... anyone can help me? I'm stucked with this.
Thanks!
First of all, QProcess::startDetached() is a static method. There is no need to instantiate a QProcess object before calling it. You just need to do:
QProcess::startDetached("openvpn-gui.exe");
Now, We have two overloads for QProcess::startDetached():
QProcess::startDetached(const QString& command):
There is no difference between this and using system function from the started program's point of view. So, on windows, if you had something like:
QProcess::startDetached("openvpn-gui.exe --connect m2smart.ovpn");
It would be the same as opening cmd.exe and typing openvpn-gui.exe --connect m2smart.ovpn.
QProcess::startDetached(const QString &program, const QStringList &arguments, const QString &workingDirectory = QString(), qint64 *pid = Q_NULLPTR):
From the docs:
On Windows, The arguments are quoted and joined into a command line that is compatible with the CommandLineToArgvW() Windows function.
So, If you used something like this in your program:
QString cmd("openvpn-gui.exe");
QStringList args;
args << "--connect m2smart.ovpn";
QProcess::startDetached(cmd, args);
This would be like opening cmd.exe and typing openvpn-gui.exe "--connect m2smart.ovpn".
In the first case, the first argument passed to openvpn-gui.exe is --connect and the second argument is m2smart.ovpn.
In the second case, there is only one argument that is --connect m2smart.ovpn.
Related
When running this command from the command line prompt:
magick convert report_pages/test_0.png report_pages/test_1.png report_pages/test_2.png report_pages/test.pdf
It works perfectly. Generating a PDF from the three test png images. The location where I'm running this command is the location of the Qt Application compiled executable that I'm developing. This is a Windows application.
I want to run that same command from said Qt application.
In order to do this I use this code:
QProcess pdfmerge;
#ifdef Q_OS_WIN
arguments.prepend("convert");
#endif
pdfmerge.start(PDF_GEN_APPLICATION,arguments);
bool all_good = pdfmerge.waitForFinished();
this->cmd = QString(PDF_GEN_APPLICATION) + " " + arguments.join(" ");
Inside a class. PDF_GEN_APPLICATION is just a define for the string "magick".
Arguments is a list first of the word "convert" and then of the list of PNG.
As a matter a fact I took the string that I run in the CMD console from the output of printing this->cmd.
When I try to run the Qt Application I get the error that the process "Failed To Start". With no other clues, I was wondering if anyone here could help me. In order to run my Qt Application I just press play in QtCreator, in case that has anything to do with it.
Your command should be "magick" and the first argument should be "convert".
e.g.
int main(int argc, char* argv[]) {
QProcess p;
p.start("magick", {"convert", "test.bmp", "test.png"});
p.waitForFinished();
return p.exitCode();
}
I am trying to call an executable with qprocess and pass some arguments which might (and most probably will) contain spaces (not all of them).
The executable is a python script that has been packaged with Py2exe. The python script uses optparse to parse the arguments.
If I call the py2exe.exe in cmd.exe the call is like this:
pythonExecutable.exe -aarg_a -barg_b -c"path with spaces" -darg_d
A call like this will be successful.
I want to do this through a Qt application using Qprocess, but I can't find a way to do it because Qprocess will strip any quotes("") leaving the arguments broken wherever spaces appear.
I seem to be missing something, can anyone help with this issue?
that won't be much of an issue if u use the QProcess in a more proper way
QString program = "pythonExecutable.exe";
QStringList arguments;
arguments <<"-aarg_a"<< "-barg_b"<< "-c\"path with spaces\""<< "-darg_d";
QProcess *myProcess = new QProcess(parent);
myProcess->start(program, arguments);
normaly when u have arguments with space and do't need a " symbol
you just have to pass the argument in a QStringList
QString program = "pythonExecutable.exe";
QStringList arguments;
arguments <<"a"<< "path with spaces";
QProcess *myProcess = new QProcess(parent);
myProcess->start(program, arguments);
this program is a modified version of example program listed in the Qt docs Here
I have a problem with the GetCommandLine() API.
It usually returns the executable name followed by a space and arguments. As documentation says, the first token may not have the complete path to the image and blah blah blah.
I never had problems until now that I used CreateProcess with lpApplicationName not NULL.
If I use:
CreateProcess(NULL, "\"c:\\myexe.exe\" param1 param2", ...)
GetCommandLine returns "c:\myexe.exe param1 param2" as expected.
But if I use:
CreateProcess("C:\myexe.exe", "param1 param2")
GetCommandLine returns only "param1 param2".
How do I know if the executable name is given on the command line if another application launches mine?
Also, MFC startup code assumes that the first token on the command line is the executable name and skips it. But if you launch a MFC application with the second CreateProcess API example, MFC's code will skip the first argument.
Not your problem. It's the job of the other application to construct the command line properly. You should simply assume that the first argument is an executable name as expected and skip over it.
I have a workaround which can be helpful in a case like this.
I guess we always be able to check how our module was been started.
In this case we should check first argument.
I will write code because I have some problem with English.
Here two ways:
The first case. we can compare module name with first command line argument.
something like this:
const TCHAR* csCommandLine = ::GetCommandLine();
// Attention!!! the first symbol can be quete
if (*csCommandLine == _T('\"'))
csCommandLine++;
TCHAR sModuleFileName[MAX_PATH];
DWORD dwModuleFileName = ::GetModuleFileName(NULL, sModuleFileName, MAX_PATH);
if (dwModuleFileName && !_tcsncmp(csCommandLine, sModuleFileName, dwModuleFileName)) {
// The command line contains the module name.
}
The second case. we can try to get file attributes for the first command line argument
something like this:
// Attention!!! don't use it case if you are going to pass a file path in command line arguments.
int nArgc;
LPTSTR* szArglist = ::CommandLineToArgvW(::GetCommandLine(), &nArgc);
if (nArgc && ::GetFileAttributes(szArglist[0]) != INVALID_FILE_ATTRIBUTES) {
// The command line contains the module name.
}
::LocalFree(szArglist);
I hope it can be helpful someone.
Regards, Vladimir
I have a Windows executable (whoami) which is crashing every so often. It's called from another process to get details about the current user and domain. I'd like to know what parameters are passed when it fails.
Does anyone know of an appropriate way to wrap the process and write it's command line arguments to log while still calling the process?
Say the command is used like this:
'whoami.exe /all'
I'd like a script to exist instead of the whoami.exe (with the same filename) which will write this invocation to log and then pass on the call to the actual process.
From a batch file:
echo Parameters: %* >> logfile.txt
whoami.exe %*
With the caveat that you can have problems if the parameters contain spaces (and you passed the in escaping with "), because the command-line parser basically de-escapes them and they should be re-escaped before passed to an other executable.
You didn't note which programming language. It is not doable from a .bat file if that's what you wanted, but you can do it in any programming language. Example in C:
int main(int argc, void **argv)
{
// dump contents of argv to some log file
int i=0;
for (i=0; i<argc; i++)
printf("Argument #%d: %s\n", argv[i]);
// run the 'real' program, giving it the rest of argv vector (1+)
// for example spawn, exec or system() functions can do it
return 0; // or you can do a blocking call, and pick the return value from the program
}
I don't think using a "script" will work, since the intermediate should have a .exe extension for your ploy to work.
I would write a very small command line program to do this; something like the following (written in Delphi/Virtual Pascal so it will result in a Win32 executable, but any compiled language should do):
program PassThrough;
uses
Dos; // Imports the Exec routine
const
PassTo = 'Original.exe'; // The program you really want to call
var
CommandLine: String;
i: Integer;
f: Text;
begin
CommandLine := '';
for i := 1 to ParamCount do
CommandLine := CommandLine + ParamStr(i) + ' ';
Assign(f,'Passthrough.log');
Append(f);
Writeln(f, CommandLine); // Write a line in the log
Close(f);
Exec(PassTo, CommandLine); // Run the intended program
end.
Can't you just change the calling program to log the parameters it used to call the process, and the exit code?
This would be way easier than trying to dig into whoami.exe
Look for whoami.exe, BACK IT UP, replace it with your own executable and see do whatever you like with it's parameters (maybe save them in a text file).
If you can reproduce the crash, use Process Explorer before crashed process is terminated to see its command line.
http://technet.microsoft.com/en-us/sysinternals/bb896653.aspx
This is related to: How do I read the results of a system() call in C++?
I am trying to do the exact the same thing only that my program needs to pass 'multiple parameters with spaces' to the command. I need the command line output and the exit code from the process.
Example: An example with Textpad. The application I'm really using prints stuff on stdout.
string command1 = "\"C:\Program Files\TextPad 5\Textpad.exe\" C:\readme0.txt";
string command2 = "\"C:\Program Files\TextPad 5\Textpad.exe\" \"C:\read me2.txt\"";
cout << system(command1.c_str()) << endl;
cout << system(command1.c_str()) << endl;
Output:
0
'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
1
The first call to system passes and second one fails with the error above. _popen in Windows works similarly on Windows so no help there. I can easily do this on Linux as I can escape spaces in the parameters without having to use quotes.
An alternative is to write a huge chunk of non-cross-platform code as listed here:
http://msdn.microsoft.com/en-us/library/ms682499(VS.85).aspx
But in case I want to avoid that, are there any alternatives to system() and _popen() on Windows?
Thanks!
The lowest level Windows API function used by _popen() and system() is CreateProcess().
However CreateProcess() is not that simple to use - especially when you want to get the process'es output or write to the process'es input.
CreateProcess() will definitely work with file names that contain space characters (as long as they are written in quotation marks the way you did that).
The following solves the spaces in the path problem. Catching the output of the command is much more difficult, however:
#include <string>
#include <cstdlib>
using namespace std;
int main() {
string cmd = "\"c:\\program files\\notepad++\\notepad++.exe\"";
system( cmd.c_str() );
return 0;
}
A bunch of utility libraries have taken that chunk of non-portable code and wrapped it up with a portable interface. For an example, see Qt's QProcess.
I do this (note - this is VB.NET code), so I can write the output of the command to my log file (it's wrapped in a RunCommand() method):
Try
Dim myprocess As New Process()
myprocess.StartInfo.FileName = "C:\Program Files\TextPad 5\Textpad.exe"
myprocess.StartInfo.RedirectStandardOutput = True
myprocess.StartInfo.UseShellExecute = False
' inArgs are the arguments on the command line to the program
myprocess.StartInfo.WindowStyle = ProcessWindowStyle.Hidden
myprocess.StartInfo.Arguments = "C:\readme0.txt"
' the dir to set as default when the program runs
Then myprocess.StartInfo.WorkingDirectory = "C:\Program Files\TextPad 5\"
myprocess.Start()
' grab a reader to the standard output of the program
procReader = myprocess.StandardOutput()
' read all the output from the process
While (Not procReader.EndOfStream)
procLine = procReader.ReadLine()
' write the output to my log
writeNotes(procLine)
End While
procReader.Close()
Catch ex As Exception
' Write the error to my log
writeErrors("Couldn't execute command "C:\Program Files\TextPad 5\Textpad.exe", ex)
End Try
I think ShellExecute() is what you're looking for.
Never use system() in Windows !
Just redirect i/o handles.