How to wait for ShellExecute to run?

放肆的年华 提交于 2019-11-28 07:26:40
Roger Rowland

There is a CodeProject article that shows how, by using ShellExecuteEx instead of ShellExecute:

SHELLEXECUTEINFO ShExecInfo = {0};
ShExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
ShExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
ShExecInfo.hwnd = NULL;
ShExecInfo.lpVerb = NULL;
ShExecInfo.lpFile = "c:\\MyProgram.exe";        
ShExecInfo.lpParameters = "";   
ShExecInfo.lpDirectory = NULL;
ShExecInfo.nShow = SW_SHOW;
ShExecInfo.hInstApp = NULL; 
ShellExecuteEx(&ShExecInfo);
WaitForSingleObject(ShExecInfo.hProcess, INFINITE);
CloseHandle(ShExecInfo.hProcess);

The crucial point is the flag SEE_MASK_NOCLOSEPROCESS, which, as MSDN says

Use to indicate that the hProcess member receives the process handle. This handle is typically used to allow an application to find out when a process created with ShellExecuteEx terminates

Also, note that:

The calling application is responsible for closing the handle when it is no longer needed.

You can also use CreateProcess instead of ShellExecute/ShellExecuteEx. This function includes a cmd.exe wrapper option, returning the exit code, and returning stdout. (The includes may not be perfect).

Notes: In my use, I knew that there had to be stdout results, but the PeekedNamePipe function wouldn't always return the bytes count on the first try, hence the loop there. Perhaps, someone can figure this out and post a revision? Also, maybe an alternate version should be produced which returns stderr separately?

#include <stdio.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <Shellapi.h>


/*
Note: 
    The exitCode for a "Cmd Process" is not the exitCode
    for a sub process launched from it!  That can be retrieved
    via the errorlevel variable in the command line like so:
    set errorlevel=&[launch command]&echo.&echo exitCode=%errorlevel%&echo.
    The stdOut vector will then contain the exitCode on a seperate line
*/
BOOL executeCommandLine( const CStringW &command,
                         DWORD &exitCode,
                         const BOOL asCmdProcess=FALSE,
                         std::vector<CStringW> *stdOutLines=NULL )
{
    // Init return values
    BOOL bSuccess = FALSE;
    exitCode = 0;
    if( stdOutLines ) stdOutLines->clear();

    // Optionally prepend cmd.exe to command line to execute
    CStringW cmdLine( (asCmdProcess ? L"cmd.exe /C " : L"" ) +
                      command );

    // Create a pipe for the redirection of the STDOUT 
    // of a child process. 
    HANDLE g_hChildStd_OUT_Rd = NULL;
    HANDLE g_hChildStd_OUT_Wr = NULL;
    SECURITY_ATTRIBUTES saAttr; 
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES); 
    saAttr.bInheritHandle = TRUE; 
    saAttr.lpSecurityDescriptor = NULL; 
    bSuccess = CreatePipe( &g_hChildStd_OUT_Rd, 
                           &g_hChildStd_OUT_Wr, &saAttr, 0);
    if( !bSuccess ) return bSuccess;         
    bSuccess = SetHandleInformation( g_hChildStd_OUT_Rd, 
                                     HANDLE_FLAG_INHERIT, 0 );
    if( !bSuccess ) return bSuccess;         

    // Setup the child process to use the STDOUT redirection
    PROCESS_INFORMATION piProcInfo; 
    STARTUPINFO siStartInfo;    
    ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );
    ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
    siStartInfo.cb = sizeof(STARTUPINFO); 
    siStartInfo.hStdError = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

    // Execute a synchronous child process & get exit code
    bSuccess = CreateProcess( NULL, 
      cmdLine.GetBuffer(),  // command line 
      NULL,                 // process security attributes 
      NULL,                 // primary thread security attributes 
      TRUE,                 // handles are inherited 
      0,                    // creation flags 
      NULL,                 // use parent's environment 
      NULL,                 // use parent's current directory 
      &siStartInfo,         // STARTUPINFO pointer 
      &piProcInfo );        // receives PROCESS_INFORMATION    
    if( !bSuccess ) return bSuccess;         
    WaitForSingleObject( piProcInfo.hProcess, (DWORD)(-1L) );
    GetExitCodeProcess( piProcInfo.hProcess, &exitCode );   
    CloseHandle( piProcInfo.hProcess );
    CloseHandle( piProcInfo.hThread );

    // Return if the caller is not requesting the stdout results
    if( !stdOutLines ) return TRUE;

    // Read the data written to the pipe
    DWORD bytesInPipe = 0;
    while( bytesInPipe==0 ){
        bSuccess = PeekNamedPipe( g_hChildStd_OUT_Rd, NULL, 0, NULL, 
                                  &bytesInPipe, NULL );
        if( !bSuccess ) return bSuccess;
    }
    if( bytesInPipe == 0 ) return TRUE; 
    DWORD dwRead; 
    CHAR *pipeContents = new CHAR[ bytesInPipe ];    
    bSuccess = ReadFile( g_hChildStd_OUT_Rd, pipeContents, 
                         bytesInPipe, &dwRead, NULL);
    if( !bSuccess || dwRead == 0 ) return FALSE; 

    // Split the data into lines and add them to the return vector
    std::stringstream stream( pipeContents );
    std::string str;
    while( getline( stream, str ) ) 
        stdOutLines->push_back( CStringW( str.c_str() ) );

    return TRUE;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!