5 / 5 / 1
Регистрация: 05.11.2013
Сообщений: 58

И вновь консоль, redirect console output

28.01.2014, 14:51. Показов 3977. Ответов 9
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Коллеги, задавал вопрос на шарповской ветке, но ответа не получил, т.к. похоже требуется low-level подход, используя WinAPI, т.к. стандартными классами NET задачу не решить (по моему мнению)

Пишу обертку для консольной аппликухи (главная), которая в свою очередь запускает другие консольные процессы (дочерние). Нужно перенаправить строки, что выводятся в консоль, в текстбокс на форме. Так вот если редиректить stdout/stderr главной, она вываливается при запуске первого дочернего процесса с Access Violation (я так подозреваю, при попытке установить редирект дочернего процесса, т.к. свой вывод уже редирекчен), поэтому стандартный класс NET'a Process не подходит.

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

Вопрос, как лучше/проще реализовать перехват вывода в появляюшихся дочернем процессах, вывод которых перенаправляется в главное консольное окно? Перечислять все окна и искать по GetClassName == ConsoleWindowClass?

С уважением.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
28.01.2014, 14:51
Ответы с готовыми решениями:

как сделать в MS VS 2010 express output не в консоль в output windows
сабж заранее спасибо

Передать значения полей в метод для output в console
Всем привет Есть листы List<string> resultElements = resultCommand.Split(' ').Where(c => c != string.Empty).ToList();// получил...

Output/Input в консоль без C Run-Time (CRT)
Добрый день! Пишу небольшой загрузчик без CRT с использованием некоторых Nt-функций. Как таковой вывод информации на обозрение не так...

9
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16480 / 7443 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
28.01.2014, 15:33
Вся проблема в том, как я понимаю, что главное родительское приложение
при создании дочерних не устанавливает флаг наследования хэндлов.
Из-за этого стандартных хэндл вывода, который попадает в дочерние
процессы через механизм редиректа, в контексте этих процессов невалиден.

Можно сделать так: в главном приложении установить перехватчик на
функцию CreateProcess и в перехватчике принудительно взводить этот флаг.
Это, вероятно, самое простое решение.
0
5 / 5 / 1
Регистрация: 05.11.2013
Сообщений: 58
28.01.2014, 17:36  [ТС]
А если исходники главного приложения недоступны, то только инжектить свою длл в его адресное пространство и через нее уже перехватывать, так?

Добавлено через 8 минут
А флаг наследования - это один из битов в ConsoleFlags в структуре RTL_USER_PROCESS_PARAMETERS ?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16480 / 7443 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
28.01.2014, 17:43
Цитата Сообщение от Mif Посмотреть сообщение
А если исходники главного приложения недоступны, то только инжектить свою длл в его адресное пространство и через нее уже перехватывать, так?
Можно и проще.
Например, запустить приложение в режиме отладки (программно), дождаться,
пока в него будет загружена библиотека kernel32.dll, затем быстренько
найти адрес экспорта CreateProcessW и поменять на свой.

Цитата Сообщение от Mif Посмотреть сообщение
А флаг наследования - это один из битов в ConsoleFlags в структуре RTL_USER_PROCESS_PARAMETERS ?
Я имел в виду пятый аргумент функции CreateProcess, управляющий наследованием
хэндлов в дочерние процессы - bInheritHandles.
1
5 / 5 / 1
Регистрация: 05.11.2013
Сообщений: 58
29.01.2014, 12:35  [ТС]
Прежде чем начать разбираться с глобальными хуками( и программной отладкой), пытаюсь через windbg понять, где фэйлится главное приложение.
Access Violation возникает на команде
rep movs dword ptr es:[edi],dword ptr [esi] , где esi обнулен.
В esi нулевое значение (если я правильно дизассемблировал) попадает из eax, который в совю очередь заполняется вызовом:
call __fdopen

Вот листинг:
Assembler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
call    __open_osfhandle
push    offset aW       ; "w"
push    eax             ; int
call    __fdopen
push    0               ; size_t
push    4               ; int
mov     esi, eax
push    0               ; char *
mov     ecx, 8
mov     edi, offset stru_4213C0
push    offset stru_4213C0 ; FILE *
rep movsd
call    _setvbuf
add     esp, 20h
push    0FFFFFFF6h      ; nStdHandle
call    ebx ; GetStdHandle
push    4000h           ; int
push    eax             ; __int32
call    __open_osfhandle
push    offset aR       ; "r"
push    eax             ; int
call    __fdopen  <--- отсюда, насколько понимаю, появляется 0 в еах
push    0               ; size_t
push    4               ; int
mov     esi, eax   <--- Из eax приходит 0
push    0               ; char *
mov     ecx, 8
mov     edi, offset stru_4213A0
push    offset stru_4213A0 ; FILE *
rep movsd  <--- здесь Access Violation
Так вот, если запускать приложение из командного интерпретатора, то структура RTL_USER_PROCESS_PARAMETERS выглядит так (приведена частично):

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
0:000> dt _RTL_USER_PROCESS_PARAMETERS 0x00172068
ntdll!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : 0x1088
   +0x004 Length           : 0x1088
   +0x008 Flags            : 1
   +0x00c DebugFlags       : 0
   +0x010 ConsoleHandle    : (null) 
   +0x014 ConsoleFlags     : 0
   +0x018 StandardInput    : (null) 
   +0x01c StandardOutput   : (null) 
   +0x020 StandardError    : (null) 
   +0x024 CurrentDirectory : _CURDIR
Перед вызовом CreateProcess для запуска дочернего процесса, она выглядит так:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
0:000> dt _RTL_USER_PROCESS_PARAMETERS 0x00172068
ntdll!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : 0x1088
   +0x004 Length           : 0x1088
   +0x008 Flags            : 1
   +0x00c DebugFlags       : 0
   +0x010 ConsoleHandle    : 0x00002e4c Void
   +0x014 ConsoleFlags     : 0
   +0x018 StandardInput    : 0x00000003 Void
   +0x01c StandardOutput   : 0x00000007 Void
   +0x020 StandardError    : 0x0000000b Void
   +0x024 CurrentDirectory : _CURDIR
Т.е. видно, что stdin/out/err инициализируется. Эти же хэндлы фигурируют в _RTL_USER_PROCESS_PARAMETERS дочернего процесса:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
1:001> dt _RTL_USER_PROCESS_PARAMETERS 0x002520c0
ntdll!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : 0x10ec
   +0x004 Length           : 0x10ec
   +0x008 Flags            : 0x4001
   +0x00c DebugFlags       : 0
   +0x010 ConsoleHandle    : 0x00002e4c Void
   +0x014 ConsoleFlags     : 1
   +0x018 StandardInput    : 0x00000003 Void
   +0x01c StandardOutput   : 0x00000007 Void
   +0x020 StandardError    : 0x0000000b Void
   +0x024 CurrentDirectory : _CURDIR
Т.е. перенаправление дочернего ввода/вывода налицо. Соответственно, флаг наследования взведен и проблема не в этом.
Если же запускать главный процесс из моего приложения, то эта структура для запущенного главного процесса перед Access Violation выглядит так:

Assembler
1
2
3
4
5
6
7
8
9
10
11
12
0:000> dt _RTL_USER_PROCESS_PARAMETERS 0x001d2290
ntdll!_RTL_USER_PROCESS_PARAMETERS
   +0x000 MaximumLength    : 0x10f2
   +0x004 Length           : 0x10f2
   +0x008 Flags            : 0x4001
   +0x00c DebugFlags       : 0
   +0x010 ConsoleHandle    : 0x00002cfc Void
   +0x014 ConsoleFlags     : 0
   +0x018 StandardInput    : (null) 
   +0x01c StandardOutput   : 0x000004dc Void
   +0x020 StandardError    : 0x000004e4 Void
   +0x024 CurrentDirectory : _CURDIR
Т.е. старт главного процесса с инициализированными хэндлами консоли приводит в конечном итоге к исключению. Как Вы думаете, отчего такое может быть?
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16480 / 7443 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
29.01.2014, 13:12
Цитата Сообщение от Mif Посмотреть сообщение
Эти же хэндлы фигурируют в _RTL_USER_PROCESS_PARAMETERS дочернего процесса:
...
Т.е. перенаправление дочернего ввода/вывода налицо. Соответственно, флаг наследования взведен и проблема не в этом.
Хэндлы валидны только в контексте процесса, который их создал.
Они могут наследоваться в другие процессы, но только когда это делается явно.

Смотрите, что происходит:

1) Родительский процесс, назовем его Parent, вызывает CreateFile и получает хэндл файла,
причем при вызове функции в аргументе SECURITY_ATTRIBUTES указывается bInheritHandle = TRUE,
то есть, данный хэндл может наследоваться дочерними процессами (может - не означает обязан).

2) Parent запускает дочерний процесс по имени FirstChild.
Для запуска используется функция CreateProcess, причем в аргументе bInheritHandles тоже
указывается TRUE. Это означает, что все хэндлы, созданные в Parent с флагом bInheritHandle =
TRUE, будут валидны также в контексте процесса FirstChild. Сами хэндлы могут передаваться
через STARTUPINFO, командную строку, канал или другие механизмы, это не важно.
В данном случае хэндл файла записывается в STARTUPINFO.hStdOutput и дочерний FirstChild
пишет в этот хэндл, "думая", что это хэндл консоли.

3) FirstChild запускает еще один дочерний процесс по имени SecondChild, тоже через CreateProcess.
Но, ничего не подозревая о происходящем, флаг bInheritHandles в CreateProcess не выставляет.
Это значит, что никакие хэндлы из FirstChild в SecondChild не наследуются.

4) SecondChild пишет в консоль, для этого неявно используется хэндл hStdOutput,
автоматически позаимствованный от FirstChild через STARTUPINFO. Но в контексте SecondChild
этот хэндл уже не имеет смысла, поэтому ничего не получается.

Inheritance
http://msdn.microsoft.com/en-u... 85%29.aspx

Handle Inheritance
http://msdn.microsoft.com/en-u... 85%29.aspx
0
5 / 5 / 1
Регистрация: 05.11.2013
Сообщений: 58
29.01.2014, 14:21  [ТС]
Цитата Сообщение от Убежденный Посмотреть сообщение
3) FirstChild запускает еще один дочерний процесс по имени SecondChild, тоже через CreateProcess.
Да, но только до этого этапа не доходит. Parent крэшится на этапе вывода в консоль строки (примерно), в случае, если он наследует свои консольные хэндлы от моего приложения, и не крэшится, если он запускается из консоли и хэндлы ввода/вывода/ошибок не наследуются.
Цитата Сообщение от Убежденный Посмотреть сообщение
Но, ничего не подозревая о происходящем, флаг bInheritHandles в CreateProcess не выставляет.
Выставляет, я в отладчике смотрел.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16480 / 7443 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
29.01.2014, 15:25
Цитата Сообщение от Mif Посмотреть сообщение
Да, но только до этого этапа не доходит. Parent крэшится на этапе вывода в консоль строки (примерно), в случае, если он наследует свои консольные хэндлы от моего приложения, и не крэшится, если он запускается из консоли и хэндлы ввода/вывода/ошибок не наследуются.
Значит, наследование хэндлов реализовано с ошибками.
Если делать правильно, дочернему процессу должно быть "все равно", куда
выводить - консоль, файл, канал и т.д.
0
5 / 5 / 1
Регистрация: 05.11.2013
Сообщений: 58
30.01.2014, 11:53  [ТС]
Попробовал с флагом redirectstandardinput - Parent запускается (Access Violation не происходит) и запускает children, но в итоге висит окно консоли Parent'a, видимо флаг CREATE_NO_WINDOW не выставляется, или игнорируется.

Добавлено через 2 часа 18 минут
Опытным путем выяснил, что при обнулении StandardInput у Parent процесса (модифицировал память Parent'a в windbg) вызывается тот же access violation, видимо Parent пытается что-то читать из stdin, хотя в консоли это никак не отображается и никакого пользовательского ввода Parent не ждет, но тем не менее. Появилась мысль - вместо написания обертки, пропатчить Parent, подсунув ему в GetStdHandle() хэндл StandardOutput, который ненулевой. Или RedirectStandardInput выставить в true, но как при этом скрыть окно консоли?
Вопрос на стыке WinAPI, asm и C#...

Добавлено через 18 часов 7 минут
В итоге реализовал так (может, кому-то будет полезно):
- из своего приложения стартую parent с перенаправлением stdin/out/err.
- вызываю Process.WaitForInputIdle() (NET framework), удостоверяюсь, что процесс инициализирован полностью.
- вызываю ShowWindowAsync(), передавая ей значение поля Process.MainWindowHandle в качестве хэндла и константу SW_HIDE, тем самым пряча окно консоли.
- наслаждаюсь выводом консоли в мое окно.
Спасибо коллеге Убежденному за помощь!

Есть небольшой issue в виде мелькающего окна консоли, но "на скорость не влияет".
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16480 / 7443 / 1187
Регистрация: 02.05.2013
Сообщений: 11,617
Записей в блоге: 1
30.01.2014, 12:09
Цитата Сообщение от Mif Посмотреть сообщение
- вызываю ShowWindowAsync(), передавая ей значение поля Process.MainWindowHandle в качестве хэндла и константу SW_HIDE, тем самым пряча окно консоли.
Есть небольшой issue в виде мелькающего окна консоли
В Win32 это можно исправить: в STARTUPINFO::dwFlags добавить
флаг STARTF_USESHOWWINDOW, а в wShowWindow записать SW_HIDE.
В этом случае окно консоли вообще не появится. Но вот как то же самое
сделать средствами .NET - не в курсе...
1
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
30.01.2014, 12:09
Помогаю со студенческими работами здесь

Windows Forms и Console(out): в консоль не выводится информация
Добрый вечер, сразу к сути: Program.cs using System.Runtime.InteropServices; .... static class Win32 { ///...

Можно ли как-то осуществить вывод кириллицы в консоль, не используя Lucida Console
Собственно сабж в заголовке. Есть способ оставить стандартный Consolas?

Вновь потоки и вновь я не знаю что делать
Привет! Есть 10 потоков, каждый из которых должен отправлять запрос Вот, как отправляю сам запрос: try ...

Касперский вновь и вновь находит вирус
Здравствуйте! Установлен Kaspersky Internet Security 2016. Находит вирус trojan.multi.genautoruntask.b в system memory. Лечу с...

Скрипт вызывал консоль при закрытии страницы (exit console), тело которой формировалось на той же странице
Скрипт вызывал консоль при закрытии страницы (exit console), тело которой формировалось на той же странице, где размещался скрипт. Т.е....


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Опции темы

Новые блоги и статьи
Тестирование Pull Request в Kubernetes с vCluster
Mr. Docker 19.07.2025
Часто сталкиваюсь с серьезной дилемой при настройке тестовых окружений для проверки Pull Request в Kubernetes. С одной стороны, каждый PR требует изолированной среды — только так можно гарантировать,. . .
Мой 7 минутный ролик с крамольным предложением про шахматы, предлагаю заценить
_Ivana 18.07.2025
p2UhJNMGY94
Десять Middleware Node.js для эффективного кодинга
Reangularity 18.07.2025
Когда я только начинал работать с Node. js, количество пакетов в npm меня буквально парализовало. Сегодня их больше 1,3 миллиона — попробуй разберись, что стоит твоего внимания, а что нет. Я потратил. . .
Context и глубины Android
mobDevWorks 18.07.2025
В Android разработки Context напоминает воздух - он везде, жизненно необходим, но мало кто может детально объяснить его природу. Мы привыкли получать его как параметр, передавать дальше и. . .
Результаты исследования от команды MCM (июль 2025 г.)
Programma_Boinc 18.07.2025
Результаты исследования от команды MCM (июль 2025 г. ) Как сообщалось в наших предыдущих публикациях, мы изучаем гены, которые имеют наибольший рейтинг и ассоциируются с различными видами рака, в. . .
ИИ-чатбот на React с OpenAI и LangChain.js
Reangularity 17.07.2025
React давно стал для меня золотым стандартом фронтенд-разработки. Его компонентная структура, виртуальный DOM и однонаправленный поток данных идеально подходят для создания динамичных интерфейсов. . .
Пишем адаптер для локального хранилища S3 на C#
stackOverflow 16.07.2025
Разработка современных приложений часто требует интеграции с объектными хранилищами, и Amazon S3 стал де-факто стандартом в этой области. Однако работа с облачными сервисами в процессе разработки. . .
Старые замки
kumehtar 16.07.2025
Смотрел тут фото, попались пара старых замков. И сразу бросилось в глаза из отличие. Например: Замок Бистон, в англии. Разрушенное сооружение. Но - не испорченное людьми, по крайней мере - на. . .
Java и Eclipse Store: Сверхбыстрые приложения с In-Memory DB
Javaican 15.07.2025
Eclipse Store — это микро-движок персистентности для Java, который позволяет хранить и извлекать нативные Java-объекты без необходимости преобразования данных или использования объектно-реляционного. . .
EmBitz, создание проекта, отладка, прошивка
locm 15.07.2025
Создание проекта для Blue Pill (STM32F103C8T6) в EmBitz 2. 30, написания кода blink, запуск отладки в ОЗУ, заливка релизной прошивки во flash используя ST-Link и др. . . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru