Visualizzazione dei risultati da 1 a 7 su 7
  1. #1
    Utente di HTML.it
    Registrato dal
    Sep 2005
    Messaggi
    247

    Interagire con programmi a linea di comando

    Salve a tutti.

    Mi sto scervellando per cercare di capire come posso far sì che il mio programma Delphi interagisca con un programma a linea di comando come fosse un essere umano.

    Vorrei che fosse in grado di avviare l'eseguibile di Apache in una console, e questo lo so fare, vorrei che leggesse l'output della console e che rispondesse adeguatamente, anche con l'invio di un abort (Ctrl+C). Vorrei però che facesse tutto questo in background, senza che l'utente veda alcunché.

    Ho cercato con Google, ma senza successo.

  2. #2
    dovresti poter gestire le pipe (ma sarà poi femminile?), che sono quasi sicuro esistono anche sotto dos.
    Io per applicazioni gui sotto linux, sia per kylix che per lazarus, utilizzo la libreria libc che ha alcune funzioni che gestiscono le pipe (che sono poi le stesse funzioni che si utilizzano in C).
    Però sai bene che per unix tutto è un file, coso che non è per il dos.

    Codice PHP:

    var
     ...
      
    str string;

      
    CmdArr : array[0..512of char;
      
    StrArr : array[0..512of char;
      
    buf pointer;
      
    size longint;
      
      
    integer;
      
    PIOFILE;
      
    pPipeStr Pointer;

    ...

    // in str c'è il comando (devi finire con #0)
    // potrebbe essere str := 'ls -la ' +#0; (o l'equivalente dos str := 'DIR ' + #0;
                   
    StrCopy(cmdArr, @str[1]);

    // apro la comunicazione 
                     
    := popen(CmdArr'r');
                     if 
    assigned(Fthen
                     begin
                          repeat
    // posso usare le normali funzioni di input
    // leggi riga dopo riga come se apparissa sul monitor
                            
    pPipeStr := fgets(StrArr1024F);
    // lo converte poi con StrPas o simili.

                          
    until not Assigned(pPipeStr);
    // ricordiamoci di chiudere la comunicazione
                          
    pClose(F);
                     
    end;

    // posso anche scrivere su un pipe, anche se è meno frequente
    // doverlo fare tra una applicazione gui ed una console 
    ciao
    sergio

  3. #3
    Moderatore di Programmazione L'avatar di alka
    Registrato dal
    Oct 2001
    residenza
    Reggio Emilia
    Messaggi
    24,466

    Moderazione

    Il linguaggio va indicato anche nel titolo, come da Regolamento.
    MARCO BREVEGLIERI
    Software and Web Developer, Teacher and Consultant

    Home | Blog | Delphi Podcast | Twitch | Altro...

  4. #4
    Utente di HTML.it
    Registrato dal
    Sep 2005
    Messaggi
    247

    Re: Moderazione

    Originariamente inviato da alka
    Il linguaggio va indicato anche nel titolo, come da Regolamento.
    Chiedo scusa, l'ho sempre fatto... stavolta me lo sono dimenticato.

    Ringrazio mondobimbi per la risposta. Ancora non ho avuto il tempo di leggerla con attenzione, ma non mancherò...

    Ciao

  5. #5
    Io uso questa unit, automatizza la gestione delle named pipes:

    codice:
    unit Redirect;
    
    interface
    
    uses
      Windows, SysUtils, Classes;
    
    type
      TRedirector = class;
      TPriorityClass = (pcDefault, pcIdle, pcNormal, pcHigh, pcRealtime);
      TDataEvent = procedure(Sender: TRedirector; Buffer: Pointer; Size: Integer) of object;
    
      TPipeError = record
        hRead: DWORD;
        hWrite: DWORD;
      end;
    
      TRedirector = class{$IFDEF COMPONENT}(TComponent){$ENDIF}
      private
        FAvailable: Integer;
        procedure ReadStdOutput;
        procedure ReadStdError;
        procedure ProcessTerminated;
      protected
        FProcessInfo: TProcessInformation;
        FExitCode: Integer;
        FExecutable: string;
        FCommandline: string;
        FDefaultErrorMode: Boolean;
        FStartSuspended: Boolean;
        FKillOnDestroy: Boolean;
        FDirectory: string;
        FEnvironment: Pointer;
        FInitialPriority: TPriorityClass;
        FPipeInput: TPipeError;
        FPipeOutput: TPipeError;
        FPipeError: TPipeError;
        FThread: TThread;
        FOnData: TDataEvent;
    
        FOnErrorData: TDataEvent;
        FOnTerminated: TNotifyEvent;
        FShowWindow: Integer;
        FLastData: Boolean;
        procedure Error(msg: string);
        procedure WinError(msg: string);
        procedure CreatePipes;
        procedure ClosePipes;
        function GetRunning: Boolean;
        function GetExitCode: Integer;
        function GetProcessID: Integer;
        function GetThreadID: Integer;
        function GetProcessHandle: Integer;
        procedure SetShowWindow(Value: Integer);
        function GetThreadHandle: Integer;
        procedure SetExecutable(Value: string);
        function GetCommandLine: string;
        procedure SetCommandLine(Value: string);
        procedure SetDefaultErrorMode(Value: Boolean);
        procedure SetStartSuspended(Value: Boolean);
        procedure SetInitialPriority(Value: TPriorityClass);
        procedure SetDirectory(Value: string);
        procedure SetEnvironment(Value: Pointer);
        property ProcessHandle: Integer read GetProcessHandle;
        property ThreadHandle: Integer read GetThreadHandle;
      public
        destructor Destroy; override;
        procedure Terminate(dwExitCode: Integer);
        procedure Execute;
    
        procedure SendData(Buffer: Pointer; BufferSize: Integer);
        procedure SendText(s: string);
        property Running: Boolean read GetRunning;
        property ExitCode: Integer read GetExitCode;
        property ProcessID: Integer read GetProcessID;
        property ThreadID: Integer read GetThreadID;
        property Environment: Pointer read FEnvironment write SetEnvironment;
        property LastData: boolean read FLastData;
      published
        property KillOnDestroy: Boolean read FKillOnDestroy write FKillOnDestroy;
        property Executable: string read FExecutable write SetExecutable;
        property CommandLine: string read GetCommandLine write SetCommandLine;
        property ShowWindow: Integer read FShowWindow write SetShowWindow default SW_SHOWDEFAULT;
        property DefaultErrorMode: Boolean read FDefaultErrorMode write SetDefaultErrorMode;
        property StartSuspended: Boolean read FStartSuspended write SetStartSuspended;
        property InitialPriority: TPriorityClass read FInitialPriority write SetInitialPriority;
        property Directory: string read FDirectory write SetDirectory;
        property OnData: TDataEvent read FOnData write FOnData;
    
        property OnErrorData: TDataEvent read FOnErrorData write FOnErrorData;
        property OnTerminated: TNotifyEvent read FOnTerminated write FOnTerminated;
      end;
    
    implementation
    
    const
      DUPLICATE_CLOSE_SOURCE = 1;
      DUPLICATE_SAME_ACCESS = 2;
    
    type
      TRedirectorThread = class(TThread)
      protected
        FRedirector: TRedirector;
        procedure Execute; override;
        constructor Create(ARedirector: TRedirector);
      end;
    
    ////////////////////////////////////////////////////////////////////////////////
    // Misc. internal methods
    ////////////////////////////////////////////////////////////////////////////////
    
    procedure TRedirector.Error(msg: string);
    begin
      raise Exception.Create(msg);
      TerminateProcess(ProcessHandle, 0)
    end;
    
    procedure TRedirector.WinError(msg: string);
    begin
      Error(msg + IntToStr(GetLastError));
    end;
    
    procedure TRedirector.CreatePipes;
    var
      SecAttr: TSecurityAttributes;
    const
      PIPE_SIZE = 0; //--was: 1024;
    begin
      SecAttr.nLength := SizeOf(SecAttr);
      SecAttr.lpSecurityDescriptor := nil;
      SecAttr.bInheritHandle := true;
    
      with FPipeInput do
      begin
        if not CreatePipe(hRead, hWrite, @SecAttr, PIPE_SIZE)
          then WinError('Error on STDIN pipe creation : ');
        if not DuplicateHandle(GetCurrentProcess, hRead, GetCurrentProcess,
          @hRead, 0, true, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS)
          then WinError('Error on STDIN pipe duplication : ');
      end;
      with FPipeOutput do
      begin
        if not CreatePipe(hRead, hWrite, @SecAttr, PIPE_SIZE)
          then WinError('Error on STDOUT pipe creation : ');
        if not DuplicateHandle(GetCurrentProcess, hWrite, GetCurrentProcess,
          @hWrite, 0, true, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS)
          then WinError('Error on STDOUT pipe duplication : ');
      end;
      with FPipeError do
      begin
        if not CreatePipe(hRead, hWrite, @SecAttr, PIPE_SIZE)
          then WinError('Error on STDERR pipe creation : ');
        if not DuplicateHandle(GetCurrentProcess, hWrite, GetCurrentProcess,
          @hWrite, 0, true, DUPLICATE_CLOSE_SOURCE or DUPLICATE_SAME_ACCESS)
          then WinError('Error on STDERR pipe duplication : ');
      end;
    end;
    
    procedure TRedirector.ClosePipes;
    begin
      with FPipeInput do
      begin
        if hRead <> 0 then CloseHandle(hRead);
        if hWrite <> 0 then CloseHandle(hWrite);
        hRead := 0;
        hWrite := 0;
      end;
      with FPipeOutput do
      begin
        if hRead <> 0 then CloseHandle(hRead);
        if hWrite <> 0 then CloseHandle(hWrite);
        hRead := 0;
        hWrite := 0;
      end;
      with FPipeError do
      begin
        if hRead <> 0 then CloseHandle(hRead);
        if hWrite <> 0 then CloseHandle(hWrite);
        hRead := 0;
        hWrite := 0;
      end;
    end;
    
    ////////////////////////////////////////////////////////////////////////////////
    // Property implementations
    ////////////////////////////////////////////////////////////////////////////////
    
    function TRedirector.GetRunning: Boolean;
    begin
      Result := ProcessHandle <> 0;
    end;
    
    function TRedirector.GetExitCode: Integer;
    begin
      if Running then
        Result := STILL_ACTIVE
      else
        Result := FExitCode;
    end;
    
    function TRedirector.GetProcessID: Integer;
    begin
      Result := FProcessInfo.dwProcessID;
    end;
    
    function TRedirector.GetThreadID: Integer;
    begin
      Result := FProcessInfo.dwThreadID;
    end;
    
    function TRedirector.GetProcessHandle: Integer;
    begin
      Result := FProcessInfo.hProcess;
    end;
    
    function TRedirector.GetThreadHandle: Integer;
    begin
      Result := FProcessInfo.hThread;
    end;
    
    procedure TRedirector.SetExecutable(Value: string);
    begin
      if (ANSICompareText(Value, Executable) = 0) or not Running
        then
        FExecutable := Value
      else if Running
        then
        Error('Cannot change Executable while process is active');
    end;
    
    procedure TRedirector.SetCommandLine(Value: string);
    begin
      if (ANSICompareText(Value, Commandline) = 0) or not Running
        then
        FCommandline := Value
      else if Running
        then
        Error('Cannot change Commandline while process is active');
    end;
    
    function TRedirector.GetCommandLine: string;
    begin
      Result := FExecutable;
      if Result = ''
        then
        Result := FCommandline
      else
        Result := FExecutable + ' ' + FCommandline;
    end;
    {
    procedure TRedirector.XSetCommandLine (value : string);
    var
      n1,
      n2      : integer;
    begin
      if (ANSICompareText (value, CommandLine) = 0) or (not Running) then begin
        n1 := Length(value)+1;
        n2 := Pos(' ', value);
        if (n2>0) and (n2<n1) then n1 := n2;
        n2 := Pos('-', value);
        if (n2>0) and (n2<n1) then n1 := n2;
        n2 := Pos('/', value);
        if (n2>0) and (n2<n1) then n1 := n2;
        FExecutable := Copy (value, 1, n1-1);
        //FCommandline := Copy (value, 1, n1-1);
       // FParameters := Trim(Copy (value, Length(FExecutable)+1, Length(value)));
        FCommandline := Trim(Copy (value, Length(FExecutable)+1, Length(value)));
      end else if Running then Error('Cannot change CommandLine while process is active');
    end;
    }
    
    procedure TRedirector.SetDefaultErrorMode(Value: Boolean);
    begin
      if (Value = DefaultErrorMode) or not Running then
        FDefaultErrorMode := Value
      else if Running then
    
        Error('Cannot change DefaultErrorMode while process is active');
    end;
    
    procedure TRedirector.SetStartSuspended(Value: Boolean);
    begin
      if (Value = DefaultErrorMode) or not Running then
        FStartSuspended := Value
      else if Running then
    
        Error('Cannot change StartSuspended while process is active');
    end;
    
    procedure TRedirector.SetInitialPriority(Value: TPriorityClass);
    begin
      if (Value <> InitialPriority) and not Running then
        FInitialPriority := Value
      else if Running then
    
        Error('Cannot change InititalPriority while process is active');
    end;
    
    procedure TRedirector.SetDirectory(Value: string);
    begin
      if (ANSICompareText(Value, Directory) = 0) or (not Running) then
        FDirectory := Value
      else if Running then
    
        Error('Cannot change Directory while process is active');
    end;
    
    procedure TRedirector.SetEnvironment(Value: Pointer);
    begin
      if (Value = Environment) or not Running then
        FEnvironment := Value
      else if Running then
    
        Error('Cannot change Environment while process is active');
    end;
    
    procedure TRedirector.SetShowWindow(Value: Integer);
    begin
      if (Value = ShowWindow) or not Running then
        FShowWindow := Value
      else if Running then
    
        Error('Cannot change ShowWindow while process is active');
    end;
    <continua>

  6. #6

    Seconda parte

    codice:
    procedure TRedirector.ReadStdOutput;
    var
      BytesRead: DWord;
      Buffer: Pointer;
    begin
      GetMem(Buffer, FAvailable);
      try
        if not ReadFile(FPipeOutput.hRead, Buffer^, FAvailable, BytesRead, nil) then
        begin
          FThread.Terminate;
          WinError('Error reading STDOUT pipe : ');
        end;
        if Assigned(FOnData) then
          FOnData(Self, Buffer, BytesRead);
      finally
        FreeMem(Buffer);
      end;
    end;
    
    procedure TRedirector.ReadStdError;
    var
      BytesRead: DWord;
      Buffer: Pointer;
    begin
      GetMem(Buffer, FAvailable);
      try
        if not ReadFile(FPipeError.hRead, Buffer^, FAvailable, BytesRead, nil) then
        begin
          FThread.Terminate;
          WinError('Error reading STDERR pipe : ');
        end;
        if Assigned(FOnErrorData) then
          FOnErrorData(Self, Buffer, BytesRead);
      finally
        FreeMem(Buffer);
      end;
    end;
    
    procedure TRedirector.ProcessTerminated;
    begin
      FThread.Terminate;
      ClosePipes;
      CloseHandle(FProcessInfo.hProcess);
      CloseHandle(FProcessInfo.hThread);
      FillChar(FProcessInfo, SizeOf(FProcessInfo), 0);
      if Assigned(FOnTerminated) then FOnTerminated(Self);  //-- moved from 2nd line to last line! (hdo, 2001-09-02)
    end;
    
    ////////////////////////////////////////////////////////////////////////////////
    // Public methods
    ////////////////////////////////////////////////////////////////////////////////
    
    procedure TRedirector.Terminate(dwExitCode: Integer);
    begin
      if Running
        then
        TerminateProcess(ProcessHandle, dwExitCode)
      else
        Error('Cannot Terminate an inactive process');
    end;
    
    procedure TRedirector.Execute;
    var
      StartupInfo: TStartupInfo;
      szExecutable, szCommandline, szDirectory: PChar;
      liPriorityClass: Integer;
    begin
      if Running then Error('Process is already active');
      if Trim(CommandLine) = '' then Error('No commandline to run');
      FLastData := false;
      try
        CreatePipes;
    
        FillChar(StartupInfo, SizeOf(StartupInfo), 0);
        StartupInfo.cb := SizeOf(StartupInfo);
    
        StartupInfo.wShowWindow := FShowWindow;
        StartupInfo.hStdInput := FPipeInput.hRead;
        StartupInfo.hStdOutput := FPipeOutput.hWrite;
        StartupInfo.hStdError := FPipeError.hWrite;
        StartupInfo.dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
    
        if Trim(Executable) = '' then
          szExecutable := nil
        else
          szExecutable := PChar(FExecutable);
        if Trim(Commandline) = '' then
          szCommandline := nil
        else
          szCommandline := PChar(FCommandline);
        if Trim(Directory) = '' then
          szDirectory := nil
        else
          szDirectory := PChar(FDirectory);
    
        liPriorityClass := 0;
        case FInitialPriority of
          pcIdle: liPriorityClass := IDLE_PRIORITY_CLASS;
          pcNormal: liPriorityClass := NORMAL_PRIORITY_CLASS;
          pcHigh: liPriorityClass := HIGH_PRIORITY_CLASS;
          pcRealtime: liPriorityClass := REALTIME_PRIORITY_CLASS;
        end;
    
        if CreateProcess(szExecutable, szCommandline, nil, nil, true,
          (CREATE_DEFAULT_ERROR_MODE and Integer(FDefaultErrorMode))
          or (CREATE_SUSPENDED and Integer(FStartSuspended) or liPriorityClass),
          Environment, szDirectory, StartupInfo, FProcessInfo)
          then
        begin
          FThread := TRedirectorThread.Create(Self);
        end
        else
          WinError('Error creating process : ');
      except
        on Exception do
        begin
          ClosePipes;
          CloseHandle(FProcessInfo.hProcess);
          CloseHandle(FProcessInfo.hThread);
          FillChar(FProcessInfo, SizeOf(FProcessInfo), 0);
          raise;
        end;
      end;
    end;
    
    procedure TRedirector.SendData(Buffer: Pointer; BufferSize: Integer);
    var
      BytesWritten: DWord;
    begin
      if not Running then Error('Can''t send data to an inactive process');
      if not WriteFile(FPipeInput.hWrite, Buffer^, BufferSize, BytesWritten, nil)
        then WinError('Error writing to STDIN pipe : ');
    end;
    
    procedure TRedirector.SendText(s: string);
    begin
      SendData(PChar(s), Length(s));
    end;
    
    destructor TRedirector.Destroy;
    begin
      if Running and KillOnDestroy then
      begin
        FOnTerminated := nil;
        FThread.Terminate;
        Terminate(0);
      end;
      inherited Destroy;
    end;
    
    constructor TRedirectorThread.Create(ARedirector: TRedirector);
    begin
      FRedirector := ARedirector;
      inherited Create(false);
    end;
    
    procedure TRedirectorThread.Execute;
    var
      Idle: Boolean;
    begin
      FreeOnTerminate := true;
      repeat
        Idle := true;
    
        //-- check for StdOutout-Pipe
        if PeekNamedPipe(FRedirector.FPipeOutput.hRead, nil, 0, nil,
          @FRedirector.FAvailable, nil) and (FRedirector.FAvailable > 0) then
        begin
          Synchronize(FRedirector.ReadStdOutput);
          Idle := false;
        end;
    
        //-- check for StdErr-Pipe
        if PeekNamedPipe(FRedirector.FPipeError.hRead, nil, 0, nil,
          @FRedirector.FAvailable, nil) and (FRedirector.FAvailable > 0) then
        begin
          Synchronize(FRedirector.ReadStdError);
          Idle := false;
        end;
    
        //-- check if process terminated
        if Idle and (WaitForSingleObject(FRedirector.ProcessHandle,
          100) = WAIT_OBJECT_0) then
        begin
          FRedirector.FLastData := true;
    
          //-- process has finished, read pipes until they are empty
          repeat
            Idle := true;
    
            //-- check for StdOutout-Pipe a last time
            if PeekNamedPipe(FRedirector.FPipeOutput.hRead, nil, 0, nil,
              @FRedirector.FAvailable, nil) and (FRedirector.FAvailable > 0) then
            begin
              Synchronize(FRedirector.ReadStdOutput);
              Idle := false;
            end;
    
            //-- check for StdErr-Pipe a last time
            if PeekNamedPipe(FRedirector.FPipeError.hRead, nil, 0, nil,
              @FRedirector.FAvailable, nil) and (FRedirector.FAvailable > 0) then
            begin
              Synchronize(FRedirector.ReadStdError);
              Idle := false;
            end;
    
          until Idle;
    
          //-- self-destruct
          if not Terminated then Synchronize(FRedirector.ProcessTerminated);
        end;
    
      until Terminated and Idle;
    end;
    
    end.
    Salva il tutto in un file redirect.pas ed usa i metodi della classe TRedirector.

  7. #7
    Moderatore di Programmazione L'avatar di LeleFT
    Registrato dal
    Jun 2003
    Messaggi
    17,320

    Moderazione

    Ho riunito le discussioni.


    Ciao.
    "Perchè spendere anche solo 5 dollari per un S.O., quando posso averne uno gratis e spendere quei 5 dollari per 5 bottiglie di birra?" [Jon "maddog" Hall]
    Fatti non foste a viver come bruti, ma per seguir virtute e canoscenza

Permessi di invio

  • Non puoi inserire discussioni
  • Non puoi inserire repliche
  • Non puoi inserire allegati
  • Non puoi modificare i tuoi messaggi
  •  
Powered by vBulletin® Version 4.2.1
Copyright © 2025 vBulletin Solutions, Inc. All rights reserved.