Launching external application and block current application till the launched application quit - c++

I am not sure whether there is a way to
Launch an external application, and block current application till the launched application had quit.
Currently, I am using the following non-blocking method (MFC) to launch
std::string str = "Notepad2.exe";
// Non-blocking. Return immediately.
WinExec(str.c_str(), SW_SHOW);

code:
SHELLEXECUTEINFO sei = {0};
sei.cbSize = sizeof (SHELLEXECUTEINFO);
sei.fMask = SEE_MASK_NOCLOSEPROCESS;
sei.lpVerb = "open";
sei.lpFile = "notepad.exe";
sei.nShow = SW_SHOWNORMAL;
if (ShellExecuteEx (&sei))
{
WaitForSingleObject (sei.hProcess, INFINITE);
}
As Jerry points out, this is bad for your own GUI. But if the process you launch, directly or indirectly, does a broadcast SendMessage then this can cause catastrophic deadlock, because your process has a window but isn't pumping any messages: the launched process is waiting for your code to handle its message, and you're waiting for it. Clang...
You could use MsgWaitForMultipleObjects instead, or better still, split the launch-and-wait off into a thread and simply disable any part of your UI you don't want the user to interact with.

ShellExecuteEx with SEE_MASK_NOCLOSEPROCESS will (among other things) give you a handle to the new process. You can do a WaitForSingleObject on that handle, which will be signaled when it terminates. BTW, for a GUI program, this is almost always a bad idea -- making your GUI unresponsive until another program exits is really a poor idea.

If you just want to launch a process, you don't have to use ShellExecuteEx, use CreateProcess instead. Here is an untested example:
std::wstring commandLine = L"Notepad2.exe"
std::wstring::size_type length = commandLine.size();
boost::scoped_array<WCHAR> buffer(new WCHAR[length + 1]);
std::copy(commandLine.begin(), commandLine.end(), buffer.get());
buffer[length] = L'\0';
STARTUPINFO startupInfo;
ZeroMemory(&startupInfo, sizeof startupInfo);
startupInfo.cb = sizeof startupInfo;
PROCESS_INFORMATION processInfo;
CreateProcess(NULL, buffer.get(), NULL, NULL, FALSE, 0, NULL, NULL, &startupInfo, &processInfo);
CloseHandle(processInfo.hThread);
WaitForSingleObject(processInfo.hProcess, INFINITE);
CloseHandle(processInfo.hProcess);

Related

Read process's 'stdout' output in Win32 C++ Desktop Application

I started a Visual C++ Desktop Application (win32) and created a button, that creates a process.
Here is the button code:
INT_PTR CALLBACK Btn_Click(HWND hWnd)
{
wchar_t cmd[] = L"cmd.exe /c testprogram.exe";
STARTUPINFOW startInf;
memset(&startInf, 0, sizeof startInf);
PROCESS_INFORMATION procInf;
memset(&procInf, 0, sizeof procInf);
BOOL p = CreateProcess(NULL, cmd, NULL, NULL, TRUE,
CREATE_NO_WINDOW, NULL, NULL, &startInf, &procInf);
if (p)
{
CloseHandle(procInf.hProcess);
CloseHandle(procInf.hThread);
}
return TRUE;
}
I want to read the process's stdout output (cmd.exe or testprogram.exe) while it is 'running' and set it as a string. After setting the content of a created Text input.
I tried this answer, but it freezes the application. (because of ReadFile() and while() loop)
How to read output from cmd.exe using CreateProcess() and CreatePipe()
I searched the forum for a better answer, but every example is for a console app.
Update:
I can read the output of ping.exe but the window stays frozen until ping.exe closes. the for() loop (and ReadFile()) blocks the window .
wchar_t command[] = L"ping localhost";
STARTUPINFOW startInf;
memset(&startInf, 0, sizeof startInf);
PROCESS_INFORMATION procInf;
memset(&procInf, 0, sizeof procInf);
HANDLE cout_r = NULL;
HANDLE cout_w = NULL;
SECURITY_ATTRIBUTES sec_a;
//memset(&sec_a, 1, sizeof sec_a);
sec_a.nLength = sizeof(SECURITY_ATTRIBUTES);
sec_a.lpSecurityDescriptor = NULL;
CreatePipe(&cout_r, &cout_w, &sec_a, 0);
SetHandleInformation(cout_r, HANDLE_FLAG_INHERIT, 0);
//startInf.cb = sizeof(STARTUPINFO);
startInf.hStdOutput = cout_w;
startInf.dwFlags |= STARTF_USESTDHANDLES;
BOOL p = CreateProcess(NULL, command, NULL, NULL, TRUE,
NORMAL_PRIORITY_CLASS | CREATE_NO_WINDOW, NULL, NULL,
&startInf, &procInf);
if (p)
{
CloseHandle(procInf.hProcess);
CloseHandle(procInf.hThread);
CloseHandle(cout_w);
}
else
{
SetWindowTextA(hedit, "Failed");
}
char buf[1024];
CHAR chBuf[4096];
DWORD dwRead;
for (;;)
{
BOOL bSuccess = ReadFile(cout_r, chBuf, 4096, &dwRead, NULL);
if (!bSuccess || dwRead == 0) break;
std::string s(chBuf, dwRead);
std::wstring stemp = std::wstring(s.begin(), s.end());
OutputDebugStringW(stemp.c_str());
}
I searched for asynchronous I/O on ReadFile and pipes.
If anyone can provide me an example on how to do async Readfile without for() loop would be good.
If your UI thread stops pumping messages Windows treats it as frozen. This means you should not perform blocking I/O on this thread.
You could use asynchronous (overlapped) I/O with ReadFile but doing the whole child process operation in another thread is a lot easier. The button press would start the thread and once the thread has read stdout it can signal back to your main window by sending a WM_APP message.

Detecting a process crash in C++/Win32

I'm working on a software that contains 2 programs : the Qt Main exe + an OpenGL Game exe
We use always the Qt Main exe at the first. When we click on the button "start game", we execute the OpenGL Game exe. No probleme to do that.
The problem is that sometime we have a crash in the OpenGL Game exe and we want to send a crash report containing the log to our compagny crash-report mail.
I found a function (registerwaitforsingleobject) in Win32 API but I don't know if the process has crashed or not :
https://learn.microsoft.com/en-gb/windows/desktop/api/winbase/nf-winbase-registerwaitforsingleobject
I would like to use only the win32 api (WinXP-WinVist to Win10)
Thanks in advance
I found the solution to my problem : I used GetExitCodeProcess function from the Win32 API (https://learn.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess).
This is the example code :
int main(int argc, char** arv)
{
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
const char * prgm = "CrashedProcess.exe";
LPSTR prgmLpstr = const_cast<LPSTR>(prgm);
// Start the child process.
if (!CreateProcess(NULL, // No module name (use command line)
prgmLpstr, // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
FALSE, // Set handle inheritance to FALSE
0, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&si, // Pointer to STARTUPINFO structure
&pi) // Pointer to PROCESS_INFORMATION structure
)
{
printf("CreateProcess failed (%d).\n", GetLastError());
return -1;
}
// Wait until child process exits.
auto ret = WaitForSingleObject(pi.hProcess, INFINITE);
printf("WaitForSingleObject ret = %x\n", ret);
if (ret == WAIT_OBJECT_0)
{
printf("WaitForSingleObject ret ret == WAIT_OBJECT_0\n");
}
BOOL b = FALSE;
DWORD n = 0;
b = GetExitCodeProcess(pi.hProcess, &n);
if (n == 0xC0000005)
{
printf("Process Crashed !!!\n");
}
// Close process and thread handles.
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
printf("WaitForSingleObject end\n");
return 0;
}
CrashedProcess.exe source code :
int main()
{
int * ptr = nullptr;
*ptr = 123;
return 0;
}
Use a ready-made solution such as CrashRpt http://crashrpt.sourceforge.net/. It will install a proper handler and report/submit what you need when exceptions are thrown.
You can use Interprocess Communications with a pipe.
Send a message at time laps (for example, 1 second). If there's no response you can be almost sure that the other program is crashed.
Another approach, less elegant, is testing for a log file, modified every x-seconds, with a value meaning "I'm still alive, not crashed".
Assuming the program you care about has a normal Windows event loop, the more or less standard way to check that it's processing messages in a reasonably timely fashion is to use SendMessageTimeout to send it a WM_NULL message.
The program won't do anything in response to the WM_NULL, but SendMessageTimeout will time out if the message stays in its message queue for too long (where you pick what "too long" means when you make the call). So you send it, and then check whether it timed out or not.
But note: this only applies to programs that does process Windows messages. If it doesn't have a message loop, it'll always timeout. Oh, and a program is multi-threaded, one thread can "crash" (e.g., go into an infinite loop) while others continue to operate normally, so it's not always easy to even define what "crashed" means.

start on certain PID c++

I'am trying to start batch file on certain PID, or get PID what program started on. I really have no idea how to do that.
system("start C:\\testing\\vw.bat");
Sleep(2000); //1000 = 1s
After this code is executed I need to close "vw.bat", but not close other batch files that are running.
PROCESS_INFORMATION pi;
STARTUPINFO si{};
si.cb = sizeof(si);
BOOL success = CreateProcess("start C:\\testing\\vw.bat", NULL, NULL, NULL, TRUE, 0, NULL, "C:\\testing\\", &si, &pi);
if (success)
{
int pid = pi.dwProcessId;
}
You can get the PID of your process using system-dependant system calls. For instance, on linux, using pidof. Now, please note using the system() function is not recommended, and other ways of doing what you want exist, like spawning a child process, which will let you kill the process easily.

RegisterWaitForSingleObject crash sometime if handle is closed immediately

I'm getting a crash sometimes in RegisterWaitForSingleObject (1 out of 10). It seems that although RegisterWaitForSingleObject returns, the internal thread pool is not yet ready.
HANDLE processHandle = OpenProcess (PROCESS_ALL_ACCESS, FALSE, processID);
// CRASH IN INTERNAL SOMETIMES
RegisterWaitForSingleObject (&hWaitForChild_,processHandle,OnChildProcessExit, 0,INFINITE,WT_EXECUTEONLYONCE);
// If I sleep here, then it seems ok.
//std::this_thread::sleep_for (std::chrono::milliseconds (10));
CloseHandle (processHandle);
I can replicate this with a simple sample here. 1 in 10 times, it will crash. How should I be synchronizing it properly without resorting to sleep hack.
https://filedn.com/l3TGy7Y83c247u0RDYa9fkp/temp/stackoverflow/testregister.cpp
based on your code spinet:
// THIS CRASHS HERE SOMETIMES
if (! RegisterWaitForSingleObject (
&hWaitForChild_
,processHandle
, OnChildProcessExit
, 0 //this
, INFINITE
, WT_EXECUTEONLYONCE))
{
LogDebug ("RegisterWaitForSingleObject failed");
}
// If this is enabled, then it won't crash
//std::this_thread::sleep_for (std::chrono::milliseconds (10));
if (! CloseHandle (processHandle)) // !!!
LogDebug ("RegisterWaitForSingleObject Closehandle failed");
so you close processHandle just after you call RegisterWaitForSingleObject for this handle. however if read about RegisterWaitForSingleObject:
If this handle is closed while the wait is still pending, the
function's behavior is undefined.
if look more deep - try understand - how is RegisterWaitForSingleObject worked internally ? it pass processHandle to some worker thread. and this thread begin wait for this handle. but this is (pass handle to another thread) is asynchronous operation - say for example internally can be started new thread with this handle as argument, or it can be passed to already existing working thread via some signal. but anyway - worked thread got this handle and begin wait some later. from another side - you just close processHandle after RegisterWaitForSingleObject return control. so here race - what will be first - or worked thread begin wait on handle (in this case all will be work) or you close this handle. in case you close this handle first - worked thread will be try wait on already invalid handle and raise exception - STATUS_THREADPOOL_HANDLE_EXCEPTION.
// If this is enabled, then it won't crash
//std::this_thread::sleep_for (std::chrono::milliseconds (10));
of course - by sleep you give time for worked thread to begin wait on handle. in this case he begin wait before you close handle.
solution - you must not close handle, until WAITORTIMERCALLBACK Callback will be not called. you need allocate some context, where place processHandle and pass this context to RegisterWaitForSingleObject. and when you callback will be called - you got pointer to your context back and here and close handle.
also note, that you not need open separate, second, handle for child process, but can use process handle returned by CreateProcess
Ok, I managed to solve it by keeping the handle around until I call Unregisterwait. It seems to be stable. Thanks to the answers.
I met the same problem like you that an exception occurred while debugging in Visual Studio. I tried many time and finally found the reason. If you close the handle of the process newly created, the program would crash. I tried to close the handles in the callback function, it works perfectly, like this:
typedef struct {
LPTSTR pszCmdLine;
HANDLE hEvent;
} THREAD_PARAM;
typedef struct {
TCHAR szCmdLine[1024];
HANDLE hWaitObject;
DWORD dwProcessId;
HANDLE hProcess;
DWORD dwThreadId;
HANDLE hThread;
} OBJECT_PARAM;
static void CALLBACK WaitObjectCallback(LPVOID lpParam, BOOLEAN TimerOrWaitFired)
{
OBJECT_PARAM *pobp = static_cast<OBJECT_PARAM *>(lpParam);
TCHAR szInfo[1024] = { 0 };
DWORD dwExitCode = 0;
GetExitCodeProcess(pobp->hProcess, &dwExitCode);
wnsprintf(szInfo, ARRAYSIZE(szInfo), _T("process %u [%s] exit: %u\n"), pobp->dwProcessId, pobp->szCmdLine, dwExitCode);
OutputDebugString(szInfo);
//
// unregister the wait object handle and close the process handle finally
//
UnregisterWait(pobp->hWaitObject);
CloseHandle(pobp->hProcess);
CloseHandle(pobp->hThread);
GlobalFree(lpParam);
}
static DWORD CALLBACK ThreadFunction(LPVOID lpParam)
{
THREAD_PARAM *pthp = static_cast<THREAD_PARAM *>(lpParam);
STARTUPINFO si = { sizeof(si) };
PROCESS_INFORMATION pi = { 0 };
BOOL bResult;
bResult = CreateProcess(nullptr, pthp->pszCmdLine, nullptr, nullptr, FALSE, 0, nullptr, nullptr, &si, &pi);
if (bResult)
{
OBJECT_PARAM *pobp = static_cast<OBJECT_PARAM *>(GlobalAlloc(GPTR, sizeof(OBJECT_PARAM)));
// make copy of the command line and other informations of the newly created process
lstrcpyn(pobp->szCmdLine, pthp->pszCmdLine, ARRAYSIZE(pobp->szCmdLine));
pobp->dwProcessId = pi.dwProcessId;
pobp->hProcess = pi.hProcess;
pobp->dwThreadId = pi.dwThreadId;
pobp->hThread = pi.hThread;
bResult = RegisterWaitForSingleObject(&pobp->hWaitObject, pi.hProcess, WaitObjectCallback, pobp, INFINITE, WT_EXECUTEONLYONCE);
// once it failed...
if (!bResult)
{
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
}
// Notify the thread creator that the process is created successfully
SetEvent(pthp->hEvent);
return 0;
}

TerminateProcess() doesn't close the application

I am trying to use the TerminateProcess to terminate an app launched by ShellExecuteEX like this:
SHELLEXECUTEINFO ExecuteInfo;
ExecuteInfo.fMask = SEE_MASK_FLAG_NO_UI; /* Odd but true */
ExecuteInfo.hwnd = NULL;
ExecuteInfo.cbSize = sizeof(ExecuteInfo);
ExecuteInfo.lpVerb = NULL;
ExecuteInfo.lpFile = "http://www.microsoft.com";
ExecuteInfo.lpParameters = "";
ExecuteInfo.lpDirectory = NULL;
ExecuteInfo.nShow = SW_SHOW;;
ShellExecuteEx(&ExecuteInfo);
//WaitForSingleObject(ExecuteInfo.hProcess, 0);
Sleep(4000);
TerminateProcess(ExecuteInfo.hProcess, 0);
IE gets opened but it never closes. Am I doing something wrong?
According to MSDN, fMask must be set to SEE_MASK_NOCLOSEPROCESS for .hProcess to get set. I would add a test to see if it is NULL. As a side note I've always had better luck using CreateProcess.
Edit:
This is how you do it using CreateProcess:
PROCESS_INFORMATION pi = {0};
STARTUPINFO si = {0};
si.cb = sizeof(si);
si.dwFlags = STARTF_USESHOWWINDOW;
si.wShowWindow = SW_SHOW;
CreateProcess( NULL,
"C:\\Program Files\\Internet Explorer\\iexplore.exe http://www.google.com/",
NULL,
NULL,
FALSE,
NORMAL_PRIORITY_CLASS,
NULL,
NULL,
&si,
&pi );
Sleep(4000);
TerminateProcess(pi.hProcess, 0);
You should add error-checking and could query the path of the default browser using: AssocQueryString like this:
AssocQueryString(0,ASSOCSTR_EXECUTABLE,"http","open", szExe, &cchExe);
You can get more information by checking the returned hProcess (e.g., in a debugger). Also make sure you have the SEE_MASK_NOCLOSEPROCESS flag set.
But my psychic powers say: Opening an IE document doesn't necessarily create a new process. And, if it does, it may not be the one you think it is: The process you may have created may have spawned yet another process to actually host the document. Raymond Chen mentions that about halfway down this blog post.