0

i'm working on a project where i need to launch an external program (process) and send messages to its STDIN and read its STDOUT and STDERR.

I can't use anonymous pipes because i'm in a multi-threaded asynchronous environment, I read a lot of MSDN documentation and examples and i have something that somewhat works.

I created a named pipe and created multiple handles with the same name, each one for the different streams, the wait for incoming messages from STDOUT and STDERR (of the child process) is done in another thread.

I wanted to implement the send message part where i can write into the child's STDIN but i couldn't get it to work, or so i thought. I wrote a little program so i can see what's going on and used it as my child process.

the program gives you three choices 1 write to STDOUT 2 write to STDERR and 3 exit.

I have to questions/issues :

1- when i use std::cin to read what's coming from my parent/server program i can't get the value i'm sending, but when using fread to read a byte from STDIN it works and i can get my value.

2- i created the pipes with the FILE_FLAG_OVERLAPPED do i have to use the overlapped structure for my write operation (i couldn't get it to work), because i just used the Write function and put NULL on the overlapped structure.

    STARTUPINFO si;
    PROCESS_INFORMATION pi;
    SECURITY_ATTRIBUTES sa;

    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
    if (fHideConsole)
    {
        si.dwFlags |= STARTF_USESHOWWINDOW;
        si.wShowWindow = SW_HIDE;
    }

    ZeroMemory(&pi, sizeof(pi));

    // Sets the bInheritHandle flag so that pipes are inherited (TODO depends on what is open)
    SECURITY_DESCRIPTOR saDesc;
    InitializeSecurityDescriptor(&saDesc, SECURITY_DESCRIPTOR_REVISION);
    SetSecurityDescriptorDacl(&saDesc, TRUE, NULL, FALSE);

    sa.nLength = sizeof(sa);
    sa.bInheritHandle = TRUE;
    sa.lpSecurityDescriptor = &saDesc;

    si.dwFlags |= STARTF_USESTDHANDLES;
    si.hStdOutput = INVALID_HANDLE_VALUE;
    si.hStdError = INVALID_HANDLE_VALUE;
    si.hStdInput = INVALID_HANDLE_VALUE;

    if (inReadCallback)
    {
        //if (!CreateOverlappedPipe(&fOutPipe, &outPipeWrite, &sa))
        //  err = GetLastError();
        //if (!SetHandleInformation(fOutPipe, HANDLE_FLAG_INHERIT, 0))
        //  err = GetLastError();

        fOutPipe = CreateNamedPipe(
            L"\\\\.\\Pipe\\RemoteExeOut",
            PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
            PIPE_TYPE_BYTE | PIPE_WAIT,
            PIPE_UNLIMITED_INSTANCES,           // Number of pipes
            kSizeRead,  // Out buffer size
            kSizeRead,  // In buffer size
            0,          // Timeout in ms
            &sa);


        si.hStdOutput = CreateFileW(
            L"\\\\.\\Pipe\\RemoteExeOut",
            FILE_WRITE_DATA,
            0,                         // No sharing
            &sa,
            OPEN_EXISTING,
            0,
            NULL);
    }

    if (inReadErrorCallback)
    {
        fErrPipe = CreateNamedPipe(
            L"\\\\.\\Pipe\\RemoteExeErr",
            PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
            PIPE_TYPE_BYTE | PIPE_WAIT,
            PIPE_UNLIMITED_INSTANCES,           // Number of pipes
            kSizeRead,  // Out buffer size
            kSizeRead,  // In buffer size
            0,          // Timeout in ms
            &sa);

        si.hStdError = CreateFileW(
            L"\\\\.\\Pipe\\RemoteExeErr",
            FILE_WRITE_DATA,
            0,                         // No sharing
            &sa,
            OPEN_EXISTING,
            0,
            NULL);
    }

    fInPipe = CreateNamedPipe(
        L"\\\\.\\Pipe\\RemoteExeIn",
        PIPE_ACCESS_OUTBOUND,   //PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
        PIPE_TYPE_BYTE | PIPE_WAIT,
        PIPE_UNLIMITED_INSTANCES,           // Number of pipes
        kSizeRead,  // Out buffer size
        kSizeRead,  // In buffer size
        0,          // Timeout in ms
        &sa
    );
    si.hStdInput = CreateFileW(
        L"\\\\.\\Pipe\\RemoteExeIn",
        FILE_READ_DATA,
        0,                         // No sharing
        &sa,
        OPEN_EXISTING,
        0,
        NULL); 

    if (CreateProcessW(nullptr, cmd, nullptr, nullptr, TRUE, 0, inEnvironment, currDir, &si, &pi))
    {
        // custom code to send to thread
        fIsRunning = true;
        fPid = pi.dwProcessId;

        WaitData* wData = new WaitData{ pi.dwProcessId, static_cast<DWORD>(inTimeoutSeconds), this, inTerminatedCallback };
        wData->readHandle = fOutPipe;
        wData->readCallback = inReadCallback;
        wData->errorHandle = fErrPipe;
        wData->errorCallback = inReadErrorCallback;

        fWaitingTask = new VTask(NULL, 0, eTaskStylePreemptive, WaitFct);
        fWaitingTask->SetKind('WLEP');
        fWaitingTask->SetKindData(reinterpret_cast<sLONG_PTR>(wData));
        fWaitingTask->Run();
    }
    else
    {
        vThrowWin32Error(GetLastError());   // custom error handling
    }

    delete[] cmd;
    delete[] currDir;
    // Close process and thread handles.
    if (pi.hProcess != INVALID_HANDLE_VALUE)
        CloseHandle(pi.hProcess);
    if (pi.hThread != INVALID_HANDLE_VALUE)
        CloseHandle(pi.hThread);

    if (si.hStdInput != INVALID_HANDLE_VALUE)
        CloseHandle(si.hStdInput);
    if (si.hStdOutput != INVALID_HANDLE_VALUE)
        CloseHandle(si.hStdOutput);
    if (si.hStdError != INVALID_HANDLE_VALUE)
        CloseHandle(si.hStdError);

my write function :

DWORD dwWritten = 0;
OVERLAPPED ovr;

BOOL success = WriteFile(fInPipe, inBuffer, inSize, &dwWritten, nullptr);

if (!success)
{
    DWORD err = GetLastError();
    if (err == ERROR_BROKEN_PIPE)
        DebugMsg(" !!!!!!!!! BROKEN PIPE !!!!!!!");
    if (err == ERROR_IO_PENDING)
        DebugMsg(" IO IS PENDING ASYNC USE");
}

Thanks in Advance.

Bob Maza
  • 49
  • 7
  • I'm not sure I see the problem with using anonymous pipes. Can you please elaborate? – Some programmer dude Oct 08 '21 at 09:03
  • for what you create 3 pipe pair, when 1 is enough ? (if you only not want separate stdout and stderr ). then for what you create files for asyncronous I/O when you really not use it ? (std::cin, fread, put NULL on the overlapped structure) – RbMm Oct 08 '21 at 10:08
  • @Someprogrammerdude you can't use anonymous pipes in to do asynchronous I/O. – Bob Maza Oct 08 '21 at 12:00
  • @RbMm i'm having a lot of trouble understanding what you're trying to say – Bob Maza Oct 08 '21 at 12:01
  • @BobMaza - we can use anonymous pipes for asynchronous I/O. another question than need use `ZwCreateNamedPipeFile` use for this. i try ask - for what you use so mamy pipe pairs when single pair is enouh. and for what you want asynchronous I/O when you really not do it – RbMm Oct 08 '21 at 12:05
  • Apparently anonymous pipes are just named pipes with a random name under the hood. See e.g. [this old answer](https://stackoverflow.com/questions/60645/overlapped-i-o-on-anonymous-pipe/51448441#51448441) for more information. – Some programmer dude Oct 08 '21 at 12:45
  • i am doing it for the reading part , and when i try to use the overlapped structure in my write function it doesn't work that's the whole reason why i'm posting – Bob Maza Oct 08 '21 at 12:46
  • @Someprogrammerdude - this was before win7, begin from 7 - possible create piper pair with empty name. – RbMm Oct 08 '21 at 13:09

0 Answers0