Resource icon

C++ Klasse zum Ausführen einer Kommandozeile in einem externen cmd.exe Process unter Windows

Programmiersprache(n)
C++
Betriebssystem(e)
Windows 7
Wenn es (aus welchen Gründen auch immer) nötig sein sollte eine Kommandozeile extern auszuführen, gibt es eine Reihe C-Style Möglichkeiten, wie die Standardfunktion system() oder auch Nicht-Standardfunktionen wie _popen() oder _exec(). Jede dieser Funktionen deckt bestimmte Möglichkeiten ab.

Im folgenden habe ich mit Hilfe der Windows API versucht, eine C++ Klasse zu entwickeln, die eine Reihe des Verhaltens dieser Funktionen in nur einer überladenen Methode abdeckt.
Es ist sowohl möglich das Arbeitsverzeichnis festzulegen, als auch die Ausgabe des stdout und stderr einer Variablen zuzuweisen und den Rückgabewert des externen Prozesses zu ermitteln. Detailliertere Beschreibungen finden sich in den Kommentaren der Headerdatei.

cmdprocess.h
C++:
// cmdprocess.h

/* cmdProcess::Exec() führt eine Kommandozeile in einem cmd.exe Prozess aus
*
* Argumente:
*   sCmdLine_In (erforderlich)
*      Referenz auf einen std::string; Beinhaltet die auszuführende Komanndozeile
*       (Einzelbefehl, verkettete Befehle oder Pfad zu einer Batchdatei).
*   sWorkDir_In (optional)
*      Referenz auf einen std::string; Beinhaltet das Arbeitsverzeichnis in dem
*       die Kommandozeile ausgeführt werden soll.
*      Wird dieses Argument nicht übergeben, wird die Kommandozeile im
*       Arbeitsverzeichnis des Programms ausgeführt.
*   vRead_Out (optional)
*      Referenz auf einen std::vector<std::string>; Beinhaltet nach der Ausführung
*       die Ausgabezeilen der des cmd.exe Prozesses zum stdout und stderr Stream.
*      Wenn vRead_Out übergeben wird, dürfen keine Befehle ausgeführt werden, die
*       Benutzerinteraktion verlangen (z.B. PAUSE, SET /P, CHOICE, TIMEOUT, etc.).
*      Ebenfalls wird bei Übergabe dieses Arguments die Ausgabe von stdout und stderr
*       in das Konsolefenster unterdrückt.
*   uExitCode_Out (optional)
*      Referenz auf ein long unsigned int; Beinhaltet nach der Ausführung den
*       Rückgabewert des cmd.exe Prozesses.
*
* Rückgabewert:
*   boolescher Wert
*      true wenn der cmd.exe Prozess erfolgreich ausgeführt und beendet wurde,
*       anderenfalls false
*/

#ifndef CMDPROCESS_H
#define CMDPROCESS_H

#include <string>
#include <vector>

class cmdProcess
{
  const std::string sComSpec, sOpt;
  long unsigned uDummy;

  void ComposeCmdArgs(const std::string& sCmdLine, std::vector<char>& vArgs) const;
  bool WaitForCmd(long unsigned& uExitCode_Out, const std::string& sCmdLine_In, const std::string& sWorkDir_In);
  bool WaitForPipe(std::vector<std::string>& vPipeRead_Out, long unsigned& uExitCode_Out, const std::string& sCmdLine_In, const std::string& sWorkDir_In);

public:
  cmdProcess();
  bool Exec(const std::string& sCmdLine_In);
  bool Exec(const std::string& sCmdLine_In, long unsigned& uExitCode_Out);
  bool Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In);
  bool Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In, long unsigned& uExitCode_Out);
  bool Exec(const std::string& sCmdLine_In, std::vector<std::string>& vRead_Out);
  bool Exec(const std::string& sCmdLine_In, std::vector<std::string>& vRead_Out, long unsigned& uExitCode_Out);
  bool Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In, std::vector<std::string>& vRead_Out);
  bool Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In, std::vector<std::string>& vRead_Out, long unsigned& uExitCode_Out);
};

#endif // CMDPROCESS_H

cmdprocess.cpp
C++:
// cmdprocess.cpp

#include <windows.h>
#include <sstream>
#include "cmdprocess.h"

cmdProcess::cmdProcess()
  : sComSpec("%comspec%")
  , sOpt("/c \"")
  , uDummy()
{
}

void cmdProcess::ComposeCmdArgs(const std::string& sCmdLine, std::vector<char>& vArgs) const
{
  vArgs.assign(sOpt.begin(), sOpt.end());
  std::copy(sCmdLine.begin(), sCmdLine.end(), std::back_inserter<std::vector<char> >(vArgs));
  vArgs.push_back('\"');
  vArgs.push_back('\0');
}

bool cmdProcess::WaitForCmd(long unsigned& uExitCode_Out, const std::string& sCmdLine_In, const std::string& sWorkDir_In)
{
  PROCESS_INFORMATION pi = {0};
  STARTUPINFOA si = {0};
  char rgchComSpec[512] = {0};
  std::vector<char> vCmdLine(sCmdLine_In.size() + 6);
  DWORD dwExitCode = 0;

  ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
  ZeroMemory(&si, sizeof(STARTUPINFOA));
  si.cb = sizeof(STARTUPINFOA);

  ComposeCmdArgs(sCmdLine_In, vCmdLine);

  if (!ExpandEnvironmentStringsA(sComSpec.c_str(), rgchComSpec, 512)
      || !CreateProcessA(rgchComSpec, &vCmdLine.front(), NULL, NULL, TRUE, 0, NULL, (sWorkDir_In.empty()) ? NULL : sWorkDir_In.c_str(), &si, &pi))
    return false;

  if (WaitForSingleObject(pi.hProcess, INFINITE) != WAIT_OBJECT_0
      || !GetExitCodeProcess(pi.hProcess, &dwExitCode))
  {
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return false;
  }
  uExitCode_Out = dwExitCode;

  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);

  return true;
}

bool cmdProcess::WaitForPipe(std::vector<std::string>& vPipeRead_Out, long unsigned& uExitCode_Out, const std::string& sCmdLine_In, const std::string& sWorkDir_In)
{
  HANDLE hChildStdOutR = NULL, hChildStdOutW = NULL;
  PROCESS_INFORMATION pi = {0};
  STARTUPINFOA si = {0};
  SECURITY_ATTRIBUTES sa = {0};
  char rgchRead[8192] = {0}, rgchComSpec[512] = {0};
  DWORD dwRead = 0, dwExitCode = 0;
  std::size_t c = 0;
  std::string sLine;
  std::stringstream ssRead(std::ios_base::app | std::ios_base::in | std::ios_base::out);
  std::vector<char> vCmdLine(sCmdLine_In.size() + 6);

  vPipeRead_Out.clear();
  sa.nLength = sizeof(SECURITY_ATTRIBUTES);
  sa.lpSecurityDescriptor = NULL;
  sa.bInheritHandle = TRUE;

  if (!ExpandEnvironmentStringsA(sComSpec.c_str(), rgchComSpec, 512)
      || !CreatePipe(&hChildStdOutR, &hChildStdOutW, &sa, 0))
    return false;

  if (!SetHandleInformation(hChildStdOutR, HANDLE_FLAG_INHERIT, HANDLE_FLAG_INHERIT))
  {
    CloseHandle(hChildStdOutR);
    CloseHandle(hChildStdOutW);
    return false;
  }

  ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));
  ZeroMemory(&si, sizeof(STARTUPINFOA));
  si.cb = sizeof(STARTUPINFOA);
  si.hStdOutput = hChildStdOutW;
  si.hStdError = hChildStdOutW;
  si.dwFlags = STARTF_USESTDHANDLES;

  ComposeCmdArgs(sCmdLine_In, vCmdLine);

  if (!CreateProcessA(rgchComSpec, &vCmdLine.front(), NULL, NULL, TRUE, 0, NULL, (sWorkDir_In.empty()) ? NULL : sWorkDir_In.c_str(), &si, &pi))
  {
    CloseHandle(hChildStdOutR);
    CloseHandle(hChildStdOutW);
    return false;
  }

  CloseHandle(hChildStdOutW);

  while (ReadFile(hChildStdOutR, rgchRead, 8191, &dwRead, NULL) && dwRead)
  {
    rgchRead[dwRead] = 0;
    ssRead << rgchRead;
  }

  CloseHandle(hChildStdOutR);

  if (!GetExitCodeProcess(pi.hProcess, &dwExitCode))
  {
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    return false;
  }
  uExitCode_Out = dwExitCode;

  CloseHandle(pi.hProcess);
  CloseHandle(pi.hThread);

  while (std::getline(ssRead, sLine))
  {
    while ((c = sLine.size()) && sLine.at(c - 1) == '\r')
      sLine = sLine.substr(0, c - 1);
    vPipeRead_Out.push_back(sLine);
  }

  return true;
}

bool cmdProcess::Exec(const std::string& sCmdLine_In)
{
  return WaitForCmd(uDummy, sCmdLine_In, "");
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, long unsigned& uExitCode_Out)
{
  return WaitForCmd(uExitCode_Out, sCmdLine_In, "");
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In)
{
  return WaitForCmd(uDummy, sCmdLine_In, sWorkDir_In);
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In, long unsigned& uExitCode_Out)
{
  return WaitForCmd(uExitCode_Out, sCmdLine_In, sWorkDir_In);
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, std::vector<std::string>& vRead_Out)
{
  return WaitForPipe(vRead_Out, uDummy, sCmdLine_In, "");
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, std::vector<std::string>& vRead_Out, long unsigned& uExitCode_Out)
{
  return WaitForPipe(vRead_Out, uExitCode_Out, sCmdLine_In, "");
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In, std::vector<std::string>& vRead_Out)
{
  return WaitForPipe(vRead_Out, uDummy, sCmdLine_In, sWorkDir_In);
}

bool cmdProcess::Exec(const std::string& sCmdLine_In, const std::string& sWorkDir_In, std::vector<std::string>& vRead_Out, long unsigned& uExitCode_Out)
{
  return WaitForPipe(vRead_Out, uExitCode_Out, sCmdLine_In, sWorkDir_In);
}

Im folgenden Beispiel werden *.exe Dateien im Windows Verzeichnis aufgelistet.
Die Ausgabe des DIR Befehls wird dabei zeilenweise einem std::vector zugewiesen.

main.cpp
C++:
// main.cpp

#include <string>
#include <vector>
#include <iostream>
#include "cmdprocess.h"

int main()
{
  // Klassenobjekt
  cmdProcess cmdProc;
  // der DIR Befehl soll im Windowsverzeichnis ausgeführt werden und Executables listen
  const std::string
    cmdLn("dir /a-d /b *.exe"),
    workDir("C:\\Windows");
  // vector<string>, dem die Ausgabe des DIR Befehls zeilenweise zugewiesen werden soll
  std::vector<std::string> caughtLines;
  // Variable, der der Rückgabewert des aufgerufenen Prozesses zugewiesen werden soll
  long unsigned returnValue = 0;
  // iterator für die Ausgabe der eingelesenen Zeilen
  std::vector<std::string>::iterator it;

  // Aufruf der cmdProcess::Exec() Methode
  if (cmdProc.Exec(cmdLn, workDir, caughtLines, returnValue))
  {
    // Ausgabe des Rückgabewertes und der eingelesenen Zeilen
    std::cout << "CMD Prozess wurde gestartet und mit Rueckgabewert "
      << returnValue << " beendet.\n\nGelesene Zeilen:\n";
    for (it = caughtLines.begin(); it != caughtLines.end(); ++it)
      std::cout << *it << '\n';

    return 0;
  }

  std::cerr << "Fehler!\n";
  return 1;
}
Autor
German
First release
Last update
Bewertung
5,00 Stern(e) 1 Bewertungen

Latest reviews

Hat mir sehr gut geholfen. Kann man immer wieder mal gebrauchen.

LG
Oben