Форум программистов, компьютерный форум, киберфорум
C/C++: WinAPI
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 4.59/64: Рейтинг темы: голосов - 64, средняя оценка - 4.59
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750

Многкратное перенаправление ввода/вывода (CreatePipe)

15.07.2021, 13:00. Показов 13268. Ответов 36

Студворк — интернет-сервис помощи студентам
Мне нужно многократно перехватить ввод/вывод дочернего процесса code.exe, например, такого:
C++
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
int main()
{
while(true)
   {
    int a;
    std::cin >> a;
    std::cout << a*a;
   }    
return 0;
}
Для однократного перенаправления ввода/вывода создал следующее:
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#include <windows.h>
#include <iostream>
 
int main()
{
    setlocale(0, "");
    
    SECURITY_ATTRIBUTES sa;//атрибуты защиты канала
        sa.lpSecurityDescriptor = NULL; //защита по умолчанию
        sa.nLength = sizeof(SECURITY_ATTRIBUTES);
        sa.bInheritHandle = true;//разрешаем наследование дескрипторов
 
    //обработка ошибок
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT);
 
    //дескрипторы пайпов
    HANDLE child_in, parent_out, child_out, parent_in;
    //создаем анонимные каналы
    CreatePipe(&parent_out, &child_in, &sa, 0); //канал для stdin дочернего процесса
    CreatePipe(&child_out, &parent_in, &sa, 0); //канал для stdout дочернего процесса
            
    STARTUPINFO si; 
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;//скрываем окно дочернего процесса
    //подменяем дескрипторы
    si.hStdOutput = parent_in;
    si.hStdError = parent_in; 
    si.hStdInput = parent_out;
 
    //Создаём дочерний процесс
    WCHAR ModuleName[] = L"C:\\code.exe";
    PROCESS_INFORMATION pi;
    CreateProcess(ModuleName,NULL,NULL,NULL,TRUE,ABOVE_NORMAL_PRIORITY_CLASS,NULL,NULL,&si,&pi);                // здесь будут дескрипторы и идентификаторы нового процесса и его первичного потока
    
 
    DWORD num;//количество переданых/прочитаных байт
    char buf[1024] = {};//буфер для чтения данных
    WCHAR s[] = L"2";//передаваемое значение
    int sz = int(sizeof(s)/sizeof(WCHAR));
        
        //передача данных
        WriteFile(child_in, s, sz + 1, &num, NULL);
        std::wcout << L"Данные переданы: " << s << std::endl;
        CloseHandle(child_in);//закрываем канал stdin дочернего процесса
        
        //чтение данных
        ReadFile(child_out, buf, 1023, &num, NULL);
        std::cout << "Данные прочитаны: " << buf << std::endl;
        CloseHandle(parent_out);//закрываем родительский принимающий пайп
    
    CloseHandle(parent_in);
    CloseHandle(child_out);
    TerminateProcess(pi.hProcess, 0);//убиваем дочерний процесс
    CloseHandle(pi.hProcess);
    CloseHandle(pi.hThread);
    
    return 0;
}
Всё работает.
Но как добиться многократного перенаправления, чтобы не запускать многократно дочерний процесс?
Чтобы один раз запустить code.exe и многократно передавать в него и читать данные.
Как это сделать?
Ведь просто поместить в цикл процесс записи/чтения не получится, так как пайпы закрываются уже в первой итерации:
C++
1
2
3
4
5
6
7
8
9
10
11
12
while (true)
    {
        //передача данных
        WriteFile(child_in, s, sz + 1, &num, NULL);
        std::wcout << L"Данные переданы: " << s << std::endl;
        CloseHandle(child_in);//закрываем канал stdin дочернего процесса
 
        //чтение данных
        ReadFile(child_out, buf, 1023, &num, NULL);
        std::cout << "Данные прочитаны: " << buf << std::endl;
        CloseHandle(parent_out);//закрываем родительский принимающий пайп
    }
А не закрывать пайпы нельзя. Виснет.
Как быть?
Подскажите, кто знает, или дайте ссылку где почитать об этом.
0
Programming
Эксперт
39485 / 9562 / 3019
Регистрация: 12.04.2006
Сообщений: 41,671
Блог
15.07.2021, 13:00
Ответы с готовыми решениями:

Перенаправление потока вывода
Здравствуйте! Задача такова - нужно перенаправить поток вывода с cout на файловый в одном из конструкторов класса. Итог - необработанное...

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

Перенаправление вывода при запуске процесса
Создаю файл, в который хочу перенаправить вывод. Заполняю структуру STARTUPINFO. Не забываю туда вписать дескриптор только что созданного...

36
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
15.07.2021, 18:28
Студворк — интернет-сервис помощи студентам
Цитата Сообщение от LVV Посмотреть сообщение
это я вроде бы с нормальных источников взял... спасибо пересмотрю...
Это зависит от того, в каком виде ожидаются команды и в каком виде будут ответы... Данная code.exe работает с байтовой строкой и ждет в конце '\n'
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
15.07.2021, 18:30  [ТС]
Хотя нет. Просто я перепутал с WCHAR для ModuleName[]
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
15.07.2021, 18:30
Цитата Сообщение от LVV Посмотреть сообщение
Или там всё по очередям и дескрипторам сокетов распределено
Там все реализовано на многопоточной основе. Каждый поток обрабатывает один запрос. Накладки никак не должно быть
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
15.07.2021, 18:34  [ТС]
Цитата Сообщение от liv Посмотреть сообщение
в каком виде ожидаются команды и в каком виде будут ответы
Я собираюсь работать с базой данных через командную строку. Значит, всё таки char.
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
15.07.2021, 18:37
LVV, ну, дерзайте... По программе вопросов нет?
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
15.07.2021, 18:41  [ТС]
Цитата Сообщение от liv Посмотреть сообщение
По программе вопросов нет?
Ну, при белом просмотре, вроде бы кое-что понятно. Спасибо.
0
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
16.07.2021, 18:43  [ТС]
liv, я заметил странное поведение "примерчика".
Он умудряется дважды передать и прочитать данные в незацикленный дочерний модуль.
Тоесть, ехе-шник code1.exe компилирую вот из такого кода:
C++
1
2
3
4
5
6
7
8
#include <iostream>
int main()
{
    int a;
    std::cin >> a;
    std::cout << a*a;
return 0;
}
По идее, возможна лиш однократная передача/чтение данных.
Но по факту - передача/чтение происходит дважды.
И лишь на третий раз: Time out!
Мистика.
Миниатюры
Многкратное перенаправление ввода/вывода (CreatePipe)  
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
17.07.2021, 11:02
LVV, не пробовал...
Было сказано, code.exe не менять!
Посмотрю в понедельник
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
18.07.2021, 07:17  [ТС]
Пардон. Проблему решил. Виновата, как всегда моя невнимательность (дважды запускался процесс).
0
Эксперт CЭксперт С++
 Аватар для liv
5120 / 4574 / 855
Регистрация: 07.10.2015
Сообщений: 9,462
18.07.2021, 08:44
LVV,
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
26.07.2021, 19:12  [ТС]
Неделю не могу справиться с проблемой.
Переделал пример, для ввода/чтения строковых данных.
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
#define WIN32_LEAN_AND_MEAN 
#define _CRT_SECURE_NO_WARNINGS //для strcpy
#include <windows.h>
#include <iostream>
#include <conio.h>
#include <string>
 
 
using namespace std;
 
//структура данных для общего доступа основного и треда пайпов
struct TheadParms
{
    //дескрипторы событий
    HANDLE  hePipeStart;
    HANDLE  hePipeReply;
    //дескрипторы пайпов
    HANDLE  child_in;
    HANDLE  parent_out;
    HANDLE  child_out;
    HANDLE  parent_in;
    
    //буфера
    char    read[1024];     //буфер для чтения данных
    char    send[1024];     //передаваемое значение
};
 
//тред пайпов
BOOL PipesProc(LPSTR lpData)
{
    TheadParms *ptp = (TheadParms*)lpData;     //переданный параметр - адрес общих данных
    DWORD       num;                            //количество переданых/прочитаных байт
    
    while (1)                                   
    {
        WaitForSingleObject(ptp->hePipeStart, INFINITE);   //ждем бесконечно событие
 
        //передача данных
        WriteFile(ptp->child_in, ptp->send, lstrlenA(ptp->send), &num, NULL);   //передаем готовые данные
        std::cout << "Данные переданы: " << ptp->send << std::endl;                          //выведем переданные данные здесь
 
        //чтение данных
        std::memset(ptp->read, 0, sizeof(ptp->read));//обнуляем буфер для чтения
        ReadFile(ptp->child_out, ptp->read, sizeof(ptp->read), &num, NULL);
        
        SetEvent(ptp->hePipeReply); //и взводим событие готовности
    }
}
 
int main()
{
    setlocale(0, "");
       
    SECURITY_ATTRIBUTES sa;//атрибуты защиты канала
    sa.lpSecurityDescriptor = NULL; //защита по умолчанию
    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = true;//разрешаем наследование дескрипторов
 
    //обработка ошибок
    SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX | SEM_NOALIGNMENTFAULTEXCEPT);
 
    TheadParms tp = {};         //структура общих данных
 
    //создаем анонимные каналы
    CreatePipe(&tp.parent_out, &tp.child_in, &sa, 0); //канал для stdin дочернего процесса
    CreatePipe(&tp.child_out, &tp.parent_in, &sa, 0); //канал для stdout дочернего процесса
 
    STARTUPINFO si;
    ZeroMemory(&si, sizeof(STARTUPINFO));
    si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
    si.wShowWindow = SW_HIDE;//скрываем окно дочернего процесса
          
    //подменяем дескрипторы
    si.hStdOutput = tp.parent_in;
    si.hStdError = tp.parent_in;
    si.hStdInput = tp.parent_out;
 
    //Создаём дочерний процесс
    WCHAR ModuleName[] = L"code.exe";
    WCHAR *CmdLine = nullptr;
 
    PROCESS_INFORMATION pi;
    CreateProcess(ModuleName,
        CmdLine,
        NULL,
        NULL,
        TRUE,
        ABOVE_NORMAL_PRIORITY_CLASS,
        NULL,
        NULL,
        &si,
        &pi
    );
        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
 
    //Создаем события для синхронизации
    tp.hePipeStart = CreateEvent(NULL,   // no security
        FALSE,  // explicit auto reset req
        FALSE,  // initial event reset
        NULL);  // no name
 
    tp.hePipeReply = CreateEvent(NULL,   // no security
        FALSE,  // explicit auto reset req
        FALSE,  // initial event reset
        NULL);  // no name
 
    //Создаем тред пайпов
    DWORD dwPipesThreadID;
    HANDLE hPipesThread = CreateThread((LPSECURITY_ATTRIBUTES)NULL, 0,
    (LPTHREAD_START_ROUTINE)PipesProc, &tp, 0, &dwPipesThreadID);  //параметром передаем адрес структуры данных
 
    string s;//строка вводимых данных
 
    while (1)
    {
        cout << "Введите данные, для передачи дочернему процессу: " ;
        getline(cin, s);
        s += '\n';
        strcpy(tp.send, s.c_str());//копируем строку в массив char
        
        SetEvent(tp.hePipeStart);//даем отмашку для треда пайпов
        
        switch (WaitForSingleObject(tp.hePipeReply, 2000))//ждем ответ 2 сек
        {
        case WAIT_OBJECT_0:                                 //есть ответ
            cout << "Данные прочитаны: " << tp.read << endl;   //выводим
            break;
 
        case WAIT_TIMEOUT:                                  //увы, таймаут
            cout << "Time out! " << endl;
        }
            
        if (_getch() == 0x1b)                                   //ждем нажатие на клавишу
            break;                                              //выход по Esc
    }
 
    //закрываем все, что понаоткрывали...
    CloseHandle(tp.child_in);//закрываем канал stdin дочернего процесса
    CloseHandle(tp.parent_out);//закрываем родительский принимающий пайп
    CloseHandle(tp.parent_in);
    CloseHandle(tp.child_out);
 
    CloseHandle(tp.hePipeReply);
    CloseHandle(tp.hePipeStart);
    TerminateThread(hPipesThread, 0);
 
    TerminateProcess(pi.hProcess, 0);//убиваем дочерний процесс
    return 0;
}
Всё отлично работает для дочернего процесса типа такого:
C++
1
2
3
4
5
6
7
8
.............
string s;
while(true)
{
getline (cin, s);
cout << "Hello " << s;
}
.............
Но когда в качестве дочернего процесса пытаюсь использовать базу данных (MySQL или MariaDB),
то перенаправление потоков не срабатывает.
Например, запускается база данных нормально:
C++
1
2
3
4
5
..............
WCHAR ModuleName[] = L"C:\\Program Files\\MariaDB 10.6\\bin\\mysql.exe";
WCHAR CmdLine[] = L" \"--defaults-file=C:\\Program Files\\MariaDB 10.6\\data\\my.ini\" -uroot \"-pMyParole\"";
CreateProcess(ModuleName, CmdLine, NULL, NULL, TRUE, ABOVE_NORMAL_PRIORITY_CLASS, NULL, NULL, &si, &pi);
..............
Но при вводе правильной команды, например SHOW DATABASES; происходит Time out!
Если передать неправильную команду, то считывается сообщение об ошибке и процесс (база данных) закрывается.
Хотя при запуске MariaDB вручную те же команды можно вводить многократно и процесс (база данных) не закрывается.

Заметил, что перехваченное сообщение об ошибке отличается от выведенного в режиме ручного ввода той же комады в командной строке базы данных (смотрите рисунок).
Почему так?
Подскажите, кто знает, хотя бы в каком направлении копать и с чем это может быть связвано: с неочищенным буфером обмена, с перехватом не того потока, с кодировкой символов... ???
0
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
26.07.2021, 19:18  [ТС]
Забыл вложить рисунок
Миниатюры
Многкратное перенаправление ввода/вывода (CreatePipe)  
0
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
06.08.2021, 06:55  [ТС]
Считывание данных из mysql.exe происходит нормально только при завершении дочернего процесса.

Если команда неправильная, то процесс (mysql.exe) закрывается аварийно по ошибке Got an error reading communication packets, после чего чтение данных ReadFile срабатвает (отвисает) и читается информация об ошибке.

Если команда правильная, то ReadFile "виснет". Но при ри ручном закрытии дочернего процесса в момент зависания ReadFile, чтение срабатывает (отвисает) и в результате читается ответ на запрос к mysql.exe.


Странно, что закрытия дочернего процесса не требуется, если это обычный зацикленный процесс, типа такого:
C++
1
2
3
4
5
6
7
 ............
while(true)
{
getline (cin, s);
cout << "Hello " << s;
}
............
Почему для обычного процесса code.exe всё срабатывает неограниченное количество раз как и ожидалось, а для mysql.exe - не работает (срабатывает единыжды лишь при закрытии процесса)?
0
68 / 55 / 13
Регистрация: 26.07.2021
Сообщений: 191
06.08.2021, 10:37
Цитата Сообщение от LVV Посмотреть сообщение
Почему для обычного процесса code.exe всё срабатывает неограниченное количество раз как и ожидалось, а для mysql.exe - не работает (срабатывает единыжды лишь при закрытии процесса)?
На прошлой странице уже спрашивали, но уместно спросить ещё раз: что там насчёт символа(ов) конца строки в конце порции данных, передаваемых в mysql.exe?
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
06.08.2021, 11:46  [ТС]
tiun, я как-то не задумывался над этим.
Думал, что \n\0 достаточно. Ведь без треда срабатывало нормально (правда, при закрытии концов pipe)
Спасибо. Поработаю над этим. Может быть в этом вся причина зависаний...
0
68 / 55 / 13
Регистрация: 26.07.2021
Сообщений: 191
06.08.2021, 11:58
Цитата Сообщение от LVV Посмотреть сообщение
Думал, что \n\0 достаточно.
Это зависит от того, как принимающая программа написана/откомпилирована. Одним достаточно "\n", другие требуют "\r\n" (а в MacOS признаком конца строки вообще был "\r").

Цитата Сообщение от LVV Посмотреть сообщение
Ведь без треда срабатывало нормально (правда, при закрытии концов pipe)
Вот именно что при закрытии.
Представьте, что Вы пишете программу, которая читает файл побайтно, сама его на строки разбирает и что-то с этими строчками делает. Что для неё будет признаком того, что "вот на этом байте строка закончилась и можно передавать её в работу"? Либо то, что после этого байта из файла прочитана последовательность, означающая по какому-то соглашению конец строки, либо то, что файл кончился. А закрытие канала - это и есть полный аналог достижения конца файла.
1
 Аватар для LVV
155 / 137 / 46
Регистрация: 15.02.2010
Сообщений: 750
09.08.2021, 09:20  [ТС]
Проблема как-то непонятно исчезла.
Всё вдруг заработало после переключений --binary-mode=1. --binary-mode=0.

Правда, MySQL почему-то по-прежнему, аварийно завершает работу при любой ошибке...(Got an error reading communication packets)
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
inter-admin
Эксперт
29715 / 6470 / 2152
Регистрация: 06.03.2009
Сообщений: 28,500
Блог
09.08.2021, 09:20
Помогаю со студенческими работами здесь

Перенаправление вывода из консоли в файл, проблемы с кодировкой
Добрый день, столкнулся с проблемой. Необходимо написать небольшую программку, которая будет посылать в консоль команды(типа ping и т.д.) и...

Перенаправление потока ввода в .exe файл
Здравствуйте! Изучаю я плюсы и постоянно ищу задачи для решения. Вот, нашел такую задачу - дана программа, в которую необходимо ввести...

Mailslot и перенаправление ввода/вывода
Есть mailslot'ы, открытые как CreateMailslot так и CreateFile. Не удаётся перенаправить в/из них потоки ввода/вывода. SetStdHandle()...

c++ unix перенаправление консольного ввода\вывода
Всем доброго времени суток. Сабж. Нужно из моего приложения запустить другое и получать все данные, которое оно выводит в стандартный...

Перенаправление стандартного вывода
Как перенаправить стандартный поток вывода STD_OUTPUT_HANDLE в текстовый файл? Для перенаправления следует предусмотреть символы,...


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

Или воспользуйтесь поиском по форуму:
37
Ответ Создать тему
Новые блоги и статьи
Реалии
Hrethgir 01.03.2026
Нет, я не закончил до сих пор симулятор. Эта задача сложнее. Не получилось уйти в плавсостав, но оно и к лучшему, возможно. Точнее получалось - но сварщиком в палубную команду, а это значит, в моём. . .
Ритм жизни
kumehtar 27.02.2026
Иногда приходится жить в ритме, где дел становится всё больше, а вовлечения в происходящее — всё меньше. Плотный график не даёт вниманию закрепиться ни на одном событии. Утро начинается с быстрых,. . .
SDL3 для Web (WebAssembly): Сборка SDL3, Box2D, FreeType, SDL3_ttf, SDL3_mixer и SDL3_image из исходников с помощью CMake и Emscripten
8Observer8 27.02.2026
Недавно вышла версия 3. 4. 2 библиотеки SDL3. На странице официальной релиза доступны исходники, готовые DLL (для x86, x64, arm64), а также библиотеки для разработки под Android, MinGW и Visual Studio. . . .
SDL3 для Web (WebAssembly): Реализация движения на Box2D v3 - трение и коллизии с повёрнутыми стенами
8Observer8 20.02.2026
Содержание блога Box2D позволяет легко создать главного героя, который не проходит сквозь стены и перемещается с заданным трением о препятствия, которые можно располагать под углом, как верхнее. . .
Конвертировать закладки radiotray-ng в m3u-плейлист
damix 19.02.2026
Это можно сделать скриптом для PowerShell. Использование . \СonvertRadiotrayToM3U. ps1 <path_to_bookmarks. json> Рядом с файлом bookmarks. json появится файл bookmarks. m3u с результатом. # Check if. . .
Семь CDC на одном интерфейсе: 5 U[S]ARTов, 1 CAN и 1 SSI
Eddy_Em 18.02.2026
Постепенно допиливаю свою "многоинтерфейсную плату". Выглядит вот так: https:/ / www. cyberforum. ru/ blog_attachment. php?attachmentid=11617&stc=1&d=1771445347 Основана на STM32F303RBT6. На борту пять. . .
Камера Toupcam IUA500KMA
Eddy_Em 12.02.2026
Т. к. у всяких "хикроботов" слишком уж мелкий пиксель, для подсмотра в ESPriF они вообще плохо годятся: уже 14 величину можно рассмотреть еле-еле лишь на экспозициях под 3 секунды (а то и больше),. . .
И ясному Солнцу
zbw 12.02.2026
И ясному Солнцу, и светлой Луне. В мире покоя нет и люди не могут жить в тишине. А жить им немного лет.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru