To start off with, I'm pretty new to C++.
I am wanting to accomplish the following:
Execute the following: "SampleApp.exe -cf test.xml"
I need the shell that execute in hidden mode
I need the C++ application to wait until SampleApp is finished
If the SampleApp takes longer than X amount of time, then I need to terminate the process
I want to pipe SampleApp's output to a file (sample.log)
So far I have the following:
SHELLEXECUTEINFO lpExecInfo;
lpExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
lpExecInfo.lpFile = L"SampleApp.exe";
lpExecInfo.fMask = SEE_MASK_DOENVSUBST|SEE_MASK_NOCLOSEPROCESS;
lpExecInfo.hwnd = NULL;
lpExecInfo.lpVerb = L"open";
lpExecInfo.lpParameters = L"-cf test.xml";
lpExecInfo.lpDirectory = NULL;
lpExecInfo.nShow = SW_HIDE; // hide shell during execution
lpExecInfo.hInstApp = (HINSTANCE) SE_ERR_DDEFAIL;
ShellExecuteEx(&lpExecInfo);
// wait until the process is finished
if (lpExecInfo.hProcess != NULL)
{
::WaitForSingleObject(lpExecInfo.hProcess, INFINITE);
::CloseHandle(lpExecInfo.hProcess);
}
The above code achieves everything except piping output to a file.
However, I doesn't seem to be possible with ShellExecute.
It seems that I need to use CreateProcess instead.
I am hoping that someone with more C++ experience would be able to provide me with the CreateProcess equivalent of my code plus piping output. If not, at least confirm that what I am wanting to do is possible and point me in the right direction.
- Thanks
Unless you're feeling particularly masochistic or truly need to optimize this operation, use _popen to create the child process. That will return a FILE * from which you can read the child's output. Read from there, write to file, done.
FILE *child = _popen("child.exe", "r");
FILE *result = fopen("result.txt", "w");
// error checking omitted.
char buffer[1024];
while (fgets(buffer, sizeof(buffer), child))
fputs(buffer, result);
Doing this on your own (using the Windows API) is certainly possible and can even reduce overhead, but it's tremendously more work.
Your going to want to familiarize yourself with this code, as it's exactly what you want to do.
You will need to add some code to write to file in the ReadFromPipe function.
Related
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.
I am trying to write a C program which may lookup a url and incase a new version of it is avaiable it should be able to update itself.
The method i have tried:
Forkout a new process to Download the new binary say BINARY.tmp, code i am using to forkout the is:
int
forkout_cmd(char *cmdstr) {
pid_t pid;
char *cmd[4];
cmd[0] = "/bin/bash";
cmd[1] = "-c";
cmd[2] = cmdstr;
cmd[3] = NULL;
pid = vfork();
if( pid == -1 ) {
logmsg("Forking for upgradation failed.");
return -1;
}else if( pid == 0 ){
/* we are in child process */
execvp(cmd[0], cmd);
logmsg("execl failed while executing upgradation job.");
}else{
/* need not to wait for the child to complete. */
wait(NULL);
}
return 0;
}
The new process tries to overwrite the original BINARY
for example you may consider the routine which forks out may be doing:
forkout_cmd("wget -O BINARY.tmp https://someurl.com/BINARY_LATEST; /bin/mv -f BINARY.tmp BINARY");
But, the overwriting fails since the original binary is still in execution and hence busy on disk, can somebody provide me some suggestions here to overcome this problem.
Thanks in advance.
Rename the currently running binary to something else, write the new binary, run it, then delete the renamed binary later.
I would save binary.tmp to the same directory as the executable, verify its checksum/signature (whatever it takes to be 100% sure no error occurred), and then atomically rename it to the executable's name.
Under Linux, this can be done while the program is running, no problem whatsoever (you are only changing the link, the underlying file persists while mappings to it are open, that is until the program is closed or restarted).
I would under no circumstances rename the original file or even overwrite it. This is unsafe and not necessary. You can do all "unsafe" operations that could fail on the temp file before touching the original. If anything goes wrong in the atomic rename, you still have the working original.
Then prompt the user to restart the program (if interactive) and done.
I've tried with some solutions found in Stackoverflow, but i can't get it to work, i want to start a .LOG (.txt file) from C++, but the path folder containing it might have spaces, so when i try to start it, i get an error saying it cant find the file because the pah (containing spaces) is wrong, here is what my code looks like:
void Log (unsigned int Code,...)
{
char currdate[11] = {0};
SYSTEMTIME t;
GetLocalTime(&t);
sprintf(currdate, "%02d:%02d:%02d", t.wHour, t.wMinute, t.wSecond);
PROCESSENTRY32 pe32;
FILE* FileHwnd1;
FileHwnd1 = fopen("TEST.log","a+");
fprintf(FileHwnd1,"[%s] Code: %X\n",currdate,Code);
fclose(FileHwnd1);
char buffer[MAX_PATH];
GetModuleFileName( NULL, buffer, MAX_PATH);
char Path[50];
wsprintf(Path,"start %s\\AntiHack.log",buffer);
system(Path);//Here is where i get the containing spaces path error
}
Thanks.
I would advise you avoid the system call entirely and do the process launch yourself.
Use AssocQueryString() to find the associated process for your
extension (in this case, .log)
Setup and launch a CreateProcess() call to invoke, passing the
appropriate command line.
there are other ways to do this, but as you're noticing now, going a round-about way will always have pitfalls. The above is spot-on with how Explorer.exe launches the associated process for an extension.
You can try:
wsprintf(Path,"start \"\" \"%s\"\\\AntiHack.log",buffer);
I am trying to use CreateProcess and CreatePipe to execute a process from within a Windows Forms C++/CLR application in Visual Studio 2010.
From within my Windows forms app I want to execute a child process (console app) and return the output as a std::string, std::wstring, or System::String^ within my Windows forms app. Additionally, I do not want the newly created child process to spawn a window.
The console application is of my own creation, so I have control of it's source too.
I have seen the following examples, but I do not understand how to modify the code to accomplish what I am trying to do:
MSDN
kgui
A simpler MFC based function
The MSDN code appears to be written as two console apps, one calling the other. The code is confusing to me. I've only been working in C++ for about 4 months, so I still don't understand everything. It appears to reference a text file, which I don't need to do.
Is there a simpler way to do this than MSDN's 200+ lines of code or kgui's 300+ lines of code?
The answer here was helpful, but over simplistic. I was hoping to see a basic source example (one that doesn't involve hundreds of lines of complex code would be preferable). I would have used the MFC code, but I had difficulty adapting it to my purposes (I'm not using MFC).
Following is my adaptation of the code from Code Project:
string ExecuteExternalFile(string csExeName, string csArguments)
{
string csExecute;
csExecute=csExeName + " " + csArguments;
SECURITY_ATTRIBUTES secattr;
ZeroMemory(&secattr,sizeof(secattr));
secattr.nLength = sizeof(secattr);
secattr.bInheritHandle = TRUE;
HANDLE rPipe, wPipe;
//Create pipes to write and read data
CreatePipe(&rPipe,&wPipe,&secattr,0);
//
STARTUPINFO sInfo;
ZeroMemory(&sInfo,sizeof(sInfo));
PROCESS_INFORMATION pInfo;
ZeroMemory(&pInfo,sizeof(pInfo));
sInfo.cb=sizeof(sInfo);
sInfo.dwFlags=STARTF_USESTDHANDLES;
sInfo.hStdInput=NULL;
sInfo.hStdOutput=wPipe;
sInfo.hStdError=wPipe;
//Create the process here.
CreateProcess(0,(LPWSTR)csExecute.c_str(),0,0,TRUE,NORMAL_PRIORITY_CLASS|CREATE_NO_WINDOW,0,0,&sInfo,&pInfo);
CloseHandle(wPipe);
//now read the output pipe here.
char buf[100];
DWORD reDword;
string m_csOutput,csTemp;
BOOL res;
do
{
res=::ReadFile(rPipe,buf,100,&reDword,0);
csTemp=buf;
m_csOutput+=csTemp;
}while(res);
return m_csOutput;
}
I have tried using this from within my Windows Forms app, and while it compiles ok and doesn't cause any errors, it doesn't seem to work either. I have no idea why.
This is how I executed the above code:
std::string ping = ExecuteExternalFile("ping.exe", "127.0.0.1");
It did not appear to do anything, except that on the first execution it give a very strange 3 characters as an output, then on subsequent executions, nothing.
You are not making correct use of the ::ReadFile() function.
Read about it here: http://msdn.microsoft.com/en-us/library/ms891445.aspx
Basically, you want to fail with an error if the function ever does not return TRUE, and you want to keep looping until it yields a zero reDword.
Also, ::ReadFile() will not zero-terminate your data for you, so you have to do it yourself, like this: buf[reDword] = '\0'; (make sure your buf is 101 chars long before doing that.)
EDIT:
Since I was asked to provide some example code, here it is, though I have not gone through the trouble of actually compiling it to make sure it works, so please beware of syntax errors, and generally consider it only as a rough pointer to the direction in which it should be done:
#define BUFFER_SIZE 100
string csoutput;
for( ;; )
{
char buf[BUFFER_SIZE+1];
DWORD redword;
if( !::ReadFile(rPipe,buf,BUFFER_SIZE,&redword,0) )
{
DWORD error = ::GetLastError();
//throw new Exception( "Error " + error ); //or something similar
}
if( redword == 0 )
break;
buf[redword] = '\0';
string cstemp = buf;
csoutput += cstemp;
}
return csoutput;
Thanks to Hans Passant for the lead that got me to this clear and simple piece of code that does exactly what I was looking for.
/// this namespace call is necessary for the rest of the code to work
using namespace System::Diagnostics;
using namespace System::Text;/// added for encoding
Process^ myprocess = gcnew Process;
Encoding^ Encoding;/// added for encoding
Encoding->GetEncoding(GetOEMCP());/// added for encoding
myprocess->StartInfo->FileName = "ping.exe";
myprocess->StartInfo->Arguments = "127.0.0.1";
myprocess->StartInfo->UseShellExecute = false;
/// added the next line to keep a new window from opening
myprocess->StartInfo->CreateNoWindow = true;
myprocess->StartInfo->RedirectStandardOutput = true;
myprocess->StartInfo->StandardOutputEncoding = Encoding;/// added for encoding
myprocess->Start();
String^ output = gcnew String( myprocess->StandardOutput->ReadToEnd() );
myprocess->WaitForExit();
/// OutputBox is the name of a Windows Forms text box in my app.
OutputBox->Text = output;
EDIT: Added encoding information. See above code.
I'm having some trouble with a program. My goal is to have it open several .exe files with optional args passed. For example if I wanted to open up a pdf I could type the string below into a cmd window.
// If used in a cmd window it will open up my PDF reader and load MyPDF.pdf file
"c:\Test space\SumatraPDF.exe" "c:\Test space\Sub\MyPDF.pdf"
Here are two tries I used. The first opens the PDF but of course doesn't load the file. The second simply doesn't work.
// Opens the PDF in my program
system("\"C:\\Test space\\SumatraPDF.exe\"");
// Error I get inside of a cmd window is the comment below
// 'C:\Test' is not recognized as an internal or external command, operable program or batch file.
//system("\"C:\\Test space\\SumatraPDF.exe\" \"C:\\Test space\\Sub\\MyPDF.pdf\"");
I'm unsure of the reason why the second one does not work. It could be I'm misunderstanding something about system, or I'm not using delimiters right.
I feel like there is a library out there designed for this rather than creating a long string that uses so many delimiters.
Thanks for any help.
Welcome to Stack Overflow!
The system method works by passing it's argument to cmd /c. So you will need an extra set of quotes around it. See related question posted by sled.
As an alternative to system, take a look at the ShellExecute or ShellExecuteEx Win32 API function. It has more features although it is not as portable.
// ShellExecute needs COM to be initialized
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof(sei);
sei.lpFile = prog; // program like c:\Windows\System32\notepad.exe
sei.lpParameters = args; // program arguments like c:\temp\foo.txt
sei.nShow = SW_NORMAL; // app should be visible and not maximized or minimized
ShellExecuteEx(&sei); // launch program
CoUninitialize();
More information here.