• 70981
  • 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
835

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

Комментарии

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

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

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

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

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

«Предатели Русского мира». Кто учит нас, какими буквами писать и чью культуру любить

«Предатели Русского мира». Кто учит нас, какими буквами писать и чью культуру любить

Истерический вой, поднявшийся в российских СМИ по поводу перевода казахского языка на латиницу, блогер и публицист из Казани Римзиль Валеев оценил довольно-таки точно, на наш взгляд.
openqazaqstan
18 апр. 2017 / 16:47
  • 33145
  • 261
Школа High Tech High – дети здесь самостоятельны так же, как и профессионалы в офисах

Школа High Tech High – дети здесь самостоятельны так же, как и профессионалы в офисах

Известный казахстанский бизнесмен становится первопроходцем и создает школу, о которой можно только мечтать. То, что мы, как взрослые, делаем в бизнесе, дети будут делать в этой школе.
Zhumanova
вчера / 16:29
  • 11719
  • 3
Как выбрать новостройку в Алматы: инсайты, критерии и личный опыт

Как выбрать новостройку в Алматы: инсайты, критерии и личный опыт

Я прекрасно понимаю тех, кто долго ищет подходящую квартиру - сама искала, потратила на поиск 1,5 года. Что, прежде всего, повлияло на мой выбор при покупке квартиры в новостройке Алматы?
Zhumanova
21 апр. 2017 / 10:04
  • 3419
  • 10
«А ты покушала?» или Как завоевать сердце женщины простой заботой

«А ты покушала?» или Как завоевать сердце женщины простой заботой

Так как опыт хождения по свиданиям у меня немаленький, да и с интуицией давно дружу, я быстро отсеиваю тех, с кем будут проблемы. Как я это определяю? Очень просто.
Bonittta
22 апр. 2017 / 14:10
«Третья мировая война начнётся в мае 2017 года» – провидец из Португалии

«Третья мировая война начнётся в мае 2017 года» – провидец из Португалии

Провидец из Португалии по имени Горацио Вильегас считает, что новая мировая война состоится уже в текущем году. Считается, что португалец два года назад объявил, что Трамп станет президентом.
Seattle
21 апр. 2017 / 15:33
  • 3000
  • 12
Меня вывели из себя ученики 8 класса. Я осознала, что не хочу быть учителем

Меня вывели из себя ученики 8 класса. Я осознала, что не хочу быть учителем

Когда я пришла в первый раз на работу, слышала как доносятся крики из разных кабинетов. Крики это были не детей, а учителей, которые не могут сдержаться, когда уже все силы иссякли...
teacher1210
19 апр. 2017 / 7:56
  • 2656
  • 61
Депутат сената Айтимова: «Не нужно критиковать депутатов, так как вы сами их избирали»

Депутат сената Айтимова: «Не нужно критиковать депутатов, так как вы сами их избирали»

Наши депутаты известны не достижениями и работой, а своими высказываниями. Сегодня в СМИ вышла новость, где депутат сената Бырганым Айтимова ответила на критику народа.
tala03
20 апр. 2017 / 13:44
  • 2448
  • 20
Стихия всё спишет. Почему дамба за 200 миллионов никого не спасла?

Стихия всё спишет. Почему дамба за 200 миллионов никого не спасла?

Наводнение в Атбасаре, размытые дороги, подтопленные сёла по всему Центральному и Северному Казахстану... Что происходит?
openqazaqstan
19 апр. 2017 / 15:20
  • 2429
  • 14
Кому в Казахстане жить хорошо? Почему мы остаёмся в топ-5 стран мира по числу самоубийств

Кому в Казахстане жить хорошо? Почему мы остаёмся в топ-5 стран мира по числу самоубийств

Всё ли так хорошо у жителей Казахстана? Почему-то наша страна сохраняет твёрдые позиции в первых строчках мировых рейтингов по числу самоубийств.
openqazaqstan
вчера / 16:54
  • 2194
  • 13