Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.54/13: Рейтинг темы: голосов - 13, средняя оценка - 4.54
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16

Перенаправить StdOut в дочерний процесс CMD.exe

20.02.2016, 00:00. Показов 2614. Ответов 9
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Пишу консольное приложение, которое должно запускаться из-под cmd.exe и выводить в окно консоли некоторую инфу.
Есть ограничение: я не должен пользоваться std:: и пр., только функции WinAPI.

Итак, демо-приложение:

demo.exe
C++
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
31
32
33
34
35
36
37
38
39
#include <windows.h>
#include <Strsafe.h>
#include <iostream>
#include <fstream>
 
int main(int argc, char *argv[]) 
{
    // #1 stream через std - Чисто для сравнения
    std::cout << "std::cout - Test" << std::endl;
 
    HANDLE cOut = GetStdHandle(STD_OUTPUT_HANDLE);
    HWND cHwnd = GetConsoleWindow();
 
    size_t cbBuf = 100;
    WCHAR *buf = new WCHAR[cbBuf];
    memset(buf, '\0', cbBuf*2);
 
    StringCchPrintfW(buf, cbBuf, L"%s: 0x%X\r\n%s: %d\r\n", L"Console HWnd", cHwnd, L"Std Handle", cOut);
 
    DWORD dwWritten;
    // Пробуем записать с помощью WinAPI
    BOOL ret = WriteConsole(cOut, buf, lstrlenW(buf), &dwWritten, NULL);
 
    FreeConsole();
 
    // Дублирую инфу во внешний файл
    std::wofstream fout(L"console.log", std::ios_base::out | std::ios_base::app);
    if (!fout.is_open())
    {
        std::cout << "Unable to create/open file!\n";
        return 1;
    }
    fout << buf << std::endl;
    fout.close();
    
    delete[] buf;
 
    return 0;
}
Получаю правильный вывод в консоль, как и должно быть:

std::cout - Test
Console HWnd: 0x3412E8
Std Handle: 7
Проблема начинается, когда я пытаюсь скормить программу cmd-шному парсеру (мне так по заданию нужно).
Код для консоли cmd.exe выглядит так:

Windows Batch file
1
2
3
4
5
6
7
:: переходим в папку с нашим приложением
 
cd /d "H:\_C++\proj\stream"
 
:: запускаем синтаксический разбор
 
for /f "delims=" %a in ('demo.exe') do @echo ----- %a
Батником

Windows Batch file
1
2
3
4
5
@echo off
SetLocal EnableExtensions
cd /d "H:\_C++\proj\stream"
for /f "delims=" %%a in ('demo.exe') do @echo ----- %%a
pause


В двух словах как это работает:
cmd.exe создает дочерний процесс cmd.exe, который в свою очередь создает наш процесс demo.exe, читает его стандартный поток #1, затем перенаправляет этот вывод к родительскому процессу cmd.exe. Родительский в свою очередь выполняет команду @echo ----- %a, где вместо %a подставляет каждую из строк вывода.

В итоге, на выходе мы получаем такой текст в консоли:

----- std::cout - Test
а должен был быть:

----- std::cout - Test
----- Console HWnd: 0x3412E8
----- Std Handle: 11
Подскажите, пожалуйста, в чем ошибка и как ее исправить...
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
20.02.2016, 00:00
Ответы с готовыми решениями:

Процесс cmd.exe
Всем добрый вечер, начал замечать что после установки Windows 10, постоянно спустя 3-5 часов после запуска ПК, и запуска игр и приложений...

Запущен ли процесс? или Завершен ли дочерний процесс моей программы?
Моя программа выполняет execute('cmd', '/C ...'). На момент execute-а других экземпляров cmd.exe нет. Мне хочется узнать, когда окошко cmd...

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

9
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.02.2016, 14:11
Первое, что приходит на ум: попробовать не писать в консоль "широкие"
символы, т.е. использовать только StringCchPrintfA, WriteConsoleA и т.п.
1
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
20.02.2016, 16:27
Dragokas, помнится была подобная проблема. Помогло получение хэндла через CreateFile(CONOUT$), заместо GetStdHandle.
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
20.02.2016, 19:08  [ТС]
Убежденный, тоже самое.

jupman, заменил на:

C++
1
2
3
4
5
    HANDLE cOut = CreateFile(L"CONOUT$", GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, NULL, NULL);
    if (0 == cOut) {
        std::cout << "Cannot obtain Std Handle!\n";
        return 0;
    }
Согласно этому правилу в описании MSDN:

CONOUT$ gets a handle to the active screen buffer, even if SetStdHandle redirects the standard output handle.
Получаю вывод из-под cmd /c for :
Console HWnd: 0xE12DA
Std Handle: 15
------- std::cout - Test
Т.е. std записала в буфер потока дочернего процесса как положено.
А WriteConsole - сразу через хендл потока родительского процесса (пока дочерний еще работал, от того и строки в обратном порядке).

Может, есть способ подсмотреть реализацию std (на не очень низком уровне)?

Если несколько раз запускать из консоли, затем посмотреть лог console.log, который параллельно пишется,
то видим, что полученный Std Handle постоянно меняется, что логично, т.е. как минимум хендл потока определяется правильно (дочерний процесс при каждом запуске разный).
Может ли быть, что здесь как-то блокируется SHARE mode и дочерний процесс больше не может прочитать свой буфер (правда, как тогда объяснить что std::cout пишет одновременно туда же, но при этом читается нормально)?

Еще пробовал переподключаться к консоли

C++
1
2
3
4
5
6
7
8
    FreeConsole();
 
    if (0 == AttachConsole(ATTACH_PARENT_PROCESS)) {
        std::cout << "Unable to attach to the console!" << std::endl;
        return 1;
    }
 
    HANDLE cOut = GetStdHandle(STD_OUTPUT_HANDLE);
GetStdHandle возвращает всегда один и тот же псевдохендл (т.е. главного процесса cmd.exe).
Почему-то AttachConsole считает родительским не непосредственный cmd, из которого запущен, а на 2 ступени выше.
Ну да и ладно. Все равно не решит проблемы. Думал, может конфликт, когда и std::cout и WriteConsole имеют доступ к одному буферу.
Попробовал этот же код на другом языке (где нет понятия пространства имен std), там все аналогично.
0
Ушел с форума
Эксперт С++
 Аватар для Убежденный
16481 / 7444 / 1187
Регистрация: 02.05.2013
Сообщений: 11,616
Записей в блоге: 1
20.02.2016, 19:52
Dragokas, вот call stack вызова std::cout (Win7 x64, VC++2008SP1):
Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 # ChildEBP RetAddr  Args to Child              
00 0019b80c 688d3612 00000007 0019e068 00000001 kernel32!WriteFileImplementation
01 0019f4b8 688d2e8a 00000001 0019f53c 00000001 MSVCR90D!_write_nolock+0x702 [f:\dd\vctools\crt_bld\self_x86\crt\src\write.c @ 335]
02 0019f504 6886476e 00000001 0019f53c 00000001 MSVCR90D!_write+0x1aa [f:\dd\vctools\crt_bld\self_x86\crt\src\write.c @ 75]
03 0019f534 68863182 00000048 68921408 3bcecbc2 MSVCR90D!_flsbuf+0x28e [f:\dd\vctools\crt_bld\self_x86\crt\src\_flsbuf.c @ 189]
04 0019f590 6895c264 00000048 68921408 0019f688 MSVCR90D!fputc+0x212 [f:\dd\vctools\crt_bld\self_x86\crt\src\fputc.c @ 52]
05 0019f5a0 6895becd 00000048 68921408 3bcec8a8 MSVCP90D!std::_Fputc<char>+0x14 [f:\dd\vctools\crt_bld\self_x86\crt\src\fstream @ 80]
06 0019f688 6895bd41 00000048 68a0db78 cccccccc MSVCP90D!std::basic_filebuf<char,std::char_traits<char> >::overflow+0x10d [f:\dd\vctools\crt_bld\self_x86\crt\src\fstream @ 260]
07 0019f6ac 6895b33e 00dc780c 00000005 68a0db78 MSVCP90D!std::basic_streambuf<char,std::char_traits<char> >::xsputn+0xa1 [f:\dd\vctools\crt_bld\self_x86\crt\src\streambuf @ 378]
08 0019f6c0 00dc182e 00dc780c 00000005 3bce2cfa MSVCP90D!std::basic_streambuf<unsigned short,std::char_traits<unsigned short> >::sputn+0x1e [f:\dd\vctools\crt_bld\self_x86\crt\src\streambuf @ 169]
09 0019f834 00dc150a 68a0dbc8 00dc780c 68953a20 StdCoutImpl!std::operator<<<std::char_traits<char> >+0x26e [c:\program files (x86)\microsoft visual studio 9.0\vc\include\ostream @ 765]
0a 0019f960 00dc2538 00000001 007c4d38 007c1f48 StdCoutImpl!main+0x4a [d:\dev\garbage\stdcoutimpl\stdcoutimpl\main.cpp @ 11]
0b 0019f9b0 00dc237f 0019f9c4 7667337a 7efde000 StdCoutImpl!__tmainCRTStartup+0x1a8 [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 586]
0c 0019f9b8 7667337a 7efde000 0019fa04 77929882 StdCoutImpl!mainCRTStartup+0xf [f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c @ 403]
0d 0019f9c4 77929882 7efde000 75fda39e 00000000 kernel32!BaseThreadInitThunk+0xe
0e 0019fa04 77929855 00dc117c 7efde000 ffffffff ntdll!__RtlUserThreadStart+0x70
0f 0019fa1c 00000000 00dc117c 7efde000 00000000 ntdll!_RtlUserThreadStart+0x1b
Первый аргумент kernel32!WriteFileImplementation (кстати, неожиданно, что
используется WriteFile, а не WriteConsole, да?) - 0x00000007, то есть, тот же
самый хэндл, который возвращает GetStdHandle(STD_OUTPUT_HANDLE).
1
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
20.02.2016, 21:00
Dragokas, добавлю что сишный printf вроде так же как std::cout работает. Набросал такой вот код:

C
1
2
    printf("word0\r\n");
    WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),"word1\r\nword2\r\n",14,&Bytes,NULL);
Запустил через ваш батник. Вывод:

----- word1
----- word2
----- word0
Опять порядок не тот. Но что интересно см. на каллстек printf'a:

Call stack of main thread
Address Stack Procedure / arguments Called from Frame

0012E340 7C81CC71 kernel32.WriteConsoleA kernel32.7C81CC6C 0012E38C
0012E344 00000007 hConsole = 00000007
0012E348 0012E3E0 Buffer = 0012E3E0
0012E34C 00000008 CharsToWrite = 8
0012E350 0012E3C4 pWritten = 0012E3C4
0012E354 00000000 pReserved = NULL

0012E390 785899AD ? kernel32.WriteFile MSVCR90.785899A7
0012E394 00000007 hFile = 00000007
0012E398 0012E3E0 Buffer = 0012E3E0
0012E39C 00000008 nBytesToWrite = 8
0012E3A0 0012E3C4 pBytesWritten = 0012E3C4
0012E3A4 00000000 pOverlapped = NULL
0012FE9C 78589D37 MSVCR90.78589560 MSVCR90.78589D32 0012FE98

0012FEE0 7854FEA2 ? MSVCR90._write MSVCR90.7854FE9D 0012FEDC
0012FEE4 00000001 handle = 1
0012FEE8 00363FE0 buf = 00363FE0
0012FEEC 00000007 len = 7
0012FF00 7854FA64 MSVCR90.7854FE67 MSVCR90.7854FA5F 0012FEFC
0012FF10 78552145 MSVCR90.7854FA46 MSVCR90.78552140 0012FF0C

0012FF5C 00401017 ? MSVCR90.printf TestAPI.main+11 0012FF58
0012FF60 00402104 format = "word0
"
0012FF64 00403018
0012FF80 004011B7 ? TestAPI.main TestAPI.004011B2
Т.е. в итоге все равно WriteFile(00000007) вызывается. Странно.

Добавлено через 28 минут
Dragokas, пост выше это я не через батник запуска. А сейчас попробовал через батник так вообще бряк внутри printf'a не сработал. Ни на WriteConsole, ни на WriteFile.
0
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
20.02.2016, 21:02  [ТС]
Убежденный, jupman, спасибо. С WriteFile все замечательно отрабатывает.

Цитата Сообщение от jupman Посмотреть сообщение
Т.е. в итоге все равно WriteFile(00000007) вызывается. Странно.
Да. Действительно странно.

Если в коде printf перед std::cout

C++
1
2
3
printf("printf\r\n");
std::cout << "std::cout" << std::endl;
BOOL ret = WriteFile(cOut, "word1\r\n", 7, &dwWritten, NULL);
Вывод батника последователен:

----- printf
----- std::cout
----- word1

Если после, то вывод от printf оказывается в самом конце.

----- std::cout
----- word1
----- printf

Аналогично произойдет, если перенаправить вывод в файл:
Windows Batch file
1
cmd /c Demo.exe > out.txt
0
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
20.02.2016, 21:13
Dragokas, подебажил дальше. Пишет оказывается в MSVCR90.dll в DllMain(DLL_PROCESS_DETACH). Каллстек:

Call stack of main thread
Address Stack Procedure / arguments Called from Frame
0012E174 785899AD ? kernel32.WriteFile MSVCR90.785899A7
0012E178 00000048 hFile = 00000048 (window)
0012E17C 0012E1C4 Buffer = 0012E1C4
0012E180 00000008 nBytesToWrite = 8
0012E184 0012E1A8 pBytesWritten = 0012E1A8
0012E188 00000000 pOverlapped = NULL
0012FC80 78589D37 ? MSVCR90.78589560 MSVCR90._write+9A
0012FCC4 7854FEA2 ? MSVCR90._write MSVCR90.7854FE9D 0012FCC0
0012FCC8 00000001 handle = 1
0012FCCC 00364008 buf = 00364008
0012FCD0 00000007 len = 7
0012FCE4 7854FEF0 MSVCR90.7854FE67 MSVCR90._fflush_nolock+17 0012FCE0
0012FCE8 785B73C8 Arg1 = 785B73C8
0012FCF4 7854FF8F MSVCR90._fflush_nolock MSVCR90.7854FF8A 0012FCF0
0012FCF8 785B73C8 Arg1 = 785B73C8
0012FD34 7855005F ? MSVCR90.7854FF21 MSVCR90._flushall+2 0012FD30
0012FD3C 7854F271 MSVCR90._flushall MSVCR90.7854F26C 0012FD48
0012FD40 78542201 Includes MSVCR90.7854F271 MSVCR90._initterm+11 0012FD48
0012FD4C 7854243D MSVCR90._initterm MSVCR90.78542438 0012FD48
0012FD58 78542C95 MSVCR90.7854242E MSVCR90.78542C90 0012FD84
0012FD88 78542D5E ? MSVCR90.78542B94 MSVCR90.<ModuleEntryPoint>+1 0012FD84
0012FD9C 7C90118A MSVCR90.<ModuleEntryPoint> ntdll.7C901187 0012FD98
0012FDA0 78520000 Arg1 = 78520000
0012FDA4 00000000 Arg2 = 00000000
0012FDA8 00000001 Arg3 = 00000001
0012FDBC 7C923ABA ? ntdll.7C901176 ntdll.7C923AB5 0012FDB8
0012FE40 7C81CA96 ? <JMP.&ntdll.LdrShutdownProcess> kernel32.7C81CA91 0012FE3C
0012FF34 7C81CB0E ? kernel32.7C81CA54 kernel32.7C81CB09 0012FF30
0012FF48 00401062 ? kernel32.ExitProcess TestAPI.0040105C 0012FF44
0012FF4C 00000000 ExitCode = 0
0012FF80 004013B3 TestAPI.main TestAPI.004013AE 0012FF7C
Добавлено через 2 минуты
Это пайп в общем. Я кстати так и подумал что что-то с пайпами мутится.

File \Device\NamedPipe\Win32Pipes.00000dec.00 000001 0x48
1
Эксперт WindowsАвтор FAQ
 Аватар для Dragokas
18030 / 7733 / 892
Регистрация: 25.12.2011
Сообщений: 11,502
Записей в блоге: 16
20.02.2016, 21:34  [ТС]
Получается, в случае с For /F я так понимаю происходит образно (cmd /c Demo.exe) | cmd.exe, т.е. канал от одного cmd к другому.
А в варианте с:
Windows Batch file
1
cmd /c Demo.exe > out.txt
вероятно, опять таки Demo.exe | cmd.exe, т.е. передача через канал и только потом запись в файл.
0
232 / 135 / 19
Регистрация: 10.11.2015
Сообщений: 305
20.02.2016, 21:53
Dragokas, да вроде так. Если поставить бряк на ReadFile первого запущенного cmd то он срабатывает (считывается то что нужно, проверил) при отправке из DllMain.MSVCR90 в Demo.exe.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
20.02.2016, 21:53
Помогаю со студенческими работами здесь

Есть процесс A и дочерний процесс B, который находит хэндл родительского процесса A. И этот хэндл в разных случаях РАЗНЫЙ! Почему?
Друзья! Вот код A.exe, суть которого просто стать родительским для B.exe и висеть в системе. #include &lt;stdio.h&gt; #include...

вирус calc.exe*32 notepad.exe*32 cmd.exe cannhost.exe
Здравствуйте. помогите решить проблему. При включении компьютера висят процессы calc.exe*32 notepad.exe*32 cmd.exe cannhost.exe. Завершаю...

Дочерний процесс
Как передать аргумент size в дочерний процесс?) Как объявить массив??? На ubuntu компилятор gcc компилит под СИ и объявить массив с...

Дочерний процесс
Скажите, пожалуйста, где описывать дочерние процессы. То есть я создаю, допустим в Visual c++ процессы при помощи CreateProcess и после...

Завершить дочерний процесс
как можно завершить дочерний процесс не трогая основной тема на форуме есть но там про bat , а как это реализовать на c# ? в интернете...


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

Или воспользуйтесь поиском по форуму:
10
Ответ Создать тему
Новые блоги и статьи
http://iceja.net/ математические сервисы
iceja 20.01.2026
Обновила свой сайт http:/ / iceja. net/ , приделала Fast Fourier Transform экстраполяцию сигналов. Однако предсказывает далеко не каждый сигнал (см ограничения http:/ / iceja. net/ fourier/ docs ). Также. . .
http://iceja.net/ сервер решения полиномов
iceja 18.01.2026
Выкатила http:/ / iceja. net/ сервер решения полиномов (находит действительные корни полиномов методом Штурма). На сайте документация по API, но скажу прямо VPS слабенький и 200 000 полиномов. . .
Расчёт переходных процессов в цепи постоянного тока
igorrr37 16.01.2026
/ * Дана цепь постоянного тока с R, L, C, k(ключ), U, E, J. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа, решает её и находит переходные токи и напряжения на элементах схемы. . . .
Восстановить юзерскрипты Greasemonkey из бэкапа браузера
damix 15.01.2026
Если восстановить из бэкапа профиль Firefox после переустановки винды, то список юзерскриптов в Greasemonkey будет пустым. Но восстановить их можно так. Для этого понадобится консольная утилита. . .
Сукцессия микоризы: основная теория в виде двух уравнений.
anaschu 11.01.2026
https:/ / rutube. ru/ video/ 7a537f578d808e67a3c6fd818a44a5c4/
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru