Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 12.05.2024, 02:23   #11
northener
ПШП
Участник клуба
 
Регистрация: 15.07.2013
Сообщений: 1,877
По умолчанию

Цитата:
Сообщение от hexor_boo Посмотреть сообщение
Причина глюка: если ping, например, выводит последовательно строчки в консоль, то 7z.exe при упаковке больших объёмов меняет значение в одной строчке консоли.
Ну тогда да. Мой код не поможет выводить прогресс, если Vapaamies прав в отношении
Цитата:
Сообщение от Vapaamies Посмотреть сообщение
похоже, 7-Zip определяет, выводится ли прогресс в экранную консоль или в файл, и в файл ничего не выводит
northener вне форума Ответить с цитированием
Старый 12.05.2024, 02:35   #12
hexor_boo
Разъяснятор
Форумчанин
 
Аватар для hexor_boo
 
Регистрация: 21.04.2022
Сообщений: 114
По умолчанию

Нашёл вот тут принципиально другое решение (переделал под себя), по сравнению с большинством описанных в интернете.

Код:
Unit unMethods;

Interface
 Uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls;

 Type

  TThreadConsole=Class(TThread)
   Private
    Caption: String;
     Procedure UpdateCaption;
   Protected
    Procedure Execute; Override;
  End;

 // writes InStr to the pipe handle described by OutputPipe
 Procedure WritePipeOut(OutputPipe : THandle; InStr : String);

 // reads console output from InputPipe.  Returns the input in function
 // result.  Returns bytes of remaining information to BytesRem
 Function ReadPipeInput(InputPipe : THandle; Var BytesRem : Integer) : String;

 Procedure ConsoleExecute(strCommand : String);
 Procedure ConsoleTerminate;

 Function Win2Dos(Const aStr: String): String;
 Function Dos2Win(Const aStr: String): String;

 Var
  InputPipeRead   : THandle;
  InputPipeWrite  : THandle;
  OutputPipeRead  : THandle;
  OutputPipeWrite : THandle;
  ErrorPipeRead   : THandle;
  ErrorPipeWrite  : THandle;
  ProcessInfo     : TProcessInformation;
  thrdConsole     : TThreadConsole;
  
Implementation

 Uses
  unMain;

 // writes InStr to the pipe handle described by OutputPipe
 Procedure WritePipeOut(OutputPipe : THandle; InStr : String);
  Var
   BytesWritten : DWord;
  Begin
   // most console programs require CR/LF after their input.
   InStr:=InStr+#13#10;
   WriteFile(OutputPipe, InStr[1], Length(InStr), BytesWritten, Nil);
  End;

 // reads console output from InputPipe.  Returns the input in function
 // result.  Returns bytes of remaining information to BytesRem
 Function ReadPipeInput(InputPipe : THandle; Var BytesRem : Integer) : String;
  Var
   TextBuf   : Array [1..32767] Of Char;
   TextStr   : String;
   BytesRead : Integer;
   PipeSize  : Integer;
  Begin
   Result:='';
   PipeSize:=SizeOf(TextBuf);

   // check if there is something to read in pipe
   PeekNamedPipe(InputPipe, Nil, PipeSize, @BytesRead, @PipeSize, @BytesRem);

    If (BytesRead>0) then
     Begin
      ReadFile(
       InputPipe, TextBuf, Cardinal(PipeSize), Cardinal(BytesRead), Nil
      );
      // a requirement for Windows OS system components
      OemToChar(@TextBuf, @TextBuf);
      TextStr:=String(TextBuf);
      SetLength(TextStr, BytesRead);
      Result:=TextStr;
     End;
     
  End;

 Procedure ConsoleExecute(strCommand : String);
  Var
   saSecurity : TSecurityAttributes;
   siStart    : TStartupInfo;
  Begin

   // create pipes
    With saSecurity Do
     begin
      nLength:=SizeOf(TSecurityAttributes);
      bInheritHandle:=True;
      lpSecurityDescriptor:=Nil;
     End;
   CreatePipe(InputPipeRead, InputPipeWrite, @saSecurity, 0);
   CreatePipe(OutputPipeRead, OutputPipeWrite, @saSecurity, 0);
   CreatePipe(ErrorPipeRead, ErrorPipeWrite, @saSecurity, 0);

   // start console
   FillChar(siStart, SizeOf(siStart),#0) ;
   siStart.CB:=SizeOf(siStart) ;
   siStart.hStdInput:=InputPipeRead;
   siStart.hStdOutput:=OutputPipeWrite;
   siStart.hStdError:= ErrorPipeWrite;
   siStart.dwFlags:=STARTF_USESTDHANDLES+STARTF_USESHOWWINDOW;
   siStart.wShowWindow:=SW_HIDE;

    If CreateProcess(
     Nil,
     PChar(strCommand),
     @saSecurity,
     @saSecurity,
     True,
     CREATE_NEW_CONSOLE Or SYNCHRONIZE,
     Nil,
     Nil,
     siStart,
     ProcessInfo
    ) Then
     Begin
      thrdConsole:=TThreadConsole.Create(False);  // start monitor thread
      thrdConsole.Priority:=tpHigher;
     End;

   //WritePipeOut(InputPipeWrite, strCommand+#13#10);
   frmMain.btnRun.Enabled:=False;
   frmMain.btnExit.Enabled:=True;
  End;

 Procedure ConsoleTerminate;
  Begin
   thrdConsole.Terminate;
   // close process handles
   CloseHandle(ProcessInfo.hProcess);
   CloseHandle(ProcessInfo.hThread);
   // close pipe handles
   CloseHandle(InputPipeRead);
   CloseHandle(InputPipeWrite);
   CloseHandle(OutputPipeRead);
   CloseHandle(OutputPipeWrite);
   CloseHandle(ErrorPipeRead);
   CloseHandle(ErrorPipeWrite);

   frmMain.btnRun.Enabled:=True;
   frmMain.btnExit.Enabled:=False;
  End;

 // monitor thread execution for console output.  This must be threaded.
 // checks the error and output pipes for information every 40 ms, pulls the
 // data in and updates the memo on the form with the output
 Procedure TThreadConsole.Execute;
  Var
   BytesRem : DWord;
  Begin
    While (Not Terminated) Do
     Begin
      // read regular output stream and put on screen.
      Caption:=ReadPipeInput(OutputPipeRead, Integer(BytesRem));
       If (Caption<>'') Then
        Synchronize(UpdateCaption);

      // now read error stream and put that on screen.
      Caption:=ReadPipeInput(ErrorPipeRead, Integer(BytesRem));
       If (Caption<>'') then
        Synchronize(UpdateCaption);

      Sleep(40);
     End;
  End;

 // synchronize procedure for monitor thread - updates memo on form.
 Procedure TThreadConsole.UpdateCaption;
  Begin
   frmMain.memLog.Lines.Add(Caption);
  End;

 Function Win2Dos(Const aStr: String): String;
  Begin
   Result:=aStr;
    If (Result<>'') Then
     CharToOem(PChar(Result), PChar(Result));
  End;

 Function Dos2Win(const aStr: String): String;
  Begin
   Result := aStr;
    If (Result<>'') Then
     OemToChar(PChar(Result), PChar(Result));
  End;

End.
Вызываю так
Код:
procedure TfrmMain.btnRunClick(Sender: TObject);
begin
 ConsoleExecute(
  '"D:\Programs\7-Zip\7z.exe"'+
  ' a "D:\Data\Projects\Bell-Port\ConsoleToMemo\backup\Test.zip"'+
  ' "D:\Data\Projects\Bell-Port\ConsoleToMemo\source\*"'
 );
end;

procedure TfrmMain.btnExitClick(Sender: TObject);
begin
 ConsoleTerminate;
end;
Поток мониторит с интервалом 40мс вывод консоли, если я правильно понял. Такое ощущение, что я в шаге от правильного решения. А вы как думаете?
hexor_boo вне форума Ответить с цитированием
Старый 12.05.2024, 03:04   #13
hexor_boo
Разъяснятор
Форумчанин
 
Аватар для hexor_boo
 
Регистрация: 21.04.2022
Сообщений: 114
По умолчанию

Одновременно с запуском cmd.exe или 7z.exe запускается conhost.exe

Про этот процесс:
Цитата:
Windows 7 улучшена таким образом что
визуальные окна консоли обрабатывает сама операционная система. В
предыдущих версиях Windows процесс консоли работал под управлением
процесса csrss.exe (Client Server Runtime Process). Он запускался от системной привилегированной учетной записи.
hexor_boo вне форума Ответить с цитированием
Старый 12.05.2024, 05:32   #14
hexor_boo
Разъяснятор
Форумчанин
 
Аватар для hexor_boo
 
Регистрация: 21.04.2022
Сообщений: 114
По умолчанию

При запуске "консольного" приложения запускается conhost.exe, который фактически отвечает за работу консоли. То есть то, что я вижу в окне cmd - реально результат 7z.exe > conhost.exe > экран. Соответственно 7z отправляет по какому-то особому каналу отображение процентов упаковки процессу conhost, который уже выводит его в консоль cmd. Соответственно нужно либо выявить канал, по которому 7z отправляет проценты, либо перехватить их из conhost.exe.

Верные размышления?
hexor_boo вне форума Ответить с цитированием
Старый 12.05.2024, 16:42   #15
Vapaamies
Ваш К. О.
Участник клуба
 
Аватар для Vapaamies
 
Регистрация: 26.12.2012
Сообщений: 1,792
По умолчанию

Цитата:
Сообщение от hexor_boo Посмотреть сообщение
Соответственно нужно либо выявить канал, по которому 7z отправляет проценты, либо перехватить их из conhost.exe.
Ага, ага. А потом браться за разработку собственной ОС. Или антивируса. Или хотя бы вируса...

Цитата:
Сообщение от hexor_boo Посмотреть сообщение
Верные размышления?
Нет, неверные. Вот верные:
Код:
 function ProgressCallback(sender: Pointer; total: boolean; value: int64): HRESULT; stdcall;
 begin
   if total then
     Mainform.ProgressBar.Max := value else
     Mainform.ProgressBar.Position := value;
   Result := S_OK;
 end;

 procedure TMainForm.ExtractClick(Sender: TObject);
 begin
   with CreateInArchive(CLSID_CFormatZip) do
   begin
     OpenFile('c:\test.zip');
     SetProgressCallback(nil, ProgressCallback);
     ...
   end;
 end;
Там один модуль-обертка, никаких компонентов ставить не надо. Куда уж проще-то? Я бы уже 10 раз попробовал, а вы какой-то самопал с перехватами городите. Цель — решить задачу или навелосипедить костылей?
Vapaamies вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Отмена при CTRL+С закрытия консоли Tahoma Общие вопросы C/C++ 8 31.07.2017 17:47
После ошибки окно консоли закрывается и я не могу посмотреть, что за ошибка. NAN_13 Общие вопросы Delphi 10 30.03.2017 01:29
Вывод из консоли в Memo hotcooler17 Win Api 3 05.02.2010 20:51
Читает вывод из консоли Consol Win Api 10 31.08.2009 08:42
Как захватить весь вывод в консоли??? alexfmf Общие вопросы Delphi 5 13.05.2009 21:54