6
votes

Démarrez deux processus et connectez-les avec un tuyau dans Delphi

Je dois lancer deux programmes externes dans mon programme et connecter le stdout du premier au STDIN du deuxième programme. Comment pouvez-vous atteindre cet objectif à Delphi (RAD Studio 2009, si cela compte)? Je fonctionne dans l'environnement Windows.

En tant que commandement de commandes de commande, ma situation ressemblerait à ceci comme suit: P>

dumpdata.exe | encrypt.exe "mydata.dat"


0 commentaires

4 Réponses :


0
votes

Cette approche devrait fonctionner. Avant de vous inquiéter de l'appeler à partir de DelphI, obtenez la ligne de commande fonctionnant en exécutant dans une fenêtre d'invite de commande (fenêtre DOS).
Ensuite, appelez cette commande de Delphi avec Winexec ou Shellexecute. Il existe des options pour appeler et attendre, ou simplement "tirer et oublier".


2 commentaires

La commande fonctionne bien sur la ligne de commande. Si je veux attendre que les processus se terminent, comment pourrais-je y parvenir? Mon inquiétude est que la commande de la pipée devrait-elle être incluse dans les paramètres de Shellexecute (Ex)? Devrait-il être complètement dans le paramètre LPFILE ou partiellement dans les LPParameters?


AFAIK est le shell de commande (qui n'est pas une "DOS" Windows, il s'agit d'un substitut sans gui win32 / 64) pour gérer les opérateurs de redirection, pas l'API Windows eux-mêmes. Si oui, cette commande ne pouvait être utilisée que d'invoquer cmd.exe et de passer cette ligne de commande à elle.



2
votes

CreateProcess () vous permet de rediriger à la fois STDIN et STDOUT d'applications lancées. Votre application peut lire à partir de la première application STDOUT et d'écrire à la deuxième application STDIN.


1 commentaires

Y a-t-il un moyen de laisser sortir l'homme du milieu et de laisser les deux processus communiquer directement les uns avec les autres?



9
votes

Un test rapide qui semble fonctionner (inspiré fortement par JCL ):

enfant1: Dites 'Bonjour, Monde!' 3x à la sortie standard p> xxx pré>

enfant2: echo tout ce qui est entré sur l'entrée standard à la sortieDebugstring (peut être visualisé par DEBUGVIEW ) P>

program parent;

{$APPTYPE CONSOLE}

uses
  Windows, Classes, SysUtils;

procedure ExecutePiped(const CommandLine1, CommandLine2: string);
var
  StartupInfo1, StartupInfo2: TStartupInfo;
  ProcessInfo1, ProcessInfo2: TProcessInformation;
  SecurityAttr: TSecurityAttributes;
  PipeRead, PipeWrite: THandle;
begin
  PipeWrite := 0;
  PipeRead := 0;
  try
    SecurityAttr.nLength := SizeOf(SecurityAttr);
    SecurityAttr.lpSecurityDescriptor := nil;
    SecurityAttr.bInheritHandle := True;
    Win32Check(CreatePipe(PipeRead, PipeWrite, @SecurityAttr, 0));

    FillChar(StartupInfo1, SizeOf(TStartupInfo), 0);
    StartupInfo1.cb := SizeOf(TStartupInfo);
    StartupInfo1.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    StartupInfo1.wShowWindow := SW_HIDE;
    StartupInfo1.hStdInput := GetStdHandle(STD_INPUT_HANDLE);
    StartupInfo1.hStdOutput := PipeWrite;
    StartupInfo1.hStdError := GetStdHandle(STD_ERROR_HANDLE);

    FillChar(StartupInfo2, SizeOf(TStartupInfo), 0);
    StartupInfo2.cb := SizeOf(TStartupInfo);
    StartupInfo2.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    StartupInfo2.wShowWindow := SW_HIDE;
    StartupInfo2.hStdInput := PipeRead;
    StartupInfo2.hStdOutput := GetStdHandle(STD_OUTPUT_HANDLE);
    StartupInfo2.hStdError := GetStdHandle(STD_ERROR_HANDLE);

    FillChar(ProcessInfo1, SizeOf(TProcessInformation), 0);
    FillChar(ProcessInfo2, SizeOf(TProcessInformation), 0);

    Win32Check(CreateProcess(nil, PChar(CommandLine2), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo2,
      ProcessInfo2));

    Win32Check(CreateProcess(nil, PChar(CommandLine1), nil, nil, True, NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo1,
      ProcessInfo1));

    WaitForSingleObject(ProcessInfo2.hProcess, INFINITE);
  finally
    if PipeRead <> 0 then
      CloseHandle(PipeRead);
    if PipeWrite <> 0 then
      CloseHandle(PipeWrite);
    if ProcessInfo2.hThread <> 0 then
      CloseHandle(ProcessInfo2.hThread);
    if ProcessInfo2.hProcess <> 0 then
      CloseHandle(ProcessInfo2.hProcess);
    if ProcessInfo1.hThread <> 0 then
      CloseHandle(ProcessInfo1.hThread);
    if ProcessInfo1.hProcess <> 0 then
      CloseHandle(ProcessInfo1.hProcess);
  end;
end;

procedure Main;
begin
  ExecutePiped('child1.exe', 'child2.exe');
end;

begin
  try
    Main;
  except
    on E: Exception do
    begin
      ExitCode := 1;
      Writeln(Error, Format('[%s] %s', [E.ClassName, E.Message]));
    end;
  end;
end.


4 commentaires

Cela ressemble exactement à ce que je cherchais. Merci. Pour une raison quelconque, le programme parent me donne "une violation d'accès dans le module kernel32.dll" sur la première ligne de CreateProcess. J'ai construit tous les programmes. Peut-être que je manque quelque chose ...


Je ne vois rien dans le code qui pourrait causer une A / V. J'ai utilisé D2007 mais cela devrait également fonctionner dans D2009.


La version largement caractères de CreateProcess (qui est ce que Delphi 2009 appellerait) peut modifier la chaîne de ligne de commande, de sorte que vous ne devez pas passer un littéral à chaîne. Stockez-le dans une variable de chaîne et appelez UNIQUILLER avant de le jeter à Pchaler.


Je pense que cette réponse bénéficierait d'une discussion sur ce qui se passe. Par exemple, quelle est la partie importante de exécutée ? (C'est-à-dire que le champ Binherithandle et la manière dont les poignées sont attribuées dans les enregistrements StarTupinfo.) Est-il important que les autres champs de la poignée soient obtenus de GetStdhandle? Est-il important que la commande 2 soit exécutée avant la commande 1? La commande 1 doit-elle vraiment écrire ^ z? Les poigilles doivent-elles rester ouvertes dans le parent pendant que les enfants sont toujours en cours d'exécution?



2
votes

Voici le code corrigé pour travailler à Delphi Xe. Les chaînes de commande doivent être des variables et également définies au-dessus de la fonction exécutée. XXX PRE>

Pour tester J'ai modifié les enfants2.Pas pour écrire le texte reçu dans un fichier. P>

    program Child2;

    {$APPTYPE CONSOLE}

    uses
    Windows, SysUtils, Classes;

    procedure Main;
    var S: string;
        OutFile : TextFile;
    begin
      AssignFile(OutFile, 'test.txt');
      Rewrite(OutFile);
      while not Eof(Input) do
      begin
        Readln(S);
        Writeln(OutFile,S);
        //if S <> '' then OutputDebugString(PChar(S));
      end;
      CloseFile(OutFile);
    end;

    begin
      try
        Main;
      except
        on E: Exception do
        begin
          ExitCode := 1;
          Writeln(ErrOutput, Format('[%s] %s', [E.ClassName, E.Message]));
        end;
      end;
    end.


0 commentaires