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

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

15.07.2021, 13:00. Показов 13394. Ответов 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
Ответ Создать тему
Новые блоги и статьи
Поиск всех путей на ориентированном графе. Linux
dcc0 02.07.2026
Переработка старого кода из моей статьи. Через несколько переработок от PHP кода к C89 (надеюсь, 89). Но довольно запутанно получилось. Код для Linux. Но если убрать time и то, что с ним. . .
Сам себя обучал rest api
anaschu 02.07.2026
Педагогический лайфхак: Почему чистый REST API для ученика намного круче, чем готовые библиотеки Когда мы отказались от капризного JAR-файла AnyLogic и переписали код на стандартный HttpClient,. . .
rest api anylogic - выполнение модели на своём русском сайте
anaschu 02.07.2026
Как подружиться с AnyLogic Cloud API, победить провайдеров и развернуться Java-бэкенд в Docker на бесплатном хостинге: Двухдневный лог борьбы Всем привет! Хочу поделиться свежим (и довольно. . .
Где деньги лежат
kumehtar 02.07.2026
Это - японская подводная лодка I-52 (тип C2, кодовое имя Momi) вышла из Японии в марте 1944 года с миссией в оккупированную немцами Францию (Лорьян). Это была одна из «Янаги»-миссий по обмену. . .
Krabik для WoW 3.3.5a, многоязычный
AmbA 02.07.2026
Допилил бота, думаю что окончательно. Изменения: - добавлена многоязычность - добавлено снятие скриншотов - добавлено поддержание бафов хождения по воде (для жреца, дк и шамана) - и так, по. . .
Алиса нашла кучу ошибок компиляции и запуска в проекте, который без проблем компилировался и запускался)))
anaschu 30.06.2026
Я пока посмеюся, но завтра проверю. А вообще интерсно. Дал алисе файл, в котором точно нет ошибок компиляции и запуска, и попросил их найти. Нашла кучу))) Критические ошибки, мешающие компиляции и. . .
сукцессия 16. Общий обзор, в основном что бы другие ии поняли
anaschu 29.06.2026
# Передаточный документ: модель микоризной сукцессии (для нового чата) Этот документ предназначен для того, чтобы новый чат Claude мог продолжить работу без необходимости заново разбираться в. . .
сукцессия 15 неявная схема
anaschu 29.06.2026
Алиса Калибровка параметров симбиотической модели: технический обзор Содержание: Введение Постановка проблемы Технические аспекты реализации Процесс внедрения изменений
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru