1

我正在尝试使用用于 Dos、Powershell、Plink 等的 CreateProcess() 从 c#/.Net 编写通用交互式 shell 包装类,并且我找到了文章Why does StandardOutput.Read() block when StartInfo.RedirectStandardInput is set是真的吗?编写包装类来重定向子进程的标准输入/标准输出以获取 powershell 命令执行输出。按照他的指示,我从http://sixfeetsix.blogspot.com/2012/08/interacting-with-sub-processed-shell-in.html获得了他的源代码. 但是,任何命令行执行(例如 plink.exe、cmd.exe)都非常适合 stdin/stdout 的重定向,但 powershell.exe 无法正确获取输入/输出。看起来 powershell.exe 没有从管道的父进程和线程继承。您能否给我任何反馈以成功获取“get-help”powershell命令的retValue?

当我从 DosShell 运行“dir”命令时,它会成功返回 dos 命令“dir”的输出。但是,Powershell 不会从 powershell 提示符返回“get-help”命令。

using System;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using System.Threading;
using System.Reflection;
using System.Text.RegularExpressions;
using System.Diagnostics;


namespace PowershellWrapperPOC
{
    class Program
    {
        static void Main(string[] args)
        {
            StringBuilder testOutput = new StringBuilder();
            string retValue = null;

            DosShell ds = new DosShell();
            retValue = ds.Start(@"C:\Windows\System32\cmd.exe /k", @"C:\Windows\System32\");
            testOutput.Append(retValue.ToString());

            retValue = ds.SendAndReceive("dir");
            testOutput.Append(retValue.ToString());

            ds.Terminate();
            Debug.Print(testOutput.ToString());

            testOutput = new StringBuilder();

            PowerShell ps = new PowerShell();
            retValue = ps.Start(@"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe", @"C:\Windows\System32\");
            testOutput.Append(retValue.ToString());

            retValue = ps.SendAndReceive("get-help");
            testOutput.Append(retValue.ToString());

            ps.Terminate();
            Debug.Print(testOutput.ToString());
        }
    }

    public class PowerShell : CommonShell
    {
        public PowerShell()
        {
            base.SetPrintSendCommand(false);
            base.SetEncoding("utf8");
            base.SetExitCommand("exit");
            base.SetPrompts("> ; ");
        }
    }

    public class DosShell : CommonShell
    {
        public DosShell()
        {
            base.SetPrintSendCommand(false);
            base.SetEncoding("utf8");
            base.SetExitCommand("exit");
            base.SetPrompts(">");
        }

    }

    public class CommonShell : ShellProcess
    {
        private StringBuilder _strOutput;

        private StringBuilder _strLastOutput;

        private static string _escapeCharsPattern = "[\\[|\\(][0-9;?]*[^0-9;]";

        private int _timeout;

        public CommonShell()
        {
            _strOutput = new StringBuilder();
            _strLastOutput = new StringBuilder();
        }

        public bool RemoveEscChars
        {
            get;
            set;
        }

        protected override string Prompt
        {
            get;
            set;
        }

        protected override string ExitCommand
        {
            get;
            set;
        }

        protected override Encoding Encoding
        {
            get;
            set;
        }

        public bool PrintSendCommand
        {
            get;
            set;
        }


        new public string Start(string applicationName, string workDirectory)
        {
            if (PrintSendCommand == true)
            {
                _strOutput.Append(applicationName + "\r\n");
                _strLastOutput.Append(applicationName + "\r\n");
            }

            var results = base.Start(applicationName, workDirectory);

            // if remove esc chars?
            if (RemoveEscChars == true)
            {
                string str = results.Item3;
                str = Regex.Replace(str, _escapeCharsPattern, "");
                _strOutput.Append(str);
                _strLastOutput.Append(str);
            }
            else
            {
                _strOutput.Append(results.Item3);
                _strLastOutput.Append(results.Item3);
            }

            return _strLastOutput.ToString();
        }

        new public string SendAndReceive(string toSend)
        {
            _strLastOutput = new StringBuilder();

            if (PrintSendCommand == true)
            {
                _strLastOutput.Append(toSend + "\r\n");
                _strOutput.Append(toSend + "\r\n");
            }

            // Wait forever till getting the expected prompt
            var results = base.SendAndReceive(toSend + "\r\n");

            // if remove esc chars?
            if (RemoveEscChars == true)
            {
                string str = results.Item3;
                str = Regex.Replace(str, _escapeCharsPattern, "");
                _strOutput.Append(str);
                _strLastOutput.Append(str);
            }
            else
            {
                _strOutput.Append(results.Item3);
                _strLastOutput.Append(results.Item3);
            }

            return _strLastOutput.ToString();
        }

        public void SetPrompts(string prompt)
        {
            var enc = this.Encoding;
            byte[] utfBytes = enc.GetBytes(prompt);
            Prompt = enc.GetString(utfBytes);
        }

        public void SetEncoding(string enc)
        {
            string l_enc = enc.ToLower();

            switch (l_enc)
            {
                case "utf8":
                    this.Encoding = Encoding.UTF8;
                    break;
                case "utf7":
                    this.Encoding = Encoding.UTF7;
                    break;
                case "utf32":
                    this.Encoding = Encoding.UTF32;
                    break;
                case "ascii":
                    this.Encoding = Encoding.ASCII;
                    break;
                default:
                    return;
            }
        }

        public void SetExitCommand(string command)
        {
            ExitCommand = command;
        }

        public void SetPrintSendCommand(bool bPrintSendCommand)
        {
            PrintSendCommand = bPrintSendCommand;
        }

        public void SetRemoveEscChars(bool bRemove)
        {
            RemoveEscChars = bRemove;
        }

        public void SetTimeout(int timeout)
        {
            this._timeout = timeout;
        }

        public void Flush()
        {
            _strOutput = new StringBuilder();
            _strLastOutput = new StringBuilder();

        }

        public string GetLastOutput()
        {
            return _strLastOutput.ToString();
        }

        public string GetOutput()
        {
            return _strLastOutput.ToString();
        }

    }

    /// <summary>
    /// Kernel32 Marshaling
    /// </summary>
    public static class Kernel32
    {
        /// <summary>
        /// HANDLE_FLAG_INHERIT
        /// 
        /// If this flag is set, a child process created with the bInheritHandles parameter of CreateProcess set to TRUE will inherit the object handle.
        /// 
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724935(v=vs.85).aspx
        /// </summary>
        public const int HANDLE_FLAG_INHERIT = 1;
        /// <summary>
        /// STARTF_USESTDHANDLES
        /// 
        /// The hStdInput, hStdOutput, and hStdError members contain additional information.
        /// If this flag is specified when calling one of the process creation functions, the handles must be inheritable and the function's 
        /// bInheritHandles parameter must be set to TRUE. For more information, see Handle Inheritance.
        /// 
        /// If this flag is specified when calling the GetStartupInfo function, these members are either the handle value specified during 
        /// process creation or INVALID_HANDLE_VALUE.
        /// 
        /// Handles must be closed with CloseHandle when they are no longer needed.
        /// This flag cannot be used with STARTF_USEHOTKEY.
        /// 
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
        /// </summary>
        public const UInt32 STARTF_USESTDHANDLES = 0x00000100;
        /// <summary>
        /// STARTF_USESHOWWINDOW
        /// 
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
        /// </summary>
        public const UInt32 STARTF_USESHOWWINDOW = 0x00000001;

        /// <summary>
        /// SECURITY_ATTRIBUTES
        /// </summary>
        public struct SECURITY_ATTRIBUTES
        {
            /// <summary>
            /// The size, in bytes, of this structure. Set this value to the size of the SECURITY_ATTRIBUTES structure.
            /// </summary>
            public int length;
            /// <summary>
            /// A pointer to a SECURITY_DESCRIPTOR structure that controls access to the object. 
            /// If the value of this member is NULL, the object is assigned the default security descriptor associated with the access token of the calling process. 
            /// This is not the same as granting access to everyone by assigning a NULL discretionary access control list (DACL). 
            /// By default, the default DACL in the access token of a process allows access only to the user represented by the access token.
            /// 
            /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa379560(v=vs.85).aspx
            /// </summary>
            public IntPtr lpSecurityDescriptor;
            /// <summary>
            /// A Boolean value that specifies whether the returned handle is inherited when a new process is created. 
            /// If this member is TRUE, the new process inherits the handle.
            /// </summary>
            [MarshalAs(UnmanagedType.Bool)]
            public bool bInheritHandle;
        }

        /// <summary>
        /// STARTUPINFO
        /// </summary>
        public struct STARTUPINFO
        {
            /// <summary>
            /// The size of the structure, in bytes.
            /// </summary>
            public uint cb;
            /// <summary>
            /// Reserved; must be NULL.
            /// </summary>
            public string lpReserved;
            /// <summary>
            /// The name of the desktop, or the name of both the desktop and window station for this process. 
            /// A backslash in the string indicates that the string includes both the desktop and window station names. 
            /// </summary>
            public string lpDesktop;
            /// <summary>
            /// For console processes, this is the title displayed in the title bar if a new console window is created. 
            /// If NULL, the name of the executable file is used as the window title instead. 
            /// This parameter must be NULL for GUI or console processes that do not create a new console window.
            /// </summary>
            public string lpTitle;
            /// <summary>
            /// If dwFlags specifies STARTF_USEPOSITION, this member is the x offset of the upper left corner of a window if a new window is created, in pixels. 
            /// Otherwise, this member is ignored.
            /// 
            /// The offset is from the upper left corner of the screen. For GUI processes, the specified position is used the first time the new process calls 
            /// CreateWindow to create an overlapped window if the x parameter of CreateWindow is CW_USEDEFAULT.
            /// </summary>
            public uint dwX;
            /// <summary>
            /// If dwFlags specifies STARTF_USEPOSITION, this member is the y offset of the upper left corner of a window if a new window is created, in pixels. 
            /// Otherwise, this member is ignored.
            /// 
            /// The offset is from the upper left corner of the screen. For GUI processes, the specified position is used the first time the new process calls 
            /// CreateWindow to create an overlapped window if the y parameter of CreateWindow is CW_USEDEFAULT.
            /// </summary>
            public uint dwY;
            /// <summary>
            /// If dwFlags specifies STARTF_USESIZE, this member is the width of the window if a new window is created, in pixels. 
            /// Otherwise, this member is ignored.
            /// 
            /// For GUI processes, this is used only the first time the new process calls CreateWindow to create an overlapped window 
            /// if the nWidth parameter of CreateWindow is CW_USEDEFAULT.
            /// </summary>
            public uint dwXSize;
            /// <summary>
            /// If dwFlags specifies STARTF_USESIZE, this member is the height of the window if a new window is created, in pixels. 
            /// Otherwise, this member is ignored.
            /// 
            /// For GUI processes, this is used only the first time the new process calls CreateWindow to create an overlapped window 
            /// if the nHeight parameter of CreateWindow is CW_USEDEFAULT.
            /// </summary>
            public uint dwYSize;
            /// <summary>
            /// If dwFlags specifies STARTF_USECOUNTCHARS, if a new console window is created in a console process, 
            /// this member specifies the screen buffer width, in character columns. Otherwise, this member is ignored.
            /// </summary>
            public uint dwXCountChars;
            /// <summary>
            /// If dwFlags specifies STARTF_USECOUNTCHARS, if a new console window is created in a console process, 
            /// this member specifies the screen buffer height, in character rows. Otherwise, this member is ignored.
            /// </summary>
            public uint dwYCountChars;
            /// <summary>
            /// If dwFlags specifies STARTF_USEFILLATTRIBUTE, this member is the initial text and background colors 
            /// if a new console window is created in a console application. Otherwise, this member is ignored.
            /// 
            /// This value can be any combination of the following values: 
            /// FOREGROUND_BLUE, FOREGROUND_GREEN, FOREGROUND_RED, FOREGROUND_INTENSITY, BACKGROUND_BLUE, BACKGROUND_GREEN, 
            /// BACKGROUND_RED, and BACKGROUND_INTENSITY. For example, the following combination of values produces red text on a white background:
            /// 
            /// FOREGROUND_RED| BACKGROUND_RED| BACKGROUND_GREEN| BACKGROUND_BLUE
            /// </summary>
            public uint dwFillAttribute;
            /// <summary>
            /// A bitfield that determines whether certain STARTUPINFO members are used when the process creates a window. 
            /// This member can be one or more of the following values.
            /// 
            /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms686331(v=vs.85).aspx
            /// </summary>
            public uint dwFlags;
            /// <summary>
            /// If dwFlags specifies STARTF_USESHOWWINDOW, this member can be any of the values that can be specified in the nCmdShow parameter for the 
            /// ShowWindow function, except for SW_SHOWDEFAULT. Otherwise, this member is ignored.
            /// 
            /// For GUI processes, the first time ShowWindow is called, its nCmdShow parameter is ignored wShowWindow specifies the default value. 
            /// In subsequent calls to ShowWindow, the wShowWindow member is used if the nCmdShow parameter of ShowWindow is set to SW_SHOWDEFAULT.
            /// </summary>
            public short wShowWindow;
            /// <summary>
            /// Reserved for use by the C Run-time; must be zero.
            /// </summary>
            public short cbReserved2;
            /// <summary>
            /// Reserved for use by the C Run-time; must be NULL.
            /// </summary>
            public IntPtr lpReserved2;
            /// <summary>
            /// If dwFlags specifies STARTF_USESTDHANDLES, this member is the standard input handle for the process. 
            /// If STARTF_USESTDHANDLES is not specified, the default for standard input is the keyboard buffer.
            /// 
            /// If dwFlags specifies STARTF_USEHOTKEY, this member specifies a hotkey value that is sent as the wParam parameter of a 
            /// WM_SETHOTKEY message to the first eligible top-level window created by the application that owns the process. 
            /// If the window is created with the WS_POPUP window style, it is not eligible unless the WS_EX_APPWINDOW extended window style is also set. 
            /// 
            /// For more information, see CreateWindowEx.
            /// 
            /// Otherwise, this member is ignored.
            /// </summary>
            public IntPtr hStdInput;
            /// <summary>
            /// If dwFlags specifies STARTF_USESTDHANDLES, this member is the standard output handle for the process. 
            /// Otherwise, this member is ignored and the default for standard output is the console window's buffer.
            /// 
            /// If a process is launched from the taskbar or jump list, the system sets hStdOutput to a handle to the monitor 
            /// that contains the taskbar or jump list used to launch the process. For more information, see Remarks.
            /// 
            /// Windows 7, Windows Server 2008 R2, Windows Vista, Windows Server 2008, Windows XP, and Windows Server 2003:  
            /// This behavior was introduced in Windows 8 and Windows Server 2012.
            /// </summary>
            public IntPtr hStdOutput;
            /// <summary>
            /// If dwFlags specifies STARTF_USESTDHANDLES, this member is the standard error handle for the process. 
            /// Otherwise, this member is ignored and the default for standard error is the console window's buffer.
            /// </summary>
            public IntPtr hStdError;
        }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms684873(v=vs.85).aspx
        /// </summary>
        public struct PROCESS_INFORMATION
        {
            /// <summary>
            /// A handle to the newly created process. The handle is used to specify the process in all functions that perform operations on the process object.
            /// </summary>
            public IntPtr hProcess;
            /// <summary>
            /// A handle to the primary thread of the newly created process. The handle is used to specify the thread in all functions that perform operations on the thread object.
            /// </summary>
            public IntPtr hThread;
            /// <summary>
            /// A value that can be used to identify a process. The value is valid from the time the process is created until all handles to the process are closed and the process object is freed; at this point, the identifier may be reused.
            /// </summary>
            public uint dwProcessId;
            /// <summary>
            /// A value that can be used to identify a thread. The value is valid from the time the thread is created until all handles to the thread are closed and the thread object is freed; at this point, the identifier may be reused.
            /// </summary>
            public uint dwThreadId;
        }

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx
        /// </summary>
        /// <param name="lpApplicationName"></param>
        /// <param name="lpCommandLine"></param>
        /// <param name="lpProcessAttributes"></param>
        /// <param name="lpThreadAttributes"></param>
        /// <param name="bInheritHandles"></param>
        /// <param name="dwCreationFlags"></param>
        /// <param name="lpEnvironment"></param>
        /// <param name="lpCurrentDirectory"></param>
        /// <param name="lpStartupInfo"></param>
        /// <param name="lpProcessInformation"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CreateProcess(string lpApplicationName,
                                            string lpCommandLine,
                                            IntPtr lpProcessAttributes,
                                            IntPtr lpThreadAttributes,
                                            [MarshalAs(UnmanagedType.Bool)] bool bInheritHandles,
                                            uint dwCreationFlags,
                                            IntPtr lpEnvironment,
                                            string lpCurrentDirectory,
                                            ref STARTUPINFO lpStartupInfo,
                                            out PROCESS_INFORMATION lpProcessInformation);
        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx
        /// </summary>
        /// <param name="hObject"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CloseHandle(IntPtr hObject);

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365152(v=vs.85).aspx
        /// </summary>
        /// <param name="hReadPipe"></param>
        /// <param name="hWritePipe"></param>
        /// <param name="lpPipeAttributes"></param>
        /// <param name="nSize"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool CreatePipe(out IntPtr hReadPipe,
                                         out IntPtr hWritePipe,
                                         ref SECURITY_ATTRIBUTES lpPipeAttributes,
                                         uint nSize);
        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365779(v=vs.85).aspx
        /// </summary>
        /// <param name="hNamedPipe"></param>
        /// <param name="pBuffer"></param>
        /// <param name="nBufferSize"></param>
        /// <param name="lpBytesRead"></param>
        /// <param name="lpTotalBytesAvail"></param>
        /// <param name="lpBytesLeftThisMessage"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern unsafe bool PeekNamedPipe(IntPtr hNamedPipe,
                                                   IntPtr pBuffer,
                                                   int nBufferSize,
                                                   IntPtr lpBytesRead,
                                                   int* lpTotalBytesAvail,
                                                   IntPtr lpBytesLeftThisMessage);

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365467(v=vs.85).aspx
        /// </summary>
        /// <param name="hFile"></param>
        /// <param name="pBuffer"></param>
        /// <param name="nNumberOfBytesToRead"></param>
        /// <param name="lpNumberOfBytesRead"></param>
        /// <param name="lpOverlapped"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern unsafe bool ReadFile(IntPtr hFile,
                                              void* pBuffer,
                                              int nNumberOfBytesToRead,
                                              int* lpNumberOfBytesRead,
                                              IntPtr lpOverlapped);
        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365747(v=vs.85).aspx
        /// </summary>
        /// <param name="hFile"></param>
        /// <param name="pBuffer"></param>
        /// <param name="nNumberOfBytesToWrite"></param>
        /// <param name="lpNumberOfBytesWritten"></param>
        /// <param name="lpOverlapped"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern unsafe bool WriteFile(IntPtr hFile,
                                               void* pBuffer,
                                               int nNumberOfBytesToWrite,
                                               int* lpNumberOfBytesWritten,
                                               IntPtr lpOverlapped);

        /// <summary>
        /// http://msdn.microsoft.com/en-us/library/windows/desktop/ms724935(v=vs.85).aspx
        /// </summary>
        /// <param name="hObject"></param>
        /// <param name="dwMask"></param>
        /// <param name="dwFlags"></param>
        /// <returns></returns>
        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetHandleInformation(IntPtr hObject, int dwMask, uint dwFlags);
    }

    public abstract class ShellProcess
    {
        IntPtr _hChildStdoutR, _hChildStdoutW, _hChildStderrR, _hChildStderrW, _hChildStdinR, _hChildStdinW;
        Kernel32.SECURITY_ATTRIBUTES _sa, _sa_process, _sa_thread;
        Kernel32.STARTUPINFO _si;
        Kernel32.PROCESS_INFORMATION _pi;
        string _applicationName;

        protected abstract string Prompt { get; set; }

        protected abstract string ExitCommand { get; set; }

        protected abstract Encoding Encoding { get; set; }

        static unsafe int Write(IntPtr h, byte[] buffer, int index, int count)
        {
            int n = 0;
            fixed (byte* p = buffer)
            {
                if (!Kernel32.WriteFile(h, p + index, count, &n, IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            return n;
        }

        static unsafe int Peek(IntPtr h)
        {
            int n = 0;
            if (!Kernel32.PeekNamedPipe(h, IntPtr.Zero, 0, IntPtr.Zero, &n, IntPtr.Zero))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            return n;
        }

        static unsafe int Read(IntPtr h, byte[] buffer, int index, int count)
        {
            int n = 0;
            fixed (byte* p = buffer)
            {
                if (!Kernel32.ReadFile(h, p + index, count, &n, IntPtr.Zero))
                    throw new Win32Exception(Marshal.GetLastWin32Error());
            }
            return n;
        }

        public virtual void SendCommand(string s)
        {
            byte[] bytesToWrite = Encoding.GetBytes(s);
            Write(_hChildStdinW, bytesToWrite, 0, bytesToWrite.Length);
        }

        Tuple<string, string, string> ReadToPrompt()
        {
            StringBuilder strOutput = new StringBuilder();

            const int bufferLength = 128;
            byte[] buffer = new byte[bufferLength];
            int bytesReadCount;
            var stdOut = new StringBuilder(4096);
            var stdErr = new StringBuilder();

            string[] prompts = Prompt.Split(';');
            bool foundPrompt = false;

            while (!foundPrompt)
            {
                while (Peek(_hChildStdoutR) > 0)
                {
                    bytesReadCount = Read(_hChildStdoutR, buffer, 0, bufferLength);
                    stdOut.Append(Encoding.GetString(buffer, 0, bytesReadCount));
                    strOutput.Append(Encoding.GetString(buffer, 0, bytesReadCount));
                }

                foreach (string prompt in prompts)
                {
                    if (stdOut.ToString().Contains(prompt))
                    {
                        foundPrompt = true;
                        break;
                    }
                }
                //strOutput.Append(stdOut);

                while (Peek(_hChildStderrR) > 0)
                {
                    bytesReadCount = Read(_hChildStderrR, buffer, 0, bufferLength);
                    stdErr.Append(Encoding.GetString(buffer, 0, bytesReadCount));
                    strOutput.Append(Encoding.GetString(buffer, 0, bytesReadCount));
                }

                foreach (string prompt in prompts)
                {
                    if (stdErr.ToString().Contains(prompt))
                    {
                        foundPrompt = true;
                        break;
                    }
                }
                //strOutput.Append(stdErr);

                Thread.Sleep(20);
            }

            while (Peek(_hChildStderrR) > 0)
            {
                bytesReadCount = Read(_hChildStderrR, buffer, 0, bufferLength);
                stdErr.Append(Encoding.GetString(buffer, 0, bytesReadCount));
                strOutput.Append(Encoding.GetString(buffer, 0, bytesReadCount));
            }

            return new Tuple<string, string, string>(stdOut.ToString(), stdErr.ToString(), strOutput.ToString());
        }

        public virtual Tuple<string, string, string> SendAndReceive(string toSend)
        {
            SendCommand(toSend);
            return ReadToPrompt();
        }

        public virtual Tuple<string, string, string> Start(string applicationName, string workDirectory)
        {
            _sa = new Kernel32.SECURITY_ATTRIBUTES
            {
                bInheritHandle = true,
                lpSecurityDescriptor = IntPtr.Zero,
                length = Marshal.SizeOf(typeof(Kernel32.SECURITY_ATTRIBUTES))
            };
            _sa.lpSecurityDescriptor = IntPtr.Zero;

            _sa_process = new Kernel32.SECURITY_ATTRIBUTES
            {
                bInheritHandle = true,
                lpSecurityDescriptor = IntPtr.Zero,
                length = Marshal.SizeOf(typeof(Kernel32.SECURITY_ATTRIBUTES))
            };
            _sa_process.lpSecurityDescriptor = IntPtr.Zero;

            _sa_thread = new Kernel32.SECURITY_ATTRIBUTES
            {
                bInheritHandle = true,
                lpSecurityDescriptor = IntPtr.Zero,
                length = Marshal.SizeOf(typeof(Kernel32.SECURITY_ATTRIBUTES))
            };
            _sa_thread.lpSecurityDescriptor = IntPtr.Zero;


            if (!Kernel32.CreatePipe(out _hChildStdoutR, out _hChildStdoutW, ref _sa, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.CreatePipe(out _hChildStderrR, out _hChildStderrW, ref _sa, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.CreatePipe(out _hChildStdinR, out _hChildStdinW, ref _sa, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.SetHandleInformation(_hChildStdoutR, Kernel32.HANDLE_FLAG_INHERIT, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.SetHandleInformation(_hChildStderrR, Kernel32.HANDLE_FLAG_INHERIT, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.SetHandleInformation(_hChildStdinW, Kernel32.HANDLE_FLAG_INHERIT, 0))
                throw new Win32Exception(Marshal.GetLastWin32Error());

            _si = new Kernel32.STARTUPINFO
            {
                wShowWindow = 0,
                dwFlags = Kernel32.STARTF_USESTDHANDLES | Kernel32.STARTF_USESHOWWINDOW,
                hStdOutput = _hChildStdoutW,
                hStdError = _hChildStderrW,
                hStdInput = _hChildStdinR
            };

            _si.cb = (uint)Marshal.SizeOf(_si);
            _pi = new Kernel32.PROCESS_INFORMATION();

            if (!Kernel32.CreateProcess(null, applicationName, IntPtr.Zero, IntPtr.Zero, true, 0, IntPtr.Zero, workDirectory, ref _si, out _pi))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            _applicationName = applicationName;
            return ReadToPrompt();
        }

        public void Terminate()
        {
            SendCommand(ExitCommand);
            if (!Kernel32.CloseHandle(_hChildStderrW))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.CloseHandle(_hChildStdoutW))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.CloseHandle(_hChildStdinW))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.CloseHandle(_pi.hProcess))
                throw new Win32Exception(Marshal.GetLastWin32Error());
            if (!Kernel32.CloseHandle(_pi.hThread))
                throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}
4

2 回答 2

3

powershell 是不同的。我认为你不能简单地重定向它的输入和输出。这就是为什么我们需要使用运行空间来获取输出对象。搜索“powershell c# example”,很多代码。这是一个简单的:

从 C# 调用 PowerShell

取决于您使用的 powershell(exchange powers shell、sharepoint power shell..),您需要添加正确的管理单元。

于 2013-09-03T18:39:28.620 回答
1

听起来您不想只从 C# 调用 powershell 命令,而是充当 powershell 主机。作为 powershell 主机,您可以接收来自 Write-Host 和 Out-String 等命令的输出,并且只需将字符串流作为输入和输出。主机的示例是 powershell.exe 和 powershell_ise.exe,但它可能需要大量工作并且不会使处理 powershell 命令的结果更容易,只会更难。powershell 的强大之处在于命令的输入和输出都是真实的对象,而不仅仅是字符串你可以在这里阅读更多关于它的信息。

如果您只想执行一些 powershell 命令并接收它们的输出,那么您最好在这里使用它并使用 powershell 命令返回的对象,而不是希望它们成为 strings。这意味着您必须停止使用 Write-Host、Out-String、Format-Table 等命令。这些命令用于格式化,而不是用于处理结果。

于 2013-09-03T21:12:35.157 回答