2

Scroll down to see new update

Update:: In other word: I want to launch another program in the manner of a shell on Windows.

Need a two way communication between parent and child process using c++ on Windows. The parent is my program and the child is a random console application (like mysql terminal).

It's been a couple days searching but couldn't find any working solution for Windows. Also MS documentations is not helping.

Here i got a sample code from a question asked three years ago. How can i translate the code to Microsoft specific api and use it on 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);
}

New Update:

So based on this sample I wrote a modified version of the sample code and seems to be OK except that the redirected output is not what it's supposed to be. Here's the code:

#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;
        }
    }
}

And the output:

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
hedisam
  • 521
  • 6
  • 10
  • One way is to use named pipes, documentation and sample code is here: https://msdn.microsoft.com/en-us/library/windows/desktop/aa365590(v=vs.85).aspx For the client you probably want to inject a listener via CreateRemoteThread. – fassl Sep 10 '15 at 12:09
  • Have a look at the [client and server samples](http://www.boost.org/doc/libs/1_53_0/doc/html/boost_asio/examples.html) for [boost::asio](http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio.html). – utnapistim Sep 10 '15 at 12:36
  • See [What is the closest thing windows has to fork()?](http://stackoverflow.com/questions/985281/what-is-the-closest-thing-windows-has-to-fork) and you may want to look at the documentation for the `spawn()` function [_spawn, _wspawn Functions](https://msdn.microsoft.com/en-us/library/20y988d2.aspx) – Richard Chambers Sep 10 '15 at 12:38
  • [Creating a Child Process with Redirected Input and Output](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx) – Christopher Oicles Sep 10 '15 at 13:40
  • Thank you all for responding. Right now I'm struggling with named pipes and studying MS docs which has confused me don't know how am i supposed to execute external console apps like cmd or mysql terminal. – hedisam Sep 10 '15 at 14:09
  • @ChristopherOicles Thanks but I've seen that before. – hedisam Sep 10 '15 at 14:15
  • @fassl The [Multithreaded pipe server sample code on MS docs](https://msdn.microsoft.com/en-us/library/windows/desktop/aa365588%28v=vs.85%29.aspx) confused me. Should i use CreateProcess() func with a modified version of the sample? – hedisam Sep 10 '15 at 14:16
  • For using cmd u could call system("command"); i think. Also i dont know if i understood your question correctly... You want to communicate with another app, not redirect the output of another app to your app like other comments here suggest, right? – fassl Sep 10 '15 at 17:14
  • 1
    @fassl system() function is good for commands like "dir". when system("dir") be executed it gives you a list and then you're done. Think of a command like "ftp". when you execute ftp command it opens a session and wait for you to give it specific commands and when you're done you pass "quit" command and the ftp session ends. Doing such a thing with system() function is not possible. You can try it. – hedisam Sep 10 '15 at 18:14

1 Answers1

2

As said by Christopher Oicles in the referenced link, the logic using WinAPI is the same:

  • you first create pipes for input and output - the WinAPI function is CreatePipe
  • WinAPI allows to protect a handle for being inherited, because you do not have the fork+exec to close anything in the child - you must use SetHandleInformation(handle, HANDLE_FLAG_INHERIT, 0); before creating the child process
  • you create the child process with CreateProcess, passing the handles to the pipes created above in the fields hStdInput, hStdOutput and hStdError of the STARTUPINFO struct - do not forget to declare that you want to use them with dwFlags |= STARTF_USESTDHANDLES; in same struct.

This only makes sense with console application childs, because GUI subsystem application normally do not use standard streams, but it will work fine with the ftp command. Simply, as you process the streams yourself, the system do not create a console and the output from the child process is only available in the parent process.


In your code, there is at least one good reason for not writing what you expect. In ReadFromPipe, you get bRead bytes in your buffer, but you try to output the full buffer.

The output command should be:

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

But that is not all. A command should be terminated the same that it would be in a input file, that is with \r\n, so your command should be:

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

And last but not least, you must close the unused parts of the pipes after starting child command, at least hSTD_OUT_WRITE to allow ReadFile(hSTD_OUT_READ, ...) to return 0 as soon as the child process exits. So your code should be:

CloseHandle(hSTD_OUT_WRITE);
CloseHandle(hSTD_IN_READ);

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

With those modifications, I could successfully communicate with the ftp.exe program.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • Thanks for your answer. I'll appreciate if you check the updated question, please. – hedisam Sep 11 '15 at 20:11
  • Thank you a lot. It's so much better now. But how many command did you send to ftp.exe? it's OK with one single command. Obviously `if (!CloseHandle(hSTD_IN_WRITE))` in the `WriteToPipe(std::string)` function closes the handle and doesn't let me send commands multiple times. So I moved that just before `std::cout << "End of program.\n";`, still not working. – hedisam Sep 12 '15 at 10:29
  • @Sam: I must admit that I made 2 working tests. One with your code but passing all ftp commands in one single string: `"open host.domain\r\nuser\r\npass\r\ndir\r\n"`, the other passing many separate commands with the `CloseHandle` commented out, and explicitely closing before calling `ReadPipe` to have the child nicely exit. – Serge Ballesta Sep 12 '15 at 11:38
  • Yeah your pattern worked for me too. So it means we can't do this: `WriteToPipe("command1"); ReadFromPipe(); WriteToPipe("command2"); ReadFromPipe();` Because I need to execute my commands one after another then read each command's specific output then according to the ouput run the next command. – hedisam Sep 12 '15 at 12:23
  • As you may understood from my previous comment, I need to analysis the output. So can I use a `std::stringstream or std::string` object instead of `hPARENT_STD_OUT` ??? – hedisam Sep 12 '15 at 12:30
  • Oh, there's no need for `std::stringstream or std::string`. I just need to remove `bSuccess = WriteFile(hPARENT_STD_OUT, ...)` then analysis the `buffer` that's all. But still need to run separate commands. `Run command1` `get the output` `run command2` `get the output` – hedisam Sep 12 '15 at 13:18