Waitforsingleobject to detect a system command exit - c++

Hi I want to execute a system command for allegro and wait till it completes, since I need to access the files generated by this command. My code is not working. Can someone please help? Is the command not running? It works if I do system(command.c_str()) but does not wait.
string command = "start allegro -expert -nographic -s " + fileName1 + " " + boardFile1;
bool ret;
bool retwait;
STARTUPINFO startupinfo;
GetStartupInfo (&startupinfo);
PROCESS_INFORMATION pro2info;
LPTSTR cmdL = (LPTSTR)command.c_str();
ret = CreateProcess(NULL, cmdL, NULL, NULL, false, CREATE_NEW_CONSOLE, NULL,NULL, &startupinfo, &pro2info);
cout<<"hProcess: "<<pro2info.hProcess<<endl;
cout<<"dwProcessId: "<<pro2info.dwProcessId <<endl;
//Want to wait till the command executes
while (retwait= WaitForSingleObject (pro2info.hProcess, INFINITE)!=WAIT_OBJECT_0)
cout<<"waitprocess:true"<<endl; //The process is finished;
CloseHandle (pro2info.hProcess);

I think you don't need "start" at the beginning of command, even if you are using the flag CREATE_NEW_CONSOLE. "start" works with system (because system works with batch command), but if you are creating a new process you should only specify the path to the image file. By the way, the only way to know what's going on is to check the return of CreateProcess (and eventually GetLastError()).
Also, that's not the best way to use WaitForSingleObject. You should catch WAIT_FAILED and use GetLastError(), otherwise your next post on SO will be: "Why isn't my program waiting?" :-)
http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425%28v=vs.85%29.aspx

Related

Kill a process that run in infinite loop

I create a process from c++ code, that run a python script.
STARTUPINFO si = {0};
PROCESS_INFOMATION pi = {0};
LPTSTR cmdArgs = _T("cmd.exe /k python myScript.py");
CreateProcess(NULL, cmdArgs, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
The script run in infinite loop, for example:
while (1):
print("hello")
I want to kill the process but nothing really work.
I t ried TerminateProcess(), taskkill with pid - they report that process terminated but it still running.
How can I kill the process while it runnning?
LPTSTR cmdArgs = _T("cmd.exe /k python myScript.py");
CreateProcess(NULL, cmdArgs, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &si, &pi);
this code create cmd.exe process and it create python.exe process. question - for what create intermediate cmd.exe ?! you need direct start python.exe. use "python myScript.py" command line and start python.exe direct (if use unicode W api and empty application name - string must not be readonly (in your code it readonly - so you use A api, otherwice you got exception))
TerminateProcess not work - because you have handle of cmd.exe and you can terminate it. but not python.exe.
taskkill with pid - they report that process terminated but it still
running.
of course - pid belong to cmd.exe and it terminated. but python.exe still runing.
so again - solution very simply - direct exec target process, without cmd.exe

The Command CreateProcess C++

The Command CreateProcess With the command WaitForSingleObject
can to open an image?
If Yes How can I open the image?
I tried to open but i don't know Where to put the path to open
if (CreateProcess(NULL, "C:\ProgramData\Microsoft\Windows\Start Menu\Programs\Accessories\Paint.lnk", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
}
If you just want to open an existing image using defualt app then use ShellExectue API. For example:
ShellExecuteW(NULL, L"open", L"Z:\\cat.PNG", NULL, NULL, SW_SHOW);
You could also open image with mspaint using the same API:
ShellExecuteW(NULL, L"open", L"C:\\Windows\\system32\\mspaint.exe", L"Z:\\cat.PNG", NULL, SW_SHOW);
ShellExecuteEx will let you wait for finishing process.
You can do the same using CreateProcess. As #DavidHeffernan pointed out the second parameter of CreateProcess should point to writable memory else it will raise access violation. To make it clear I will just omit the first parameter. Example:
STARTUPINFOW process_startup_info{ 0 };
process_startup_info.cb = sizeof(process_startup_info); // setup size of strcture in bytes
PROCESS_INFORMATION process_info{ 0 };
wchar_t commandline_args[] = L"\"C:\\Windows\\system32\\mspaint.exe\" Z:\\cat.PNG";
if (CreateProcessW(NULL, commandline_args, NULL, NULL, TRUE, 0, NULL, NULL, &process_startup_info, &process_info))
{
//WaitForSingleObject(process_info.hProcess, INFINITE); // uncomment to wait till process finish
CloseHandle(process_info.hProcess);
CloseHandle(process_info.hThread);
}
I strongly recommend to read this CodeProject article (A newbie's elementary guide to spawning processes).
The CreateProcess function can only start .exe and .bat files. Use ShellExecute[Ex] if you want to launch files the same way the shell/Explorer does.
A process must have a PE EXE file as its main file and if you ask CreateProcess to start something else it is just going to fail. ShellExecute will look up the file extension in the registry to find the correct executable (or COM handler) to execute and this often ends up calling CreateProcess on your behalf with the file you specified as a command line parameter.
Using ShellExecuteEx with the SEE_MASK_NOCLOSEPROCESS flag might might give you a process handle you can wait on but you also have to be prepared for hProcess being NULL. This can happen if the registered application for the file type is already running and DDE or COM was used to open the file in this existing application instance...

Is there any way GetExitCodeProcess could set the exit code to the wrong value?

I'm maintaining code written by someone just before they retired, which means I can't find them to ask questions. :-) This is basically a C++ wrapper to launch a program. The chunk of code in question is this:
BOOL bSuccess = CreateProcess(NULL, (char *)strBatFile.c_str(),
NULL, NULL, TRUE, CREATE_NO_WINDOW, NULL, strLocalWorkingDir.c_str(), &si, &pi );
if( bSuccess )
{
DWORD dwMillisec = INFINITE;
DWORD dwWaitStatus = WaitForSingleObject( pi.hProcess, dwMillisec );
if( dwWaitStatus == WAIT_OBJECT_0 )
{
DWORD dwExitCode = NULL;
GetExitCodeProcess( pi.hProcess, &dwExitCode );
nRet = (int)dwExitCode;
}
CloseHandle( pi.hThread );
CloseHandle( pi.hProcess );
}
else
nRet = START_PROCESS_FAILED;
If just one instance is run at a time, it always works fine. If multiple are run within a very short time frame, though, about half of them are having dwExitCode set to 1 instead of 0, even though the process isn't crashing, and the log file that internal program writes is completing.
So to clarify, the process is always starting fine, and it's always getting into the if statements, but it's the value of dwExitCode set by GetExitCodeProcess that isn't containing what's expected. Since we error check on this, we're flagging a bunch of these runs as incomplete when they in fact are fine.
Is there any way this value could be set to something different than the process exit code? And/or is there a utility I could run at the same time to confirm the exit codes are what I think they are?
Thanks!
ETA: Just realised this is putting the internal program call in a .bat file - "C:\\ --flags etc..." and then calling that as a command line in the second argument, rather than just calling it directly using lpApplicationName. No idea if this makes a difference! But when I print the PID of the process, I can see it's the PID for a cmd.exe process, and then our program has a child PID. However, when I trace in Process Monitor, I can see that both parent and child are exiting with exit code 0.
Found it! The application itself was in fact returning a 0 error code...it was the shell around it that was returning 1. And that was due to that .bat file in the second argument. The name was being generated from time, so it ended up being exactly the same name if multiple instances were run too closely together. This is why the inner app would run fine...there was always a bat file there with that name. But there were access collisions when the different instances were trying to generate or clean up the bat, from what I can tell.
As a proof-of-concept hack I just added the current PID to the end of the file name, and everything worked perfectly. Now I just need to decide the real fix, which I think will likely be getting rid of the whole bat file mechanism entirely and calling the app directly.
Whew! Thanks for all the help, everyone! Sadly, the bit of code I included didn't have the offending line, but everyone's tips above helped me narrow in on the problem. :-)

Writing to a process STDIN via Windows pipes

I am trying to create a function that will spawn an instance of a program and then pipe some data into its STDIN and then read the process's output using C++. I have looked at an MSDN example located here which is rather confusing to me and when I try to use the example, I get some nasty error codes and it won't work.
HANDLE hWriteOUT, hReadOUT, hWriteIN, hReadIN;
SECURITY_ATTRIBUTES saPipe = {0};
PROCESS_INFORMATION procInfo = {0};
STARTUPINFO procSi;
DWORD dwWritten, dwRead;
char buf[512];
saPipe.nLength = sizeof(SECURITY_ATTRIBUTES);
saPipe.bInheritHandle = TRUE;
saPipe.lpSecurityDescriptor= NULL;
CreatePipe(&hReadOUT, &hWriteOUT, &saPipe, 0);
SetHandleInformation(hReadOUT, HANDLE_FLAG_INHERIT, 0);
CreatePipe(&hReadIN, &hWriteIN, &saPipe, 0);
SetHandleInformation(hReadIN, HANDLE_FLAG_INHERIT, 0);
ZeroMemory(&procSi, sizeof(STARTUPINFO));
procSi.cb = sizeof(STARTUPINFO);
procSi.hStdError = hWriteOUT;
procSi.hStdOutput = hWriteOUT;
procSi.hStdInput = hReadIN;
procSi.dwFlags |= STARTF_USESTDHANDLES;
CreateProcess(NULL, "cmd", NULL, NULL, TRUE, 0, NULL, NULL, &procSi, &procInfo);
//Gives me an error code of 18 but returns a 1 when a 0 indicates failure.
WriteFile(hWriteIN, "notepad", sizeof("notepad"), &dwWritten, NULL);
cout << GetLastError(); //This gives me error code 18 (ERROR_NO_MORE_FILES)
ReadFile(hReadOUT, buf, 512, &dwRead, NULL);
cout << buf; //This prints "Microsoft Windows [version 6.1.7601]
CloseHandle(hWriteIN);
The code fails to pipe the string "notepad" into cmd.exe but is successful in launching the command shell. If I look in task manager, there are several instances of command prompt spawned but no notepads. In addition, the ReadFile() function is the only one that seemed to have worked, but it's not even reading from the piped process (notepad that was supposed to be spawned) instead, it's reading from CMD. And even worse, it's truncating everything but the first line that it reads! (CMD prints the "Microsoft Windows....\n Copyright...\n C:\Users\Foo>...\n" but the `ReadFile() only grabs the first line)
The code is behaving as expected. There are a number of things you seem to be misunderstanding:
1) You need to send an ENTER ("\n") at the end of a command if you want cmd.exe to run it. Usually it is preferable to specify the command you want to run in CreateProcess, e.g., you could specify "cmd /c notepad" as the command line instead of just "cmd".
2) You've attached your pipes to the standard input and output of the cmd.exe process, so of course you see output from that process. If you don't want to see output from cmd.exe, don't run it; run the application you want directly, e.g., you could specify "notepad" as the command line to run.
3) When reading from a pipe, ReadFile only returns a single block of data at a time, so you need to call it in a loop.
4) Notepad is a GUI process, so it doesn't use stdin or stdout anyway. Presumably this was just a poorly chosen example and you actually want to run a command-line application?
5) Except as specifically documented, the error code (as returned by GetLastError) is only meaningful when a function has failed. None of the functions you are using are exceptions to this case, so there is no point in checking the error code unless the function returns zero to indicate that it has failed.
I know it's kinda too late to answer this question but there is also something else wrong with your code. it seems SetHandleInformation(hReadOUT, HANDLE_FLAG_INHERIT, 0); and SetHandleInformation(hReadIN, HANDLE_FLAG_INHERIT, 0); will break things. I'm not sure why, but after trying t run your code, cmd could not read values from pipe until I remove those two lines.

C++ system function hangs application

I have the following code
void reportResults()
{
wstring env(_wgetenv(L"ProgramFiles"));
env += L"\Internet Explorer\iexplore.exe";
wstringstream url;
url << "\"\"" << env.c_str() << "\" http://yahoo.com\"";
wchar_t arg[BUFSIZE];
url.get(arg, BUFSIZE);
wcout << arg << endl;
_wsystem(arg);
}
Where arg is:
""C:\Program Files\Internet Explorer\iexplore.exe" http://yahoo.com"
The program functions as expected, launching IE and navigating to Yahoo, but the calling function (reportResults) never exits. How do I get the program to exit leaving the browser alive?
Thanks.
You want to use _wspawn() instead of _wsystem(). This will spawn a new process for the browser process. _wsystem() blocks on the command that you create; this is why you're not getting back to your code. _wspawn() creates a new, separate process, which should return to your code immediately.
The _wsystem command will wait for the command in arg to return and returns the return value of the command. If you close the Internet Explorer window it will return command back to your program.
Why not just use ShellExecute to launch the default browser with a given URL?
Synopsis:
LONG r = ShellExecute(NULL, "open", "http://www.microsoft.com", NULL, NULL, SW_SHOWNORMAL);
EDIT:
I suppose since it must be IE, this might work (note, untested code):
LONG r = ShellExecute(NULL, NULL, "iexplore.exe", "http://www.microsoft.com", NULL, SW_SHOWNORMAL);
If you want to use the current implementation, you will have to fork() the process and let a child handle the browser spawning. Thus, the main process will continue and exit the function.
Instead of executing
"C:\Program Files\Internet Explorer\iexplore.exe" "http://yahoo.com"
execute
start "C:\Program Files\Internet Explorer\iexplore.exe" "http://yahoo.com"