简体   繁体   English

将输入\输出管道从父进程重定向到 cmd 子进程 C/C++ WinApi

[英]Redirect Input\Output pipes from parent process to cmd child process C/C++ WinApi

I have been trying to run a parent process that acts like a cmd input\output console.我一直在尝试运行一个类似于 cmd 输入\输出控制台的父进程。 the purpose of this is for future use, but for now I am trying to isolate this function in order to get things right.这样做的目的是为了将来使用,但现在我正在尝试隔离这个 function 以使事情正确。

What I am trying to achieve here is an input from user send it to a simple cmd child process, get the output and keep on sending commands, while keeping the process running.The problem is that the cmd process closes right after the first command is executed.我在这里想要实现的是用户的输入将其发送到一个简单的 cmd 子进程,获取 output 并继续发送命令,同时保持进程运行。问题是 ZDFFF0A7FA1A55C8C45A4966C19ZFDA 进程在第一个命令之后关闭执行。 I pasted an output of what happens right after the first command,the issue that rises once I try to keep on going and inputting more commands is that the handle is not valid (since the process is terminated).我粘贴了 output 在第一个命令之后发生的事情,一旦我尝试继续输入更多命令,就会出现的问题是句柄无效(因为进程终止)。

Help would be much appreciated!帮助将不胜感激!

Main code:主要代码:

#define  _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <strsafe.h>
#define bzero(p,size) (void) memset((p), 0 , (size))


// Constant
#define BUFSIZE 4096
// Global variables
HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;
HANDLE g_hInputFile = NULL;


// Create a child process that uses the previously created pipes for STDIN and STDOUT.
PROCESS_INFORMATION CreateChildProcess()
{
    // The following should be the child executable, see the next program example
    // Change the path accordingly...
    WCHAR szCmdline[] = L"cmd.exe";
    PROCESS_INFORMATION piProcInfo;
    STARTUPINFO siStartInfo;
    BOOL bSuccess = FALSE;
    // Set up members of the PROCESS_INFORMATION structure
    ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
    // Set up members of the STARTUPINFO structure
    // This structure specifies the STDIN and STDOUT handles for redirection
    ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
    siStartInfo.cb = sizeof(STARTUPINFO);
    siStartInfo.hStdError = g_hChildStd_OUT_Wr;
    siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
    siStartInfo.hStdInput = g_hChildStd_IN_Rd;
    siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
    // Create the child process
    bSuccess = CreateProcess(NULL, // Use szCmdLine
        szCmdline,     // command line
        NULL,          // process security attributes
        NULL,          // primary thread security attributes
        TRUE,          // handles are inherited
        CREATE_NEW_CONSOLE,             // creation flags
        NULL,          // use parent's environment
        NULL,          // use parent's current directory
        &siStartInfo,  // STARTUPINFO pointer
        &piProcInfo);  // receives PROCESS_INFORMATION
     // If an error occurs, exit the application.
    if (!bSuccess)
        printf("Error creating process, %d\n", GetLastError());

    else
    {
        wprintf(L"\nChild process ID is : %u\n", GetCurrentProcessId());
        wprintf(L"Child thread ID is : %u\n", GetCurrentThreadId());
        /*
        if (CloseHandle(piProcInfo.hProcess) != 0)
            wprintf(L"piProcInfo.hProcess handle was closed!\n");
        else
            printf("Error closing process , %d\n", GetLastError());

        if (CloseHandle(piProcInfo.hThread) != 0)
            wprintf(L"piProcInfo.hThread handle was closed!\n");
        else
            printf("Error closing process thread handle, %d\n", GetLastError());
            */
    }
    return piProcInfo;
}

void WriteToPipe(char * command)
{
    DWORD dwWritten;
    DWORD dwRead;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;
    bSuccess = WriteFile(g_hChildStd_IN_Wr, (LPVOID)command , (DWORD)strlen(command), &dwWritten, NULL);
    printf("Command: %s\n", command);
    if (!bSuccess)
    {
        wprintf(L"\nWriteFile() - Failed to write to pipe for child\'s STDIN! Error %u\n", GetLastError());
        //break;
    }
    else
        wprintf(L"\nWriteFile() - writing to the pipe for the child\'s STDIN...\n");
    // Close the pipe handle so the child process stops reading
    if (!CloseHandle(g_hChildStd_IN_Wr))
        printf("Error closing STDIN handle, %d\n", GetLastError());
    else
        wprintf(L"Closing the pipe handle...\n");

    //Read from pipe
    bSuccess = FALSE;
    bzero(chBuf, sizeof(chBuf));
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (!CloseHandle(g_hChildStd_OUT_Wr))
        printf("Error closing STDOUT handle, %d\n", GetLastError());
    for (;;)
    {
        bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
        if (!bSuccess || dwRead == 0)
        {
            wprintf(L"\nReadFile() from child's standard output failed! Error %u\n", GetLastError());
            break;
        }
        bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
        if (!bSuccess)
        {
            wprintf(L"\nWriteFile() to parent's standard output failed! Error %u\n", GetLastError());
            break;
        }
    }
}

int main()
{
    PROCESS_INFORMATION pi;
    char filename[100];
    ZeroMemory(&filename, sizeof(filename));
    strcpy(filename, "commands.txt");
    SECURITY_ATTRIBUTES saAttr;
    wprintf(L"Parent process ID %u\n", GetCurrentProcessId());
    // Set the bInheritHandle flag so pipe handles are inherited
    saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
    saAttr.bInheritHandle = TRUE;
    saAttr.lpSecurityDescriptor = NULL;
    // Create a pipe for the child process's STDOUT
    if (!CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0))
        printf("Create pipe for STDOUT failed,%d\n", GetLastError());
    else
        wprintf(L"CreatePipe() - pipe for child process\'s STDOUT pipe was created!\n");
    // Ensure the read handle to the pipe for STDOUT is not inherited
    if (!SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0))
        printf("Create handle for STDOUT failed,%d\n", GetLastError());
    else
        wprintf(L"SetHandleInformation() - pipe STDOUT read handle is not inherited!\n");
    // Create a pipe for the child process's STDIN
    if (!CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
        printf("Create pipe for STDIN failed,%d\n", GetLastError());
    else
        wprintf(L"CreatePipe() - pipe for child process\'s STDIN was created!\n");
    // Ensure the write handle to the pipe for STDIN is not inherited
    if (!SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0))
        printf("Error getting handle on STDIN,%d\n", GetLastError());
    else
        wprintf(L"Stdin SetHandleInformation() - pipe STDIN read handle is not inherited!\n");
    // Create the child process
    wprintf(L"Creating the child process...\n");
    pi = CreateChildProcess();
    char chr[1000];
    for (;;)
    {
        bzero(chr, sizeof(chr));
        printf("Enter a character: ");
        scanf("%s", chr);
        strcat(chr, "\n");
        if(strncmp(chr,"exit",4) == 0)
            break;
        WriteToPipe(chr);
    }
    if (CloseHandle(pi.hProcess) != 0)
        wprintf(L"piProcInfo.hProcess handle was closed!\n");
    else
        printf("Error closing process , %d\n", GetLastError());

    if (CloseHandle(pi.hThread) != 0)
        wprintf(L"piProcInfo.hThread handle was closed!\n");
    else
        printf("Error closing process thread handle, %d\n", GetLastError());
    printf("End of parent execution.\n");
    return 0;

}

Output: Output:

Parent process ID 10580
CreatePipe() - pipe for child process's STDOUT pipe was created!
SetHandleInformation() - pipe STDOUT read handle is not inherited!
CreatePipe() - pipe for child process's STDIN was created!
Stdin SetHandleInformation() - pipe STDIN read handle is not inherited!
Creating the child process...

Child process ID is : 10580
Child thread ID is : 1880
Enter a character: whoami
Command: whoami


WriteFile() - writing to the pipe for the child's STDIN...
Closing the pipe handle...
Microsoft Windows [Version 10.0.18363.836]
(c) 2019 Microsoft Corporation. All rights reserved.

C:\Users\Administrator\Desktop>whoami
Administrator

C:\Users\Administrator\Desktop>Clink v0.4.9 [git:2fd2c2] Copyright (c) 2012-2016 Martin Ridgers
http://mridgers.github.io/clink


ReadFile() from child's standard output failed! Error 109
Enter a character:

The problem is that the cmd process closes right after the first command is executed.问题是 cmd 进程在执行第一个命令后立即关闭。

The child process exits after this line executed:执行此行后,子进程退出:

if (!CloseHandle(g_hChildStd_IN_Wr))

To solve this problem you can use separate threads.要解决此问题,您可以使用单独的线程。 One thread for reading command from console input.从控制台输入读取命令的一个线程。 Another thread for reading result of executed command and print them out.另一个线程用于读取执行命令的结果并打印出来。 Without closing g_hChildStd_IN_Wr handle.不关闭g_hChildStd_IN_Wr句柄。

In main() method:main()方法中:

HANDLE rThread = CreateThread(NULL, 0, ReceiveCommand, NULL, 0, NULL);

HANDLE oThread = CreateThread(NULL, 0, OutputResult, NULL, 0, NULL);

WaitForSingleObject(rThread, INFINITE);

Split WriteToPipe function to two parts: WriteToPipe and ReadFromPipe . WriteToPipe function 拆分为两部分: WriteToPipeReadFromPipe

void WriteToPipe(char * command)
{
    DWORD dwWritten;
    BOOL bSuccess = FALSE;
    bSuccess = WriteFile(g_hChildStd_IN_Wr, (LPVOID)command, (DWORD)strlen(command), &dwWritten, NULL);
    printf("Command: %s\n", command);
    if (!bSuccess)
    {
        wprintf(L"\nWriteFile() - Failed to write to pipe for child\'s STDIN! Error %u\n", GetLastError());
        //break;
    }
    else
        wprintf(L"\nWriteFile() - writing to the pipe for the child\'s STDIN...\n");
}

void ReadFromPipe()
{
    DWORD dwWritten;
    DWORD dwRead;
    CHAR chBuf[BUFSIZE];
    BOOL bSuccess = FALSE;
    DWORD availableBytes;
    DWORD bytesToRead;

    //Read from pipe
    bSuccess = FALSE;
    bzero(chBuf, sizeof(chBuf));
    HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    if (!CloseHandle(g_hChildStd_OUT_Wr))
        printf("Error closing STDOUT handle, %d\n", GetLastError());
    for (;;)
    {
        PeekNamedPipe(g_hChildStd_OUT_Rd, NULL, NULL, NULL, &availableBytes, NULL);

        while (availableBytes > 0)
        {
            if (availableBytes <= BUFSIZE)
            {
                bytesToRead = availableBytes;
            }
            else
            {
                bytesToRead = BUFSIZE;
            }

            bSuccess = ReadFile(g_hChildStd_OUT_Rd, chBuf, bytesToRead, &dwRead, NULL);
            if (!bSuccess || dwRead == 0)
            {
                wprintf(L"\nReadFile() from child's standard output failed! Error %u\n", GetLastError());
                break;
            }

            availableBytes -= bytesToRead;

            bSuccess = WriteFile(hParentStdOut, chBuf, dwRead, &dwWritten, NULL);
            if (!bSuccess)
            {
                wprintf(L"\nWriteFile() to parent's standard output failed! Error %u\n", GetLastError());
                break;
            }
        }
    }
}

Using GetConsoleScreenBufferInfo to monitor cursor position to wait the output from child process print completely.使用GetConsoleScreenBufferInfo监控 cursor position 等待 output 从子进程打印完全。 Before that don't start to receive new command.在此之前不要开始接收新命令。

DWORD WINAPI ReceiveCommand(LPVOID lpParameter)
{
    char chr[1000];
    CONSOLE_SCREEN_BUFFER_INFO cbsi;
    if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cbsi))
    {
        curPos = cbsi.dwCursorPosition;
    }

    for (;;)
    {
        while (TRUE)
        {
            Sleep(50);
            if (!GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &cbsi))
            {
                // printf error message
                return 0;
            }

            if ((curPos.X == cbsi.dwCursorPosition.X) && (curPos.Y == cbsi.dwCursorPosition.Y))
            {
                // All output of the last command executed have been printed completely.
                break;
            }

            curPos = cbsi.dwCursorPosition;
        }

        bzero(chr, sizeof(chr));
        printf("Enter a character: ");
        scanf("%s", chr);
        strcat(chr, "\n");
        if (strncmp(chr, "exit", 4) == 0)
            break;

        WriteToPipe(chr);
    }

    return 0;
}

DWORD WINAPI OutputResult(LPVOID lpParameter)
{
    ReadFromPipe();
    return 0;
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 C:将子进程输出重定向到其他子进程输入和标准输出 - C : Redirect child process output to other child process input and stdout (c / c ++)试图从父进程强制EOF向子进程发送输入 - (c/c++) trying to force EOF from parent process sending input to child process 在C中重定向子进程的输入和输出 - Redirecting input and output of a child process in C Linux使用C中的两个管道在父进程和子进程之间传递值? - Linux pass value between parent process and child process using two pipes in c? C表示子进程的父进程 - C signal parent process from child 从父进程向C中的子进程发送信号 - send signal from parent process to child in C 跨平台重定向本机C / C ++中生成的过程的标准输入和输出(使用解决方案进行编辑) - Cross-platform redirect of standard input and output of spawned process in native C/C++ (edit with solution) 试图将数据从 execve 子进程传递到 C 中的父进程 - Trying to pass data from execve child process to parent process in C 在C中从父进程连续写入子进程 - Write continuously from parent process to child process in C 为什么子进程在使用fork和pipe从父级的输出中输入stdin时正在等待? - Why child process is waiting while using fork and pipes for stdin from parent's output?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM