• 74392
  • 562
  • 43
Нравится блог?
Подписывайтесь!

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

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

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

Цикл обработки сводился к следующему:

  1. Определяем индекс трэка DTS
  2. Достаем его из контейнера в отдельный файл
  3. Конвертируем в двухканальный WAV
  4. Конвертируем WAV в MP3
  5. Склеиваем трэки из оригинального контейнера и получившийся MP3 файл, выбрав его дефолтным
  6. ????????
  7. Имеем ПРОФИТ
Для каждой задачи имеется отдельная шурудилка:
  1. mkvinfo.exe
  2. mkvextract.exe
  3. valdec.exe из комплекта AC3Tools
  4. lame.exe
  5. mkvmerge.exe
  6. chmod -r 777 /mnt/hands
  7. profit.exe

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

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

 

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

 

 

  1. constructor TConsoleThread.Create(CreateSuspended: Boolean;const Filename,ParamStr: string);
  2. begin
  3. inherited Create(CreateSuspended); // родительская процедура создания класса
  4. FExitProc:=nil; // убираем ЁС
  5. FFileName:=Filename; // берем имя из параметров
  6. FParamStr:=ParamStr; // берем параметры из параметров (каламбурчик)
  7. with FSecurityAttr do
  8. begin
  9. nLength := SizeOf(TSecurityAttributes);
  10. lpSecurityDescriptor := nil;
  11. bInheritHandle := true; //делаем указатели наследуемыми
  12. end;
  13. CreatePipe(FReadHandle, FWriteHandle, @FSecurityAttr, 0); // создаем пайп
  14. SetHandleInformation(FReadHandle, HANDLE_FLAG_INHERIT, 0) ; // очередное наследование хэндла, зачем - неизвестно
  15. GetStartupInfo(FStartupInfo); // получаем структуру
  16. with FStartupInfo do
  17. begin
  18. wShowWindow := SW_HIDE; // не показывать консоль
  19. dwFlags := dwFlags or STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES; // последнее важно для наследование хэндлов
  20. hStdOutput := FWriteHandle; // перенаправляем вывод в пайп
  21. hStdError := FWriteHandle; // ошибки туда же ( lame выводит информацию именно в hStdError)
  22. end;
  23. CreateProcess(pChar(FFileName),pChar(' '+FParamStr), nil, nil, true, CREATE_UNICODE_ENVIRONMENT,
  24. nil, nil, FStartupInfo, FProcInfo); // создаем процесс. помни, воен, что bInheritHandles:=true
  25. end;

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

 

  1. procedure TConsoleThread.Execute;
  2. var
  3. k:cardinal; // тут будет код завершения процесса
  4. ReadBuf:array[0..1000] of AnsiChar; // буфер для строки, аблизательно AnsiChar
  5. BytesRead,TotalB: Cardinal; // тут будет количество прочитаных байт и оставшихся в пайпе
  6. Finish:Boolean;
  7. begin
  8. repeat
  9. Sleep(10);
  10. FillChar(ReadBuf,High(ReadBuf),0); // очищаем буфер
  11. if PeekNamedPipe(FReadHandle, @ReadBuf[0], High(ReadBuf)-1, @BytesRead,@TotalB, nil) then
  12. // проверяем пайп на наличие информации. можно было сразу читать, но процедура чтения ассинхронная
  13. // и если информации в пайп не поступит то все повиснет как. повиснет вообщем.
  14. begin
  15. if TotalB>0 then // если есть что читать
  16. if ReadFile(FReadHandle, ReadBuf, High(ReadBuf)-1, BytesRead, nil) then // если прочитали
  17. if BytesRead > 0 then // и если даже есть буквы
  18. begin
  19. ReadBuf[BytesRead] := #0; // завершаем строку
  20. AppendOut(ReadBuf); // добавляем в кучу обзего вывода
  21. end;
  22. GetExitCodeProcess(FProcInfo.hProcess, k); // периодически опрашиваем процесс на живучесть
  23. Finish:=(k<>STILL_ACTIVE) and (TotalB=0); // если процесс завершился и в буферах кончились буквы
  24. // то мы на финише
  25. end;
  26. until Finish; // финиш ли
  27. CloseHandle(FProcInfo.hProcess); // кончил - оботри станок
  28. CloseHandle(FReadHandle);
  29. CloseHandle(FWriteHandle);
  30. end;

 

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

 

  1. procedure TConsoleThread.AppendOut(S: string);
  2. begin
  3. // заглушка

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

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

 

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

 

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

 

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

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

procedure TConsoleOutputThread.SetOutput(Value: TStrings);
begin
FStrings:= Value; //привязываем компонент для вывода
end;

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

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

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

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

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

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

15 июня 2010, 7:52
872

Загрузка...
Loading...

Комментарии

faiwer
0
0
Хех, значит не 1 я такой любитель всё автоматизировать на делфях )))
нас много, и лень - наш основной двигатель=)

Оставьте свой комментарий

Спасибо за открытие блога в Yvision.kz! Чтобы убедиться в отсутствии спама, все комментарии новых пользователей проходят премодерацию. Соблюдение правил нашей блог-платформы ускорит ваш переход в категорию надежных пользователей, не нуждающихся в премодерации. Обязательно прочтите наши правила по указанной ссылке: Правила

Также можно нажать Ctrl+Enter

Популярные посты

Дорога Алматы – Хоргос. Впечатления от первой поездки

Дорога Алматы – Хоргос. Впечатления от первой поездки

Если раньше путь до Хоргоса занимал около 5 часов по Кульджинской трассе, то сейчас время до границы с Китаем сокращается до 2,5-3 часов. Уже сейчас можно ехать по 4-полосной дороге с цементобетонным покрытием.
theYakov
15 сент. 2017 / 9:35
  • 5685
  • 13
Работа в Mc Donald's. Советы и лайфхаки для гостей

Работа в Mc Donald's. Советы и лайфхаки для гостей

За три месяца я поработала в трёх разных сферах. И сегодня, хочу поведать вам о работе в одной из богатейших компаний мира. Звучит значительно лучше, чем «Я работаю в Mc Donald’s».
madiNAtty
15 сент. 2017 / 12:44
  • 3126
  • 16
Почему депутат Божко хочет ограничить приток сельской молодёжи в города?

Почему депутат Божко хочет ограничить приток сельской молодёжи в города?

Вице-спикер Мажилиса Владимир Божко остановился на некоторых деталях известного конфликта в Астане возле строящегося «Абу Даби Плаза», затронув проблему «многочисленной сельской молодёжи»...
openqazaqstan
19 сент. 2017 / 13:16
  • 2423
  • 64
«Моя депрессия длилась больше двух лет». История о том, как я была готова сдаться

«Моя депрессия длилась больше двух лет». История о том, как я была готова сдаться

По данным статистики 2017 года Казахстан занимает 4 место по самоубийствам. Говорить об этом - социальное табу. Мы игнорируем разговоры о депрессиях. Потому что нам страшно.
goribaldi
18 сент. 2017 / 15:45
  • 2345
  • 68
«Это будут твои большие похороны». Гуф высказался о Скриптоните и Басте

«Это будут твои большие похороны». Гуф высказался о Скриптоните и Басте

Досталось и Скриптониту, который ранее опубликовал запись, о том, что больше не будет сниматься в клипе Гуфа и Тимати. "Я тебя ни о чем больше не попрошу, казах ты мой дорогой" - высказался рэпер.
tala03
вчера / 15:48
  • 1870
  • 7
«Папина принцесса» или «обезьяна с гранатой»? Случай в бассейне

«Папина принцесса» или «обезьяна с гранатой»? Случай в бассейне

Мужчины спокойно подчиняются логичным правилам, большинство женщин пытаются выторговать себе особые условия. Я убеждалась в этом много раз.
ValentinaVladimirska
18 сент. 2017 / 14:25
  • 1898
  • 34
Лайфхаки для путешественников. Как я не заплатила ни цента за проживание в Испании

Лайфхаки для путешественников. Как я не заплатила ни цента за проживание в Испании

Бесплатное проживание и завтрак в Испании, как прокачать иностранный язык и где поймать бесплатные ништяки в Европе. Если вы backpacker, который пересекает границу разных стран каждую неделю -...
dianaobyrne
18 сент. 2017 / 12:16
  • 2417
  • 1
Бой Головкина и Альвареса завершился ничьей

Бой Головкина и Альвареса завершился ничьей

Канело согласен на реванш, что неудивительно, Гена тоже "за". Все это было немного ожидаемо - эксперты отмечали, что промоутеры надавят на поединок, и захотят растянуть его на трилогию.
Trequartista
17 сент. 2017 / 0:15
  • 2178
  • 17
Мой опыт вегетарианства: «Я ела овощи, и сама стала как овощ»

Мой опыт вегетарианства: «Я ела овощи, и сама стала как овощ»

Три года назад начитавшись-наслушавшись кое-каких знаменитых людей, я решила завязать  с абсолютно любым мясом. Как же возмущались все мои близкие, это надо было видеть, но я была непреклонна.
Bonittta
18 сент. 2017 / 14:33
  • 1746
  • 30