Programmaufrufe innerhalb von C/C++-Programmen
weiter zu Aufruf von Programmen in Linux2. Aufruf von Programmen in Windows, Teil 2: CreateProcess
Die hier zugrunde liegende Problemstellung ist die einer Button-Leiste, mit der Programme geöffnet werden. Die Code-Beispiele sind Ausschnitte aus dem Programm silidock.
Einleitung:
CreateProcess erzeugt einen neuen Prozess, der im gleichen Sicherheits-Kontext läuft
wie der aufrufende Prozess.
Zur Erinnerung: Wir programmieren in Windows, Backlashs müssen "escaped" werden: "C:\\pfad\\datei.exe",
sonst wird der Backlash als Escape-Sequenz interpretiert.
CreateProcess ist ab Windows XP verfügbar
( weitere Infos) .
Für Windows XP bis Windows 7 (und Windows Server 2003 - 2008) muss der Header
WinBase.h
(neben Windows.h) eingebunden werden.
Ab Windows 8 (Windows-Server 2012) der Header Processthreadsapi.h
.
Syntax:
BOOL WINAPI CreateProcess(
_In_opt_ LPCTSTR lpApplicationName, // Name des ausführbaren Moduls, z.B. "cmd" oder "notepad"
// oder eine ausführbare Datei mit absolutem oder
// (zum aktuellen Verzeichnis) relativem Pfad oder NULL
_Inout_opt_ LPTSTR lpCommandLine, // Auszuführende Kommandozeile
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes, // Attribute für den Prozess: SECURITY_ATTRIBUTES oder NULL
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes, // Attribute für den Thread: SECURITY_ATTRIBUTES oder NULL
_In_ BOOL bInheritHandles, // Vererbung des Handles (ähnlich wie file descriptors in Linux):
// TRUE oder FALSE
// bei TRUE müssen die Handles geschlossen werden!
_In_ DWORD dwCreationFlags, // Flags u.a. über Vererbung, Priorität oder NULL
_In_opt_ LPVOID lpEnvironment, // Umgebungsvariable oder NULL
_In_opt_ LPCTSTR lpCurrentDirectory, // Verzeichnis für den Prozess oder NULL
// (= aktuelles Verzeichnis des aufrufenden Prozesses)
_In_ LPSTARTUPINFO lpStartupInfo, // Zeiger auf das STARTUPINFO oder STARTUPINFOEX
// enthält u.a. Position, Größe...
_Out_ LPPROCESS_INFORMATION lpProcessInformation // zeiger auf die PROCESS_INFORMATION
// enthält Prozess- und Thread-Handles, Prozess-ID und Thread-ID
// die Handles müssen geschlossen werden
);
lpApplicationName und lpCommandLine können alternativ oder gemeinsam verwendet werden. Das
ausführbare Modul muss entweder als Name der Anwendung (lpApplicationName) oder als Kommando
(lpCommandLine) mit nachfolgendem Leerzeichen ("cmd.exe ") angegeben werden. Modulangaben in lpCommandLine können mit absolutem oder realtivem Pfad angegeben werden; es werden auch das 32-Bit-System- und das 16-Bit-System-Verzeichnis durchsucht sowie das Windows-Verzeichnis und die PATH-Variable. "cmd" oder "calc" würden also gefunden. Die Erweiterung (extension), beispielsweise "exe" oder "com", sollte ebenfalls angegeben werden. Pfade mit Leerzeichen müssen in Anführungszeichen stehen.
Wenn für lpProcessAttributes und lpThreadAttributes NULL angegeben wird, erhält der Prozess den Standard- securty descriptor. Wenn nicht Eigentümer*in oder Zugriff speziell definiert werden sollen, genügt das.
Einige der möglichen Flags sind für den Anwendungsfall hier hilfreich, denn die mit silidock aufgerufenen Programme sollen möglichst unabhängig vom aufrufenden Programm laufen:
CREATE_DEFAULT_ERROR_MODE - Der Prozess erhält einen eigenen Standard-Error-Modus statt den des aufrufenden Prozesses.
DETACHED_PROCESS - Die Konsole des aufrufenden Prozesses wird nicht vererbt. Der Prozess erhält zunächst keine Konsole (im Gegensatz zu CREATE_NEW_CONSOLE).
Das aktuelle Verzeichnis sollte geändert werden, da Shells oft ein bestimmtes Verzeichnis benötigen:
Aus dem vollständigen Dateinamen mit Pfad wird der Pfad (
path
) abgetrennt:
string file_and_path (program_call);
string path = file_and_path.substr(0, file_and_path.find_last_of("/\\") );
Damit der neu erzeugte Prozess sauber beendet werden kann, müssen die Handles hProcess und hThread mit CloseHandle geschlossen werden, am besten, sobald der Prozess erfolgreich erzeugt wurde. Ansonsten bleiben diese Handles, bis der aufrufende Prozess beendet ist.
Im Fehlerfall setzt CreateProcess einen last-error code, der mit der Funktion
GetLastError()
abgefangen werden kann. Da ein numerischer Wert für die meisten
Menschen wenig Bedeutung hat, ist es sinnvoll, mit der Funktion FormatMessage()
eine
Beschreibung des Fehlers in einer MessageBox anzuzeigen.
Code-Schnipsel:
// C++ Header
#include <iostream>
// C-Header
extern "C" {
#include <windows.h>
#include <WinBase.h.h> // Windows XP bis Windows 7
//#include <Processthreadsapi.h.h> // Windows 8 ff.
}
void execute_program(const char* program_call, const char* param )
{
string file_and_path (program_call);
string path = file_and_path.substr(0, file_and_path.find_last_of("/\\") );
STARTUPINFO si;
PROCESS_INFORMATION pi;
ZeroMemory( &si, sizeof(si) );
si.cb = sizeof(si);
ZeroMemory( &pi, sizeof(pi) );
// Start the process.
if( CreateProcess(
program_call, // executable file
(LPSTR) param, // Parameter for command line (NULL)
NULL, // Process handle
NULL, // Thread handle
FALSE, // Inheritance
CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS,// Own error mode and no console
NULL, // Environment of parent
(LPCSTR) path.c_str(),// Directory of executable file
&si, // Pointer to STARTUPINFO structure
&pi ) // Pointer to PROCESS_INFORMATION structure
== FALSE)
{
// get error description
DWORD errCode = GetLastError();
char *err;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
errCode,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // default language
(LPTSTR) &err,
0,
NULL);
// concatenate with program_call
char error_message[strlen(err) + strlen(program_call) + 1];
strcpy (error_message,err);
strcat (error_message,program_call);
// display message
MessageBox(NULL, (LPCTSTR)error_message, TEXT("Error"), MB_OK);
return;
}
// Close process and thread handles.
CloseHandle( pi.hProcess );
CloseHandle( pi.hThread );
}
zurück zu Teil 1: ShellExecute