---
title: "Загребаем консольный вывод мохнатой рукой"
description: "Как-то я писал про ужасные беды и в том же посте предположил, что было бы очень занимательно автомат..."
author: "Pie_of_Sadness"
published: "2010-06-14T19:52:43+00:00"
modified: "2010-06-14T19:52:43+00:00"
locale: "ru"
canonical_url: "https://yvision.kz/post/zagrebaem-konsolnyy-vyvod-mohnatoy-rukoy-52885"
markdown_url: "https://yvision.kz/post/zagrebaem-konsolnyy-vyvod-mohnatoy-rukoy-52885/markdown"
site_name: "Yvision.kz"
---

# Загребаем консольный вывод мохнатой рукой

> Как-то я писал про ужасные беды и в том же посте предположил, что было бы очень занимательно автомат...

![Загребаем консольный вывод мохнатой рукой](https://storage.yvision.kz/images/user/pie_of_sadness/eYE5H4hYLp2iqznlZfd4K97S86l9QV.jpg)

Как-то я писал про [ужасные беды](http://pie_of_sadness.yvision.kz/blog/37376.html) и в том же посте предположил, что было бы очень занимательно автоматизировать процесс: большие мальчики против хэндджоба.

Для получения готового mkv-контейнера, dts трэк из источника должен был пройти через несколько консольных экзекуций.

Цикл обработки сводился к следующему:
- Определяем индекс трэка DTS

- Достаем его из контейнера в отдельный файл

- Конвертируем в двухканальный WAV

- Конвертируем WAV в MP3

- Склеиваем трэки из оригинального контейнера и получившийся MP3 файл, выбрав его дефолтным

- ????????

- Имеем ПРОФИТ

Для каждой задачи имеется отдельная шурудилка:
- mkvinfo.exe

- mkvextract.exe

- valdec.exe из комплекта AC3Tools

- lame.exe

- mkvmerge.exe

- chmod -r 777 /mnt/hands

- profit.exe

Для получения консольного вывода можно использовать файлы, выполняя команду "console_blah.exe >OutFileName.txt", но это моветон и никак не комильфо. Решено было использовать Pipes(далее пайпы). Пайпы - это хитрые каналы, которые и были разработаны для подобных гнусных целей.

В задачу не входило создание унифицированного класса, но без финта ушами было бы не интересно. В итоге получился базовый класс, в задачу которого входит создание процесса, пайпа и присоединение второго к первому.

``` Type TConsoleThread = class (Tthread) //создаем потомок класса TThread, тащемта поток private FFileName: string; // путь и имя консольной шурудилки FParamStr: string; // параметры запуска на орбиту FProcInfo: TProcessInformation; // при создании процесса получает информацию об оном FStartupInfo:TStartupInfo; // показывает CreateProcess информацию для запуска FSecurityAttr:TSecurityAttributes; // параметры безопасности для пайпа FReadHandle, FWriteHandle: THandle; // Хэндлы для пайпа FExitProc:TnotifyEvent; // ебаный стыд, который к тому же не работает, оставил для суровости procedure AppendOut(S:String); virtual; // процедура добавления буфера консольного вывода в общую кучу procedure SetExitProc(Value:TnotifyEvent); // опять ебаный стыд protected procedure Execute; override; // выполнение потока, запускается после создания public constructor Create(CreateSuspended: Boolean; const Filename,ParamStr: string); // создание потока property ExitProcedurePointer:TnotifyEvent read FExitProc write SetExitProc; // под конец все тот же ЁСend; // кончел ```

``` constructor TConsoleThread.Create(CreateSuspended: Boolean;const Filename,ParamStr: string); begin inherited Create(CreateSuspended); // родительская процедура создания класса FExitProc:=nil; // убираем ЁС FFileName:=Filename; // берем имя из параметров FParamStr:=ParamStr; // берем параметры из параметров (каламбурчик) with FSecurityAttr do begin nLength := SizeOf(TSecurityAttributes); lpSecurityDescriptor := nil; bInheritHandle := true; //делаем указатели наследуемыми end; CreatePipe(FReadHandle, FWriteHandle, @FSecurityAttr, 0); // создаем пайп SetHandleInformation(FReadHandle, HANDLE_FLAG_INHERIT, 0) ; // очередное наследование хэндла, зачем - неизвестно GetStartupInfo(FStartupInfo); // получаем структуру with FStartupInfo do begin wShowWindow := SW_HIDE; // не показывать консоль dwFlags := dwFlags or STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; // последнее важно для наследование хэндлов hStdOutput := FWriteHandle; // перенаправляем вывод в пайп hStdError := FWriteHandle; // ошибки туда же ( lame выводит информацию именно в hStdError) end; CreateProcess(pChar(FFileName),pChar(' '+FParamStr), nil, nil, true, CREATE_UNICODE_ENVIRONMENT, nil, nil, FStartupInfo, FProcInfo); // создаем процесс. помни, воен, что bInheritHandles:=true end; ```

Процесс и пайп создаются, все очень круто связывается друг с другом и, казалось, на этом можно и закончить, но нет, надо еще и буквы получать.

``` procedure TConsoleThread.Execute; var k:cardinal; // тут будет код завершения процесса ReadBuf:array[0..1000] of AnsiChar; // буфер для строки, аблизательно AnsiChar BytesRead,TotalB: Cardinal; // тут будет количество прочитаных байт и оставшихся в пайпе Finish:Boolean; begin repeat Sleep(10); FillChar(ReadBuf,High(ReadBuf),0); // очищаем буфер if PeekNamedPipe(FReadHandle, @ReadBuf[0], High(ReadBuf)-1, @BytesRead,@TotalB, nil) then // проверяем пайп на наличие информации. можно было сразу читать, но процедура чтения ассинхронная// и если информации в пайп не поступит то все повиснет как. повиснет вообщем. begin if TotalB>0 then // если есть что читать if ReadFile(FReadHandle, ReadBuf, High(ReadBuf)-1, BytesRead, nil) then // если прочитали if BytesRead > 0 then // и если даже есть буквы begin ReadBuf[BytesRead] := #0; // завершаем строку AppendOut(ReadBuf); // добавляем в кучу обзего вывода end; GetExitCodeProcess(FProcInfo.hProcess, k); // периодически опрашиваем процесс на живучесть Finish:=(k<>STILL_ACTIVE) and (TotalB=0); // если процесс завершился и в буферах кончились буквы // то мы на финише end; until Finish; // финиш ли CloseHandle(FProcInfo.hProcess); // кончил - оботри станок CloseHandle(FReadHandle);CloseHandle(FWriteHandle);end; ```

и даже после этого мы не получим заветных буков, потому что нет у нас процедуры которая добавляла бы их к общему выводу. А вот и она:

``` procedure TConsoleThread.AppendOut(S: string);begin // заглушкаend; ```

Сиё унылое говно имеет место быть лишь потому, что для данного класса необходимо только создать процесс, пайп, прилинковать их, прокрутить вывод и весело завершиться.

Для чтения создаем дочерний класс:

``` Type TConsoleOutputThread = class (TConsoleThread) private FStrings: TStrings; // будем тут строки хранить procedure SetOutput(Value: TStrings); // неиллюзорно привязываемся в компоненту на форме procedure AppendOut(S:String); override; // перекрывающая функция обработки буфера public property OutputStrings: TStrings read FStrings write SetOutput; // публичная привязка, о как end; ```

При создании нового экземпляра класса TConsoleOutputThread мы присвоим ему компонент, в который он будет скидывать буквы.

``` ParserOut:=TConsoleOutputThread.Create(false,ExtractFilePath(ParamStr(0))+MKVINFO,'"'+filename+'"'); //создаем экземпляр класса TConsoleOutputThreadParserOut.FreeOnTerminate:=true; // проперти из TThread указывает что после экзекуции процесс должен почистить за собойParserOut.OutputStrings:=Outp;// Outp был объявлен и создан раньше (КО). Вместо него можно сунуть Memo1.lines ```

Осталось только дописать процедуры привязки класса/компонента для вывода информации и собственно процедуру вывода в него строк (не хотит кодом вставлять, процитируем)

> procedure TConsoleOutputThread.SetOutput(Value: TStrings); begin FStrings:= Value; //привязываем компонент для вывода end; procedure TConsoleOutputThread.AppendOut(S: string); begin FStrings.Text:=FStrings.Text+S; // добавляем буфер к имеющемуся тексту end;

Как результат: получаем уютненький построчный вывод, причем не после завершения консольного приложения, а во время его выполнения.

В подробности вдаваться не буду, скажу только что долго рыл всяческие дельфикондом, кодерзком и мсдн. Только с мсдн что-то более менее прояснилось.

Полученную утилиту сразу применил в деле. Буду теперь смотреть грусный фильм про собачку на большом экране, рыдать и дергать пинус

![Загребаем консольный вывод мохнатой рукой](https://storage.yvision.kz/images/user/pie_of_sadness/shX4WMAZ5ka789dShVAt5056R0745D.jpg)

PS: Картинка в начале является де-факто бложиков, без нее на пост и смотреть бы не стали. Вот такое вероломное оно, первое что выдал яндекс на слово "консоль".

PS1: По-хорошему можно прилинковать вывод одной консольной программы к вводу другой, так все будет быстрее и многопоточно. но лень

---

Source: [https://yvision.kz/post/zagrebaem-konsolnyy-vyvod-mohnatoy-rukoy-52885](https://yvision.kz/post/zagrebaem-konsolnyy-vyvod-mohnatoy-rukoy-52885)