Двусторонняя связь между родителями и детьми в Windows с С++

Прокрутите вниз, чтобы увидеть новое обновление

Обновление:: Другими словами: я хочу запустить другую программу наподобие оболочки в Windows.

Нужна двусторонняя связь между родительским и дочерним процессом с использованием С++ в Windows. Родитель — это моя программа, а дочерний — произвольное консольное приложение (например, терминал mysql).

Пару дней искал, но не смог найти никакого рабочего решения для Windows. Также документация MS не помогает.

Здесь я получил пример кода из вопроса, заданного три года назад. Как я могу перевести код в специальный API Microsoft и использовать его в Windows?

#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

#define Read            0
#define Write           1
#define ParentRead      read_pipe[1]
#define ParentWrite     write_pipe[0]
#define ChildRead       write_pipe[1]
#define ChildWrite      read_pipe[0]

int main()
{
int data_processed;

/** Pipe for reading for subprocess */
int read_pipe[2];
/** Pipe for writing to subprocess */
int write_pipe[2];

char buffer[100];
memset(buffer, '\0', 100);

if (pipe(read_pipe) == 0 && pipe(write_pipe) == 0)
{
    pid_t pid = fork();
    if (pid == (pid_t)-1)
    {
        fprintf(stderr, "Fork failure");
        exit(EXIT_FAILURE);
    }
    else if (pid == (pid_t)0) //Child process
    {
        close(Read);
        close(Write);
        close(ParentRead);
        close(ParentWrite);
        dup(ChildRead);
        dup(ChildWrite);
        execlp("cat", (char*)NULL);
        exit(EXIT_FAILURE);
    }
    else { //Parent process
        close(ChildRead);
        close(ChildWrite);

        write(ParentWrite, "abc", 3);
        int r = read(ParentRead, buffer, 99);
        printf("%d %d", r, errno);
        puts(buffer);
    }
}

exit(EXIT_SUCCESS);
}

Новое обновление:

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

#include <iostream>
#include <Windows.h>
#include <string>

HANDLE hSTD_IN_READ = NULL;
HANDLE hSTD_IN_WRITE = NULL;
HANDLE hSTD_OUT_READ = NULL;
HANDLE hSTD_OUT_WRITE = NULL;

void WriteToPipe(std::string);
void ReadFromPipe();


int main()
{
    SECURITY_ATTRIBUTES sa;

    std::cout << "\nStart of parent execution: ";

    // Set the bInheritHandle flag so pipe handles are inherited. 

    sa.nLength = sizeof(SECURITY_ATTRIBUTES);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = NULL;

    // Create pip for child process stdout
    if (!CreatePipe(&hSTD_OUT_READ, &hSTD_OUT_WRITE, &sa, 0)) {
        std::cout << "Error: CreatePipe STDOUT.\n";
        return -1;
    }
    // Ensure the read handle to the pipe for stdout is not inherited.
    if (!SetHandleInformation(hSTD_OUT_READ, HANDLE_FLAG_INHERIT, 0)) {
        std::cout << "Error: STD_OUT_READ CreatePipe.\n";
        return -1;
    }
    // Create pipe for child process's stdin
    if (!CreatePipe(&hSTD_IN_READ, &hSTD_IN_WRITE, &sa, 0)) {
        std::cout << "Error: CreatePipe STDIN.\n";
        return -1;
    }
    // Ensure the write handle to the pipe for STDIN is not inherited. 
    if (!SetHandleInformation(hSTD_IN_WRITE, HANDLE_FLAG_INHERIT, 0)) {
        std::cout << "Error: STD_IN_WRITE CreatePipe.\n";
        return -1;
    }
    // Create Child Process

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    BOOL bSuccess = FALSE;

    ZeroMemory(&pi, sizeof(PROCESS_INFORMATION));

    ZeroMemory(&si, sizeof(STARTUPINFO));
    // This structure specifies the STDIN and STDOUT handles for redirection.
    si.cb = sizeof(STARTUPINFO);
    si.hStdError = hSTD_OUT_WRITE;
    si.hStdInput = hSTD_IN_READ;
    si.hStdOutput = hSTD_OUT_WRITE;
    si.dwFlags |= STARTF_USESTDHANDLES;

    bSuccess = CreateProcess(TEXT("c:\\sqlite3.exe"), NULL, NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
    if (!bSuccess) {
        std::cout << "Error in CreateProcess.\n";
        return -1;
    }
    else {

        CloseHandle(pi.hProcess);
        CloseHandle(pi.hThread);
    }

    // Now execute your command then read the child process's output.
    WriteToPipe(".databases");
    ReadFromPipe();

    std::cout << "End of program.\n";
    return 0;
}

void WriteToPipe(std::string buffer)
{
    DWORD bWritten, BUFFSIZE = buffer.size();
    BOOL bSuccess = FALSE;


        bSuccess = WriteFile(hSTD_IN_WRITE, buffer.c_str(), BUFFSIZE, &bWritten, NULL);
        if (!bSuccess)
            std::cout << "WritetoPipe::Couldn't Write to std_in_write.\n";

        // Close the pipe handle so the child process stops reading.
        if (!CloseHandle(hSTD_IN_WRITE))
            std::cout << "WriteToPipe::Couldn't close the handle after Writing to std_in_write.\n";
    }

void ReadFromPipe()
{
    DWORD bRead, bWritten;
    const DWORD BUFFSIZE = 4096;
    CHAR buffer[BUFFSIZE];
    BOOL bSuccess = FALSE;
    HANDLE hPARENT_STD_OUT = GetStdHandle(STD_OUTPUT_HANDLE);

    for (;;) {
        bSuccess = ReadFile(hSTD_OUT_READ, buffer, BUFFSIZE, &bRead, NULL);
        if (!bSuccess || bRead == 0) {
            std::cout << "ReadFromPipe::Exiting after ReadFile.\n";
            break;
        }
        bSuccess = WriteFile(hPARENT_STD_OUT, buffer, BUFFSIZE, &bWritten, NULL);
        if (!bSuccess) {
            std::cout << "ReadFromPipe::Exiting after WriteFile.\n";
            break;
        }
    }
}

И вывод:

Start of parent execution: seq  name             file
---  ---------------  ----------------------------------------------------------
0    main
ówVE_■   öΘ/ P╞ów    (┴M (┴M         êΘ/ (┴M     £Θ/ ╕å¥w  M áΘ/ ,╟₧w     Ω/ fr#u  M ╝îíw@r#u╕   (∞/     @∩/        ΣΘ/ @               @∩/ (∞/ ⁿΘ/ ép#u@∩/ (∞/ @∩/ <ε/ o#u(∞/ @∩/ 8∩/ 4o#u┘╥₧w        ╓   ┤Ω/ ┘jƒw  M A÷₧w  M A÷₧wÿìM     ■≤₧w╪½└(     M    8┘M     M A÷₧wÿìM  ²   ¿½└(ªfM   M ~fM   xçM δ/   M A÷₧wxêM     ■≤₧wx¼└(      M    ╚½└(  M            δ/ ù    wáw  Vδ/ ╓   |δ/ ┘jƒwⁿδ/ Vδ/   ½└(αφ/ ⁿδ/
kƒw
kƒw    ⁿδ/ ░δ/ (°     M ╚╜M ΦÉM    îM Ç   ÉìM └ 3 6 9 5 7 \   ╨φ/ |δ/ πlƒw╢êM ¿δ/ \   αφ/ ^      ¼φ/ πiƒw╨φ/ áδ/     `ε/ ≥iƒw    ┐M \ ^ ¿δ/ S -   M A÷₧wÿêM     ■≤₧wP¡└(p     M h   1 6 2 2 ╠ ╔8 0   - 3 6 9 5 7 ╠ ╔8 1 4 - öfM 0 1       &▄₧w  B·  A÷₧w       ÿêM c : ┘╥₧w@¡└(  M ÉêM     ╠ ╔  M ╠ ╔ê├M   äM Ç   ╨∞/ ╠ ╔φ/ Ä╘Γ á∞/
╬M    \φ/        φ/ \φ/ ╠ ╔=·  zφ/   M ÉêM ñÉM ≡∞/ Ç    ë½w┬ǃwL  |φ/    ÿêM      └&       ê╤M ╕∞/           /  ówVE_■   dφ/ P╞ów    ÿêM ÿêM ╕φ/ á∩/     ÿêM     `M S B _ E tφ/ ,╟₧w    ÿêM °∩/ @X#u  M     ÿêM     ɱ/ SX#áwɱ/ ┐M                               DZ/     P   P6*uá∩/ ÿêM                                                                                                                                                                                                                                                                                                                                                                                                                                                                                               WΓF┼CÑ■ ìεπ╙≡WΓF┼CÑ■ ìεπ╙≡                           _╩╝H±/ ñU#u          DZ/     t⌠/ ▒U#u
        (                                                                                                             ┘╥₧w╨▒└(┘╥₧w╪▒└(  M ┐M     ╚▒└(  M ░╛M             ≈         NM    ╠ ╔                              α╛M   M ░╛M ±   ▒X#u   ░╛M Ç      Ç      (╟M     ╕╛M ╠  └ M    L       ëM ╕╛M    ÿêM              ╪ÅM    ╪ÅM    `M ówVE_■   ▄±/ P╞ów    ┐M   M         ╕╛M ┐M ╕╛M     α±/ ╕╛M Φ±/ ,╟₧w    ⁿ±/ ?ñ#u  M     ┐M ╚≈/     ╚≈/ g⌡Θv╩ê∞v╬∩Θv╪D╔@┬Q┌ P_$u    ╬ ╧ ╨ ╤ ╥ ╙ ╘ ╒ ╓ ╫ ╪ ┘ ┌ █ ▄ ▌ ▐ ▀ α ß Γ π Σ σ                                                     αëM         â   É▀█ D       'M                                            ö   É   É   l@                       L      é£        "   ┐M ?      α²    √   @┤└(└           └ M                +   M     >  >          M        └ M                M ░  &  &  M H          ñ   ¿                 @   ≡²     α²             á&    `°/                        £  t  h╝M     +             █               [           h  @           M                                                        P°/ ┐M ╨⌡/  α² ╨&  °█     ¿                 ██                     ╕╛M
                       ¿       ■≤₧  └(    ñ              ╕╛M                  °█ c               └        M █  █'   └ M Ω    X╝M &         └     ■       ┘╥₧wá   £   £  t          └ M Ç   @   @  ñ     °█ $ & Φ╛M                                                           ╠⌠v   î÷/ á÷/ PΩ¥w╠⌠vî÷/       H⌠v   |⌠vH⌠v ⌠v▌Θ¥w░÷/         └≈/ ≤τ¥w┬Q┌ ÿ   Φ¥w    2   ⌠v  ⌠v   ì                °≈/ H⌠v                                                                                                                                                                                                                                                                            ⌠╢└(∞≈/ ┤ïΘv½½½½╝îíw╪ïΘvÿ   ┬Q┌ φïΘv░┌ ÿ   ⁿD╔@°/ ╩┌ ÿ   ■┌
   ┌S╙╫∞°/ Φå█     ┤°/ 8┌ .databas

person hedisam    schedule 10.09.2015    source источник
comment
Один из способов — использовать именованные каналы. Документация и пример кода находятся здесь: msdn.microsoft.com/en-us/library/windows/desktop/ Для клиента вы, вероятно, захотите внедрить прослушиватель через CreateRemoteThread.   -  person fassl    schedule 10.09.2015
comment
Взгляните на примеры клиента и сервера для boost::asio.   -  person utnapistim    schedule 10.09.2015
comment
См. Что самое близкое, что окна должны разветвлять ()? и вы можете посмотреть документацию по функции spawn() _spawn, Функции _wspawn   -  person Richard Chambers    schedule 10.09.2015
comment
Спасибо всем за ответ. Прямо сейчас я борюсь с именованными каналами и изучаю документы MS, которые меня смутили, я не знаю, как я должен выполнять внешние консольные приложения, такие как терминал cmd или mysql.   -  person hedisam    schedule 10.09.2015
comment
@ChristopherOicles Спасибо, но я видел это раньше.   -  person hedisam    schedule 10.09.2015
comment
@fassl многопоточный канальный сервер пример кода в документах MS смутил меня. Должен ли я использовать функцию CreateProcess() с модифицированной версией образца?   -  person hedisam    schedule 10.09.2015
comment
Для использования cmd вы можете вызвать system(command); Я думаю. Также я не знаю, правильно ли я понял ваш вопрос ... Вы хотите общаться с другим приложением, а не перенаправлять вывод другого приложения в свое приложение, как предлагают другие комментарии здесь, верно?   -  person fassl    schedule 10.09.2015
comment
Функция @fassl system() хороша для таких команд, как dir. когда выполняется system(dir), он дает вам список, и все готово. Подумайте о такой команде, как ftp. когда вы выполняете команду ftp, он открывает сеанс и ждет, пока вы дадите ему определенные команды, и когда вы закончите, вы передадите команду quit, и сеанс ftp завершится. Сделать это с помощью функции system() невозможно. Можешь попробовать.   -  person hedisam    schedule 10.09.2015


Ответы (1)


Как сказал Кристофер Ойклс в ссылка link, логика использования WinAPI такая же:

  • вы сначала создаете каналы для ввода и вывода - функция WinAPI CreatePipe
  • WinAPI позволяет защитить дескриптор от наследования, потому что у вас нет fork+exec для закрытия чего-либо в дочернем процессе — вы должны использовать SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); перед созданием дочернего процесса
  • вы создаете дочерний процесс с CreateProcess, передавая дескрипторы созданным выше каналам в полях hStdInput, hStdOutput и hStdError структуры STARTUPINFO - не забудьте объявить, что вы хотите использовать их с dwFlags |= STARTF_USESTDHANDLES; в той же структуре.

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


В вашем коде есть по крайней мере одна веская причина не писать то, что вы ожидаете. В ReadFromPipe вы получаете bRead байт в буфер, но пытаетесь вывести весь буфер.

Выходная команда должна быть:

bSuccess = WriteFile(hPARENT_STD_OUT, buffer, bRead, &bWritten, NULL);

Но это еще не все. Команда должна быть завершена так же, как и во входном файле, то есть с \r\n, поэтому ваша команда должна быть:

WriteToPipe(".databases\r\n");

И последнее, но не менее важное: вы должны закрыть неиспользуемые части каналов после запуска дочерней команды, по крайней мере, hSTD_OUT_WRITE, чтобы позволить ReadFile(hSTD_OUT_READ, ...) возвращать 0, как только дочерний процесс завершается. Итак, ваш код должен быть:

CloseHandle(hSTD_OUT_WRITE);
CloseHandle(hSTD_IN_READ);

// Now execute your command then read the child process's output.
...

С этими изменениями я мог успешно общаться с программой ftp.exe.

person Serge Ballesta    schedule 10.09.2015
comment
Спасибо за Ваш ответ. Я буду признателен, если вы проверите обновленный вопрос, пожалуйста. - person hedisam; 11.09.2015
comment
Спасибо большое. Сейчас намного лучше. Но сколько команд вы отправили на ftp.exe? это нормально с одной единственной командой. Очевидно, что if (!CloseHandle(hSTD_IN_WRITE)) в функции WriteToPipe(std::string) закрывает дескриптор и не позволяет мне отправлять команды несколько раз. Так что я переместил это как раз перед std::cout << "End of program.\n";, все еще не работает. - person hedisam; 12.09.2015
comment
@Sam: Должен признать, что я сделал 2 рабочих теста. Один с вашим кодом, но передает все ftp-команды в одну строку: "open host.domain\r\nuser\r\npass\r\ndir\r\n", другой передает множество отдельных команд с закомментированным CloseHandle и явно закрывает перед вызовом ReadPipe, чтобы дочерний процесс был красиво завершен. - person Serge Ballesta; 12.09.2015
comment
Да, ваша схема мне тоже подошла. Таким образом, это означает, что мы не можем сделать это: WriteToPipe("command1"); ReadFromPipe(); WriteToPipe("command2"); ReadFromPipe(); Поскольку мне нужно выполнить мои команды одну за другой, затем прочитать конкретный вывод каждой команды, а затем в соответствии с выводом запустить следующую команду. - person hedisam; 12.09.2015
comment
Как вы могли понять из моего предыдущего комментария, мне нужно проанализировать вывод. Итак, могу ли я использовать объект std::stringstream or std::string вместо hPARENT_STD_OUT ??? - person hedisam; 12.09.2015
comment
О, нет необходимости в std::stringstream or std::string. Мне просто нужно удалить bSuccess = WriteFile(hPARENT_STD_OUT, ...), а затем проанализировать buffer и все. Но все равно нужно запускать отдельные команды. Run command1 get the output run command2 get the output - person hedisam; 12.09.2015