Looking for an example that:
- Launches an EXE
- Waits for the EXE to finish.
- Properly closes all the handles when the executable finishes.
10 Answers
Something like this:
STARTUPINFO info={sizeof(info)}; PROCESS_INFORMATION processInfo; if (CreateProcess(path, cmd, NULL, NULL, TRUE, 0, NULL, NULL, &info, &processInfo)) { WaitForSingleObject(processInfo.hProcess, INFINITE); CloseHandle(processInfo.hProcess); CloseHandle(processInfo.hThread); } 9There is an example at (VS.85).aspx
Just replace the argv[1] with your constant or variable containing the program.
#include <windows.h> #include <stdio.h> #include <tchar.h> void _tmain( int argc, TCHAR *argv[] ) { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); if( argc != 2 ) { printf("Usage: %s [cmdline]\n", argv[0]); return; } // Start the child process. if( !CreateProcess( NULL, // No module name (use command line) argv[1], // 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; } // Wait until child process exits. WaitForSingleObject( pi.hProcess, INFINITE ); // Close process and thread handles. CloseHandle( pi.hProcess ); CloseHandle( pi.hThread ); } If you application is a Windows GUI application then using the code below to do the waiting is not ideal as messages for your application will not be getting processing. To the user it will look like your application has hung.
WaitForSingleObject(&processInfo.hProcess, INFINITE) Something like the untested code below might be better as it will keep processing the windows message queue and your application will remain responsive:
//-- wait for the process to finish while (true) { //-- see if the task has terminated DWORD dwExitCode = WaitForSingleObject(ProcessInfo.hProcess, 0); if ( (dwExitCode == WAIT_FAILED ) || (dwExitCode == WAIT_OBJECT_0 ) || (dwExitCode == WAIT_ABANDONED) ) { DWORD dwExitCode; //-- get the process exit code GetExitCodeProcess(ProcessInfo.hProcess, &dwExitCode); //-- the task has ended so close the handle CloseHandle(ProcessInfo.hThread); CloseHandle(ProcessInfo.hProcess); //-- save the exit code lExitCode = dwExitCode; return; } else { //-- see if there are any message that need to be processed while (PeekMessage(&message.msg, 0, 0, 0, PM_NOREMOVE)) { if (message.msg.message == WM_QUIT) { return; } //-- process the message queue if (GetMessage(&message.msg, 0, 0, 0)) { //-- process the message TranslateMessage(&pMessage->msg); DispatchMessage(&pMessage->msg); } } } } 1if your exe happens to be a console app, you might be interested in reading the stdout and stderr -- for that, I'll humbly refer you to this example:
It's a bit of a mouthful of code, but I've used variations of this code to spawn and read.
On a semi-related note, if you want to start a process that has more privileges than your current process (say, launching an admin app, which requires Administrator rights, from the main app running as a normal user), you can't do so using CreateProcess() on Vista since it won't trigger the UAC dialog (assuming it is enabled). The UAC dialog is triggered when using ShellExecute(), though.
Here is a new example that works on windows 10. When using the windows10 sdk you have to use CreateProcessW instead. This example is commented and hopefully self explanatory.
#ifdef _WIN32 #include <Windows.h> #include <iostream> #include <stdio.h> #include <tchar.h> #include <cstdlib> #include <string> #include <algorithm> class process { public: static PROCESS_INFORMATION launchProcess(std::string app, std::string arg) { // Prepare handles. STARTUPINFO si; PROCESS_INFORMATION pi; // The function returns this ZeroMemory( &si, sizeof(si) ); si.cb = sizeof(si); ZeroMemory( &pi, sizeof(pi) ); //Prepare CreateProcess args std::wstring app_w(app.length(), L' '); // Make room for characters std::copy(app.begin(), app.end(), app_w.begin()); // Copy string to wstring. std::wstring arg_w(arg.length(), L' '); // Make room for characters std::copy(arg.begin(), arg.end(), arg_w.begin()); // Copy string to wstring. std::wstring input = app_w + L" " + arg_w; wchar_t* arg_concat = const_cast<wchar_t*>( input.c_str() ); const wchar_t* app_const = app_w.c_str(); // Start the child process. if( !CreateProcessW( app_const, // app path arg_concat, // Command line (needs to include app path as first argument. args seperated by whitepace) 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() ); throw std::exception("Could not create child process"); } else { std::cout << "[ ] Successfully launched child process" << std::endl; } // Return process handle return pi; } static bool checkIfProcessIsActive(PROCESS_INFORMATION pi) { // Check if handle is closed if ( pi.hProcess == NULL ) { printf( "Process handle is closed or invalid (%d).\n", GetLastError()); return FALSE; } // If handle open, check if process is active DWORD lpExitCode = 0; if( GetExitCodeProcess(pi.hProcess, &lpExitCode) == 0) { printf( "Cannot return exit code (%d).\n", GetLastError() ); throw std::exception("Cannot return exit code"); } else { if (lpExitCode == STILL_ACTIVE) { return TRUE; } else { return FALSE; } } } static bool stopProcess( PROCESS_INFORMATION &pi) { // Check if handle is invalid or has allready been closed if ( pi.hProcess == NULL ) { printf( "Process handle invalid. Possibly allready been closed (%d).\n"); return 0; } // Terminate Process if( !TerminateProcess(pi.hProcess,1)) { printf( "ExitProcess failed (%d).\n", GetLastError() ); return 0; } // Wait until child process exits. if( WaitForSingleObject( pi.hProcess, INFINITE ) == WAIT_FAILED) { printf( "Wait for exit process failed(%d).\n", GetLastError() ); return 0; } // Close process and thread handles. if( !CloseHandle( pi.hProcess )) { printf( "Cannot close process handle(%d).\n", GetLastError() ); return 0; } else { pi.hProcess = NULL; } if( !CloseHandle( pi.hThread )) { printf( "Cannot close thread handle (%d).\n", GetLastError() ); return 0; } else { pi.hProcess = NULL; } return 1; } };//class process #endif //win32 1Perhaps this is the most complete?
3Bear in mind that using WaitForSingleObject can get you into trouble in this scenario. The following is snipped from a tip on my website:
The problem arises because your application has a window but isn't pumping messages. If the spawned application invokes SendMessage with one of the broadcast targets (HWND_BROADCAST or HWND_TOPMOST), then the SendMessage won't return to the new application until all applications have handled the message - but your app can't handle the message because it isn't pumping messages.... so the new app locks up, so your wait never succeeds.... DEADLOCK.
If you have absolute control over the spawned application, then there are measures you can take, such as using SendMessageTimeout rather than SendMessage (e.g. for DDE initiations, if anybody is still using that). But there are situations which cause implicit SendMessage broadcasts over which you have no control, such as using the SetSysColors API for instance.
The only safe ways round this are:
- split off the Wait into a separate thread, or
- use a timeout on the Wait and use PeekMessage in your Wait loop to ensure that you pump messages, or
- use the
MsgWaitForMultipleObjectsAPI.
Here is a solution for CreateProcessA
STARTUPINFOW initInfo = { 0 }; initInfo.cb = sizeof(initInfo); PROCESS_INFORMATION procInfo = { 0 }; CreateProcessA(PATH_FOR_EXE, NULL, NULL, NULL, FALSE, 0, NULL, NULL, (LPSTARTUPINFOA)&initInfo, &procInfo); #include <Windows.h> void my_cmd() { STARTUPINFO si; PROCESS_INFORMATION pi; ZeroMemory(&si, sizeof(si)); si.cb = sizeof(si); ZeroMemory(&pi, sizeof(pi)); // CMD command here char arg[] = "cmd.exe /c E:/Softwares/program.exe"; // Convert char string to required LPWSTR string wchar_t text[500]; mbstowcs(text, arg, strlen(arg) + 1); LPWSTR command = text; // Run process CreateProcess (NULL, command, NULL, NULL, 0, CREATE_NO_WINDOW, NULL, NULL, &si, &pi); } This works fine for me. No popup windows and cmd command runs as expected. Just needed to convert the CHAR pointer into WCHAR pointer and add extra "cmd.exe /c" before every command.