1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using Microsoft.Win32.SafeHandles;
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.Globalization;
9 using System.IO;
10 using System.Runtime.InteropServices;
11 using System.Security;
12 using System.Text;
13 using System.Threading;
14 
15 namespace System.Diagnostics
16 {
17     public partial class Process : IDisposable
18     {
19         /// <summary>
20         /// Creates an array of <see cref="Process"/> components that are associated with process resources on a
21         /// remote computer. These process resources share the specified process name.
22         /// </summary>
GetProcessesByName(string processName, string machineName)23         public static Process[] GetProcessesByName(string processName, string machineName)
24         {
25             if (processName == null)
26             {
27                 processName = string.Empty;
28             }
29 
30             Process[] procs = GetProcesses(machineName);
31             var list = new List<Process>();
32 
33             for (int i = 0; i < procs.Length; i++)
34             {
35                 if (string.Equals(processName, procs[i].ProcessName, StringComparison.OrdinalIgnoreCase))
36                 {
37                     list.Add(procs[i]);
38                 }
39                 else
40                 {
41                     procs[i].Dispose();
42                 }
43             }
44 
45             return list.ToArray();
46         }
47 
48         [CLSCompliant(false)]
Start(string fileName, string userName, SecureString password, string domain)49         public static Process Start(string fileName, string userName, SecureString password, string domain)
50         {
51             ProcessStartInfo startInfo = new ProcessStartInfo(fileName);
52             startInfo.UserName = userName;
53             startInfo.Password = password;
54             startInfo.Domain = domain;
55             startInfo.UseShellExecute = false;
56             return Start(startInfo);
57         }
58 
59         [CLSCompliant(false)]
Start(string fileName, string arguments, string userName, SecureString password, string domain)60         public static Process Start(string fileName, string arguments, string userName, SecureString password, string domain)
61         {
62             ProcessStartInfo startInfo = new ProcessStartInfo(fileName, arguments);
63             startInfo.UserName = userName;
64             startInfo.Password = password;
65             startInfo.Domain = domain;
66             startInfo.UseShellExecute = false;
67             return Start(startInfo);
68         }
69 
70         /// <summary>
71         /// Puts a Process component in state to interact with operating system processes that run in a
72         /// special mode by enabling the native property SeDebugPrivilege on the current thread.
73         /// </summary>
EnterDebugMode()74         public static void EnterDebugMode()
75         {
76             SetPrivilege(Interop.Advapi32.SeDebugPrivilege, (int)Interop.Advapi32.SEPrivileges.SE_PRIVILEGE_ENABLED);
77         }
78 
79         /// <summary>
80         /// Takes a Process component out of the state that lets it interact with operating system processes
81         /// that run in a special mode.
82         /// </summary>
LeaveDebugMode()83         public static void LeaveDebugMode()
84         {
85             SetPrivilege(Interop.Advapi32.SeDebugPrivilege, 0);
86         }
87 
88         /// <summary>Stops the associated process immediately.</summary>
Kill()89         public void Kill()
90         {
91             using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_TERMINATE))
92             {
93                 if (!Interop.Kernel32.TerminateProcess(handle, -1))
94                     throw new Win32Exception();
95             }
96         }
97 
98         /// <summary>Discards any information about the associated process.</summary>
RefreshCore()99         private void RefreshCore()
100         {
101             _signaled = false;
102         }
103 
104         /// <summary>Additional logic invoked when the Process is closed.</summary>
CloseCore()105         private void CloseCore()
106         {
107             // Nop
108         }
109 
110         /// <devdoc>
111         ///     Make sure we are watching for a process exit.
112         /// </devdoc>
113         /// <internalonly/>
EnsureWatchingForExit()114         private void EnsureWatchingForExit()
115         {
116             if (!_watchingForExit)
117             {
118                 lock (this)
119                 {
120                     if (!_watchingForExit)
121                     {
122                         Debug.Assert(_haveProcessHandle, "Process.EnsureWatchingForExit called with no process handle");
123                         Debug.Assert(Associated, "Process.EnsureWatchingForExit called with no associated process");
124                         _watchingForExit = true;
125                         try
126                         {
127                             _waitHandle = new Interop.Kernel32.ProcessWaitHandle(_processHandle);
128                             _registeredWaitHandle = ThreadPool.RegisterWaitForSingleObject(_waitHandle,
129                                 new WaitOrTimerCallback(CompletionCallback), null, -1, true);
130                         }
131                         catch
132                         {
133                             _watchingForExit = false;
134                             throw;
135                         }
136                     }
137                 }
138             }
139         }
140 
141         /// <summary>
142         /// Instructs the Process component to wait the specified number of milliseconds for the associated process to exit.
143         /// </summary>
WaitForExitCore(int milliseconds)144         private bool WaitForExitCore(int milliseconds)
145         {
146             SafeProcessHandle handle = null;
147             try
148             {
149                 handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.SYNCHRONIZE, false);
150                 if (handle.IsInvalid)
151                     return true;
152 
153                 using (Interop.Kernel32.ProcessWaitHandle processWaitHandle = new Interop.Kernel32.ProcessWaitHandle(handle))
154                 {
155                     return _signaled = processWaitHandle.WaitOne(milliseconds);
156                 }
157             }
158             finally
159             {
160                 // If we have a hard timeout, we cannot wait for the streams
161                 if (_output != null && milliseconds == Timeout.Infinite)
162                     _output.WaitUtilEOF();
163 
164                 if (_error != null && milliseconds == Timeout.Infinite)
165                     _error.WaitUtilEOF();
166 
167                 handle?.Dispose();
168             }
169         }
170 
171         /// <summary>Gets the main module for the associated process.</summary>
172         public ProcessModule MainModule
173         {
174             get
175             {
176                 // We only return null if we couldn't find a main module. This could be because
177                 // the process hasn't finished loading the main module (most likely).
178                 // On NT, the first module is the main module.
179                 EnsureState(State.HaveId | State.IsLocal);
180                 return NtProcessManager.GetFirstModule(_processId);
181             }
182         }
183 
184         /// <summary>Checks whether the process has exited and updates state accordingly.</summary>
UpdateHasExited()185         private void UpdateHasExited()
186         {
187             using (SafeProcessHandle handle = GetProcessHandle(
188                 Interop.Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION | Interop.Advapi32.ProcessOptions.SYNCHRONIZE, false))
189             {
190                 if (handle.IsInvalid)
191                 {
192                     _exited = true;
193                 }
194                 else
195                 {
196                     int localExitCode;
197 
198                     // Although this is the wrong way to check whether the process has exited,
199                     // it was historically the way we checked for it, and a lot of code then took a dependency on
200                     // the fact that this would always be set before the pipes were closed, so they would read
201                     // the exit code out after calling ReadToEnd() or standard output or standard error. In order
202                     // to allow 259 to function as a valid exit code and to break as few people as possible that
203                     // took the ReadToEnd dependency, we check for an exit code before doing the more correct
204                     // check to see if we have been signaled.
205                     if (Interop.Kernel32.GetExitCodeProcess(handle, out localExitCode) && localExitCode != Interop.Kernel32.HandleOptions.STILL_ACTIVE)
206                     {
207                         _exitCode = localExitCode;
208                         _exited = true;
209                     }
210                     else
211                     {
212                         // The best check for exit is that the kernel process object handle is invalid,
213                         // or that it is valid and signaled.  Checking if the exit code != STILL_ACTIVE
214                         // does not guarantee the process is closed,
215                         // since some process could return an actual STILL_ACTIVE exit code (259).
216                         if (!_signaled) // if we just came from WaitForExit, don't repeat
217                         {
218                             using (var wh = new Interop.Kernel32.ProcessWaitHandle(handle))
219                             {
220                                 _signaled = wh.WaitOne(0);
221                             }
222                         }
223                         if (_signaled)
224                         {
225                             if (!Interop.Kernel32.GetExitCodeProcess(handle, out localExitCode))
226                                 throw new Win32Exception();
227 
228                             _exitCode = localExitCode;
229                             _exited = true;
230                         }
231                     }
232                 }
233             }
234         }
235 
236         /// <summary>Gets the time that the associated process exited.</summary>
237         private DateTime ExitTimeCore
238         {
239             get { return GetProcessTimes().ExitTime; }
240         }
241 
242         /// <summary>Gets the amount of time the process has spent running code inside the operating system core.</summary>
243         public TimeSpan PrivilegedProcessorTime
244         {
245             get { return GetProcessTimes().PrivilegedProcessorTime; }
246         }
247 
248         /// <summary>Gets the time the associated process was started.</summary>
249         internal DateTime StartTimeCore
250         {
251             get { return GetProcessTimes().StartTime; }
252         }
253 
254         /// <summary>
255         /// Gets the amount of time the associated process has spent utilizing the CPU.
256         /// It is the sum of the <see cref='System.Diagnostics.Process.UserProcessorTime'/> and
257         /// <see cref='System.Diagnostics.Process.PrivilegedProcessorTime'/>.
258         /// </summary>
259         public TimeSpan TotalProcessorTime
260         {
261             get { return GetProcessTimes().TotalProcessorTime; }
262         }
263 
264         /// <summary>
265         /// Gets the amount of time the associated process has spent running code
266         /// inside the application portion of the process (not the operating system core).
267         /// </summary>
268         public TimeSpan UserProcessorTime
269         {
270             get { return GetProcessTimes().UserProcessorTime; }
271         }
272 
273         /// <summary>
274         /// Gets or sets a value indicating whether the associated process priority
275         /// should be temporarily boosted by the operating system when the main window
276         /// has focus.
277         /// </summary>
278         private bool PriorityBoostEnabledCore
279         {
280             get
281             {
282                 using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION))
283                 {
284                     bool disabled;
285                     if (!Interop.Kernel32.GetProcessPriorityBoost(handle, out disabled))
286                     {
287                         throw new Win32Exception();
288                     }
289                     return !disabled;
290                 }
291             }
292             set
293             {
294                 using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_SET_INFORMATION))
295                 {
296                     if (!Interop.Kernel32.SetProcessPriorityBoost(handle, !value))
297                         throw new Win32Exception();
298                 }
299             }
300         }
301 
302         /// <summary>
303         /// Gets or sets the overall priority category for the associated process.
304         /// </summary>
305         private ProcessPriorityClass PriorityClassCore
306         {
307             get
308             {
309                 using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION))
310                 {
311                     int value = Interop.Kernel32.GetPriorityClass(handle);
312                     if (value == 0)
313                     {
314                         throw new Win32Exception();
315                     }
316                     return (ProcessPriorityClass)value;
317                 }
318             }
319             set
320             {
321                 using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_SET_INFORMATION))
322                 {
323                     if (!Interop.Kernel32.SetPriorityClass(handle, (int)value))
324                         throw new Win32Exception();
325                 }
326             }
327         }
328 
329         /// <summary>
330         /// Gets or sets which processors the threads in this process can be scheduled to run on.
331         /// </summary>
332         private IntPtr ProcessorAffinityCore
333         {
334             get
335             {
336                 using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION))
337                 {
338                     IntPtr processAffinity, systemAffinity;
339                     if (!Interop.Kernel32.GetProcessAffinityMask(handle, out processAffinity, out systemAffinity))
340                         throw new Win32Exception();
341                     return processAffinity;
342                 }
343             }
344             set
345             {
346                 using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_SET_INFORMATION))
347                 {
348                     if (!Interop.Kernel32.SetProcessAffinityMask(handle, value))
349                         throw new Win32Exception();
350                 }
351             }
352         }
353 
354         /// <summary>Gets the ID of the current process.</summary>
GetCurrentProcessId()355         private static int GetCurrentProcessId()
356         {
357             return unchecked((int)Interop.Kernel32.GetCurrentProcessId());
358         }
359 
360         /// <summary>
361         /// Gets a short-term handle to the process, with the given access.  If a handle exists,
362         /// then it is reused.  If the process has exited, it throws an exception.
363         /// </summary>
GetProcessHandle()364         private SafeProcessHandle GetProcessHandle()
365         {
366             return GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_ALL_ACCESS);
367         }
368 
369         /// <summary>Get the minimum and maximum working set limits.</summary>
GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet)370         private void GetWorkingSetLimits(out IntPtr minWorkingSet, out IntPtr maxWorkingSet)
371         {
372             using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION))
373             {
374                 int ignoredFlags;
375                 if (!Interop.Kernel32.GetProcessWorkingSetSizeEx(handle, out minWorkingSet, out maxWorkingSet, out ignoredFlags))
376                     throw new Win32Exception();
377             }
378         }
379 
380         /// <summary>Sets one or both of the minimum and maximum working set limits.</summary>
381         /// <param name="newMin">The new minimum working set limit, or null not to change it.</param>
382         /// <param name="newMax">The new maximum working set limit, or null not to change it.</param>
383         /// <param name="resultingMin">The resulting minimum working set limit after any changes applied.</param>
384         /// <param name="resultingMax">The resulting maximum working set limit after any changes applied.</param>
385         private void SetWorkingSetLimitsCore(IntPtr? newMin, IntPtr? newMax, out IntPtr resultingMin, out IntPtr resultingMax)
386         {
387             using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION | Interop.Advapi32.ProcessOptions.PROCESS_SET_QUOTA))
388             {
389                 IntPtr min, max;
390                 int ignoredFlags;
391                 if (!Interop.Kernel32.GetProcessWorkingSetSizeEx(handle, out min, out max, out ignoredFlags))
392                 {
393                     throw new Win32Exception();
394                 }
395 
396                 if (newMin.HasValue)
397                 {
398                     min = newMin.Value;
399                 }
400                 if (newMax.HasValue)
401                 {
402                     max = newMax.Value;
403                 }
404 
405                 if ((long)min > (long)max)
406                 {
407                     if (newMin != null)
408                     {
409                         throw new ArgumentException(SR.BadMinWorkset);
410                     }
411                     else
412                     {
413                         throw new ArgumentException(SR.BadMaxWorkset);
414                     }
415                 }
416 
417                 // We use SetProcessWorkingSetSizeEx which gives an option to follow
418                 // the max and min value even in low-memory and abundant-memory situations.
419                 // However, we do not use these flags to emulate the existing behavior
420                 if (!Interop.Kernel32.SetProcessWorkingSetSizeEx(handle, min, max, 0))
421                 {
422                     throw new Win32Exception();
423                 }
424 
425                 // The value may be rounded/changed by the OS, so go get it
426                 if (!Interop.Kernel32.GetProcessWorkingSetSizeEx(handle, out min, out max, out ignoredFlags))
427                 {
428                     throw new Win32Exception();
429                 }
430 
431                 resultingMin = min;
432                 resultingMax = max;
433             }
434         }
435 
436         private static readonly IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1);
437 
438         /// <summary>Starts the process using the supplied start info.</summary>
439         /// <param name="startInfo">The start info with which to start the process.</param>
StartWithCreateProcess(ProcessStartInfo startInfo)440         private bool StartWithCreateProcess(ProcessStartInfo startInfo)
441         {
442             // See knowledge base article Q190351 for an explanation of the following code.  Noteworthy tricky points:
443             //    * The handles are duplicated as non-inheritable before they are passed to CreateProcess so
444             //      that the child process can not close them
445             //    * CreateProcess allows you to redirect all or none of the standard IO handles, so we use
446             //      GetStdHandle for the handles that are not being redirected
447 
448             StringBuilder commandLine = BuildCommandLine(startInfo.FileName, startInfo.Arguments);
449 
450             Interop.Kernel32.STARTUPINFO startupInfo = new Interop.Kernel32.STARTUPINFO();
451             Interop.Kernel32.PROCESS_INFORMATION processInfo = new Interop.Kernel32.PROCESS_INFORMATION();
452             Interop.Kernel32.SECURITY_ATTRIBUTES unused_SecAttrs = new Interop.Kernel32.SECURITY_ATTRIBUTES();
453             SafeProcessHandle procSH = new SafeProcessHandle();
454             SafeThreadHandle threadSH = new SafeThreadHandle();
455             bool retVal;
456             int errorCode = 0;
457             // handles used in parent process
458             SafeFileHandle standardInputWritePipeHandle = null;
459             SafeFileHandle standardOutputReadPipeHandle = null;
460             SafeFileHandle standardErrorReadPipeHandle = null;
461             GCHandle environmentHandle = new GCHandle();
462             lock (s_createProcessLock)
463             {
464                 try
465                 {
466                     // set up the streams
467                     if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
468                     {
469                         if (startInfo.RedirectStandardInput)
470                         {
471                             CreatePipe(out standardInputWritePipeHandle, out startupInfo.hStdInput, true);
472                         }
473                         else
474                         {
475                             startupInfo.hStdInput = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_INPUT_HANDLE), false);
476                         }
477 
478                         if (startInfo.RedirectStandardOutput)
479                         {
480                             CreatePipe(out standardOutputReadPipeHandle, out startupInfo.hStdOutput, false);
481                         }
482                         else
483                         {
484                             startupInfo.hStdOutput = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false);
485                         }
486 
487                         if (startInfo.RedirectStandardError)
488                         {
489                             CreatePipe(out standardErrorReadPipeHandle, out startupInfo.hStdError, false);
490                         }
491                         else
492                         {
493                             startupInfo.hStdError = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_ERROR_HANDLE), false);
494                         }
495 
496                         startupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
497                     }
498 
499                     // set up the creation flags parameter
500                     int creationFlags = 0;
501                     if (startInfo.CreateNoWindow) creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_NO_WINDOW;
502 
503                     // set up the environment block parameter
504                     IntPtr environmentPtr = (IntPtr)0;
505                     if (startInfo._environmentVariables != null)
506                     {
507                         creationFlags |= Interop.Advapi32.StartupInfoOptions.CREATE_UNICODE_ENVIRONMENT;
508                         byte[] environmentBytes = EnvironmentVariablesToByteArray(startInfo._environmentVariables);
509                         environmentHandle = GCHandle.Alloc(environmentBytes, GCHandleType.Pinned);
510                         environmentPtr = environmentHandle.AddrOfPinnedObject();
511                     }
512                     string workingDirectory = startInfo.WorkingDirectory;
513                     if (workingDirectory == string.Empty)
514                         workingDirectory = Directory.GetCurrentDirectory();
515 
516                     if (startInfo.UserName.Length != 0)
517                     {
518                         if (startInfo.Password != null && startInfo.PasswordInClearText != null)
519                         {
520                             throw new ArgumentException(SR.CantSetDuplicatePassword);
521                         }
522 
523                         Interop.Advapi32.LogonFlags logonFlags = (Interop.Advapi32.LogonFlags)0;
524                         if (startInfo.LoadUserProfile)
525                         {
526                             logonFlags = Interop.Advapi32.LogonFlags.LOGON_WITH_PROFILE;
527                         }
528 
529                         if (startInfo.Password != null)
530                         {
531                             IntPtr passwordPtr = Marshal.SecureStringToGlobalAllocUnicode(startInfo.Password);
532                             try
533                             {
534                                 retVal = Interop.Advapi32.CreateProcessWithLogonW(
535                                     startInfo.UserName,
536                                     startInfo.Domain,
537                                     passwordPtr,
538                                     logonFlags,
539                                     null,            // we don't need this since all the info is in commandLine
540                                     commandLine,
541                                     creationFlags,
542                                     environmentPtr,
543                                     workingDirectory,
544                                     startupInfo,        // pointer to STARTUPINFO
545                                     processInfo         // pointer to PROCESS_INFORMATION
546                                 );
547 
548                                 if (!retVal)
549                                     errorCode = Marshal.GetLastWin32Error();
550                             }
551                             finally { Marshal.ZeroFreeGlobalAllocUnicode(passwordPtr); }
552                         }
553                         else
554                         {
555                             unsafe
556                             {
557                                 fixed (char* passwordPtr = startInfo.PasswordInClearText ?? string.Empty)
558                                 {
559                                     retVal = Interop.Advapi32.CreateProcessWithLogonW(
560                                             startInfo.UserName,
561                                             startInfo.Domain,
562                                             (IntPtr)passwordPtr,
563                                             logonFlags,
564                                             null,            // we don't need this since all the info is in commandLine
565                                             commandLine,
566                                             creationFlags,
567                                             environmentPtr,
568                                             workingDirectory,
569                                             startupInfo,        // pointer to STARTUPINFO
570                                             processInfo         // pointer to PROCESS_INFORMATION
571                                         );
572 
573                                 }
574                             }
575                             if (!retVal)
576                                 errorCode = Marshal.GetLastWin32Error();
577                         }
578                         if (processInfo.hProcess != IntPtr.Zero && processInfo.hProcess != (IntPtr)INVALID_HANDLE_VALUE)
579                             procSH.InitialSetHandle(processInfo.hProcess);
580                         if (processInfo.hThread != IntPtr.Zero && processInfo.hThread != (IntPtr)INVALID_HANDLE_VALUE)
581                             threadSH.InitialSetHandle(processInfo.hThread);
582                         if (!retVal)
583                         {
584                             if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
585                             {
586                                 throw new Win32Exception(errorCode, SR.InvalidApplication);
587                             }
588                             throw new Win32Exception(errorCode);
589                         }
590                     }
591                     else
592                     {
593                         retVal = Interop.Kernel32.CreateProcess(
594                                 null,                // we don't need this since all the info is in commandLine
595                                 commandLine,         // pointer to the command line string
596                                 ref unused_SecAttrs, // address to process security attributes, we don't need to inherit the handle
597                                 ref unused_SecAttrs, // address to thread security attributes.
598                                 true,                // handle inheritance flag
599                                 creationFlags,       // creation flags
600                                 environmentPtr,      // pointer to new environment block
601                                 workingDirectory,    // pointer to current directory name
602                                 startupInfo,         // pointer to STARTUPINFO
603                                 processInfo      // pointer to PROCESS_INFORMATION
604                             );
605                         if (!retVal)
606                             errorCode = Marshal.GetLastWin32Error();
607                         if (processInfo.hProcess != (IntPtr)0 && processInfo.hProcess != (IntPtr)INVALID_HANDLE_VALUE)
608                             procSH.InitialSetHandle(processInfo.hProcess);
609                         if (processInfo.hThread != (IntPtr)0 && processInfo.hThread != (IntPtr)INVALID_HANDLE_VALUE)
610                             threadSH.InitialSetHandle(processInfo.hThread);
611 
612                         if (!retVal)
613                         {
614                             if (errorCode == Interop.Errors.ERROR_BAD_EXE_FORMAT || errorCode == Interop.Errors.ERROR_EXE_MACHINE_TYPE_MISMATCH)
615                             {
616                                 throw new Win32Exception(errorCode, SR.InvalidApplication);
617                             }
618                             throw new Win32Exception(errorCode);
619                         }
620                     }
621                 }
622                 finally
623                 {
624                     // free environment block
625                     if (environmentHandle.IsAllocated)
626                     {
627                         environmentHandle.Free();
628                     }
629 
630                     startupInfo.Dispose();
631                 }
632             }
633 
634             if (startInfo.RedirectStandardInput)
635             {
636                 Encoding enc = startInfo.StandardInputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleCP());
637                 _standardInput = new StreamWriter(new FileStream(standardInputWritePipeHandle, FileAccess.Write, 4096, false), enc, 4096);
638                 _standardInput.AutoFlush = true;
639             }
640             if (startInfo.RedirectStandardOutput)
641             {
642                 Encoding enc = startInfo.StandardOutputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
643                 _standardOutput = new StreamReader(new FileStream(standardOutputReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
644             }
645             if (startInfo.RedirectStandardError)
646             {
647                 Encoding enc = startInfo.StandardErrorEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
648                 _standardError = new StreamReader(new FileStream(standardErrorReadPipeHandle, FileAccess.Read, 4096, false), enc, true, 4096);
649             }
650 
651             bool ret = false;
652             if (!procSH.IsInvalid)
653             {
654                 SetProcessHandle(procSH);
655                 SetProcessId((int)processInfo.dwProcessId);
656                 threadSH.Dispose();
657                 ret = true;
658             }
659 
660             return ret;
661         }
662 
GetEncoding(int codePage)663         private static Encoding GetEncoding(int codePage)
664         {
665             Encoding enc = EncodingHelper.GetSupportedConsoleEncoding(codePage);
666             return new ConsoleEncoding(enc); // ensure encoding doesn't output a preamble
667         }
668 
669         // -----------------------------
670         // ---- PAL layer ends here ----
671         // -----------------------------
672 
673         private bool _signaled;
674 
BuildCommandLine(string executableFileName, string arguments)675         private static StringBuilder BuildCommandLine(string executableFileName, string arguments)
676         {
677             // Construct a StringBuilder with the appropriate command line
678             // to pass to CreateProcess.  If the filename isn't already
679             // in quotes, we quote it here.  This prevents some security
680             // problems (it specifies exactly which part of the string
681             // is the file to execute).
682             StringBuilder commandLine = new StringBuilder();
683             string fileName = executableFileName.Trim();
684             bool fileNameIsQuoted = (fileName.StartsWith("\"", StringComparison.Ordinal) && fileName.EndsWith("\"", StringComparison.Ordinal));
685             if (!fileNameIsQuoted)
686             {
687                 commandLine.Append("\"");
688             }
689 
690             commandLine.Append(fileName);
691 
692             if (!fileNameIsQuoted)
693             {
694                 commandLine.Append("\"");
695             }
696 
697             if (!string.IsNullOrEmpty(arguments))
698             {
699                 commandLine.Append(" ");
700                 commandLine.Append(arguments);
701             }
702 
703             return commandLine;
704         }
705 
706         /// <summary>Gets timing information for the current process.</summary>
GetProcessTimes()707         private ProcessThreadTimes GetProcessTimes()
708         {
709             using (SafeProcessHandle handle = GetProcessHandle(Interop.Advapi32.ProcessOptions.PROCESS_QUERY_LIMITED_INFORMATION, false))
710             {
711                 if (handle.IsInvalid)
712                 {
713                     throw new InvalidOperationException(SR.Format(SR.ProcessHasExited, _processId.ToString(CultureInfo.CurrentCulture)));
714                 }
715 
716                 ProcessThreadTimes processTimes = new ProcessThreadTimes();
717                 if (!Interop.Kernel32.GetProcessTimes(handle,
718                     out processTimes._create, out processTimes._exit,
719                     out processTimes._kernel, out processTimes._user))
720                 {
721                     throw new Win32Exception();
722                 }
723 
724                 return processTimes;
725             }
726         }
727 
SetPrivilege(string privilegeName, int attrib)728         private static void SetPrivilege(string privilegeName, int attrib)
729         {
730             SafeTokenHandle hToken = null;
731             Interop.Advapi32.LUID debugValue = new Interop.Advapi32.LUID();
732 
733             // this is only a "pseudo handle" to the current process - no need to close it later
734             SafeProcessHandle processHandle = Interop.Kernel32.GetCurrentProcess();
735 
736             // get the process token so we can adjust the privilege on it.  We DO need to
737             // close the token when we're done with it.
738             if (!Interop.Advapi32.OpenProcessToken(processHandle, Interop.Kernel32.HandleOptions.TOKEN_ADJUST_PRIVILEGES, out hToken))
739             {
740                 throw new Win32Exception();
741             }
742 
743             try
744             {
745                 if (!Interop.Advapi32.LookupPrivilegeValue(null, privilegeName, out debugValue))
746                 {
747                     throw new Win32Exception();
748                 }
749 
750                 Interop.Advapi32.TokenPrivileges tkp = new Interop.Advapi32.TokenPrivileges();
751                 tkp.Luid = debugValue;
752                 tkp.Attributes = attrib;
753 
754                 Interop.Advapi32.AdjustTokenPrivileges(hToken, false, tkp, 0, IntPtr.Zero, IntPtr.Zero);
755 
756                 // AdjustTokenPrivileges can return true even if it failed to
757                 // set the privilege, so we need to use GetLastError
758                 if (Marshal.GetLastWin32Error() != Interop.Errors.ERROR_SUCCESS)
759                 {
760                     throw new Win32Exception();
761                 }
762             }
763             finally
764             {
765 #if FEATURE_TRACESWITCH
766                 Debug.WriteLineIf(_processTracing.TraceVerbose, "Process - CloseHandle(processToken)");
767 #endif
768                 if (hToken != null)
769                 {
770                     hToken.Dispose();
771                 }
772             }
773         }
774 
775         /// <devdoc>
776         ///     Gets a short-term handle to the process, with the given access.
777         ///     If a handle is stored in current process object, then use it.
778         ///     Note that the handle we stored in current process object will have all access we need.
779         /// </devdoc>
780         /// <internalonly/>
GetProcessHandle(int access, bool throwIfExited)781         private SafeProcessHandle GetProcessHandle(int access, bool throwIfExited)
782         {
783 #if FEATURE_TRACESWITCH
784             Debug.WriteLineIf(_processTracing.TraceVerbose, "GetProcessHandle(access = 0x" + access.ToString("X8", CultureInfo.InvariantCulture) + ", throwIfExited = " + throwIfExited + ")");
785 #if DEBUG
786             if (_processTracing.TraceVerbose) {
787                 StackFrame calledFrom = new StackTrace(true).GetFrame(0);
788                 Debug.WriteLine("   called from " + calledFrom.GetFileName() + ", line " + calledFrom.GetFileLineNumber());
789             }
790 #endif
791 #endif
792             if (_haveProcessHandle)
793             {
794                 if (throwIfExited)
795                 {
796                     // Since haveProcessHandle is true, we know we have the process handle
797                     // open with at least SYNCHRONIZE access, so we can wait on it with
798                     // zero timeout to see if the process has exited.
799                     using (Interop.Kernel32.ProcessWaitHandle waitHandle = new Interop.Kernel32.ProcessWaitHandle(_processHandle))
800                     {
801                         if (waitHandle.WaitOne(0))
802                         {
803                             if (_haveProcessId)
804                                 throw new InvalidOperationException(SR.Format(SR.ProcessHasExited, _processId.ToString(CultureInfo.CurrentCulture)));
805                             else
806                                 throw new InvalidOperationException(SR.ProcessHasExitedNoId);
807                         }
808                     }
809                 }
810 
811                 // If we dispose of our contained handle we'll be in a bad state. NetFX dealt with this
812                 // by doing a try..finally around every usage of GetProcessHandle and only disposed if
813                 // it wasn't our handle.
814                 return new SafeProcessHandle(_processHandle.DangerousGetHandle(), ownsHandle: false);
815             }
816             else
817             {
818                 EnsureState(State.HaveId | State.IsLocal);
819                 SafeProcessHandle handle = SafeProcessHandle.InvalidHandle;
820                 handle = ProcessManager.OpenProcess(_processId, access, throwIfExited);
821                 if (throwIfExited && (access & Interop.Advapi32.ProcessOptions.PROCESS_QUERY_INFORMATION) != 0)
822                 {
823                     if (Interop.Kernel32.GetExitCodeProcess(handle, out _exitCode) && _exitCode != Interop.Kernel32.HandleOptions.STILL_ACTIVE)
824                     {
825                         throw new InvalidOperationException(SR.Format(SR.ProcessHasExited, _processId.ToString(CultureInfo.CurrentCulture)));
826                     }
827                 }
828                 return handle;
829             }
830         }
831 
832         /// <devdoc>
833         ///     Gets a short-term handle to the process, with the given access.  If a handle exists,
834         ///     then it is reused.  If the process has exited, it throws an exception.
835         /// </devdoc>
836         /// <internalonly/>
GetProcessHandle(int access)837         private SafeProcessHandle GetProcessHandle(int access)
838         {
839             return GetProcessHandle(access, true);
840         }
841 
CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, ref Interop.Kernel32.SECURITY_ATTRIBUTES lpPipeAttributes, int nSize)842         private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, ref Interop.Kernel32.SECURITY_ATTRIBUTES lpPipeAttributes, int nSize)
843         {
844             bool ret = Interop.Kernel32.CreatePipe(out hReadPipe, out hWritePipe, ref lpPipeAttributes, nSize);
845             if (!ret || hReadPipe.IsInvalid || hWritePipe.IsInvalid)
846             {
847                 throw new Win32Exception();
848             }
849         }
850 
851         // Using synchronous Anonymous pipes for process input/output redirection means we would end up
852         // wasting a worker threadpool thread per pipe instance. Overlapped pipe IO is desirable, since
853         // it will take advantage of the NT IO completion port infrastructure. But we can't really use
854         // Overlapped I/O for process input/output as it would break Console apps (managed Console class
855         // methods such as WriteLine as well as native CRT functions like printf) which are making an
856         // assumption that the console standard handles (obtained via GetStdHandle()) are opened
857         // for synchronous I/O and hence they can work fine with ReadFile/WriteFile synchronously!
CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)858         private void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
859         {
860             Interop.Kernel32.SECURITY_ATTRIBUTES securityAttributesParent = new Interop.Kernel32.SECURITY_ATTRIBUTES();
861             securityAttributesParent.bInheritHandle = Interop.BOOL.TRUE;
862 
863             SafeFileHandle hTmp = null;
864             try
865             {
866                 if (parentInputs)
867                 {
868                     CreatePipeWithSecurityAttributes(out childHandle, out hTmp, ref securityAttributesParent, 0);
869                 }
870                 else
871                 {
872                     CreatePipeWithSecurityAttributes(out hTmp,
873                                                           out childHandle,
874                                                           ref securityAttributesParent,
875                                                           0);
876                 }
877                 // Duplicate the parent handle to be non-inheritable so that the child process
878                 // doesn't have access. This is done for correctness sake, exact reason is unclear.
879                 // One potential theory is that child process can do something brain dead like
880                 // closing the parent end of the pipe and there by getting into a blocking situation
881                 // as parent will not be draining the pipe at the other end anymore.
882                 SafeProcessHandle currentProcHandle = Interop.Kernel32.GetCurrentProcess();
883                 if (!Interop.Kernel32.DuplicateHandle(currentProcHandle,
884                                                      hTmp,
885                                                      currentProcHandle,
886                                                      out parentHandle,
887                                                      0,
888                                                      false,
889                                                      Interop.Kernel32.HandleOptions.DUPLICATE_SAME_ACCESS))
890                 {
891                     throw new Win32Exception();
892                 }
893             }
894             finally
895             {
896                 if (hTmp != null && !hTmp.IsInvalid)
897                 {
898                     hTmp.Dispose();
899                 }
900             }
901         }
902 
EnvironmentVariablesToByteArray(IDictionary<string, string> sd)903         private static byte[] EnvironmentVariablesToByteArray(IDictionary<string, string> sd)
904         {
905             // get the keys
906             string[] keys = new string[sd.Count];
907             byte[] envBlock = null;
908             sd.Keys.CopyTo(keys, 0);
909 
910             // sort both by the keys
911             // Windows 2000 requires the environment block to be sorted by the key
912             // It will first converting the case the strings and do ordinal comparison.
913 
914             // We do not use Array.Sort(keys, values, IComparer) since it is only supported
915             // in System.Runtime contract from 4.20.0.0 and Test.Net depends on System.Runtime 4.0.10.0
916             // we workaround this by sorting only the keys and then lookup the values form the keys.
917             Array.Sort(keys, StringComparer.OrdinalIgnoreCase);
918 
919             // create a list of null terminated "key=val" strings
920             StringBuilder stringBuff = new StringBuilder();
921             for (int i = 0; i < sd.Count; ++i)
922             {
923                 stringBuff.Append(keys[i]);
924                 stringBuff.Append('=');
925                 stringBuff.Append(sd[keys[i]]);
926                 stringBuff.Append('\0');
927             }
928             // an extra null at the end indicates end of list.
929             stringBuff.Append('\0');
930             envBlock = Encoding.Unicode.GetBytes(stringBuff.ToString());
931             return envBlock;
932         }
933     }
934 }
935