Как мне вызвать :: CreateProcess в C ++ для запуска исполняемого файла Windows?

43

Ищем пример, который:

  • Запускает EXE
  • Ожидает завершения EXE.
  • Правильно закрывает все дескрипторы, когда исполняемый файл заканчивается.
Теги:
winapi

8 ответов

65
Лучший ответ

Что-то вроде этого:

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);
}
  • 1
    Строка WaitForSingleObject должна быть :: WaitForSingleObject (processInfo.hProcess, INFINITE) (без '&')
  • 2
    Зачем использовать :: для указания глобального пространства имен в WaitForSingleObject, но не в других вызовах API?
Показать ещё 8 комментариев
18

Пример: http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx

Просто замените argv[1] на константу или переменную, содержащую программу.

#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 environment block
        NULL,           // Use parent 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 );
}
9

Если приложение является графическим приложением Windows, то использование кода ниже для ожидания не является идеальным, поскольку сообщения для вашего приложения не будут обрабатываться. Пользователю это будет выглядеть, как ваше приложение повесило.

WaitForSingleObject(&processInfo.hProcess, INFINITE)

Что-то вроде непроверенного кода ниже может быть лучше, поскольку он будет продолжать обрабатывать очередь сообщений Windows, и ваше приложение будет оставаться отзывчивым:

//-- 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);
      }
    }
  }
}
  • 2
    Нет, это не то, как вы объединяете цикл обработки сообщений и ожидание. Использовать MsgWaitForMultipleObjects (QS_ALLEVENTS); [ msdn.microsoft.com/en-us/library/ms684242(VS.85).aspx]
  • 2
    +1 Код работал для меня.
7

Если ваш exe окажется консольным приложением, вам может быть интересно прочитать stdout и stderr - для этого я смиренно отсылаю вас к этому примеру:

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

Это немного глоток кода, но я использовал вариации этого кода для появления и чтения.

7

Если вы хотите начать процесс, который имеет больше привилегий, чем ваш текущий процесс (например, запуск приложения администратора, требующего прав администратора, из основного приложения, выполняемого как обычный пользователь), вы должны не может сделать это с помощью CreateProcess() в Vista, поскольку он не будет запускать диалог UAC (если он включен). Диалоговое окно UAC запускается при использовании ShellExecute().

5

Возможно, это самое полное? http://goffconcepts.com/techarticles/development/cpp/createprocess.html

3

Вот новый пример, который работает на Windows 10. При использовании Windows10 SDK вы должны вместо этого использовать CreateProcessW. Этот пример прокомментирован и, надеюсь, не требует пояснений.

#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 environment block
            NULL,           // Use parent 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
3

Помните, что использование WaitForSingleObject может вызвать у вас проблемы в этом сценарии. На моем веб-сайте выложено следующее:

Проблема возникает из-за того, что ваше приложение имеет окно, но не передает сообщения. Если порожденное приложение вызывает SendMessage с одной из целей широковещательной передачи (HWND_BROADCAST или HWND_TOPMOST), то SendMessage не будет возвращаться к новому приложению, пока все приложения не обработают это сообщение, но ваше приложение не сможет обработать это сообщение, потому что оно isn ' t откачивание сообщений.... поэтому новое приложение блокируется, поэтому ваше ожидание никогда не преуспевает.... DEADLOCK.

Если у вас есть абсолютный контроль над созданным приложением, то есть меры, которые вы можете предпринять, например, использовать SendMessageTimeout, а не SendMessage (например, для инициирования DDE, если кто-то все еще использует это). Но есть ситуации, которые вызывают неявные трансляции SendMessage, над которыми у вас нет контроля, например, например, с помощью API SetSysColors.

Единственными безопасными способами это:

  • отделить Wait в отдельном потоке или
  • используйте тайм-аут в режиме ожидания и используйте PeekMessage в вашем цикле ожидания, чтобы убедиться, что вы накачаете сообщения, или
  • используйте API MsgWaitForMultipleObjects.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню