从C#中的CreateProcessAsUser捕获标准输出

我目前正在使用pinvoke CreateProcessAsUser api在asp.net网站内启动进程。 这非常适合模拟已登录的AD用户。

然而,不知何故,控制台输出的结果字符串始终为空。 我找不到出了什么问题。 没有崩溃或任何东西只是没有输出。

public static class Process { 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; [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); ///  /// 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 ///  public static bool Start(string command, string workingDirectory) { bool ret; try { var identity = WindowsIdentity.GetCurrent(); if (identity == null) { Trace.LogException("Start import conversion: Get current identity token failed", null); 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) { Trace.LogException( "Start import conversion: DuplicateTokenEx failed with " + new Win32Exception().Message, null); 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); var safeHandle = new SafeFileHandle(stdoutReadHandle, true); string result; try { var encoding = Encoding.GetEncoding(GetConsoleOutputCP()); var reader = new StreamReader( new FileStream(safeHandle, FileAccess.Read, 0x1000, true), encoding); result = reader.ReadToEnd(); reader.Close(); } finally { if (!safeHandle.IsClosed) { safeHandle.Close(); } } if (ret == false || exitCode > 0) { Trace.LogException( "Start import conversion: CreateProcessAsUser failed with " + lastException.Message + " => Exitcode: " + exitCode + " => Output: " + (string.IsNullOrEmpty(result) ? string.Empty : result), null); return false; } ret = CloseHandle(DupedToken); if (ret == false) { Trace.LogException("Start import conversion: Closing token failed with " + new Win32Exception().Message, null); } } catch (Exception e) { ret = false; Trace.LogFatalException(e); } 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; } } 

全局定义这些变量:

 private const int CREATE_UNICODE_ENVIRONMENT = 0x00000400; private static IntPtr Pi_EnvBlock = default(IntPtr); 

并调用CreateProcessAsUser:

 ret = CreateProcessAsUser(DupedToken, null, command, ref sa, ref sa, true, CREATE_UNICODE_ENVIRONMENT, Pi_EnvBlock, workingDirectory, ref si, out pi); 

另外,我建议您将您的class级名称更改为不同于Process的class级名称。 这可能会导致一些问题。