How to detect when subprocess asks for input in Windows

后端 未结 2 1305
名媛妹妹
名媛妹妹 2021-02-20 17:12

I have a subprocess that either quits with a returncode, or asks something and waits for user input.

I would like to detect when the process asks the question and quit i

2条回答
  •  有刺的猬
    2021-02-20 17:53

    if we not want let to child process process user input, but simply kill it in this case, solution can be next:

    • start child process with redirected stdin to pipe.
    • pipe server end we create in asynchronous mode and main set pipe buffer to 0 size
    • before start child - write 1 byte to this pipe.
    • because pipe buffer is 0 size - operation not complete, until another side not read this byte
    • after we write this 1 byte and operation in progress (pending) - start child process.
    • finally begin wait what complete first: write operation or child process ?
    • if write complete first - this mean, that child process begin read from stdin - so kill it at this point

    one possible implementation on c++:

    struct ReadWriteContext : public OVERLAPPED
    {
        enum OpType : char { e_write, e_read } _op;
        BOOLEAN _bCompleted;
    
        ReadWriteContext(OpType op) : _op(op), _bCompleted(false)
        {
        }
    };
    
    VOID WINAPI OnReadWrite(DWORD dwErrorCode, DWORD dwNumberOfBytesTransfered, OVERLAPPED* lpOverlapped)
    {
        static_cast(lpOverlapped)->_bCompleted = TRUE;
        DbgPrint("%u:%x %p\n", static_cast(lpOverlapped)->_op, dwErrorCode, dwNumberOfBytesTransfered);
    }
    
    void nul(PCWSTR lpApplicationName)
    {
        ReadWriteContext wc(ReadWriteContext::e_write), rc(ReadWriteContext::e_read);
    
        static const WCHAR pipename[] = L"\\\\?\\pipe\\{221B9EC9-85E6-4b64-9B70-249026EFAEAF}";
    
        if (HANDLE hPipe = CreateNamedPipeW(pipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, 
            PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1, 0, 0, 0, 0))
        {
            static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
            PROCESS_INFORMATION pi;
            STARTUPINFOW si = { sizeof(si)};
            si.dwFlags = STARTF_USESTDHANDLES;
            si.hStdInput = CreateFileW(pipename, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
    
            if (INVALID_HANDLE_VALUE != si.hStdInput)
            {
                char buf[256];
    
                if (WriteFileEx(hPipe, "\n", 1, &wc, OnReadWrite))
                {
                    si.hStdError = si.hStdOutput = si.hStdInput;
    
                    if (CreateProcessW(lpApplicationName, 0, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
                    {
                        CloseHandle(pi.hThread);
    
                        BOOLEAN bQuit = true;
    
                        goto __read;
                        do 
                        {
                            bQuit = true;
    
                            switch (WaitForSingleObjectEx(pi.hProcess, INFINITE, TRUE))
                            {
                            case WAIT_OBJECT_0:
                                DbgPrint("child terminated\n");
                                break;
                            case WAIT_IO_COMPLETION:
                                if (wc._bCompleted)
                                {
                                    DbgPrint("child read from hStdInput!\n");
                                    TerminateProcess(pi.hProcess, 0);
                                }
                                else if (rc._bCompleted)
                                {
    __read:
                                    rc._bCompleted = false;
                                    if (ReadFileEx(hPipe, buf, sizeof(buf), &rc, OnReadWrite))
                                    {
                                        bQuit = false;
                                    }
                                }
                                break;
                            default:
                                __debugbreak();
                            }
                        } while (!bQuit);
    
                        CloseHandle(pi.hProcess);
                    }
                }
    
                CloseHandle(si.hStdInput);
    
                // let execute pending apc
                SleepEx(0, TRUE);
            }
    
            CloseHandle(hPipe);
        }
    }
    

    another variant of code - use event completion, instead apc. however this not affect final result. this variant of code give absolute the same result as first:

    void nul(PCWSTR lpApplicationName)
    {
        OVERLAPPED ovw = {}, ovr = {};
    
        if (ovr.hEvent = CreateEvent(0, 0, 0, 0))
        {
            if (ovw.hEvent = CreateEvent(0, 0, 0, 0))
            {
                static const WCHAR pipename[] = L"\\\\?\\pipe\\{221B9EC9-85E6-4b64-9B70-249026EFAEAF}";
    
                if (HANDLE hPipe = CreateNamedPipeW(pipename, PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED, 
                    PIPE_TYPE_BYTE|PIPE_READMODE_BYTE|PIPE_WAIT, 1, 0, 0, 0, 0))
                {
                    static SECURITY_ATTRIBUTES sa = { sizeof(sa), 0, TRUE };
                    PROCESS_INFORMATION pi;
                    STARTUPINFOW si = { sizeof(si)};
                    si.dwFlags = STARTF_USESTDHANDLES;
                    si.hStdInput = CreateFileW(pipename, FILE_GENERIC_READ|FILE_GENERIC_WRITE, 0, &sa, OPEN_EXISTING, 0, 0);
    
                    if (INVALID_HANDLE_VALUE != si.hStdInput)
                    {
                        char buf[256];
    
                        if (!WriteFile(hPipe, "\n", 1, 0, &ovw) && GetLastError() == ERROR_IO_PENDING)
                        {
                            si.hStdError = si.hStdOutput = si.hStdInput;
    
                            if (CreateProcessW(lpApplicationName, 0, 0, 0, TRUE, CREATE_NO_WINDOW, 0, 0, &si, &pi))
                            {
                                CloseHandle(pi.hThread);
    
                                BOOLEAN bQuit = true;
    
                                HANDLE h[] = { ovr.hEvent, ovw.hEvent, pi.hProcess };
    
                                goto __read;
                                do 
                                {
                                    bQuit = true;
    
                                    switch (WaitForMultipleObjects(3, h, false, INFINITE))
                                    {
                                    case WAIT_OBJECT_0 + 0://read completed
    __read:
                                        if (ReadFile(hPipe, buf, sizeof(buf), 0, &ovr) || GetLastError() == ERROR_IO_PENDING)
                                        {
                                            bQuit = false;
                                        }
                                        break;
                                    case WAIT_OBJECT_0 + 1://write completed
                                        DbgPrint("child read from hStdInput!\n");
                                        TerminateProcess(pi.hProcess, 0);
                                        break;
                                    case WAIT_OBJECT_0 + 2://process terminated
                                        DbgPrint("child terminated\n");
                                        break;
                                    default:
                                        __debugbreak();
                                    }
                                } while (!bQuit);
    
                                CloseHandle(pi.hProcess);
                            }
                        }
    
                        CloseHandle(si.hStdInput);
                    }
    
                    CloseHandle(hPipe);
                    // all pending operation completed here.
                }
    
                CloseHandle(ovw.hEvent);
            }
    
            CloseHandle(ovr.hEvent);
        }
    }
    

提交回复
热议问题