-1

There is an example of how to call CreateProcess on stack exchange here, however this seems to be no longer supported on Windows 10, and you must use the unicode version CreateProcessW.

In a similar vein to the ASCI version, I am looking for an example that:

  1. Launches an EXE
  2. Waits for the EXE to finish.
  3. Properly closes all the handles when the executable finishes.
Blue7
  • 1,750
  • 4
  • 31
  • 55
  • I've answered my own question, as I spent a while working out how to do this and have since decided to use an external library (Poco) to do instead. As far as I know there is no example on how to use CreateProcessW, so I have posted one here. I also didn't want the time I spent writing this to be a waste. – Blue7 Oct 19 '17 at 14:10
  • 2
    The thing is that `CreateProcess` is one of a very large number of functions with `W` versions. How to use wide strings isn't specific to any of these; you can learn that separately and apply it to any one of these functions, including `CreateProcess`. – chris Oct 19 '17 at 14:16
  • That's true. You can close this question if you think it is unsuitable for stack exchange. To be honest I just didn't want the time I spent writing the code to be a waste. – Blue7 Oct 19 '17 at 14:26

2 Answers2

5

There is nothing new about Windows 10 in this respect. Your question is really about Unicode versus ANSI, and Visual Studio's new default settings which use Unicode.

CreateProcess is a macro, it is defined as

#ifdef UNICODE
#define CreateProcess  CreateProcessW
#else
#define CreateProcess  CreateProcessA
#endif // !UNICODE

In addition STARTUPINFO is a macro for STARTUPINFOA and STARTUPINFOW

CreateProcessA uses ANSI char strings char* and STARTUPINFOA

CreateProcessW uses Unicode wide char strings wchar_t* and STARTUPINFOW

If you insist on using ANSI (not recommended) then go to Project Settings -> Character Set and disable Unicode.

If you insist on using ANSI version with Unicode settings (still not recommended), you need

//Using ANSI when UNICODE is defined (not recommended):
STARTUPINFOA si = { sizeof(si) };
PROCESS_INFORMATION pi;
std::string path = "c:\\windows\\notepad.exe \"c:\\test\\_text.txt\"";

//if not using C++11 or later, force a zero at the end 
//to make sure path is null-ternminated
path.push_back(0);

if(CreateProcessA(NULL, &path[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
{
    WaitForSingleObject(pi.hProcess, INFINITE); 
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
}

Above code will work as long as the directory names are ANSI compatible. But the recommended version is:

//recommended:
{
    STARTUPINFO si = { sizeof(si) };
    PROCESS_INFORMATION pi;
    std::wstring path = L"c:\\windows\\notepad.exe \"c:\\test\\_text.txt\"";

    //if not using C++11 or later, force a zero at the end 
    //to make sure path is null-ternminated
    path.push_back(0);

    if(CreateProcess(0, &path[0], NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi))
    {
        WaitForSingleObject(pi.hProcess, INFINITE);
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }
}

Also, don't cast from constant string to string as follows:

wchar_t* arg_concat = const_cast<wchar_t*>( input.c_str() );

The second argument in CreateProcess should be wchar_t* because the process may modify the command line.

Barmak Shemirani
  • 30,904
  • 6
  • 40
  • 77
1

This example is commented and hopefully self explanatory.

#ifdef _WIN32
#include <windows.h>
#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
        {
            // 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");
                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
Blue7
  • 1,750
  • 4
  • 31
  • 55
  • `stdio.h` is a deprecated C-compatibility header. Use `cstdio` in C++ instead. Additionally, you really should use the `nullptr` type instead of `NULL`, whereever possible. Additionally use the types `true` and `false` instead of C-style defines for them. – tambre Oct 19 '17 at 14:14
  • 1
    @tambre: it's best to pass the WinAPI appropriate types. They're typically mapped to the ones you'd think, but it's not guaranteed. – AndyG Oct 19 '17 at 14:37