Programmaufrufe innerhalb von C/C++-Programmen

weiter zu Aufruf von Programmen in Linux

2. Aufruf von Programmen in Windows Teil 1: ShellExecute

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:

Die Windows-API bietet mehrere Möglichkeiten Programme aufzurufen. Der Weg über die Funktion ShellExecute ist vielleicht der, welcher am häufigsten beschritten wird, deshalb wird er hier kurz vorgestellt.
Die für den hier vorgesehenen Zweck bessere Alternative ist jedoch die Funktion CreateProcess.
ShellExecute ist leistungsfähiger und flexibler, sie kann nicht nur Programme starten, sondern auch Dokumente ausdrucken, Dateiinhalte anzeigen und vieles mehr. Damit sind jedoch nicht nur Einbußen bezüglich der Performance verbunden, sondern auch ein höheres Sicherheitsrisiko.
CreateProcess kann nicht viel mehr als das, was hier beabsichtigt ist und das ist eine gute Voraussetzung.
Zur Erinnerung: Wir programmieren in Windows, Backlashs müssen "escaped" werden: "C:\\pfad\\dateie.exe", sonst wird der Backlash als Escape-Sequenz interpretiert.

ShellExecute

ShellExecute ist nicht dazu gedacht, Informationen über die geöffneten Programme zu erhalten; dafür ist die Funktion ShellExecuteEx vorgesehen.

Es wird von Microsoft sozusagen als "good practice" empfohlen, immmer vor einem Aufruf von ShellExecute das Component Object Model (COM) zu initialisiern, das unter anderem eine Interprozesskommunikation ermöglicht. Da ShellExecute nach der hier bevorzugten Strategie ohnehin schon leistungsstärker ist als gewünscht, soll die Funktion jedoch nicht noch zusätzlich aufgebläht werden um zahlreiche Funktionen, die gar nicht benötigt werden und hier lediglich weitere potenzielle Sicherheitslücken bedeuten.

ShellExecute ist ab Windows XP verfügbar ( weitere Infos) .

Syntax:


HINSTANCE ShellExecute(
  _In_opt_  HWND hwnd,                // Zeiger auf Fenster, wird benutzt, um beispielsweise einen Error-Code anzuzeigen, sonst NULL
  _In_opt_  LPCTSTR lpOperation,      // ein Verb, das die auszuführende Aktion beschreibt: edit, explore, find, open, print oder NULL
  _In_      LPCTSTR lpFile,           // auszuführende Datei
  _In_opt_  LPCTSTR lpParameters,     // Parameter, wenn die Datei ausführbar ist oder NULL
  _In_opt_  LPCTSTR lpDirectory,      // Verzeichnis, in der die Aktion ausgeführt werden soll oder NULL (= aktuelles Verzeichnis)
  _In_      INT nShowCmd              // Flag für die Anzeige der Datei oder des Programms: 
                                          // z.B. maximiert (SW_SHOWMAXIMIZED) oder 
                                          // minimiert und nicht aktivieren (SW_SHOWMINNOACTIVE) oder 
                                          // einfach aktivieren und anzeigen (SW_SHOW)
);
    				
Entweder lpFile/Datei oder lpDirectoty/Verzeichnis müssen mit absolutem Pfad angegeben werden.

Die Beschreibung der Funktion basiert auf Windows-spezifischen Datentypen und Codeanmerkungen.

_In_ und _In_opt_ werden in der Microsoft-Quellcodeanmerkungssprache (SAL) definiert.
_In_ bedeutet einfach, dass Daten an die aufgerufene Funktion übergeben und als schreibgeschützt behandelt werden. Im Gegensatz zu _In_opt_, bei der die Parameter optional sind, sind bei einem einfachen _In_ die Parameter Pflicht.

Die hier benutzten Windows-Datentypen:
HWND - Zeiger auf ein Fenster
LPCTSTR - char*, wenn Unicode definiert ist als Zeiger auf einen 16-bit Unicode char (LPCWSTR), sonst auf einen 8-bit (ANSI) char (LPCSTR)
INT - 32-bit signed Integer
Der Rückgabewert ist ein Zeiger mit einem Wert > 32 wenn die Funktion erfolgreich ausgeführt wurde. Über einen Wert <=32 können diverse Fehlerfälle abgefragt werden.

Code-Schnipsel:


// C++ Header
#include <iostream>
#include <sstream>

// C-Header
extern "C" {
#include <windows.h>
#include <Shellapi.h>
#include <Winuser.h> // for error messages
}

void execute_program(const char* program_call, const char* param )
{
    int ex = (int)   ShellExecute(NULL, "open", program_call, param, NULL, SW_SHOW );
    if(ex <= 32)
    {
        stringstream ss; // parse error code to string
        ss << ex;
        string ex_str = ss.str();

        string note = ex_str + string(": An error occured when calling: ") + program_call + "\n";
        string error_string = "";

        if (ex == ERROR_FILE_NOT_FOUND)
        {
            error_string = "Specified file not found.";
        }
        else if (ex == ERROR_BAD_FORMAT)
        {
            error_string = "Invalid .exe file.";
        }
        else if (ex == SE_ERR_ACCESSDENIED)
        {
            error_string = "No access to file.";
        }
        else if (ex == SE_ERR_NOASSOC)
        {
            error_string = "No application associated with the given file name extension.";
        }
        else if (ex == SE_ERR_OOM)
        {
            error_string = "Not enough memory.";
        }
        else
        {
            error_string = "Unexpected error.";        
        }
        MessageBox(NULL, (note + error_string ).c_str(), NULL, MB_OK ); // owner window, message, title, buttons
    }
}
    				
"open" startet im Falle einer ausführbaren Datei das Programm, kann aber auch Webseiten, Verzeichnisse oder Textdateien öffnen oder alles Mögliche tun, sofern das in die Registry eingetragen wird.

weiter zu Teil 2: CreateProcess