Unicode中的cmd.exe输入/输出管道重定向

时间:2018-03-23 16:42:20

标签: c++ windows unicode cmd pipe

我有一个GUI程序,它是一个"包装器"到cmd.exe。 从这个GUI程序,我可以通过cmd.exe发送和接收命令

我正在使用管道重定向,并且已经阅读了各种引用:

Creating a Child Process with Redirected Input and Output

Redirect Input and Output of Powershell.exe to Pipes in C++

windows cmd pipe not unicode even with /U switch

首先,我启动" cmd.exe / U"的实例,以便我告诉cmd.exe生成Unicode输出 然后我使用ReadFile / WriteFile来读取和写入管道。

如果我使用ANSI,一切正常,但我遇到了与Unicode相关的2个问题:

1)如果我想使用WriteFile将数据传递给管道,我必须先将它从Unicode转换为Ansi。以Unicode格式传递数据不起作用:具体来说,在我的WriteFile之后读取输出时,cmd会输出一个"更多?"串。 如果我用ANSI编写输入,它工作正常,cmd正确输出命令的结果。 已使用/ U开关启动cmd。

2)当我从cmd启动一个外部程序(如ping,netstat,ipconfig等)时,控制台输出成功地为内部命令(如cd,dir等)启用了unicode。我收到的输出是ANSI,所以我收到了损坏的数据。 我想当外部程序通过cmd运行时/ U开关没有效果?是否有可能的解决方案,并使cmd中的所有内容都以Unicode格式输出?

以下是我的代码示例,我在错误所在的行上放置了注释:

bool StartCmdPipe()
{
    static SECURITY_ATTRIBUTES SA;
    static STARTUPINFOW SI;
    static PROCESS_INFORMATION PI;
    static HANDLE StdInPipeRead;
    static HANDLE StdInPipeWrite;
    static HANDLE StdOutPipeRead;
    static HANDLE StdOutPipeWrite;
    static wstring sWorkDir;
    WCHAR* pBuffer;
    unsigned long BytesRead;

    sWorkDir = _wgetenv(L"SystemDrive");
    sWorkDir += L"\\";
    bJustOpened = true;

    SA.nLength = sizeof(SA);
    SA.bInheritHandle = true;
    SA.lpSecurityDescriptor = NULL;

    if (!CreatePipe(&StdInPipeRead, &StdInPipeWrite, &SA, 0) || !CreatePipe(&StdOutPipeRead, &StdOutPipeWrite, &SA, 0))
    {
        return false;
    }

    // Set Pipe for process to create
    ZeroMemory(&SI, sizeof(SI));
    SI.cb = sizeof(SI);
    SI.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
    SI.wShowWindow = SW_HIDE;
    SI.hStdInput = StdInPipeRead; //GetStdHandle(STD_INPUT_HANDLE);
    SI.hStdOutput = StdOutPipeWrite;
    SI.hStdError = StdOutPipeWrite;

    // Launch cmd process
    g_bCreateProcessSuccess = CreateProcessW(
        NULL,
        L"cmd.exe /U",
        NULL,
        NULL,
        true,
        0,
        NULL,
        (WCHAR*)sWorkDir.c_str(),
        &SI,
        &PI);

    g_sCommandLineInput = L"";
    g_bkeepCmdRunning = true;



    if (g_bCreateProcessSuccess)
    {
        do
        {
            DWORD TotalBytesAvail = 0;
            PeekNamedPipe(StdOutPipeRead, 0, 0, 0, &TotalBytesAvail, 0);

            pBuffer = (WCHAR*)malloc(TotalBytesAvail);

            if (TotalBytesAvail > 0) 
            {
                // First problem is: while internal commands work fine (dir, cd, etc.) and return output in unicode format, 
                // when I launch another process (ipconfig, netstat, ping, etc.), output is returned in ANSI format.
                // Even if I launched cmd.exe with /U switch
                ReadFile(StdOutPipeRead, pBuffer, TotalBytesAvail, &BytesRead, NULL);
            }
            else BytesRead = 0;

            if (BytesRead > 0)
            {
                wprintf(pBuffer);
            }

            free(pBuffer);

            // g_sCommandLineInput is a global wstring variable which gets filled each time user wants to send new command.
            if (g_sCommandLineInput.length() > 0)
            {
                DWORD numberofbyteswritten = 0;
                g_sCommandLineInput += L"\n"; //Append /n to make the cmd process interpret the data as a command to launch

                // Second problem is, why do I have to send command in ANSI format, even if I launched cmd with the /U switch?
                string sCommndLineAnsi = WStringToString(g_sCommandLineInput);
                WriteFile(StdInPipeWrite, sCommndLineAnsi.c_str(), sCommndLineAnsi.length() * sizeof(CHAR), &numberofbyteswritten, NULL);

                //Reset command
                g_sCommandLineInput = L"";
            }

            Sleep(100);
        }
        while (g_bkeepCmdRunning);

        TerminateProcess(PI.hProcess, 0);

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

    CloseHandle(StdOutPipeRead);
    CloseHandle(StdOutPipeWrite);

    return true;
}

0 个答案:

没有答案