2015-05-27 3 views
4

Если вы используете CreateProcess с флагом CREATE_NEW_CONSOLE, новый процесс имеет свои стандартные команды ввода, вывода и ошибок, направленные в новое окно консоли. Если вы хотите переопределить потоки ввода-вывода, вы можете сделать это, установив дескрипторы в полях STARTUPINFO hStdOutput, hStdInput и hStdError и установив флаг STARTF_USESTDHANDLES.CreateProcess с новым окном консоли, но переопределите некоторые дескрипторы ввода/вывода

Но что, если вы хотите переопределить только одну из ручек? Например, я мог бы перенаправить stderr в файл, оставив stdout и stdin подключенными к новому окну консоли.

Флаг STARTF_USESTDHANDLES сообщает CreateProcess, чтобы заменить все дескрипторы вместо подключения к ним для нового окна консоли. Похоже, мы должны предоставить все три ручки. Очевидно, я могу установить hStdError в дескриптор файла журнала, но какие значения следует использовать для hStdInput и hStdOutput?

Я попытался с помощью NULL, который, кажется, работает на Windows, 8.1, но он не работает на Windows 7.

Я также думал о создании первого окна консоли, а затем вызова CreateProcess с ручками к новому буферов консольного окна (и опускания флага CREATE_NEW_CONSOLE). К сожалению, родительский процесс также является консольным приложением, и похоже, что консольное приложение не может создать второе консольное окно.

+0

Обходное решение, если выяснится, что опубликованные ответы не работают: вместо запуска 'program.exe' запуска' cmd.exe/c program.exe> ​​CON

ответ

4

Согласно этой статье MSDN Поддержка:

How to spawn console processes with redirected standard handles

Если родительский процесс только хочет, чтобы перенаправить один или два стандартных ручек, с указанием GetStdHandle() для конкретных ручек заставляет ребенка создавать стандартная ручка, как обычно, без перенаправления. Например, если родительский процесс необходим только для перенаправления стандартного вывода и ошибок дочернего процесса, то член hStdInput структуры STARTUPINFO заполняются следующим образом:

hStdInput = GetStdHandle(STD_INPUT_HANDLE); 

Согласно GetStdHandle() documentation:

STD_INPUT_HANDLE
(DWORD) -10
стандартное устройство ввода. Первоначально это консольный входной буфер, CONIN $.

STD_OUTPUT_HANDLE
(DWORD) -11
Стандартное устройство вывода. Изначально это активный буфер экрана консоли, CONOUT $.

STD_ERROR_HANDLE
(DWORD) -12
стандартное устройство ошибок. Изначально это активный буфер экрана консоли, CONOUT $.

...

Стандартные дескрипторы процесса могут быть перенаправлены вызовом SetStdHandle, и в этом случае GetStdHandle возвращает перенаправленный дескриптор. Если стандартные дескрипторы были перенаправлены, вы можете указать значение CONIN $ в вызове функции CreateFile, чтобы получить дескриптор входного буфера консоли. Аналогично, вы можете указать значение CONOUT $, чтобы получить дескриптор активного экранного буфера консоли.

Attach/отсоединять поведение

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

Если существующее значение стандартного дескриптора равно NULL, или существующее значение стандартного дескриптора выглядит как псевдо-ручка консоли, ручка заменяется ручкой консоли.

Когда родитель использует как CREATE_NEW_CONSOLE и STARTF_USESTDHANDLES для создания процесса консоли, стандартные ручки не будут заменены, если текущее значение стандартного ручки не является NULL или консоль pseudohandle.

Так что, если STDIN родительского процесса не был перенаправлен, GetStdHandle(STD_INPUT_HANDLE) будет возвращать либо NULL или псевдо-дескриптор, ссылающийся на CONIN$. Когда это значение передается дочернему процессу через STARTUPINFO, дочерний процесс получит дескриптор консоли для STDIN какой бы консоли он не работал. С другой стороны, если STDIN родительского процесса был перенаправлен, возвращается GetStdHandle(STD_INPUT_HANDLE) фактический дескриптор реального файла/pipe/etc, который ребенок наследует и получает доступ.

То же самое относится к STDOUT и STDERR.

Итак, если вы хотите перенаправить ручки STDIN/OUT/ERR ребенка, вы должны установить hStdInput/Output/Error в свои собственные ручки. Если вы хотите, чтобы ребенок получал дескрипторы по умолчанию, используйте GetStdHandle() и дайте CreateProcess() решить, какие дескрипторы получат ребенок, исходя из того, перенаправляется ли родитель или нет.

+0

Если вы создаете новую консоль для ребенок, по умолчанию будет использовать вход/выход этой консоли. Если родитель хочет, чтобы ребенок наследовал вход родителя, родитель должен перенаправить вход ребенка. Родитель может определить, был ли переназначен его собственный вход, а затем передать его вместе с дочерним при желании. Ребенок не может этого решить. –

+1

'GetStdHandle (STD_INPUT_HANDLE)' возвращает дескриптор текущего входного сигнала вызывающего процесса. Если вход НЕ был перенаправлен, дескриптор будет ссылаться на 'CONIN $' - стандартный ввод текущей консоли. Если дочерний процесс получает этот дескриптор для ввода, он будет считывать со стандартного ввода какой бы то ни было консоли, в которой он запускается. С другой стороны, если вход родителя был перенаправлен, дескриптор будет фактическим дескриптором для real file/pipe/etc, который ребенок может наследовать и читать. То же самое касается вывода (дескриптор вывода по умолчанию, ссылающийся на 'CONOUT $') –

+0

Это более подробно описано в разделе 'Замечания' в документации' 'GetStdHandle()' (https://msdn.microsoft.com/) ан-нас/библиотека/окна/настольные/ms683231.aspx). –

1

Если я правильно истолковал this documentation, вы должны использовать GetStdHandle(STD_INPUT_HANDLE) и GetStdHandle(STD_OUTPUT_HANDLE) для двух других ручек.

2

В Windows 7, по крайней мере, documentation here (как указано в существующих ответах) вводит в заблуждение. Рекомендуемый подход работает только в том случае, если родительский процесс не имеет перенаправленного ввода.

Это реальное поведение, как заметил на моей машине:

  • Когда процесс открывает описатель CONOUT $ (например) возвращенный дескриптор всегда имеет то же числовое значение (на моей машине, дескриптор для CONOUT $ всегда равен 7), если этот дескриптор уже не существует.

  • Итак, если вы являетесь консольным процессом, и вы не запускались с перенаправленным выходом, ваш стандартный дескриптор вывода равен 7. Если вы откроете другой дескриптор для CONOUT $, он будет иметь другое значение. Если вы закроете ручку 7, а затем откройте дескриптор CONOUT $, вы снова получите 7.

  • Когда запускается консольный процесс, в дочернем процессе обычно присутствует дескриптор CONOUT $ с магическим значением 7 (независимо от перенаправления). Если вы использовали STARTF_USESTDHANDLES, то стандартный вывод ребенка будет идти на консоль тогда и только тогда, когда вы укажете 7 в качестве стандартного дескриптора вывода.Если вы укажете дескриптор CONOUT $, который имеет любое другое значение, это не сработает.

  • Иногда, когда запускается консольный процесс с перенаправлением стандартного вывода, в дочернем процессе нет дескриптора CONOUT $. В частности, это происходит, когда cmd.exe запускает консольный процесс со стандартным перенаправлением вывода. До сих пор я не мог понять, какую комбинацию параметров использовать с CreateProcess, чтобы это произошло.

  • Поведение CONIN $ аналогично; на моей машине, магия значение Conin $ равно 3. (я не исследовал, как стандартные работы ошибка.)

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

Вместо этого запустите прокси-процесс с CREATE_NEW_CONSOLE и безSTARTF_USESTDHANDLES. От этот процесс, так как вы знаете, как он был запущен, вы знаете, что стандартные дескрипторы будут иметь правильные магические значения, поэтому их можно указать как ручки для дочернего процесса.

+0

На моей машине Win7 вызов 'CreateFile (« CONOUT $ », ...)' в консольном процессе без перенаправленного вывода не возвращается 7. В качестве теста я дважды вызывал 'CreateFile()' и всегда получал '0x00000017' и '0x0x0000001B', соответственно. 'GetStdHandle (STD_OUTPUT_HANDLE)' возвращает '0x00000007' без перенаправления. Это соответствует рекомендациям Microsoft в разделе «Как создавать консольные процессы с перенаправленными стандартными дескрипторами»] (поддержка https: //.microsoft.com/en-us/kb/190351), и с вашим комментарием, что передача 7 с помощью STARTF_USESTDHANDLES делает дочерний процесс использованием собственного вывода консоли. –

+2

До Windows 8 консольные буферы обрабатываются консольным хостингом, conhost.exe (или csrss.exe до Win7), который поддерживает таблицу обработки каждого процесса. (Нижние 2 бита всегда устанавливаются, что заставляет их базовый API перенаправлять вызовы на соответствующие функции с поддержкой LPC, такие как 'DuplicateConsoleHandle'.) Поскольку это новое консольное окно, должно быть безопасно предположить, что conhost.exe будет инициализирован таблица дескрипторов до 3 для входного буфера и 7 и 11 для вывода и ошибки. – eryksun

+2

До Windows 8, открывая псевдо-устройства 'CON',' CONIN $ 'и' CONOUT $ 'перенаправляет недокументированную функцию 'OpenConsoleW'. Это использует LPC для вызова 'conhost! SrvOpenConsole' в хост-процессе консоли, который вызывает' conhost! AllocateIoHandle' для выделения нового дескриптора для консольного буфера. – eryksun