Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi CommandLineInterpreter aufrufen und Daten übermitteln (https://www.delphipraxis.net/211178-commandlineinterpreter-aufrufen-und-daten-uebermitteln.html)

KodeZwerg 9. Aug 2022 08:55

CommandLineInterpreter aufrufen und Daten übermitteln
 
Hallo liebe Delphi Gemeinde,

ich bin etwas am verzweifeln und bin mir sicher das der Fehler vor dem Rechner sitzt :D

Ich hatte vor eine Art Batch ersatz für Pascal zu entwerfen wo der User eine Liste an Befehlen einer Methode übergibt die eine CLI startet und die Befehle nach und nach abarbeitet.
Soweit so gut, bis hier hin bin ich gekommen aber es funktioniert einfach nicht, ich finde leider nicht den Punkt an dem ich etwas falsch mache und erhoffe mir Hilfe.
Delphi-Quellcode:
function ExecuteBatch(const ABatch: TStringList): Boolean;
  procedure LWritePipeOut(OutputPipe: THandle; InString: PChar);
  var
    byteswritten: DWORD;
    AnsiBuf: AnsiString;
  begin
    AnsiBuf := String(InString) + #13#10;
    WriteFile(OutputPipe, AnsiBuf, Length(AnsiBuf), byteswritten, nil);
  end;
var
  i: Integer;
  LCLI: AnsiString;
  LSecurityAttribute : TSecurityAttributes;
  LProcessInformation: TProcessInformation;
  LStartUpInfo: TStartUpInfo;
  NewStdIn, WriteStdIn: THandle;
begin
  LCLI := GetEnvironmentVariable('COMSPEC');
  UniqueString(LCLI);

  LSecurityAttribute.nlength := SizeOf(TSecurityAttributes) ;
  LSecurityAttribute.binherithandle := True;
  LSecurityAttribute.lpsecuritydescriptor := nil;
  CreatePipe(NewStdIn, WriteStdIn, @LSecurityAttribute, 0);

  FillChar(LStartUpInfo, Sizeof(LStartUpInfo), #0) ;
  LStartUpInfo.hStdInput := NewStdIn;
  LStartUpInfo.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
  LStartUpInfo.wShowWindow := SW_Show;
  LStartUpInfo.cb := SizeOf(LStartUpInfo);
  LStartUpInfo.lpTitle := PChar('Batch Console');

  if CreateProcess(nil, PChar(LCLI), @LSecurityAttribute, @LSecurityAttribute, True,
                   CREATE_NEW_CONSOLE or SYNCHRONIZE,
                   nil, nil, LStartUpInfo, LProcessInformation) then
    begin
      for i := 0 to Pred(ABatch.Count) do
        begin
          LWritePipeOut(WriteStdIn, PChar(ABatch.Strings[i]));
        end;
      CloseHandle(LProcessInformation.hProcess);
      CloseHandle(LProcessInformation.hThread);
      Result := True
    end
    else
      Result := False;
  CloseHandle(NewStdIn);
  CloseHandle(WriteStdIn);
end;
Momentan wird eine CLI gestartet aber keiner der Befehle erscheint in der CLI.

Uwe Raabe 9. Aug 2022 09:13

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Selbst unter der Annahme, dass du hier das Rad neu erfinden willst, könnte ein Spicken in einem existierenden Rad vielleicht hilfreich sein: https://github.com/TurboPack/DOSCommand

TiGü 9. Aug 2022 09:29

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Immer ein bisschen gucken und mitdenken wenn man Quelltexte von Stack Overflow kopiert.
So funktioniert das:

Delphi-Quellcode:
function ExecuteBatch(const ABatch: TStringList): Boolean;

    procedure LWritePipeOut(OutputPipe: THandle; InString: AnsiString);
    var
        byteswritten: DWORD;
    begin
        InString := InString + sLineBreak;
        WriteFile(OutputPipe, InString[1], Length(InString), byteswritten, nil);
    end;

var
    i: Integer;
    LCLI: AnsiString;
    LSecurityAttribute: TSecurityAttributes;
    LProcessInformation: TProcessInformation;
    LStartUpInfo: TStartupInfoA;
    NewStdIn, WriteStdIn: THandle;
begin
    Result := False;

    LCLI := GetEnvironmentVariable('COMSPEC');
    UniqueString(LCLI);

    LSecurityAttribute.nlength := SizeOf(TSecurityAttributes);
    LSecurityAttribute.binherithandle := True;
    LSecurityAttribute.lpsecuritydescriptor := nil;
    CreatePipe(NewStdIn, WriteStdIn, @LSecurityAttribute, 0);

    FillChar(LStartUpInfo, Sizeof(LStartUpInfo), #0);
    LStartUpInfo.hStdInput := NewStdIn;
    LStartUpInfo.dwFlags := STARTF_USESTDHANDLES + STARTF_USESHOWWINDOW;
    LStartUpInfo.wShowWindow := SW_Show;
    LStartUpInfo.cb := SizeOf(LStartUpInfo);
    LStartUpInfo.lpTitle := PAnsiChar('Batch Console');

    if CreateProcessA(nil, PAnsiChar(LCLI), @LSecurityAttribute, @LSecurityAttribute, True,
        CREATE_NEW_CONSOLE or SYNCHRONIZE,
        nil, nil, LStartUpInfo, LProcessInformation) then
    begin
        for i := 0 to Pred(ABatch.Count) do
        begin
            LWritePipeOut(WriteStdIn, PChar(ABatch.Strings[i]));
        end;
        CloseHandle(LProcessInformation.hProcess);
        CloseHandle(LProcessInformation.hThread);
        Result := True
    end;

    CloseHandle(NewStdIn);
    CloseHandle(WriteStdIn);
end;

procedure TForm1.Button1Click(Sender: TObject);
var
    sl: TStringlist;
begin
    sl := TStringlist.create;
    sl.Add('ping 127.0.0.1');
    sl.Add('ping 8.8.8.8');
    if ExecuteBatch(sl) then
    begin
        Caption := 'fertig';
    end;
    sl.Free;
end;

KodeZwerg 9. Aug 2022 09:42

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1509905)
Selbst unter der Annahme, dass du hier das Rad neu erfinden willst, könnte ein Spicken in einem existierenden Rad vielleicht hilfreich sein: https://github.com/TurboPack/DOSCommand

Vielen Dank für diesen Tipp!

Zitat:

Zitat von TiGü (Beitrag 1509907)
Immer ein bisschen gucken und mitdenken wenn man Quelltexte von Stack Overflow kopiert.

Vielen Dank für Deinen Beitrag, es macht nun das von mir erhoffte!


Falls es okay ist würde ich dazu gerne noch eine Frage hier stellen, ist es möglich das hStdInput nach meiner For Schleife wieder an den User zurückzugeben?
(Beispiel man sendet "Pause")

TiGü 9. Aug 2022 10:26

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Verstehe die Frage nicht?!
Was macht denn der User mit dem Handle?

KodeZwerg 9. Aug 2022 11:05

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Zitat:

Zitat von TiGü (Beitrag 1509909)
Verstehe die Frage nicht?!
Was macht denn der User mit dem Handle?

Ich habe mich bestimmt falsch ausgedrückt und versuche es per beispiel:
Delphi-Quellcode:
procedure TForm1.Button1Click(Sender: TObject);
var
    sl: TStringlist;
begin
    sl := TStringlist.create;
    sl.Add('pause');
    if ExecuteBatch(sl) then
    begin
        Caption := 'fertig';
    end;
    sl.Free;
end;
Wie kann ich den User die Kontrolle über die Eingabe "zurück-geben" bitte?

//edit
Also programmatisch zwischen Pipe und GetStdHandle(STD_INPUT_HANDLE) (vermute ich das ich das meine) wechseln

himitsu 9. Aug 2022 11:40

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Außerdem ist es modern, wenn man sich mit dem passenden ConHost verbindet, anstatt die CMD.exe (COMSPEC) einzubinden.

Frag mich aber bitte nicht, wie man das macht ... ich hatte mal versucht das in der Hilfe zu finden,
bzw. im neuen Windows-Terminal im QuellCode abzugucken, aber da sieht ja keiner durch.


https://github.com/microsoft/terminal
https://apps.microsoft.com/store/det...l/9N0DX20HK701
bzw. im aktuellen Windows 11 ist der nun vorinstalliert.

https://www.youtube.com/watch?v=8gw0rXPMMPE
https://www.youtube.com/watch?v=DJIlT7HuQ_4
https://www.youtube.com/watch?v=KMudkRcwjCw



PS: Du könntest auch den Windows-Scripting-Host (WSH) verwenden
und dann WBScipt, JavaScript oder PowerShell nutzen,
sowie optional auch z.B. Pyhon, PascalScript uvm. was sich nachrüsten lässt.

TurboMagic 9. Aug 2022 21:50

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Zitat:

Zitat von Uwe Raabe (Beitrag 1509905)
Selbst unter der Annahme, dass du hier das Rad neu erfinden willst, könnte ein Spicken in einem existierenden Rad vielleicht hilfreich sein: https://github.com/TurboPack/DOSCommand

Alternativ ist das auch unter Tools/GetIt zum Installieren zu finden...

KodeZwerg 10. Aug 2022 08:03

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Zitat:

Zitat von TurboMagic (Beitrag 1509935)
Zitat:

Zitat von Uwe Raabe (Beitrag 1509905)
Selbst unter der Annahme, dass du hier das Rad neu erfinden willst, könnte ein Spicken in einem existierenden Rad vielleicht hilfreich sein: https://github.com/TurboPack/DOSCommand

Alternativ ist das auch unter Tools/GetIt zum Installieren zu finden...

Ich habe es mir noch nicht angeschaut, aus eurem Wissen heraus, kann man dann damit das erreichen was ich vorhabe?
- eine konsole öffnen (das ist denke ich mal klar wie kloßbrühe)
- befehle übermitteln als ob man sie selbst in einer konsole getippt hätte (ich mache es halt per stringlist)
- die kontrolle über StdIn wieder an die konsole übergeben (das ist das einzige was mir momentan fehlt)

KodeZwerg 10. Aug 2022 08:05

AW: CommandLineInterpreter aufrufen und Daten übermitteln
 
Zitat:

Zitat von himitsu (Beitrag 1509918)
Außerdem ist es modern, wenn man sich mit dem passenden ConHost verbindet, anstatt die CMD.exe (COMSPEC) einzubinden.

Frag mich aber bitte nicht, wie man das macht ... ich hatte mal versucht das in der Hilfe zu finden,
bzw. im neuen Windows-Terminal im QuellCode abzugucken, aber da sieht ja keiner durch.


https://github.com/microsoft/terminal
https://apps.microsoft.com/store/det...l/9N0DX20HK701
bzw. im aktuellen Windows 11 ist der nun vorinstalliert.

https://www.youtube.com/watch?v=8gw0rXPMMPE
https://www.youtube.com/watch?v=DJIlT7HuQ_4
https://www.youtube.com/watch?v=KMudkRcwjCw



PS: Du könntest auch den Windows-Scripting-Host (WSH) verwenden
und dann WBScipt, JavaScript oder PowerShell nutzen,
sowie optional auch z.B. Pyhon, PascalScript uvm. was sich nachrüsten lässt.

Ich habe es mir angeschaut und erschreckender weise festgestellt das meine windows.pas nicht mal annähernd das abdeckt was gefordert wird :(


Alle Zeitangaben in WEZ +1. Es ist jetzt 13:48 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz