用pinvoke, C#里可以创建子进程,但却没有捕获到子进程输出, 求高人指点哪儿错了?

我想要在程序里面impersonate成当前用户,然后创建一个子进程(powershell.exe)
用来执行脚本,然后想要在父进程里抓住输出, 结果是进程创建没有问题,但是抓不到输出,大家看看哪儿错了?

下面是代码片段:

namespace TestCreateProcessAsUser
{
    public static class ProcessA
    {
        static readonly IntPtr INVALID_HANDLE_VALUE = (IntPtr)(-1);
        static readonly HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);

        const int STD_INPUT_HANDLE = -10;
        public const UInt32 Infinite = 0xffffffff;
        const int STARTF_USESTDHANDLES = 0x100;
        private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400;
        private static IntPtr Pi_EnvBlock = default(IntPtr);

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool SetHandleInformation(IntPtr hObject, HANDLE_FLAGS dwMask, HANDLE_FLAGS dwFlags);

        [DllImport("kernel32.dll", EntryPoint = "CloseHandle", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool CloseHandle(IntPtr handle);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern UInt32 WaitForSingleObject(IntPtr handle, UInt32 milliseconds);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern int GetConsoleOutputCP();

        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        public static extern IntPtr CreateNamedPipe(string name, int openMode, int pipeMode, int maxInstances, int outBufSize, int inBufSize, int timeout, IntPtr lpPipeAttributes);

        [DllImport("kernel32.dll", CharSet = CharSet.Auto)]
        public static extern IntPtr CreateFile(string lpFileName, int dwDesiredAccess, int dwShareMode, IntPtr lpSecurityAttributes, int dwCreationDisposition, int dwFlagsAndAttributes, HandleRef hTemplateFile);

        [DllImport("kernel32.dll", CharSet = CharSet.Ansi, SetLastError = true)]
        public static extern IntPtr GetStdHandle(int whichHandle);

        [DllImport("kernel32.dll", SetLastError = true)]
        public static extern bool GetExitCodeProcess(IntPtr process, ref UInt32 exitCode);

        [DllImport("advapi32.dll", EntryPoint = "CreateProcessAsUser", SetLastError = true, CharSet = CharSet.Ansi,
            CallingConvention = CallingConvention.StdCall)]
        public static extern bool CreateProcessAsUser(IntPtr hToken, String lpApplicationName, String lpCommandLine,
                                                      ref SECURITY_ATTRIBUTES lpProcessAttributes,
                                                      ref SECURITY_ATTRIBUTES lpThreadAttributes, bool bInheritHandle,
                                                      int dwCreationFlags, IntPtr lpEnvironment,
                                                      String lpCurrentDirectory, ref STARTUPINFO lpStartupInfo,
                                                      out PROCESS_INFORMATION lpProcessInformation);

        [DllImport("advapi32.dll", EntryPoint = "DuplicateTokenEx")]
        public static extern bool DuplicateTokenEx(IntPtr ExistingTokenHandle, uint dwDesiredAccess,
                                                   ref SECURITY_ATTRIBUTES lpThreadAttributes, int TokenType,
                                                   int ImpersonationLevel, ref IntPtr DuplicateTokenHandle);


        /// <summary>
        /// This is class is designed to operate inside an ASP.NET web application.
        /// The assumption is that the calling thread is operating with an impersonated security token.
        /// This class will change the imperonated security token to a primary token, and call CreateProcessAsUser.
        /// To use this function, the following security priviliges need to be set for the ASPNET account 
        /// using the local security policy MMC snap-in. CreateProcessAsUser requirement.
        /// "Replace a process level token"/SE_ASSIGNPRIMARYTOKEN_NAME/SeAssignPrimaryTokenPrivilege
        /// "Adjust memory quotas for a process"/SE_INCREASE_QUOTA_NAME/SeIncreaseQuotaPrivilege
        /// </summary>
        public static bool Start(string command, string workingDirectory)
        {
            bool ret;
            try
            {

                var identity = WindowsIdentity.GetCurrent();
                if (identity == null)
                {
                    Logger.LogContent("Start import conversion:  Get current identity token failed");
                    return false;
                }

                IntPtr Token = identity.Token;

                const uint GENERIC_ALL = 0x10000000;

                const int SecurityImpersonation = 2;
                const int TokenType = 1;

                var DupedToken = new IntPtr(0);

                var sa = new SECURITY_ATTRIBUTES { bInheritHandle = false };
                sa.nLength = Marshal.SizeOf(sa);
                sa.lpSecurityDescriptor = (IntPtr)0;

                ret = DuplicateTokenEx(Token, GENERIC_ALL, ref sa, SecurityImpersonation, TokenType, ref DupedToken);
                if (ret == false)
                {
                    Logger.LogContent(
                        "Start import conversion: DuplicateTokenEx failed with " + new Win32Exception().Message);
                    return false;
                }


                IntPtr stdoutReadHandle;
                IntPtr stdoutWriteHandle;
                IntPtr stdinHandle = GetStdHandle(STD_INPUT_HANDLE);
                CreatePipe(out stdoutReadHandle, out stdoutWriteHandle, false);
                SetHandleInformation(stdoutReadHandle, HANDLE_FLAGS.INHERIT, HANDLE_FLAGS.INHERIT);

                var si = new STARTUPINFO();
                si.cb = Marshal.SizeOf(si);
                si.lpDesktop = "";
                si.dwFlags = STARTF_USESTDHANDLES;
                si.hStdInput = stdinHandle;
                si.hStdOutput = stdoutWriteHandle;
                si.hStdError = stdoutWriteHandle;

                PROCESS_INFORMATION pi;
                ret = CreateProcessAsUser(DupedToken, null, command, ref sa, ref sa, true, 0, (IntPtr)0, workingDirectory,
                                          ref si, out pi);
                UInt32 exitCode = 123456;
                if (pi.hProcess != IntPtr.Zero)
                {
                    WaitForSingleObject(pi.hProcess, 180000);
                    GetExitCodeProcess(pi.hProcess, ref exitCode);
                }

                var lastException = new Win32Exception();

                //CloseHandle(pi.hProcess);
                //CloseHandle(pi.hThread);
                //CloseHandle(stdoutWriteHandle);
                //CloseHandle(stdinHandle);

                CloseHandle(pi.hProcess);
                CloseHandle(pi.hThread);
                CloseHandle(stdoutWriteHandle);
                CloseHandle(stdinHandle);

                var safeHandle = new SafeFileHandle(stdoutReadHandle, true);
                string result;
                try
                {
                    var encoding = Encoding.GetEncoding(GetConsoleOutputCP());
                    var reader =
                        new StreamReader(
                            new FileStream(safeHandle, FileAccess.Read, 0x1000, false),
                            encoding);

                    result = reader.ReadToEnd();
                    Logger.LogContent("redirectred output = " + result);
                    reader.Close();
                }
                finally
                {
                    if (!safeHandle.IsClosed)
                    {
                        safeHandle.Close();
                    }


                }

                if (ret == false || exitCode > 0)
                {
                    Logger.LogContent(
                        "Start import conversion: CreateProcessAsUser failed with " + lastException.Message + " => Exitcode: " + exitCode + " => Output: " + (string.IsNullOrEmpty(result) ? string.Empty : result));
                    return false;
                }


                ret = CloseHandle(DupedToken);

                if (ret == false)
                {
                    Logger.LogContent("Start import conversion: Closing token failed with " + new Win32Exception().Message
                                       );
                }

            }
            catch (Exception e)
            {
                ret = false;
                Logger.LogContent("final = " + e.Message);
            }
            return ret;
        }

        private static void CreatePipe(out IntPtr parentHandle, out IntPtr childHandle, bool parentInputs)
        {
            string pipename = @"\\.\pipe\" + Guid.NewGuid().ToString();

            parentHandle = CreateNamedPipe(pipename, 0x40000003, 0, 0xff, 0x1000, 0x1000, 0, IntPtr.Zero);
            if (parentHandle == INVALID_HANDLE_VALUE)
            {
                throw new Win32Exception();
            }

            int childAcc = 0x40000000;
            if (parentInputs)
            {
                childAcc = -2147483648;
            }
            childHandle = CreateFile(pipename, childAcc, 3, IntPtr.Zero, 3, 0x40000080, NullHandleRef);
            if (childHandle == INVALID_HANDLE_VALUE)
            {
                throw new Win32Exception();
            }
        }

        [Flags]
        enum HANDLE_FLAGS
        {
            INHERIT = 1,
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public int cb;
            public String lpReserved;
            public String lpDesktop;
            public String lpTitle;
            public uint dwX;
            public uint dwY;
            public uint dwXSize;
            public uint dwYSize;
            public uint dwXCountChars;
            public uint dwYCountChars;
            public uint dwFillAttribute;
            public uint dwFlags;
            public short wShowWindow;
            public short cbReserved2;
            public IntPtr lpReserved2;
            public IntPtr hStdInput;
            public IntPtr hStdOutput;
            public IntPtr hStdError;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public IntPtr hProcess;
            public IntPtr hThread;
            public uint dwProcessId;
            public uint dwThreadId;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public IntPtr lpSecurityDescriptor;
            public bool bInheritHandle;
        }
    }
}

   class Program
    {
        static void Main(string[] args)
        {
            ProcessA.Start(@"powershell.exe -executionPolicy bypass c:\test.ps1", null);
        }
    }

http://blog.csdn.net/troubleshooter/article/details/7265355