简体   繁体   English

将Powershell.exe的输入和输出重定向到C ++中的管道

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

I am trying to execute powershell commands in C++ and get its output through pipes. 我试图在C ++中执行powershell命令并通过管道获取其输出。

My program works perfectly for cmd.exe. 我的程序适用于cmd.exe。 However, when I try to do the same thing with powershell.exe, I only get "W" as an output. 但是,当我尝试使用powershell.exe做同样的事情时,我只得到“W”作为输出。

I have commented the line in the code below that needs to be modified to execute powershell.exe Below is my code that works for cmd.exe: 我已经注释了下面代码中需要修改以执行powershell.exe的行以下是我的代码,适用于cmd.exe:

        HANDLE stdinRd, stdinWr, stdoutRd, stdoutWr;
        DWORD readFromCmd();
        DWORD writeToCmd(CString command);
        int main(int argc,char* argv[])
        {
            SECURITY_ATTRIBUTES sa={sizeof(SECURITY_ATTRIBUTES), NULL, true};
            if(!CreatePipe(&stdinRd, &stdinWr, &sa, 1000000) || !CreatePipe(&stdoutRd,&stdoutWr, &sa, 1000000)) 
            {
                printf("CreatePipe()");
            }
            STARTUPINFO si;
            PROCESS_INFORMATION pi;
            GetStartupInfo(&si);
            si.dwFlags = STARTF_USESTDHANDLES | STARTF_USESHOWWINDOW;
            si.wShowWindow = SW_HIDE;
            si.hStdOutput = stdoutWr;
            si.hStdError = stdoutWr;                  
            si.hStdInput = stdinRd; 

    // If powershell.exe is invoked, it does not work, however works for cmd.exe    
            //if(!CreateProcess(TEXT("C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe"), NULL, NULL, NULL, TRUE,0, NULL, TEXT("C:\\Windows"), &si, &pi))
            if(!CreateProcess(TEXT("C:\\Windows\\System32\\cmd.exe"), NULL, NULL, NULL, TRUE,0, NULL, TEXT("C:\\Windows"), &si, &pi))
            {
                printf("CreateProcess()");  
                printf("CreateProcess() failed in initiatecmd(CString,int) method",0);
                return -1;
            }

            writeToCmd(L"dir");
            Sleep(1000);
            readFromCmd();
            getchar();
            TerminateProcess(pi.hProcess,0);
            CloseHandle(pi.hProcess);
            return 0;

        }
        DWORD writeToCmd(CString command)
        {
            DWORD ret;
            DWORD numberofbyteswritten;
            command.AppendChar('\n');

            LPSTR command_ANSI;
            int size_needed = WideCharToMultiByte(CP_UTF8,0,command.GetString(),-1,NULL,0,NULL,NULL);
            command_ANSI = (LPSTR) calloc(1, ( size_needed + 1 )* sizeof(char));
            WideCharToMultiByte(CP_UTF8,0,command.GetString(),-1,command_ANSI,size_needed,NULL,NULL);

            ret = WriteFile(stdinWr, command_ANSI, size_needed-1, &numberofbyteswritten, NULL);
            if(ret==0)
            {
                printf("WriteFile()");
                printf("WriteFile() method failed in writeToCmd(CString) method",0);
                return 0;
            }

            CStringA temp;
            temp.Format("%d",numberofbyteswritten);
            temp += " bytes (Command:";
            temp+=command;
            temp+=") are successfully written to cmd";
            printf("%s",temp);
            return 1;
        }

        DWORD readFromCmd()
        {
            CString output_jsonstring;
            DWORD ret;
            DWORD dwRead;

            while(1)
            {
                DWORD totalbytesavailable;

                if(PeekNamedPipe(stdoutRd, NULL, 0, NULL, &totalbytesavailable, 0) == 0)
                {
                    printf("PeekNamedPipe()");
                    printf("PeekNamedPipe() method failed in responseHandler() method",0);
                    return 0;
                }
                if(totalbytesavailable != 0)
                {
                    char output_cmd[1000000];
                    if(ReadFile(stdoutRd, output_cmd, min(1000000,totalbytesavailable), &dwRead, NULL)==0)
                    {
                        printf("ReadFile()");
                        printf("ReadFile() method failed in responseHandler() method",0);
                        return 0;
                    }
                    int min = min(1000000,totalbytesavailable);
                    output_cmd[min]='\0';
                    printf("\n%s",output_cmd);
                }   
                if(totalbytesavailable == 0)
                    break;

                Sleep(100);
            }
            return 1;
        }

If the CreateProcess() is used for powershell, it does not work the same way, but I get only W as output. 如果CreateProcess()用于powershell,它的工作方式不同,但我只得到W作为输出。

What is the reason for this? 这是什么原因? And How to get over this problem? 以及如何克服这个问题?

EDIT 1 : If I display the output_cmd in a loop character by character as output_cmd[i] where i = 0 to strlen(output_cmd), I get an output as given below: 编辑1:如果我逐个字符地显示output_cmd作为output_cmd [i],其中i = 0到strlen(output_cmd),我得到如下给出的输出:

indows P ower S hell C opyright ( C ) 2 0 1 4 M icrosoft C orporation . indows Power S hell C版权所有(C)2 0 1 4 M icrosoft C orporation。 A llrightsreserved . 版权所有 。

PSC : \\ W indows > PSC:\\ W indows>

and the application hangs after that! 然后应用程序挂起! It doesn't take in any input, or give any output after that! 它不接受任何输入,或者在此之后给出任何输出!

You passed string to wrong place: 你把字符串传递给错误的地方:

CreateProcess(TEXT("C:\\\\Windows\\\\System32\\\\cmd.exe")

actually the first parameter should be NULL: CreateProcess(NULL, TEXT("C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe") 实际上第一个参数应该是NULL: CreateProcess(NULL, TEXT("C:\\\\Windows\\\\System32\\\\WindowsPowerShell\\\\v1.0\\\\powershell.exe")

Your main point of confusion seem to be around wide characters or byte characters. 你的主要困惑点似乎是宽字符或字节字符。 In classic ASCII strings, each character is one byte. 在经典ASCII字符串中,每个字符都是一个字节。 Modern systems use Unicode, and the two most popular flavors are UTF-8 (popular on unix) and UTF-16 which most of the Windows API uses. 现代系统使用Unicode,两种最流行的风格是UTF-8 (在unix上很流行)和UTF-16 ,大多数Windows API都使用它。 Windows most often (always?) uses the little-endian variety, where the first byte is the lower 8 bits and the second byte is the upper 8 bits. 最常见的Windows(总是?)使用little-endian变种,其中第一个字节是低8位,第二个字节是高8位。 In unicode, the first 127 codepoints are backward compatible with the first 127 characters of ASCII, so the letter "W" in ASCII is 0x57 and in UTF-16 it is 0x57 0x00 . 在unicode中,前127个代码点向后兼容ASCII的前127个字符,因此ASCII中的字母“W”为0x57 ,而UTF-16中的字母“W”为0x57 0x00

You are mixing ReadFile with printf. 您正在将ReadFile与printf混合使用。 ReadFile uses an explicit length for the buffer and bytes read, and so it can happily transfer UTF-16 as binary data. ReadFile使用显式长度来读取缓冲区和字节,因此它可以很好地将UTF-16作为二进制数据传输。 However, printf comes from an old tradition of ASCII strings that are terminated with a NUL byte. 但是,printf来自一个旧的传统的ASCII字符串,它以NUL字节终止。 So from printf's perspective you are giving it a string of length 1 because the second byte is 0x00 . 所以从printf的角度来看,你给它一个长度为1的字符串,因为第二个字节是0x00

Have a look at this question about wide characters with printf to see what you should do differently. 看看有关printf的宽字符的这个问题 ,看看你应该做些什么不同的事情。

By default, PowerShell writes UTF-16 to its console, where-as the old cmd.exe was still using ASCII strings. 默认情况下,PowerShell将UTF-16写入其控制台,其中旧的cmd.exe仍在使用ASCII字符串。 It turns out that PowerShell doesn't use it's input handle at all though, unless you pass the option -Command - . 事实证明,PowerShell根本不使用它的输入句柄,除非你传递选项-Command - With that option however, it switches back to ASCII strings for output and input. 但是,使用该选项,它会切换回ASCII字符串以进行输出和输入。 So, all you really need to do is pass that command line option, and things should start working just like for Cmd.exe. 因此,您真正需要做的就是通过该命令行选项,事情应该像Cmd.exe一样开始工作。

I was working on a perl module for this, not C++, but you might find my source code helpful. 我正在为此创建一个perl模块,而不是C ++,但您可能会发现我的源代码很有帮助。

BTW, I'm disturbed by the other mis-information on this page: 顺便说一句,我对此页面上的其他错误信息感到不安:

  • In Windows, Pipe handles, Console handles, and File handles each have different behavior and are not "all pipes". 在Windows中,管道句柄,控制台句柄和文件句柄各自具有不同的行为,而不是“所有管道”。 It is valid to say they are all Handles, and that you can read/write to each of them and use them for stdin/stdout/stderr of a program. 可以说它们都是句柄,并且您可以读取/写入每个句柄并将它们用于程序的stdin / stdout / stderr。

  • while(1) { if (!condition) break; ... } while(1) { if (!condition) break; ... } is absolutely functionally equivalent to while(condition) { ... } and there is no reason to avoid it aside from style. while(1) { if (!condition) break; ... }绝对在功能上等同于while(condition) { ... }并且除了样式之外没有理由避免它。 If your condition doesn't comfortably fit in a one-line expression it is perfectly reasonable to use while(1) . 如果你的情况不适合单行表达,那么使用while(1)是完全合理的。

  • You should NOT set the first argument of CreateProcess to NULL because it un-ambiguously tells windows which program you intend to execute. 您不应该将CreateProcess的第一个参数设置为NULL,因为它不明确地告诉Windows您要执行哪个程序。 If you pass it in the second argument then you need to make sure it is quoted properly because a path with a space in it could run a different program than intended or even become a security bug. 如果你在第二个参数中传递它,那么你需要确保它被正确引用,因为其中包含空格的路径可能运行与预期不同的程序,甚至成为安全漏洞。 You don't have to use the first argument, but do it if you can. 不必使用第一个参数,但如果你能做到这一点。

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM