将Powershell.exe的输入和输出重定向到C ++中的管道 [英] Redirect Input and Output of Powershell.exe to Pipes in C++

查看:251
本文介绍了将Powershell.exe的输入和输出重定向到C ++中的管道的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我试图在C ++中执行powershell命令并通过管道获取其输出.

I am trying to execute powershell commands in C++ and get its output through pipes.

我的程序非常适合cmd.exe.但是,当我尝试使用powershell.exe做同样的事情时,我只会得到"W"作为输出.

My program works perfectly for cmd.exe. However, when I try to do the same thing with powershell.exe, I only get "W" as an output.

我已经注释了下面代码中需要修改以执行powershell.exe的行 以下是我的适用于cmd.exe的代码:

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:

        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;
        }

如果将CreateProcess()用于powershell,则它的工作方式不同,但是我只能得到W作为输出.

If the CreateProcess() is used for powershell, it does not work the same way, but I get only W as output.

这是什么原因? 和 如何解决这个问题?

What is the reason for this? And How to get over this problem?

如果我将循环中的output_cmd逐个字符显示为output_cmd [i],其中i = 0到strlen(output_cmd),则会得到如下输出:

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:

i n d o w s P o w e r S h e l l C o p y r h g t(C)2 0 1 4 M i c r o s o f t C o r p o r a t i o n.

i n d o w s P o w e r S h e l l C o p y r i g h t ( C ) 2 0 1 4 M i c r o s o f t C o r p o r a t i o n . A l l r i g h t s r e s e r v e d .

P S C:\ W i n d o w s>

P S C : \ W i n d o w s >

,然后应用程序挂起!它不会接受任何输入,也不会在此之后提供任何输出!

and the application hangs after that! It doesn't take in any input, or give any output after that!

推荐答案

您的主要困惑点似乎是宽字符或字节字符.在经典的ASCII字符串中,每个字符都是一个字节.现代系统使用Unicode,两种最受欢迎​​的方式是 UTF-8 (在Unix)和大多数Windows API使用的 UTF-16 . Windows最经常(总是?)使用little-endian变体,其中第一个字节为低8位,第二个字节为高8位.在unicode中,前127个代码点与ASCII的前127个字符向后兼容,因此ASCII中的字母"W"为0x57,在UTF-16中的字母为0x57 0x00.

Your main point of confusion seem to be around wide characters or byte characters. In classic ASCII strings, each character is one byte. 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. 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. 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.

您正在将ReadFile与printf混合使用. ReadFile对缓冲区和读取的字节使用显式长度,因此可以愉快地将UTF-16传输为二进制数据.但是,printf来自一个以NUL字节终止的ASCII字符串的古老传统.因此,从printf的角度来看,您要给它一个长度为1的字符串,因为第二个字节是0x00.

You are mixing ReadFile with printf. ReadFile uses an explicit length for the buffer and bytes read, and so it can happily transfer UTF-16 as binary data. However, printf comes from an old tradition of ASCII strings that are terminated with a NUL byte. So from printf's perspective you are giving it a string of length 1 because the second byte is 0x00.

看看这个

Have a look at this question about wide characters with printf to see what you should do differently.

默认情况下,PowerShell将UTF-16写入其控制台,因为旧的cmd.exe仍在使用ASCII字符串.事实证明,除非您通过选项-Command -,PowerShell根本不会使用它的输入句柄.但是,使用该选项时,它将切换回ASCII字符串以进行输出和输入.因此,您真正需要做的就是传递该命令行选项,一切应该像Cmd.exe一样开始工作.

By default, PowerShell writes UTF-16 to its console, where-as the old cmd.exe was still using ASCII strings. It turns out that PowerShell doesn't use it's input handle at all though, unless you pass the option -Command -. With that option however, it switches back to ASCII strings for output and input. So, all you really need to do is pass that command line option, and things should start working just like for Cmd.exe.

我正在为此使用Perl模块,而不是C ++,但是您可能会发现我的源代码很有帮助.

I was working on a perl module for this, not C++, but you might find my source code helpful.

顺便说一句,我对此页面上的其他错误信息感到不安:

BTW, I'm disturbed by the other mis-information on this page:

  • 在Windows中,管道句柄,控制台句柄和文件句柄分别具有不同的行为,并且不是所有管道".可以说它们都是句柄,您可以对它们进行读写,并将它们用于程序的stdin/stdout/stderr.

  • In Windows, Pipe handles, Console handles, and File handles each have different behavior and are not "all pipes". 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.

while(1) { if (!condition) break; ... } 在功能上绝对等同于 while(condition) { ... } 除了风格之外,没有理由避免使用它.如果您的情况不适用于单行表达式,则使用while(1)是完全合理的.

while(1) { if (!condition) break; ... } is absolutely functionally equivalent to while(condition) { ... } and there is no reason to avoid it aside from style. If your condition doesn't comfortably fit in a one-line expression it is perfectly reasonable to use while(1).

您不应将CreateProcess的第一个参数设置为NULL,因为它明确地告诉Windows您打算执行哪个程序.如果在第二个参数中传递该参数,则需要确保正确引用该参数,因为其中带有空格的路径可能会运行与预期不同的程序,甚至会成为安全漏洞.您没有必须使用第一个参数,但可以使用它.

You should NOT set the first argument of CreateProcess to NULL because it un-ambiguously tells windows which program you intend to execute. 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.

这篇关于将Powershell.exe的输入和输出重定向到C ++中的管道的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆