ВВОД/ВЫВОД cmd.exe через PIPES в DELPHI

Discussion in 'С/С++, C#, Rust, Swift, Go, Java, Perl, Ruby' started by Sc0rpi0n, 31 Aug 2010.

  1. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    Помогите пожалуйсто, бъюсь целый месяц, ни на одном форуме не смогли помочь.
    Задача програмы выполнять консольные комманды и захватывать их вывод.

    вот мой код

    PHP:
    program Project1;

    {
    $APPTYPE CONSOLE}

    uses
      SysUtils
    ,Windows;
    var
      
    SecurityAttributesTSecurityAttributes;
      
    ProcessInfoTProcessInformation;
      
    StartUpInfoTStartupInfo;
      
    newstdin,newstdout,read_stdout,write_stdin,hConsoleInputTHandle;
      
    Buffer1,Buffer2:  array[0..1033of Char;
      
    BytesRead,BytesAvail,NumReadCardinal;
      
    InputRecTInputRecord;
      
    BuffLenInteger;
    begin
      SecurityAttributes
    .nLength:=SizeOf(TStartUpInfo);
      
    SecurityAttributes.bInheritHandle:=True;
      
    SecurityAttributes.lpSecurityDescriptor:=nil;
      
    CreatePipe(newstdin,write_stdin,@SecurityAttributes,0);
      
    CreatePipe(read_stdout,newstdout,@SecurityAttributes,0);
      
    ZeroMemory(@StartUpInfo,SizeOf(TStartUpInfo));
      
    StartUpInfo.cb:=SizeOf(TStartUpInfo);
      
    StartUpInfo.dwFlags:=STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
      
    StartUpInfo.hStdInput:=newstdin;
      
    StartUpInfo.hStdOutput:=newstdout;
      
    StartUpInfo.hStdError:=newstdout;
      
    CreateProcess('C:\\Windows\\System32\\cmd.exe',nil,nil,nil,True,0,nil,nil,StartUpInfo,ProcessInfo);
      
    hConsoleInput:=GetStdHandle(STD_INPUT_HANDLE);
      while 
    True do
      
    begin
        PeekNamedPipe
    (read_stdout,@Buffer1,1023,@BytesRead,@BytesAvail,nil);
        if 
    BytesRead <> 0 then
        begin
          ZeroMemory
    (@Buffer1,SizeOf(Buffer1));
          if 
    BytesAvail 1023 then
          begin
            
    while BytesRead >= 1023 do
            
    begin
              ReadFile
    (read_stdout,Buffer1,SizeOf(Buffer1),BytesRead,nil);
              
    Buffer1[BytesRead]:=#13;
              
    Buffer1[BytesRead]:=#10;
              
    Write(Buffer1);
              
    ZeroMemory(@Buffer1,SizeOf(Buffer1));
            
    end;
          
    end
          
    else
          
    begin
            ReadFile
    (read_stdout,Buffer1,1023,BytesRead,nil);
            
    Buffer1[BytesRead]:=#13;
            
    Buffer1[BytesRead+1]:=#10;
            
    Write(Buffer1);
            
    ZeroMemory(@Buffer1,SizeOf(Buffer1));
          
    end;
        
    end;
        if 
    PeekConsoleInput(hConsoleInput,InputRec,1,NumRead) and (NumRead >=1then
          begin
          
    if (InputRec.EventType KEY_EVENT) and InputRec.Event.KeyEvent.bKeyDown then
          begin
            Read
    (Buffer2);
            
    Bufflen:=Length(Buffer2);
            
    Buffer2[Bufflen]:=#13;
            
    Buffer2[Bufflen+1]:=#10;
            
    WriteFile(write_stdin,Buffer2,Length(Buffer2),BytesRead,nil); 
          
    end
          
    else
            
    begin
              ReadConsoleInput
    (hConsoleInputInputRec1,NumRead);
              
    ZeroMemory(@Buffer2,SizeOf(Buffer2));
            
    end;
          
    end;
      
    end;
      
    CloseHandle(ProcessInfo.hThread);
      
    CloseHandle(ProcessInfo.hProcess);
      
    CloseHandle(newstdin);
      
    CloseHandle(newstdout);
      
    CloseHandle(read_stdout);
      
    CloseHandle(write_stdin);
    end.
    Проблемма со вводом в консоль, пробовал много вариантов.

    Проблема наверно в этом куске кода

    PHP:
          if (InputRec.EventType KEY_EVENT) and InputRec.Event.KeyEvent.bKeyDown then
          begin
            Read
    (Buffer2);
            
    Bufflen:=Length(Buffer2);
            
    Buffer2[Bufflen]:=#13;
            
    Buffer2[Bufflen+1]:=#10;
            
    WriteFile(write_stdin,Buffer2,Length(Buffer2),BytesRead,nil); 
          
    end
          
    else
            
    begin
              ReadConsoleInput
    (hConsoleInputInputRec1,NumRead);
              
    ZeroMemory(@Buffer2,SizeOf(Buffer2));
            
    end;
          
    end;
    программа не корректо работает, не принимает ввод, тоесть принимает но както через жопу ,выполняет токо первую комманду и то через попу, а потом вообще пише "Продолжить?" и виснет.

    скриншот прилагаю

    [​IMG]

    п.с. надеюсь slesh поможет.))
     
  2. slesh

    slesh Elder - Старейшина

    Joined:
    5 Mar 2007
    Messages:
    2,702
    Likes Received:
    1,224
    Reputations:
    455
    Я вот чтото недопер одного. А зачем такая сложность? Тыбы лучше описал что тебе именно надо.
    Зачем пайпы юзать? Винда и так делает напрямую передачу данных между хендлами.
    Пайпы нужны тока для того, когда ты будешь делать ввод и вывод в приложение которое не имеет собственных хендлов ввода-вывода т.е. не консольное.
    Или когда тебе программно надо посылать данные, то тоже надо их юзать.

    А когда у тебя есть уже консоль, то её хендлы ты можеш юзать для этих целей.
    Проще говоря это выглядит так:
    Code:
     program Project2;
    
    {$APPTYPE CONSOLE}
    
    uses
      Windows;
    
    
    function RunCMD(stdin, stdout, stderr: THandle) : THandle;
    var
      pi : TProcessInformation;
      si : TStartupInfo;
    begin
      ZeroMemory(@si, SizeOf(TStartupInfo));
      si.cb := SizeOf(TStartupInfo);
    // зададим хендлы ввода/вывода
      si.hStdInput := stdin;
      si.hStdOutput := stdout;
      si.hStdError := stderr;
      si.dwFlags := STARTF_USESTDHANDLES or STARTF_USESHOWWINDOW;
      si.wShowWindow := SW_HIDE;
    
    // запустим процесс
      if (CreateProcess(nil, 'cmd.exe', nil, nil, true, 0, nil, nil, si, pi)) then
      begin
        result := pi.hProcess; // если запустился то вернем хендл процесса
      end
      else
      begin
        result := INVALID_HANDLE_VALUE; // если не запустился то ошибку
      end;
    end;
    
    var
      ph : THandle;
      stdin : THandle;
      stdout : THandle;
      stderr : THandle;
    begin
      stdin := GetStdHandle(STD_INPUT_HANDLE);  // хендл ввода
      stdout := GetStdHandle(STD_OUTPUT_HANDLE); // хендл вывода
      stderr := GetStdHandle(STD_ERROR_HANDLE); // хендл ошибки
    
      writeln('Start Process');
      ph := RunCMD(stdin, stdout, stderr); // запускаем процесс
      if (ph <> INVALID_HANDLE_VALUE) then  // если запустился нормально
      begin
        WaitForSingleObject(ph, INFINITE); // ждем пока процесс работает
      end;
      writeln('Stop Process');
      readln;
    end.
    
     
  3. z0mbyak

    z0mbyak Active Member

    Joined:
    10 Apr 2010
    Messages:
    536
    Likes Received:
    200
    Reputations:
    293
    На дельфях не пишу, на вб 6.0(на котором я писАл) работа с хендлами тоже из разряда научной фантастики, поэтому это было реализовано через маленький финт: всю выводимую на экран инфу после выполнения команды - записываешь в текстовый файл путем добавления к команде > Путь_к_файлу\файл, затем просто считываешь его в текстбокс или еще куда...
    P.S. Подводные грабли - обязательно будут глюки с кодировкой, но это решается...
    chcp 866 :)
     
  4. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    slesh, спасибо за ответ. Но я в упор не вижу где происходит ввод/вывод.
    Мне надо. Запустить процесс, получить вывод. Потом сделать ввод, затем считать вывод. и так постоянно. объясните пожалуйсто как это реализовать.
     
  5. alexey-m

    alexey-m Elder - Старейшина

    Joined:
    15 Jul 2009
    Messages:
    518
    Likes Received:
    100
    Reputations:
    37
    Дескрипторы, возвращенные функцией GetStdHandle, могут быть использованы программой для чтения или записи в консоль функциями ReadFile и WriteFile, или любой из консольных функций, которые обращаются к консольному буферу ввода или экранному буферу (например, функциям ReadConsoleInput, WriteConsole, или GetConsoleScreenBufferInfo).

    Вот и используй дескрипторы stdin, stdout, stderr для чтения\записи при успешном создании процесса.
     
    #5 alexey-m, 12 Sep 2010
    Last edited: 12 Sep 2010
  6. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    slesh, alexey - спасибо!

    всё работает, просто я не протестировал был!
    Slesh, а почему консоль дублируется??? как это убрать?

    Посоветуйте плиз как доделать прогу под сетевое использование. т.е. клиент посылвает серверу команды, а сервер отсылает вывод. Токо не надо предлогать юзать сокеты как пайпы.
     
  7. Sc0rpi0n

    Sc0rpi0n Banned

    Joined:
    23 Feb 2010
    Messages:
    75
    Likes Received:
    22
    Reputations:
    16
    короче мне надо удалённо выполнять команды и получать вывод!

    slesh, я тебя умалаю, помогите пожалуйсто. ты один на этом свете кто может помочь!

    я шас б**ть провалюсь! ни**я не получается!!! это пи**ец просто какойто. у меня уже истерека месяц над этим е**сь.... блин ((((((((((((((((((((((((((((((((((((((((((((
    форум пхпшиников недотраханых, никто ничего не знает...
     
    #7 Sc0rpi0n, 12 Sep 2010
    Last edited by a moderator: 13 Sep 2010