[C++] Konsolenbefehl mit auswählbarem Parameter

effekt660

Lt. Commander
Registriert
Dez. 2010
Beiträge
1.659
Hey alle zusammen,
ich bin gerade dabei eine mehr oder weniger kleine .exe zu schreiben, die mir ein paar Konsolenbefehle ausführt. Über Sinn und Unsinn möchte ich mich gar nicht unterhalten. Eine .bat ist natürlich deutlich sinnvoller, allerdings mache ich das, um ein wenig vertrauter damit zu werden.

Jetzt möchte ich aber, dass man bei einem Befehl den Parameter auswählen kann und selbst eingibt. dieser soll dann in den Befehl eingelesen werden und ausgeführt werden.

Hier ein kleiner Ausschnitt davon, was ich ausprobiert habe. Ich hoffe ihr erkennt, was ich vor habe.

Code:
# include <iostream>
# include <conio.h>
# include <windows.h>
# include <iomanip>
# include <stdlib.h>

using namespace std;

int main(){
	int MTU;
	
	cin>>MTU;

	char Befehl[] = "netsh interface ipv4 set subinterface Ethernet mtu="<<MTU<<" store=persistent"; 
	system(Befehl);
	
	getch();
	return 0;
}
 
Hallo,

in der main() Funktion bekommt man als Übergabeparameter vom System die Anzahl der Argumente (argc), sowie Argumente als Array von char* (argv). Das erste Element ist immer der Name des aufgerufenen Programms an sich.

Code:
#include <iostream>

using namespace std;

int main(int argc, char** argv) {

	for (int i = 0; i < argc; i++)
	{
		cout << argv[i];
		cout << "\n";
	}

	return 0;

}

Die Ausgabe würde dann so ausschauen:

Code:
~$ ./testprog param1 param2 param3
./testprog
param1
param2
param3
 
Okay, danke erst mal.
Allerdings habe ich den Zusammenhang zu meinem Vorhaben noch nicht ganz geblickt. Dass die int Variable in dem char Befehl nicht gut aufgehoben ist, habe ich mir schon gedacht, allerdings wollte ich auch eher damit zeigen, was ich vor habe.
 
Also wenn es dir darum geht, dass du per Argument den netsh Befehl ausführen willst, würde es so gehen:

Code:
#include <iostream>
#include <string>

using namespace std;

int main(int argc, char** argv) {

// Anzahl der Argumente checken
	if(argc < 2) {
		cout << "Ungültiges Argument\n";
		cout << "Befehl: testprog 12345";
		return -1;
	}

	// Argument ermitteln
	string mtu = argv[1];

	// Befehl zusammenbauen
	string cmd = "netsh interface ipv4 set subinterface Ethernet mtu=" + mtu + " store=persistent"; 

	// Befehl ausführen (string in char* über die Funktion c_str() umwandeln)
// Die System-Funktion akzeptiert nur char* Argumente
	system(cmd.c_str());

	// System mitteilen, dass das Programm erfolgreich ausgeführt wurde
	return 0;
}

Ist zwar nicht der schönste Code, aber als Beispiel sollte es taugen :)
 
Also eigentlich möchte ich nur realisieren, dass ich die MTU vorher eingebe und diese dann in den netsh interface ipv4 set subinterface Ethernet mtu=**** store=persistent eingetragen wird. Damit ich z.B. eine MTU von 1464 oder 1456 oder eine beliebige andere im System festlegen kann.
 
Achso, du willst die direkt im Programm eingeben :)

Das würde beispielsweise so gehen:

Code:
#include <iostream>
#include <string>

using namespace std;

int main(int argc, char** argv) {
	string mtu;

	cout << "Bitte MTU eingeben: ";

	// Wert einlesen und speichern
	cin >> mtu;

	string cmd = "netsh interface ipv4 set subinterface Ethernet mtu=" + mtu + " store=persistent"; 

	system(cmd.c_str());

	return 0;
}
 
Ich hätte noch eine kurze Frage. Und zwar könnte mir vielleicht einer sagen, ob man eine Rückmeldung in der CMD bekommt, wenn man einen regedit Wert ändert? Ich habe das unten stehende Programm fehlerfrei compiliert und auch in der cmd, beim ausführen kam keine Fehlermeldung/Rückmeldung, egal ob als Admin ausgeführt, oder nicht. Wenn ich jetzt nach dem Ausführen allerdings in die regedit gehe und gucke, ob die Änderung übernommen wurde, stelle fest, dass dies nicht der Fall ist.


Code:
# include <iostream>
# include <iomanip>
# include <conio.h>
# include <stdio.h>
# include <stdlib.h>
# include <windows.h>
 
using namespace std;
 
int main() {
	getch();
	
	HKEY hkey;
	
	DWORD dwNumber = 0;
	RegOpenKeyEx(HKEY_LOCAL_MACHINE,(LPCTSTR)"SOFTWARE\Policies\Microsoft\Windows\Skydrive",0,KEY_ALL_ACCESS,&hkey);
	RegSetValueEx(hkey,(LPCTSTR)"DisableFileSync",0,REG_DWORD,(BYTE*)&dwNumber,sizeof(DWORD));
RegCloseKey(hkey);
  	
	return 0;
}
 
Du überprüfst ja in deinem Code auch nirgends auch nur einen einzigen Rückgabewert. Diese Funktionen haben alle Rückgabewerte, und zwar nicht um sonst (das gilt übrigens auch für den system()-Aufruf in dem Beispiel, das weiter oben jemand gepostet hat).
Ergänzung ()

regedit.exe selbst kehrt meiner Meinung nach immer mit einem Exitcode von 0 zurück, es sei denn, es geht richtig fies was schief. Einfach mal mit regedit.exe und anschließendem echo %errorlevel% ausprobieren.
Ergänzung ()

Noch was, den cast nach LPCTSTR kannst und solltest du dir sparen. Stattdessen solltest du das Stringliteral in das _T-Makro wrappen. Also

Code:
_T( "SOFTWARE\Policies\Microsoft\Windows\Skydrive" )

denn nur so ist gewährleistet, daß das Stringliteral wirklich als wide string übergeben wird, wenn UNICODE gesetzt ist. Ansonsten castet du nämlich ganz brutal die Adresse eines normalen ASCII Stringliterals und behauptest, es sei ein wide string Literal, obwohl das eine glatte Lüge ist. Gleiches gilt auch für das 2. Stringliteral. Außerdem wird in C++ mit den C++ Castoperatoren (static_cast, dynamic_cast, reinterpret_cast, const_cast) gecastet, nicht mit dem alten C-Cast-Vorschlaghammer.

P.S. Erklärung zum _T()-Makro (microsoft-spezifisch): Wenn UNICODE gesetzt ist, expandiert _T( "Das ist ein String." ) zu L"Das ist ein String." (was vom Typ const wchar_t* ist ). Ist UNICODE nicht gesetzt, expandiert das Makro einfach zu "Das ist ein String." (Typ ist const char*). Ähnliches passiert mit dem Typ LPCTSTR. Mit UNICODE ist das bloß ein Synonym für const wchar_t*. Ohne UNICODE bedeutet es const char*.
 
Zuletzt bearbeitet:
Also selbst ohne return 0 Befehl wird das Programm mit dem return Code 0 beendet.

Wenn ich das LPCTSTR durch _T ersetze, kommt die Meldung, dass _T nicht deklariert sei. habe es als String, const wchar_t und const char deklariert. Funktioniert aber alles nicht.
 
effekt660 schrieb:
Also selbst ohne return 0 Befehl wird das Programm mit dem return Code 0 beendet.

Wie meinst du das? Kann dir nicht ganz folgen. EDIT: Meinst du, wenn du aus deinem C++-Code das return 0; entfernst? Falls du das meinst, ja, das ist so. In C++ ist das return-Statement in der main()-Funktion optional. Wird es weggelassen, fügt der Compiler implizit ein return 0; ein.

effekt660 schrieb:
Wenn ich das LPCTSTR durch _T ersetze, kommt die Meldung, dass _T nicht deklariert sei.

Code:
#include <tchar.h>

sollte das beheben.
 
Zuletzt bearbeitet:
Ok. Funktioniert soweit.
Was bedeutet "unknown escape sequence? Die Warnung bekomme ich für diese Zeilen:

Code:
RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("\SOFTWARE\Policies\Microsoft\Windows\Skydrive"),0,KEY_ALL_ACCESS,&hkey);
	RegSetValueEx(hkey,_T("\DisableFileSync"),0,REG_DWORD,(BYTE*)&dwNumber,sizeof(DWORD));
 
Oh ja, hatte ich übersehen. Die Backslashes in den Pfadnamen mußt du escapen. Also _T( "\\SOFTWARE\\Policies\\Microsoft\\Windows\\Skydrive" ). Ein Backslash in einem String-Literal beginnt normalerweise eine Escape-Sequenz, also ein Steuerzeichen. Beispiele: \t wird zu einem Tabulator; \n zu einer neuen Zeile; usw ... willst du tatsächlich das \-Zeichen, sagst du dem Compiler mit \\ "Das hier ist KEINE Escape-Sequenz sondern das Backslash-Zeichen."

Und vergiß nicht, die Rückgabewerte von RegOpenKeyEx(), RegSetValueEx() und RegCloseKey() zu überpüfen.
 
Zuletzt bearbeitet:
Ja gut, da hätte ich eigentlich auch selbst drauf kommen können...

Wo genau gehört das echo denn hin? In alle 3 Befehle oder hinterher(wobei das bei mir nicht funktioniert, oder gehört das echo in eine bestimmte Funktion?)?

Vielen Dank bis hierher schon mal :volllol:
 
Das return 0 oben im Code ist nur ob dein Programm fehlerfrei durch ist oder nicht.
Wenn dein prog dann von einem anderen aufgerufen wird, kannst dem so mitteilen was Sache ist.
Nur statt return 0 würde ich die vorgesehene Konstante nutzen, hierfür EXIT_SUCCESS

Konstanten sind halt dazu gut wenn sich mal etwas an den return Werten der API ändert, dann müsstest es überall in deinem Code auch ändern. Die Konstanten hingegen werden von den API Typen mit angepasst und dein Code läuft dennoch sauber.

Wie mein Prof sagen würde, read the fucking manual :D
ERROR_SUCCESS ist eine Konstante und sollte so auch benutzt werden.

Code:
x = RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("\SOFTWARE\Policies\Microsoft\Windows\Skydrive"),0,KEY_ALL_ACCESS,&hkey);

if (x == ERROR_SUCCESS)
// alles gut
  ...
  // beende programm mit erfolg code
  return EXIT_SUCCESS
else
  // regedit fehlgeschlagen mit error nummer
  return x; // oder wenn error nummer nach außen nicht ausgegeben werden soll, EXIT_FAILURE
Return value

If the function succeeds, the return value is ERROR_SUCCESS.
If the function fails, the return value is a nonzero error code defined in Winerror.h. You can use the FormatMessage function with the FORMAT_MESSAGE_FROM_SYSTEM flag to get a generic description of the error.

Die restlichen return values:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx
 
Zuletzt bearbeitet:
effekt660 schrieb:
Wo genau gehört das echo denn hin? In alle 3 Befehle oder hinterher(wobei das bei mir nicht funktioniert, oder gehört das echo in eine bestimmte Funktion?)?

Nein, damit meinte ich, du kannst ja mal in der CMD die regedit.exe aufrufen und dann nach dem Schließen von regedit in dem selben CMD-Fenster mit

echo %errorlevel%

den Rückgabewert von regedit.exe ausgeben lassen. Das hat jetzt mit deinem C++-Programm nix zu tun.
 
Zuletzt bearbeitet:
Achso, alles klar. Dann bedanke ich mich erstmal. :)

Ich habe gerade mal den errorlevel anzeigen lassen und mir wird eine 5 ausgegeben. Was bedeutet das genau?
 
Zuletzt bearbeitet:
Was der errorlevel-Wert im Konkreten bedeutet, hängt immer von dem spezifischen Befehl ab, den du vorher abgesetzt hast, aber in der Regel lautet die Konvention:

0 = alles ok
alles andere = Mist, da ist was schiefgelaufen
 
Hey noch mal...

Das ganze sieht jetzt so bei mir aus:

Code:
HKEY hkey;
	
	DWORD dwNumber = 0;
	RegOpenKeyEx(HKEY_LOCAL_MACHINE,_T("\\SOFTWARE\\Policies\\Microsoft\\Windows\\Skydrive"),0,KEY_ALL_ACCESS,&hkey);
	RegSetValueEx(hkey,_T("\\DisableFileSync"),0,REG_DWORD,(BYTE*)&dwNumber,sizeof(DWORD));
  	RegCloseKey(hkey);

Aber der Wert bleibt einfach auf 1 in der regedit. Wofür steht das: 0,KEY_ALL_ACCESS,&hkey genau?
 
Himmel, Arsch und Zwirn, jetzt überprüfe halt endlich mal die Rückgabewerte von RegOpenKeyEx(), RegSetValueEx und so weiter! Man kommt sich vor, als redet man zu einer Wand ...
 
Zurück
Oben