Pergunta

Procurando um exemplo que:

  1. Inicia um EXE
  2. Aguarda a conclusão do EXE.
  3. Fecha corretamente todos os identificadores quando o executável termina.
Foi útil?

Solução

Algo assim:

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);
}

Outras dicas

Há um exemplo em http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx

Basta substituir o argv[1] com sua constante ou variável que contém o programa.

#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 );
}

Se seu aplicativo for um aplicativo GUI do Windows, usar o código abaixo para fazer a espera não é ideal, pois as mensagens para seu aplicativo não serão processadas.Para o usuário, parecerá que seu aplicativo travou.

WaitForSingleObject(&processInfo.hProcess, INFINITE)

Algo como o não testado o código abaixo pode ser melhor, pois continuará processando a fila de mensagens do Windows e seu aplicativo permanecerá responsivo:

//-- 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);
      }
    }
  }
}

se o seu exe for um aplicativo de console, você pode estar interessado em ler o stdout e o stderr - para isso, humildemente encaminharei você para este exemplo:

http://support.microsoft.com/default.aspx?scid=kb;EN-US;q190351

É um pouco complicado de código, mas usei variações desse código para gerar e ler.

Em uma nota semi-relacionada, se você deseja iniciar um processo que tenha mais privilégios do que o seu processo atual (por exemplo, iniciar um aplicativo administrativo, que requer direitos de administrador, no aplicativo principal executado como usuário normal), você não pode faça isso usando CreateProcess() no Vista, pois isso não acionará a caixa de diálogo do UAC (supondo que esteja ativado).A caixa de diálogo do UAC é acionada ao usar ShellExecute().

Tenha em mente que usar WaitForSingleObject pode causar problemas neste cenário.O seguinte foi extraído de uma dica no meu site:

O problema surge porque seu aplicativo possui uma janela, mas não está bombeando mensagens.Se o aplicativo gerado invocar SendMessage com um dos destinos de transmissão (HWND_BROADCAST ou HWND_TOPMOST), então SendMessage não retornará ao novo aplicativo até que todos os aplicativos tenham manipulado a mensagem - mas seu aplicativo não pode manipular a mensagem porque não está bombeando mensagens....então o novo aplicativo trava, para que sua espera nunca seja bem-sucedida....IMPASSE.

Se você tiver controle absoluto sobre o aplicativo gerado, existem medidas que você pode tomar, como usar SendMessageTimeout em vez de SendMessage (por exemplo,para iniciações DDE, se alguém ainda estiver usando isso).Mas há situações que causam transmissões SendMessage implícitas sobre as quais você não tem controle, como usar a API SetSysColors, por exemplo.

As únicas maneiras seguras de contornar isso são:

  1. dividir o Wait em um thread separado ou
  2. use um tempo limite em Wait e use PeekMessage em seu loop Wait para garantir que você bombeie mensagens, ou
  3. use o MsgWaitForMultipleObjects API.

Aqui está um novo exemplo que funciona no Windows 10.Ao usar o SDK do Windows10, você deve usar CreateProcessW.Este exemplo é comentado e esperançosamente autoexplicativo.

#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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top