Launch console application which uses environment variables from QT Gui application - c++

I am currently making a GUI using QT4.8 which basically needs to launch a console application. However, because this console application tries to fetch some environment variables, I can't seem to manage to make this work.
I am using QProcess obviously and have tried several solutions :
process->start("./yarpbridge", QStringList() << "--from" << "tmp.ini");
This solution does not spawn a console window and besides, by redirecting the output to qDebug(), it prints the erros corresponding to the lack of environment variables.
process->start("gnome-terminal", QStringList() << "-e" << "zsh" << "-c" << "\"./yarpbridge --from tmp.ini"\");
This solution does launch a console window but it nevertheless displays the error messages because somehow .zshrc was probably not consulted when opening the console window.
Would you have a solution that would allow me to do this, and even better that would not only work with "gnome-terminal" and "zsh" users ?
Thanks a lot,

Can you post the error you are getting?
It is very strange because you don't need to start a terminal in order to run a CLI program, maybe after posting your error message I might get an idea what the problem is.
Also you can try this as well:
#include <stdio.h>
char buffer[1024];
FILE* fd = popen("/path/to/yarpbridge", "r");
if (fd == NULL) {
// Error: do something
}
while(NULL != fgets(buffer, sizeof(buffer), fd)) {
QString s(buffer);
s = s.stripWhiteSpace();
// s contains the output, pretty much as readAllStandardOutput() in QProcess
}
// don't forget to close file.
close (fd);

Related

Ways to execute system commands in qt program without opening console window

I am running system() to move files in qt. This leads to blinking of console windows, Is there any way to stop the opening and closing (blinking) of console windows or any alternative ways for system() to hide console windows.
buffer = QString("move \"%2\\*.ico\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
qPrintable(buffer);
system(qPrintable(buffer));
buffer = QString("move \"%2\\*.jpg\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
system(qPrintable(buffer));
buffer = QString("move \"%2\\*.jpeg\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
system(qPrintable(buffer));
buffer = QString("move \"%2\\*.png\" \"%2\\%1\" 2>nul")
.arg(images).arg(dir);
system(qPrintable(buffer));
Here %2 is directory and %1 is inputted folder name.
One option is to use QProcess to run external commands.
Example:
QString program = "move";
QStringList args;
args << QString("\"%1\\*.ico\"").arg(dir);
args << QString("\"%1\\%2\"").arg(dir).arg(images);
QProcess::execute(program, args);

How to open/spawn a file with glib/gtkmm in Windows

I've already tried:
GError *pError = NULL;
string uri = g_filename_to_uri(file.c_str(), NULL, &pError);
if (!g_app_info_launch_default_for_uri(uri.c_str(), NULL, &pError)) {
cout << "Failed to open uri: " << pError->message;
}
Here I get the error "URIs not supported". Is the uri I create here wrong?
My second approach was to spawn the file with an asynchronous command line:
file = quoteStr(file);
try {
Glib::spawn_command_line_async(file);
} catch (Glib::SpawnError error) {
cout << error.what();
} catch (Glib::ShellError error) {
cout << error.what();
}
Here the Glib::SpawnError exception is thrown with the error: "Failed to execute helper program (Invalid argument)". I mean, when I execute the quoted absolute file path in the Windows cmd, it opens the file (in this case a pdf file). Does this function work different?
Hopefully this is related and can provide a real answer rather than just a (clever!) workaround.
I ran into a strange situation: Launching a file (specifically an HTML document) by g_app_info_launch_default_for_uri() or gtk_show_uri_on_window() worked when the executable was run from my build directory. However, it did not work if I copied the exe to another directory (for distribution) and ran it from there.
In the latter case, I got the same error as your 2nd quote:
Failed to execute helper program (Invalid argument)
The build directory is not in my path, and nor is it special for any other reason (it's in a temp RAM drive). So I was completely baffled.
I then thought about that error... What helper program could it possibly be talking about?
And why might that program be found when running from the build directory? Well, my build uses a libtool wrapper, and that puts a bunch of things in the path, so that we don't need to copy all the DLLs etc in just to test builds.
So, I went to investigate whether there was anything relevant-looking in paths that might be searched by the MSYS2 shell and its libtool wrapper. The prime suspect, of course, is C:\msys64\mingw64\bin. And look what I found there:
gspawn-win64-helper-console.exe
After copying this executable to the directory from which my application is launched, my program now successfully launches the URI, regardless of which folder its executable currently resides in.
Edit
After updating my packages in MSYS2, it was back to the same error - as it seems now this is the helper that is required:
gspawn-win64-helper.exe
That actually makes more sense, since my application is graphical, not console. I guess maybe something changed here recently. You could distribute both to be extra safe.
I had a similar problem and I had to give up using glib to do that and ended up implementing a simple crossplatform (win, mac and linux) compatible way to do it:
// open an URI, different for each operating system
void
openuri(const char *url)
{
#ifdef WIN32
ShellExecute(GetActiveWindow(),
"open", url, NULL, NULL, SW_SHOWNORMAL);
#elif defined(__APPLE__)
char buffer[512];
::snprintf(buffer, sizeof(buffer), "open %s", url);
::system(buffer);
#else
char buffer[512];
::snprintf(buffer, sizeof(buffer), "xdg-open %s", url);
::system(buffer);
#endif
}
... it's not very nice but it's small and it works :)

Returning output from bash script to calling C++ function

I am writing a baby program for practice. What I am trying to accomplish is basically a simple little GUI which displays services (for Linux); with buttons to start, stop, enable, and disable services (Much like the msconfig application "Services" tab in Windows). I am using C++ with Qt Creator on Fedora 21.
I want to create the GUI with C++, and populating the GUI with the list of services by calling bash scripts, and calling bash scripts on button clicks to do the appropriate action (enable, disable, etc.)
But when the C++ GUI calls the bash script (using system("path/to/script.sh")) the return value is only for exit success. How do I receive the output of the script itself, so that I can in turn use it to display on the GUI?
For conceptual example: if I were trying to display the output of (systemctl --type service | cut -d " " -f 1) into a GUI I have created in C++, how would I go about doing that? Is this even the correct way to do what I am trying to accomplish? If not,
What is the right way? and
Is there still a way to do it using my current method?
I have looked for a solution to this problem but I can't find information on how to return values from Bash to C++, only how to call Bash scripts from C++.
We're going to take advantage of the popen function, here.
std::string exec(char* cmd) {
FILE* pipe = popen(cmd, "r");
if (!pipe) return "ERROR";
char buffer[128];
std::string result = "";
while(!feof(pipe)) {
if(fgets(buffer, 128, pipe) != NULL)
result += buffer;
}
pclose(pipe);
return result;
}
This function takes a command as an argument, and returns the output as a string.
NOTE: this will not capture stderr! A quick and easy workaround is to redirect stderr to stdout, with 2>&1 at the end of your command.
Here is documentation on popen. Happy coding :)
You have to run the commands using popen instead of system and then loop through the returned file pointer.
Here is a simple example for the command ls -l
#include <stdio.h>
#include <stdlib.h>
int main() {
FILE *process;
char buff[1024];
process = popen("ls -l", "r");
if (process != NULL) {
while (!feof(process)) {
fgets(buff, sizeof(buff), process);
printf("%s", buff);
}
pclose(process);
}
return 0;
}
The long approach - which gives you complete control of stdin, stdout, and stderr of the child process, at the cost of fairly significant complexity - involves using fork and execve directly.
Before forking, set up your endpoints for communication - pipe works well, or socketpair. I'll assume you've invoked something like below:
int childStdin[2], childStdout[2], childStderr[2];
pipe(childStdin);
pipe(childStdout);
pipe(childStderr);
After fork, in child process before execve:
dup2(childStdin[0], 0); // childStdin read end to fd 0 (stdin)
dup2(childStdout[1], 1); // childStdout write end to fd 1 (stdout)
dup2(childStderr[1], 2); // childStderr write end to fd 2 (stderr)
.. then close all of childStdin, childStdout, and childStderr.
After fork, in parent process:
close(childStdin[0]); // parent cannot read from stdin
close(childStdout[1]); // parent cannot write to stdout/stderr
close(childStderr[1]);
Now, your parent process has complete control of the std i/o of the child process - and must safely multiplex childStdin[1], childStdout[0], and childStderr[0], while also monitoring for SIGCLD and eventually using a wait-series call to check the process termination code. pselect is particularly good for dealing with SIGCLD while dealing with std i/o asynchronously. See also select or poll of course.
If you want to merge the child's stdout and stderr, just dup2(childStdout[1], 2) and get rid of childStderr entirely.
The man pages should fill in the blanks from here. So that's the hard way, should you need it.

Getting the messages back to the same command line from where the MFC application was launched

I am executing an MFC Application from command line which takes four command line arguments.One of the argument is the directory path.
If the path is wrong then I want to show a Message "Bad Path" on the same command line
Note : For showing I don't want to take a new command line .
Basically that's not supported. There is sort of known "workaround" for that, using AttachConsole(-1) to attach parent process console. Has disadvantages of course (like, parent console will not wait for your EXE to terminate, because it's not a "console" app). Anyways, basic idea:
void WriteToParentConsole()
{
if (AttachConsole(-1))
{
char msg[] = "Bad Path!";
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), msg, sizeof(msg), NULL, NULL);
// send ENTER (optional)
// ::SendMessage(GetConsoleWindow(), WM_CHAR, VK_RETURN, 0);
FreeConsole();
}
}
You can check out this article for example, or just google something on AttachConsole/GUI vs Console for more information:
http://www.tillett.info/2013/05/13/how-to-create-a-windows-program-that-works-as-both-as-a-gui-and-console-application/

Decide an application to be of console/windows subsystem at run time

I have an MFC application that generates some reports and shows the same in the GUI.
I have a requirement of running it as a console application, when passed with certain commandline argument.
In the console mode it will generate the reports/errors in standard output/error and I should be able to redirect the same to any file.
For Ex.
C:/temp MyApp.exe --console > report.txt should run my exe in console mode and redirect all the output to a text file.
But if I run it without any console argument, it should be like a default MFC application.
To achieve my requirement, so far I have done is, changed the Linker > System > Subsytem from Windows to Console and added WinMainCRTStartup at
Linker > Advanced > Entry Point
So now my app works fine when I run it with --console parameter from console/batch files.
But when I run it directly, it still opens a cmd window (of course because it is now a console application). However, I am using FreeConsole() method to get rid of it but it still flashes for a brief second.
So I am just curious if there is a way to get rid of it completely, either by deciding the application's subsytem at run time or any other trick?
Any suggestion will be appreciated.
I'd suggest to keep your GUI application with the windows subsystem.
At the very beginning, when parsing command line, instead of creating the GUI windows (MFC inistialisation), you could create a console or attach with AttachConsole()
As a proof of concept, here how it could look like (for example in CxxxApp::InitInstance()):
... // init code but before the the CMainFrame is created
if(...) { // suppose you want to go to the console
if (! AttachConsole(ATTACH_PARENT_PROCESS)) // try to hijack existing console of command line
AllocConsole(); // or create your own.
DWORD nw,nr; // demo with windows native console i/o
char buff[32];
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), L"Who are you ? ", 14, &nw, NULL);
ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE), buff, sizeof(buff), &nr, NULL);
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), "Hello ", 6, &nw, NULL);
WriteConsoleA(GetStdHandle(STD_OUTPUT_HANDLE), buff, nr, &nw, NULL);
ReadConsoleA(GetStdHandle(STD_INPUT_HANDLE), buff, 1, &nr, NULL);
...
}
If you want to use C++ cin/cout, additional work is however needed. This other SO question addresses for example the redirecting of cout.