Wie rufe ich ::CreateProcess in C++ auf, um eine ausführbare Windows-Datei zu starten?
Frage
Suchen Sie nach einem Beispiel, das:
- Startet eine EXE-Datei
- Wartet auf den Abschluss der EXE-Datei.
- Schließt alle Handles ordnungsgemäß, wenn die ausführbare Datei beendet ist.
Lösung
Etwas wie das:
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);
}
Andere Tipps
Ein Beispiel gibt es unter http://msdn.microsoft.com/en-us/library/ms682512(VS.85).aspx
Ersetzen Sie einfach die argv[1]
mit Ihrer Konstante oder Variablen, die das Programm enthält.
#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 );
}
Wenn es sich bei Ihrer Anwendung um eine Windows-GUI-Anwendung handelt, ist die Verwendung des folgenden Codes zum Warten nicht ideal, da Nachrichten für Ihre Anwendung nicht verarbeitet werden.Für den Benutzer sieht es so aus, als ob Ihre Anwendung hängen geblieben wäre.
WaitForSingleObject(&processInfo.hProcess, INFINITE)
So etwas wie das ungetestet Der folgende Code ist möglicherweise besser, da er weiterhin die Windows-Nachrichtenwarteschlange verarbeitet und Ihre Anwendung weiterhin reagiert:
//-- 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);
}
}
}
}
Wenn es sich bei Ihrer Exe-Datei zufällig um eine Konsolen-App handelt, könnten Sie daran interessiert sein, stdout und stderr zu lesen – dafür verweise ich Sie demütig auf dieses Beispiel:
http://support.microsoft.com/default.aspx?scid=kb;EN-US;q190351
Es ist ein bisschen ein bisschen Code, aber ich habe Variationen dieses Codes zum Erzeugen und Lesen verwendet.
Eine halbwegs verwandte Anmerkung: Wenn Sie einen Prozess starten möchten, der über mehr Berechtigungen als Ihr aktueller Prozess verfügt (z. B. das Starten einer Admin-App, für die Administratorrechte erforderlich sind, von der Haupt-App aus, die als normaler Benutzer ausgeführt wird), ist dies nicht möglich Verwenden Sie dazu CreateProcess() unter Vista, da dadurch das UAC-Dialogfeld nicht ausgelöst wird (vorausgesetzt, es ist aktiviert).Der UAC-Dialog wird jedoch ausgelöst, wenn ShellExecute() verwendet wird.
Vielleicht ist dies die vollständigste?http://goffconcepts.com/techarticles/development/cpp/createprocess.html
Denken Sie daran, dass mit WaitForSingleObject
kann Sie in diesem Szenario in Schwierigkeiten bringen.Das Folgende ist ein Auszug aus einem Tipp auf meiner Website:
Das Problem entsteht, weil Ihre Anwendung ein Fenster hat, aber keine Nachrichten weiterleitet.Wenn die erzeugte Anwendung SendMessage mit einem der Broadcast-Ziele aufruft (HWND_BROADCAST oder HWND_TOPMOST), dann kehrt SendMessage erst dann zur neuen Anwendung zurück, wenn alle Anwendungen die Nachricht verarbeitet haben – Ihre App kann die Nachricht jedoch nicht verarbeiten, da sie keine Nachrichten pumpt....Daher stürzt die neue App ab und Ihr Warten hat keinen Erfolg....SACKGASSE.
Wenn Sie die absolute Kontrolle über die erzeugte Anwendung haben, können Sie Maßnahmen ergreifen, z. B. die Verwendung von SendMessageTimeout anstelle von SendMessage (z. B.für DDE-Initiierungen, falls das noch jemand nutzt).Es gibt jedoch Situationen, die implizite SendMessage-Broadcasts verursachen, über die Sie keine Kontrolle haben, wie beispielsweise die Verwendung der SetSysColors-API.
Die einzig sicheren Möglichkeiten, dies zu umgehen, sind:
- das Wait in einen separaten Thread abspalten, oder
- Verwenden Sie eine Zeitüberschreitung für das Warten und verwenden Sie PeekMessage in Ihrer Warteschleife, um sicherzustellen, dass Sie Nachrichten pumpen, oder
- Benutze die
MsgWaitForMultipleObjects
API.
Hier ist ein neues Beispiel, das unter Windows 10 funktioniert.Wenn Sie das Windows10-SDK verwenden, müssen Sie stattdessen CreateProcessW verwenden.Dieses Beispiel ist kommentiert und hoffentlich selbsterklärend.
#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