1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 
4 using System;
5 using System.Collections.Generic;
6 using System.ComponentModel;
7 using System.Diagnostics;
8 using System.Diagnostics.CodeAnalysis;
9 using System.IO;
10 using System.Runtime.InteropServices;
11 using System.Security.Cryptography.X509Certificates;
12 using System.Text;
13 using System.Text.RegularExpressions;
14 using System.Threading;
15 using System.Reflection;
16 using Microsoft.Win32.SafeHandles;
17 
18 using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME;
19 using Microsoft.Build.Utilities;
20 
21 namespace Microsoft.Build.Shared
22 {
23     /// <summary>
24     /// Interop methods.
25     /// </summary>
26     internal static class NativeMethodsShared
27     {
28         #region Constants
29 
30         internal const uint ERROR_INSUFFICIENT_BUFFER = 0x8007007A;
31         internal const uint STARTUP_LOADER_SAFEMODE = 0x10;
32         internal const uint S_OK = 0x0;
33         internal const uint S_FALSE = 0x1;
34         internal const uint ERROR_ACCESS_DENIED = 0x5;
35         internal const uint ERROR_FILE_NOT_FOUND = 0x80070002;
36         internal const uint FUSION_E_PRIVATE_ASM_DISALLOWED = 0x80131044; // Tried to find unsigned assembly in GAC
37         internal const uint RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG = 0x40;
38         internal const uint FILE_TYPE_CHAR = 0x0002;
39         internal const Int32 STD_OUTPUT_HANDLE = -11;
40         internal const uint RPC_S_CALLPENDING = 0x80010115;
41         internal const uint E_ABORT = (uint)0x80004004;
42 
43         internal const int FILE_ATTRIBUTE_READONLY = 0x00000001;
44         internal const int FILE_ATTRIBUTE_DIRECTORY = 0x00000010;
45         internal const int FILE_ATTRIBUTE_REPARSE_POINT = 0x00000400;
46 
47         private const string kernel32Dll = "kernel32.dll";
48         private const string mscoreeDLL = "mscoree.dll";
49 
50 #if FEATURE_HANDLEREF
51         internal static HandleRef NullHandleRef = new HandleRef(null, IntPtr.Zero);
52 #endif
53 
54         internal static IntPtr NullIntPtr = new IntPtr(0);
55 
56         // As defined in winnt.h:
57         internal const ushort PROCESSOR_ARCHITECTURE_INTEL = 0;
58         internal const ushort PROCESSOR_ARCHITECTURE_ARM = 5;
59         internal const ushort PROCESSOR_ARCHITECTURE_IA64 = 6;
60         internal const ushort PROCESSOR_ARCHITECTURE_AMD64 = 9;
61 
62         internal const uint INFINITE = 0xFFFFFFFF;
63         internal const uint WAIT_ABANDONED_0 = 0x00000080;
64         internal const uint WAIT_OBJECT_0 = 0x00000000;
65         internal const uint WAIT_TIMEOUT = 0x00000102;
66 
67 #if FEATURE_CHARSET_AUTO
68         internal const CharSet AutoOrUnicode = CharSet.Auto;
69 #else
70         internal const CharSet AutoOrUnicode = CharSet.Unicode;
71 #endif
72 
73         #endregion
74 
75         #region Enums
76 
77         private enum PROCESSINFOCLASS : int
78         {
79             ProcessBasicInformation = 0,
80             ProcessQuotaLimits,
81             ProcessIoCounters,
82             ProcessVmCounters,
83             ProcessTimes,
84             ProcessBasePriority,
85             ProcessRaisePriority,
86             ProcessDebugPort,
87             ProcessExceptionPort,
88             ProcessAccessToken,
89             ProcessLdtInformation,
90             ProcessLdtSize,
91             ProcessDefaultHardErrorMode,
92             ProcessIoPortHandlers, // Note: this is kernel mode only
93             ProcessPooledUsageAndLimits,
94             ProcessWorkingSetWatch,
95             ProcessUserModeIOPL,
96             ProcessEnableAlignmentFaultFixup,
97             ProcessPriorityClass,
98             ProcessWx86Information,
99             ProcessHandleCount,
100             ProcessAffinityMask,
101             ProcessPriorityBoost,
102             MaxProcessInfoClass
103         };
104 
105         private enum eDesiredAccess : int
106         {
107             DELETE = 0x00010000,
108             READ_CONTROL = 0x00020000,
109             WRITE_DAC = 0x00040000,
110             WRITE_OWNER = 0x00080000,
111             SYNCHRONIZE = 0x00100000,
112             STANDARD_RIGHTS_ALL = 0x001F0000,
113 
114             PROCESS_TERMINATE = 0x0001,
115             PROCESS_CREATE_THREAD = 0x0002,
116             PROCESS_SET_SESSIONID = 0x0004,
117             PROCESS_VM_OPERATION = 0x0008,
118             PROCESS_VM_READ = 0x0010,
119             PROCESS_VM_WRITE = 0x0020,
120             PROCESS_DUP_HANDLE = 0x0040,
121             PROCESS_CREATE_PROCESS = 0x0080,
122             PROCESS_SET_QUOTA = 0x0100,
123             PROCESS_SET_INFORMATION = 0x0200,
124             PROCESS_QUERY_INFORMATION = 0x0400,
125             PROCESS_ALL_ACCESS = SYNCHRONIZE | 0xFFF
126         }
127 
128         /// <summary>
129         /// Flags for CoWaitForMultipleHandles
130         /// </summary>
131         [Flags]
132         public enum COWAIT_FLAGS : int
133         {
134             /// <summary>
135             /// Exit when a handle is signaled.
136             /// </summary>
137             COWAIT_NONE = 0,
138 
139             /// <summary>
140             /// Exit when all handles are signaled AND a message is received.
141             /// </summary>
142             COWAIT_WAITALL = 0x00000001,
143 
144             /// <summary>
145             /// Exit when an RPC call is serviced.
146             /// </summary>
147             COWAIT_ALERTABLE = 0x00000002
148         }
149 
150         /// <summary>
151         /// Processor architecture values
152         /// </summary>
153         internal enum ProcessorArchitectures
154         {
155             // Intel 32 bit
156             X86,
157 
158             // AMD64 64 bit
159             X64,
160 
161             // Itanium 64
162             IA64,
163 
164             // ARM
165             ARM,
166 
167             // Who knows
168             Unknown
169         }
170 
171         #endregion
172 
173         #region Structs
174 
175         /// <summary>
176         /// Structure that contain information about the system on which we are running
177         /// </summary>
178         [StructLayout(LayoutKind.Sequential)]
179         internal struct SYSTEM_INFO
180         {
181             // This is a union of a DWORD and a struct containing 2 WORDs.
182             internal ushort wProcessorArchitecture;
183             internal ushort wReserved;
184 
185             internal uint dwPageSize;
186             internal IntPtr lpMinimumApplicationAddress;
187             internal IntPtr lpMaximumApplicationAddress;
188             internal IntPtr dwActiveProcessorMask;
189             internal uint dwNumberOfProcessors;
190             internal uint dwProcessorType;
191             internal uint dwAllocationGranularity;
192             internal ushort wProcessorLevel;
193             internal ushort wProcessorRevision;
194         }
195 
196         /// <summary>
197         /// Wrap the intptr returned by OpenProcess in a safe handle.
198         /// </summary>
199         internal class SafeProcessHandle : SafeHandleZeroOrMinusOneIsInvalid
200         {
201             // Create a SafeHandle, informing the base class
202             // that this SafeHandle instance "owns" the handle,
203             // and therefore SafeHandle should call
204             // our ReleaseHandle method when the SafeHandle
205             // is no longer in use
SafeProcessHandle()206             private SafeProcessHandle() : base(true)
207             {
208             }
ReleaseHandle()209             protected override bool ReleaseHandle()
210             {
211                 return CloseHandle(handle);
212             }
213         }
214 
215         /// <summary>
216         /// Contains information about the current state of both physical and virtual memory, including extended memory
217         /// </summary>
218         [StructLayout(LayoutKind.Sequential, CharSet = AutoOrUnicode)]
219         internal class MemoryStatus
220         {
221             /// <summary>
222             /// Initializes a new instance of the <see cref="T:MemoryStatus"/> class.
223             /// </summary>
MemoryStatus()224             public MemoryStatus()
225             {
226 #if (CLR2COMPATIBILITY)
227             _length = (uint)Marshal.SizeOf(typeof(NativeMethodsShared.MemoryStatus));
228 #else
229             _length = (uint)Marshal.SizeOf<NativeMethodsShared.MemoryStatus>();
230 #endif
231             }
232 
233             /// <summary>
234             /// Size of the structure, in bytes. You must set this member before calling GlobalMemoryStatusEx.
235             /// </summary>
236             private uint _length;
237 
238             /// <summary>
239             /// Number between 0 and 100 that specifies the approximate percentage of physical
240             /// memory that is in use (0 indicates no memory use and 100 indicates full memory use).
241             /// </summary>
242             public uint MemoryLoad;
243 
244             /// <summary>
245             /// Total size of physical memory, in bytes.
246             /// </summary>
247             public ulong TotalPhysical;
248 
249             /// <summary>
250             /// Size of physical memory available, in bytes.
251             /// </summary>
252             public ulong AvailablePhysical;
253 
254             /// <summary>
255             /// Size of the committed memory limit, in bytes. This is physical memory plus the
256             /// size of the page file, minus a small overhead.
257             /// </summary>
258             public ulong TotalPageFile;
259 
260             /// <summary>
261             /// Size of available memory to commit, in bytes. The limit is ullTotalPageFile.
262             /// </summary>
263             public ulong AvailablePageFile;
264 
265             /// <summary>
266             /// Total size of the user mode portion of the virtual address space of the calling process, in bytes.
267             /// </summary>
268             public ulong TotalVirtual;
269 
270             /// <summary>
271             /// Size of unreserved and uncommitted memory in the user mode portion of the virtual
272             /// address space of the calling process, in bytes.
273             /// </summary>
274             public ulong AvailableVirtual;
275 
276             /// <summary>
277             /// Size of unreserved and uncommitted memory in the extended portion of the virtual
278             /// address space of the calling process, in bytes.
279             /// </summary>
280             public ulong AvailableExtendedVirtual;
281         }
282 
283         [StructLayout(LayoutKind.Sequential)]
284         private struct PROCESS_BASIC_INFORMATION
285         {
286             public IntPtr ExitStatus;
287             public IntPtr PebBaseAddress;
288             public IntPtr AffinityMask;
289             public IntPtr BasePriority;
290             public IntPtr UniqueProcessId;
291             public IntPtr InheritedFromUniqueProcessId;
292 
293             public int Size
294             {
295                 get { return (6 * IntPtr.Size); }
296             }
297         };
298 
299         /// <summary>
300         /// Contains information about a file or directory; used by GetFileAttributesEx.
301         /// </summary>
302         [StructLayout(LayoutKind.Sequential)]
303         public struct WIN32_FILE_ATTRIBUTE_DATA
304         {
305             internal int fileAttributes;
306             internal uint ftCreationTimeLow;
307             internal uint ftCreationTimeHigh;
308             internal uint ftLastAccessTimeLow;
309             internal uint ftLastAccessTimeHigh;
310             internal uint ftLastWriteTimeLow;
311             internal uint ftLastWriteTimeHigh;
312             internal uint fileSizeHigh;
313             internal uint fileSizeLow;
314         }
315 
316         /// <summary>
317         /// Contains the security descriptor for an object and specifies whether
318         /// the handle retrieved by specifying this structure is inheritable.
319         /// </summary>
320         [StructLayout(LayoutKind.Sequential)]
321         internal class SecurityAttributes
322         {
SecurityAttributes()323             public SecurityAttributes()
324             {
325 #if (CLR2COMPATIBILITY)
326             _nLength = (uint)Marshal.SizeOf(typeof(NativeMethodsShared.SecurityAttributes));
327 #else
328             _nLength = (uint)Marshal.SizeOf<NativeMethodsShared.SecurityAttributes>();
329 #endif
330             }
331 
332             private uint _nLength;
333 
334             public IntPtr lpSecurityDescriptor;
335 
336             public bool bInheritHandle;
337         }
338 
339         private class SystemInformationData
340         {
341             /// <summary>
342             /// Architecture as far as the current process is concerned.
343             /// It's x86 in wow64 (native architecture is x64 in that case).
344             /// Otherwise it's the same as the native architecture.
345             /// </summary>
346             public readonly ProcessorArchitectures ProcessorArchitectureType;
347 
348             /// <summary>
349             /// Actual architecture of the system.
350             /// </summary>
351             public readonly ProcessorArchitectures ProcessorArchitectureTypeNative;
352 
353             /// <summary>
354             /// Convert SYSTEM_INFO architecture values to the internal enum
355             /// </summary>
356             /// <param name="arch"></param>
357             /// <returns></returns>
ConvertSystemArchitecture(ushort arch)358             private static ProcessorArchitectures ConvertSystemArchitecture(ushort arch)
359             {
360                 switch (arch)
361                 {
362                     case PROCESSOR_ARCHITECTURE_INTEL:
363                         return ProcessorArchitectures.X86;
364                     case PROCESSOR_ARCHITECTURE_AMD64:
365                         return ProcessorArchitectures.X64;
366                     case PROCESSOR_ARCHITECTURE_ARM:
367                         return ProcessorArchitectures.ARM;
368                     case PROCESSOR_ARCHITECTURE_IA64:
369                         return ProcessorArchitectures.IA64;
370                     default:
371                         return ProcessorArchitectures.Unknown;
372                 }
373             }
374 
375             /// <summary>
376             /// Read system info values
377             /// </summary>
SystemInformationData()378             public SystemInformationData()
379             {
380                 ProcessorArchitectureType = ProcessorArchitectures.Unknown;
381                 ProcessorArchitectureTypeNative = ProcessorArchitectures.Unknown;
382 
383                 if (IsWindows)
384                 {
385                     var systemInfo = new SYSTEM_INFO();
386 
387                     GetSystemInfo(ref systemInfo);
388                     ProcessorArchitectureType = ConvertSystemArchitecture(systemInfo.wProcessorArchitecture);
389 
390                     GetNativeSystemInfo(ref systemInfo);
391                     ProcessorArchitectureTypeNative = ConvertSystemArchitecture(systemInfo.wProcessorArchitecture);
392                 }
393                 else
394                 {
395                     try
396                     {
397                         // On Unix run 'uname -m' to get the architecture. It's common for Linux and Mac
398                         using (
399                             var proc =
400                                 Process.Start(
401                                     new ProcessStartInfo("uname")
402                                     {
403                                         Arguments = "-m",
404                                         UseShellExecute = false,
405                                         RedirectStandardOutput = true,
406                                         CreateNoWindow = true
407                                     }))
408                         {
409                             string arch = null;
410                             if (proc != null)
411                             {
412                                 // Since uname -m simply returns kernel property, it should be quick.
413                                 // 1 second is the best guess for a safe timeout.
414                                 proc.WaitForExit(1000);
415                                 arch = proc.StandardOutput.ReadLine();
416                             }
417 
418                             if (!string.IsNullOrEmpty(arch))
419                             {
420                                 if (arch.StartsWith("x86_64", StringComparison.OrdinalIgnoreCase))
421                                 {
422                                     ProcessorArchitectureType = ProcessorArchitectures.X64;
423                                 }
424                                 else if (arch.StartsWith("ia64", StringComparison.OrdinalIgnoreCase))
425                                 {
426                                     ProcessorArchitectureType = ProcessorArchitectures.IA64;
427                                 }
428                                 else if (arch.StartsWith("arm", StringComparison.OrdinalIgnoreCase))
429                                 {
430                                     ProcessorArchitectureType = ProcessorArchitectures.ARM;
431                                 }
432                                 else if (arch.StartsWith("i", StringComparison.OrdinalIgnoreCase)
433                                          && arch.EndsWith("86", StringComparison.OrdinalIgnoreCase))
434                                 {
435                                     ProcessorArchitectureType = ProcessorArchitectures.X86;
436                                 }
437                             }
438                         }
439                     }
440                     catch
441                     {
442                         ProcessorArchitectureType = ProcessorArchitectures.Unknown;
443                     }
444 
445                     ProcessorArchitectureTypeNative = ProcessorArchitectureType;
446                 }
447             }
448         }
449 
450         #endregion
451 
452         #region Member data
453 
454         /// <summary>
455         /// Default buffer size to use when dealing with the Windows API.
456         /// </summary>
457         /// <remarks>
458         /// This member is intentionally not a constant because we want to allow
459         /// unit tests to change it.
460         /// </remarks>
461         internal static int MAX_PATH = 260;
462 
463 
464         /// <summary>
465         /// Cached value for IsUnixLike (this method is called frequently during evaluation).
466         /// </summary>
467         private static readonly bool s_isUnixLike = IsLinux || IsOSX || IsBSD;
468 
469         /// <summary>
470         /// Gets a flag indicating if we are running under a Unix-like system (Mac, Linux, etc.)
471         /// </summary>
472         internal static bool IsUnixLike
473         {
474             get { return s_isUnixLike; }
475         }
476 
477         /// <summary>
478         /// Gets a flag indicating if we are running under Linux
479         /// </summary>
480         internal static bool IsLinux
481         {
482 #if CLR2COMPATIBILITY
483             get { return false; }
484 #else
485             get { return RuntimeInformation.IsOSPlatform(OSPlatform.Linux); }
486 #endif
487         }
488 
489         /// <summary>
490         /// Gets a flag indicating if we are running under flavor of BSD (NetBSD, OpenBSD, FreeBSD)
491         /// </summary>
492         internal static bool IsBSD
493         {
494 #if CLR2COMPATIBILITY
495             get { return false; }
496 #else
497             get
498             {
499                 return RuntimeInformation.IsOSPlatform(OSPlatform.Create("FREEBSD")) ||
500                        RuntimeInformation.IsOSPlatform(OSPlatform.Create("NETBSD")) ||
501                        RuntimeInformation.IsOSPlatform(OSPlatform.Create("OPENBSD"));
502             }
503 #endif
504         }
505 
506         private static readonly object IsMonoLock = new object();
507 
508         private static bool? _isMono;
509 
510         /// <summary>
511         /// Gets a flag indicating if we are running under MONO
512         /// </summary>
513         internal static bool IsMono
514         {
515             get
516             {
517                 if (_isMono != null) return _isMono.Value;
518 
519                 lock (IsMonoLock)
520                 {
521                     if (_isMono == null)
522                     {
523                         // There could be potentially expensive TypeResolve events, so cache IsMono.
524                         // Also, VS does not host Mono runtimes, so turn IsMono off when msbuild is running under VS
525                         _isMono = !BuildEnvironmentHelper.Instance.RunningInVisualStudio &&
526                                   Type.GetType("Mono.Runtime") != null;
527                     }
528                 }
529 
530                 return _isMono.Value;
531             }
532         }
533 
534         /// <summary>
535         /// Gets a flag indicating if we are running under some version of Windows
536         /// </summary>
537         internal static bool IsWindows
538         {
539 #if CLR2COMPATIBILITY
540             get { return true; }
541 #else
542             get { return RuntimeInformation.IsOSPlatform(OSPlatform.Windows); }
543 #endif
544         }
545 
546 #if MONO
547         private static bool? _isOSX;
548 #endif
549         /// <summary>
550         /// Gets a flag indicating if we are running under Mac OSX
551         /// </summary>
552         internal static bool IsOSX
553         {
554 #if MONO
555             get
556             {
557                 if (!_isOSX.HasValue)
558                 {
559                     _isOSX = File.Exists("/usr/lib/libc.dylib");
560                 }
561 
562                 return _isOSX.Value;
563             }
564 #elif CLR2COMPATIBILITY
565             get { return false; }
566 #else
567             get { return RuntimeInformation.IsOSPlatform(OSPlatform.OSX); }
568 #endif
569         }
570 
571         /// <summary>
572         /// Gets a string for the current OS. This matches the OS env variable
573         /// for Windows (Windows_NT).
574         /// </summary>
575         internal static string OSName
576         {
577             get { return IsWindows ? "Windows_NT" : "Unix"; }
578         }
579 
580         /// <summary>
581         /// OS name that can be used for the msbuildExtensionsPathSearchPaths element
582         /// for a toolset
583         /// </summary>
GetOSNameForExtensionsPath()584         internal static string GetOSNameForExtensionsPath()
585         {
586             return IsOSX ? "osx" : IsUnixLike ? "unix" : "windows";
587         }
588 
589         /// <summary>
590         /// The base directory for all framework paths in Mono
591         /// </summary>
592         private static string s_frameworkBasePath;
593 
594         /// <summary>
595         /// The directory of the current framework
596         /// </summary>
597         private static string s_frameworkCurrentPath;
598 
599         /// <summary>
600         /// Gets the currently running framework path
601         /// </summary>
602         internal static string FrameworkCurrentPath
603         {
604             get
605             {
606                 if (s_frameworkCurrentPath == null)
607                 {
608                     var baseTypeLocation = AssemblyUtilities.GetAssemblyLocation(typeof(string).GetTypeInfo().Assembly);
609 
610                     s_frameworkCurrentPath =
611                         Path.GetDirectoryName(baseTypeLocation)
612                         ?? string.Empty;
613                 }
614 
615                 return s_frameworkCurrentPath;
616             }
617         }
618 
619         /// <summary>
620         /// Gets the base directory of all Mono frameworks
621         /// </summary>
622         internal static string FrameworkBasePath
623         {
624             get
625             {
626                 if (s_frameworkBasePath == null)
627                 {
628                     var dir = FrameworkCurrentPath;
629                     if (dir != string.Empty)
630                     {
631                         dir = Path.GetDirectoryName(dir);
632                     }
633 
634                     s_frameworkBasePath = dir ?? string.Empty;
635                 }
636 
637                 return s_frameworkBasePath;
638             }
639         }
640 
641         /// <summary>
642         /// System information, initialized when required.
643         /// </summary>
644         /// <remarks>
645         /// Initially implemented as <see cref="Lazy{SystemInformationData}"/>, but
646         /// that's .NET 4+, and this is used in MSBuildTaskHost.
647         /// </remarks>
648         private static SystemInformationData SystemInformation
649         {
650             get
651             {
652                 if (!_systemInformationInitialized)
653                 {
654                     lock (SystemInformationLock)
655                     {
656                         if (!_systemInformationInitialized)
657                         {
658                             _systemInformation = new SystemInformationData();
659                             _systemInformationInitialized = true;
660                         }
661                     }
662                 }
663                 return _systemInformation;
664             }
665         }
666 
667         private static SystemInformationData _systemInformation;
668         private static bool _systemInformationInitialized;
669         private static readonly object SystemInformationLock = new object();
670 
671         /// <summary>
672         /// Architecture getter
673         /// </summary>
674         internal static ProcessorArchitectures ProcessorArchitecture => SystemInformation.ProcessorArchitectureType;
675 
676         /// <summary>
677         /// Native architecture getter
678         /// </summary>
679         internal static ProcessorArchitectures ProcessorArchitectureNative => SystemInformation.ProcessorArchitectureTypeNative;
680 
681 #endregion
682 
683 #region Set Error Mode (copied from BCL)
684 
685         private static readonly Version s_threadErrorModeMinOsVersion = new Version(6, 1, 0x1db0);
686 
SetErrorMode(int newMode)687         internal static int SetErrorMode(int newMode)
688         {
689 #if FEATURE_OSVERSION
690             if (Environment.OSVersion.Version < s_threadErrorModeMinOsVersion)
691             {
692                 return SetErrorMode_VistaAndOlder(newMode);
693             }
694 #endif
695             int num;
696             SetErrorMode_Win7AndNewer(newMode, out num);
697             return num;
698         }
699 
700         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
701         [DllImport("kernel32.dll", EntryPoint = "SetThreadErrorMode", SetLastError = true)]
SetErrorMode_Win7AndNewer(int newMode, out int oldMode)702         private static extern bool SetErrorMode_Win7AndNewer(int newMode, out int oldMode);
703 
704         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
705         [DllImport("kernel32.dll", EntryPoint = "SetErrorMode", ExactSpelling = true)]
SetErrorMode_VistaAndOlder(int newMode)706         private static extern int SetErrorMode_VistaAndOlder(int newMode);
707 
708 #endregion
709 
710 #region Wrapper methods
711 
712         /// <summary>
713         /// Really truly non pumping wait.
714         /// Raw IntPtrs have to be used, because the marshaller does not support arrays of SafeHandle, only
715         /// single SafeHandles.
716         /// </summary>
717         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
718         [DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
WaitForMultipleObjects(uint handle, IntPtr[] handles, bool waitAll, uint milliseconds)719         public static extern Int32 WaitForMultipleObjects(uint handle, IntPtr[] handles, bool waitAll, uint milliseconds);
720 
721         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
722         [DllImport("kernel32.dll", SetLastError = true)]
GetSystemInfo(ref SYSTEM_INFO lpSystemInfo)723         internal static extern void GetSystemInfo(ref SYSTEM_INFO lpSystemInfo);
724 
725         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
726         [DllImport("kernel32.dll", SetLastError = true)]
GetNativeSystemInfo(ref SYSTEM_INFO lpSystemInfo)727         internal static extern void GetNativeSystemInfo(ref SYSTEM_INFO lpSystemInfo);
728 
729         /// <summary>
730         /// Get the last write time of the fullpath to a directory. If the pointed path is not a directory, or
731         /// if the directory does not exist, then false is returned and fileModifiedTimeUtc is set DateTime.MinValue.
732         /// </summary>
733         /// <param name="fullPath">Full path to the file in the filesystem</param>
734         /// <param name="fileModifiedTimeUtc">The UTC last write time for the directory</param>
GetLastWriteDirectoryUtcTime(string fullPath, out DateTime fileModifiedTimeUtc)735         internal static bool GetLastWriteDirectoryUtcTime(string fullPath, out DateTime fileModifiedTimeUtc)
736         {
737             // This code was copied from the reference manager, if there is a bug fix in that code, see if the same fix should also be made
738             // there
739 
740             fileModifiedTimeUtc = DateTime.MinValue;
741 
742             if (IsWindows)
743             {
744                 WIN32_FILE_ATTRIBUTE_DATA data = new WIN32_FILE_ATTRIBUTE_DATA();
745                 bool success = false;
746 
747                 success = GetFileAttributesEx(fullPath, 0, ref data);
748                 if (success)
749                 {
750                     if ((data.fileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0)
751                     {
752                         long dt = ((long)(data.ftLastWriteTimeHigh) << 32) | ((long)data.ftLastWriteTimeLow);
753                         fileModifiedTimeUtc = DateTime.FromFileTimeUtc(dt);
754                     }
755                     else
756                     {
757                         // Path does not point to a directory
758                         success = false;
759                     }
760                 }
761 
762                 return success;
763             }
764 
765             fileModifiedTimeUtc = Directory.GetLastWriteTimeUtc(fullPath);
766             return true;
767         }
768 
769         /// <summary>
770         /// Takes the path and returns the short path
771         /// </summary>
GetShortFilePath(string path)772         internal static string GetShortFilePath(string path)
773         {
774             if (!IsWindows)
775             {
776                 return path;
777             }
778 
779             if (path != null)
780             {
781                 int length = GetShortPathName(path, null, 0);
782                 int errorCode = Marshal.GetLastWin32Error();
783 
784                 if (length > 0)
785                 {
786                     StringBuilder fullPathBuffer = new StringBuilder(length);
787                     length = GetShortPathName(path, fullPathBuffer, length);
788                     errorCode = Marshal.GetLastWin32Error();
789 
790                     if (length > 0)
791                     {
792                         string fullPath = fullPathBuffer.ToString();
793                         path = fullPath;
794                     }
795                 }
796 
797                 if (length == 0 && errorCode != 0)
798                 {
799                     ThrowExceptionForErrorCode(errorCode);
800                 }
801             }
802 
803             return path;
804         }
805 
806         /// <summary>
807         /// Takes the path and returns a full path
808         /// </summary>
809         /// <param name="path"></param>
810         /// <returns></returns>
GetLongFilePath(string path)811         internal static string GetLongFilePath(string path)
812         {
813             if (IsUnixLike)
814             {
815                 return path;
816             }
817 
818             if (path != null)
819             {
820                 int length = GetLongPathName(path, null, 0);
821                 int errorCode = Marshal.GetLastWin32Error();
822 
823                 if (length > 0)
824                 {
825                     StringBuilder fullPathBuffer = new StringBuilder(length);
826                     length = GetLongPathName(path, fullPathBuffer, length);
827                     errorCode = Marshal.GetLastWin32Error();
828 
829                     if (length > 0)
830                     {
831                         string fullPath = fullPathBuffer.ToString();
832                         path = fullPath;
833                     }
834                 }
835 
836                 if (length == 0 && errorCode != 0)
837                 {
838                     ThrowExceptionForErrorCode(errorCode);
839                 }
840             }
841 
842             return path;
843         }
844 
845         /// <summary>
846         /// Retrieves the current global memory status.
847         /// </summary>
GetMemoryStatus()848         internal static MemoryStatus GetMemoryStatus()
849         {
850             if (NativeMethodsShared.IsWindows)
851             {
852                 MemoryStatus status = new MemoryStatus();
853                 bool returnValue = NativeMethodsShared.GlobalMemoryStatusEx(status);
854                 if (!returnValue)
855                 {
856                     return null;
857                 }
858 
859                 return status;
860             }
861 
862             return null;
863         }
864 
865         /// <summary>
866         /// Get the last write time of the fullpath to the file.
867         /// </summary>
868         /// <param name="fullPath">Full path to the file in the filesystem</param>
869         /// <returns>The last write time of the file, or DateTime.MinValue if the file does not exist.</returns>
870         /// <remarks>
871         /// This method should be accurate for regular files and symlinks, but can report incorrect data
872         /// if the file's content was modified by writing to it through a different link, unless
873         /// MSBUILDALWAYSCHECKCONTENTTIMESTAMP=1.
874         /// </remarks>
GetLastWriteFileUtcTime(string fullPath)875         internal static DateTime GetLastWriteFileUtcTime(string fullPath)
876         {
877             DateTime fileModifiedTime = DateTime.MinValue;
878 
879             if (IsWindows)
880             {
881                 if (Traits.Instance.EscapeHatches.AlwaysUseContentTimestamp)
882                 {
883                     return GetContentLastWriteFileUtcTime(fullPath);
884                 }
885 
886                 WIN32_FILE_ATTRIBUTE_DATA data = new WIN32_FILE_ATTRIBUTE_DATA();
887                 bool success = false;
888 
889                 success = NativeMethodsShared.GetFileAttributesEx(fullPath, 0, ref data);
890 
891                 if (success)
892                 {
893                     long dt = ((long)(data.ftLastWriteTimeHigh) << 32) | ((long)data.ftLastWriteTimeLow);
894                     fileModifiedTime = DateTime.FromFileTimeUtc(dt);
895 
896                     // If file is a symlink _and_ we're not instructed to do the wrong thing, get a more accurate timestamp.
897                     if ((data.fileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) == FILE_ATTRIBUTE_REPARSE_POINT && !Traits.Instance.EscapeHatches.UseSymlinkTimeInsteadOfTargetTime)
898                     {
899                         fileModifiedTime = GetContentLastWriteFileUtcTime(fullPath);
900                     }
901                 }
902             }
903             else if (File.Exists(fullPath))
904             {
905                 fileModifiedTime = File.GetLastWriteTimeUtc(fullPath);
906             }
907 
908             return fileModifiedTime;
909         }
910 
911         /// <summary>
912         /// Get the last write time of the content pointed to by a file path.
913         /// </summary>
914         /// <param name="fullPath">Full path to the file in the filesystem</param>
915         /// <returns>The last write time of the file, or DateTime.MinValue if the file does not exist.</returns>
916         /// <remarks>
917         /// This is the most accurate timestamp-extraction mechanism, but it is too slow to use all the time.
918         /// See https://github.com/Microsoft/msbuild/issues/2052.
919         /// </remarks>
GetContentLastWriteFileUtcTime(string fullPath)920         private static DateTime GetContentLastWriteFileUtcTime(string fullPath)
921         {
922             DateTime fileModifiedTime = DateTime.MinValue;
923 
924             using (SafeFileHandle handle =
925                 CreateFile(fullPath,
926                     GENERIC_READ,
927                     FILE_SHARE_READ,
928                     IntPtr.Zero,
929                     OPEN_EXISTING,
930                     FILE_ATTRIBUTE_NORMAL, /* No FILE_FLAG_OPEN_REPARSE_POINT; read through to content */
931                     IntPtr.Zero))
932             {
933                 if (!handle.IsInvalid)
934                 {
935                     FILETIME ftCreationTime, ftLastAccessTime, ftLastWriteTime;
936                     if (!GetFileTime(handle, out ftCreationTime, out ftLastAccessTime, out ftLastWriteTime) != true)
937                     {
938                         long fileTime = ((long)(uint)ftLastWriteTime.dwHighDateTime) << 32 |
939                                         (long)(uint)ftLastWriteTime.dwLowDateTime;
940                         fileModifiedTime =
941                             DateTime.FromFileTimeUtc(fileTime);
942                     }
943                 }
944             }
945 
946             return fileModifiedTime;
947         }
948 
949         /// <summary>
950         /// Did the HRESULT succeed
951         /// </summary>
HResultSucceeded(int hr)952         public static bool HResultSucceeded(int hr)
953         {
954             return (hr >= 0);
955         }
956 
957         /// <summary>
958         /// Did the HRESULT Fail
959         /// </summary>
HResultFailed(int hr)960         public static bool HResultFailed(int hr)
961         {
962             return (hr < 0);
963         }
964 
965         /// <summary>
966         /// Given an error code, converts it to an HRESULT and throws the appropriate exception.
967         /// </summary>
968         /// <param name="errorCode"></param>
ThrowExceptionForErrorCode(int errorCode)969         public static void ThrowExceptionForErrorCode(int errorCode)
970         {
971             // See ndp\clr\src\bcl\system\io\__error.cs for this code as it appears in the CLR.
972 
973             // Something really bad went wrong with the call
974             // translate the error into an exception
975 
976             // Convert the errorcode into an HRESULT (See MakeHRFromErrorCode in Win32Native.cs in
977             // ndp\clr\src\bcl\microsoft\win32)
978             errorCode = unchecked(((int)0x80070000) | errorCode);
979 
980             // Throw an exception as best we can
981             Marshal.ThrowExceptionForHR(errorCode);
982         }
983 
984         /// <summary>
985         /// Kills the specified process by id and all of its children recursively.
986         /// </summary>
KillTree(int processIdToKill)987         internal static void KillTree(int processIdToKill)
988         {
989             // Note that GetProcessById does *NOT* internally hold on to the process handle.
990             // Only when you create the process using the Process object
991             // does the Process object retain the original handle.
992 
993             Process thisProcess = null;
994             try
995             {
996                 thisProcess = Process.GetProcessById(processIdToKill);
997             }
998             catch (ArgumentException)
999             {
1000                 // The process has already died for some reason.  So shrug and assume that any child processes
1001                 // have all also either died or are in the process of doing so.
1002                 return;
1003             }
1004 
1005             try
1006             {
1007                 DateTime myStartTime = thisProcess.StartTime;
1008 
1009                 // Grab the process handle.  We want to keep this open for the duration of the function so that
1010                 // it cannot be reused while we are running.
1011                 SafeProcessHandle hProcess = OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION, false, processIdToKill);
1012                 if (hProcess.IsInvalid)
1013                 {
1014                     return;
1015                 }
1016 
1017                 try
1018                 {
1019                     try
1020                     {
1021                         // Kill this process, so that no further children can be created.
1022                         thisProcess.Kill();
1023                     }
1024                     catch (Win32Exception e)
1025                     {
1026                         // Access denied is potentially expected -- it happens when the process that
1027                         // we're attempting to kill is already dead.  So just ignore in that case.
1028                         if (e.NativeErrorCode != ERROR_ACCESS_DENIED)
1029                         {
1030                             throw;
1031                         }
1032                     }
1033 
1034                     // Now enumerate our children.  Children of this process are any process which has this process id as its parent
1035                     // and which also started after this process did.
1036                     List<KeyValuePair<int, SafeProcessHandle>> children = GetChildProcessIds(processIdToKill, myStartTime);
1037 
1038                     try
1039                     {
1040                         foreach (KeyValuePair<int, SafeProcessHandle> childProcessInfo in children)
1041                         {
1042                             KillTree(childProcessInfo.Key);
1043                         }
1044                     }
1045                     finally
1046                     {
1047                         foreach (KeyValuePair<int, SafeProcessHandle> childProcessInfo in children)
1048                         {
1049                             childProcessInfo.Value.Dispose();
1050                         }
1051                     }
1052                 }
1053                 finally
1054                 {
1055                     // Release the handle.  After this point no more children of this process exist and this process has also exited.
1056                     hProcess.Dispose();
1057                 }
1058             }
1059             finally
1060             {
1061                 thisProcess.Dispose();
1062             }
1063         }
1064 
1065         /// <summary>
1066         /// Returns the parent process id for the specified process.
1067         /// Returns zero if it cannot be gotten for some reason.
1068         /// </summary>
GetParentProcessId(int processId)1069         internal static int GetParentProcessId(int processId)
1070         {
1071             int ParentID = 0;
1072 #if !CLR2COMPATIBILITY
1073             if (IsUnixLike)
1074             {
1075                 string line = null;
1076 
1077                 try
1078                 {
1079                     // /proc/<processID>/stat returns a bunch of space separated fields. Get that string
1080                     using (var r = FileUtilities.OpenRead("/proc/" + processId + "/stat"))
1081                     {
1082                         line = r.ReadLine();
1083                     }
1084                 }
1085                 catch // Ignore errors since the process may have terminated
1086                 {
1087                 }
1088 
1089                 if (!string.IsNullOrWhiteSpace(line))
1090                 {
1091                     // One of the fields is the process name. It may contain any characters, but since it's
1092                     // in parenthesis, we can finds its end by looking for the last parenthesis. After that,
1093                     // there comes a space, then the second fields separated by a space is the parent id.
1094                     string[] statFields = line.Substring(line.LastIndexOf(')')).Split(new[] { ' ' }, 4);
1095                     if (statFields.Length >= 3)
1096                     {
1097                         ParentID = Int32.Parse(statFields[2]);
1098                     }
1099                 }
1100             }
1101             else
1102 #endif
1103             {
1104                 SafeProcessHandle hProcess = OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION, false, processId);
1105 
1106                 if (!hProcess.IsInvalid)
1107                 {
1108                     try
1109                     {
1110                         // UNDONE: NtQueryInformationProcess will fail if we are not elevated and other process is. Advice is to change to use ToolHelp32 API's
1111                         // For now just return zero and worst case we will not kill some children.
1112                         PROCESS_BASIC_INFORMATION pbi = new PROCESS_BASIC_INFORMATION();
1113                         int pSize = 0;
1114 
1115                         if (0 == NtQueryInformationProcess(hProcess, PROCESSINFOCLASS.ProcessBasicInformation, ref pbi, pbi.Size, ref pSize))
1116                         {
1117                             ParentID = (int)pbi.InheritedFromUniqueProcessId;
1118                         }
1119                     }
1120                     finally
1121                     {
1122                         hProcess.Dispose();
1123                     }
1124                 }
1125             }
1126 
1127             return (ParentID);
1128         }
1129 
1130         /// <summary>
1131         /// Returns an array of all the immediate child processes by id.
1132         /// NOTE: The IntPtr in the tuple is the handle of the child process.  CloseHandle MUST be called on this.
1133         /// </summary>
GetChildProcessIds(int parentProcessId, DateTime parentStartTime)1134         internal static List<KeyValuePair<int, SafeProcessHandle>> GetChildProcessIds(int parentProcessId, DateTime parentStartTime)
1135         {
1136             List<KeyValuePair<int, SafeProcessHandle>> myChildren = new List<KeyValuePair<int, SafeProcessHandle>>();
1137 
1138             foreach (Process possibleChildProcess in Process.GetProcesses())
1139             {
1140                 using (possibleChildProcess)
1141                 {
1142                     // Hold the child process handle open so that children cannot die and restart with a different parent after we've started looking at it.
1143                     // This way, any handle we pass back is guaranteed to be one of our actual children.
1144                     SafeProcessHandle childHandle = OpenProcess(eDesiredAccess.PROCESS_QUERY_INFORMATION, false, possibleChildProcess.Id);
1145                     if (childHandle.IsInvalid)
1146                     {
1147                         continue;
1148                     }
1149 
1150                     bool keepHandle = false;
1151                     try
1152                     {
1153                         if (possibleChildProcess.StartTime > parentStartTime)
1154                         {
1155                             int childParentProcessId = GetParentProcessId(possibleChildProcess.Id);
1156                             if (childParentProcessId != 0)
1157                             {
1158                                 if (parentProcessId == childParentProcessId)
1159                                 {
1160                                     // Add this one
1161                                     myChildren.Add(new KeyValuePair<int, SafeProcessHandle>(possibleChildProcess.Id, childHandle));
1162                                     keepHandle = true;
1163                                 }
1164                             }
1165                         }
1166                     }
1167                     finally
1168                     {
1169                         if (!keepHandle)
1170                         {
1171                             childHandle.Dispose();
1172                         }
1173                     }
1174                 }
1175             }
1176 
1177             return myChildren;
1178         }
1179 
1180         /// <summary>
1181         /// Internal, optimized GetCurrentDirectory implementation that simply delegates to the native method
1182         /// </summary>
1183         /// <returns></returns>
GetCurrentDirectory()1184         internal static string GetCurrentDirectory()
1185         {
1186             if (IsWindows)
1187             {
1188                 StringBuilder sb = new StringBuilder(MAX_PATH);
1189                 int pathLength = GetCurrentDirectory(MAX_PATH, sb);
1190 
1191                 return pathLength > 0 ? sb.ToString() : null;
1192             }
1193 
1194             return Directory.GetCurrentDirectory();
1195         }
1196 
1197 #endregion
1198 
1199 #region PInvoke
1200 
1201         /// <summary>
1202         /// Gets the current OEM code page which is used by console apps
1203         /// (as opposed to the Windows/ANSI code page used by the normal people)
1204         /// Basically for each ANSI code page (set in Regional settings) there's a corresponding OEM code page
1205         /// that needs to be used for instance when writing to batch files
1206         /// </summary>
1207         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1208         [DllImport(kernel32Dll)]
GetOEMCP()1209         internal static extern int GetOEMCP();
1210 
1211         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1212         [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
1213         [return: MarshalAs(UnmanagedType.Bool)]
GetFileAttributesEx(String name, int fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation)1214         internal static extern bool GetFileAttributesEx(String name, int fileInfoLevel, ref WIN32_FILE_ATTRIBUTE_DATA lpFileInformation);
1215 
1216         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1217         [DllImport(kernel32Dll, SetLastError = true, CharSet = CharSet.Unicode)]
SearchPath( string path, string fileName, string extension, int numBufferChars, [Out] StringBuilder buffer, int[] filePart )1218         private static extern uint SearchPath
1219         (
1220             string path,
1221             string fileName,
1222             string extension,
1223             int numBufferChars,
1224             [Out] StringBuilder buffer,
1225             int[] filePart
1226         );
1227 
1228         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1229         [DllImport("kernel32.dll", PreserveSig = true, SetLastError = true)]
1230         [return: MarshalAs(UnmanagedType.Bool)]
FreeLibrary([In] IntPtr module)1231         internal static extern bool FreeLibrary([In] IntPtr module);
1232 
1233         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1234         [DllImport("kernel32.dll", PreserveSig = true, BestFitMapping = false, ThrowOnUnmappableChar = true, CharSet = CharSet.Ansi, SetLastError = true)]
GetProcAddress(IntPtr module, string procName)1235         internal static extern IntPtr GetProcAddress(IntPtr module, string procName);
1236 
1237         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1238         [DllImport("kernel32.dll", CharSet = CharSet.Unicode, PreserveSig = true, SetLastError = true)]
LoadLibrary(string fileName)1239         internal static extern IntPtr LoadLibrary(string fileName);
1240 
1241         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1242         [DllImport(mscoreeDLL, SetLastError = true, CharSet = CharSet.Unicode)]
GetRequestedRuntimeInfo(String pExe, String pwszVersion, String pConfigurationFile, uint startupFlags, uint runtimeInfoFlags, [Out] StringBuilder pDirectory, int dwDirectory, out uint dwDirectoryLength, [Out] StringBuilder pVersion, int cchBuffer, out uint dwlength)1243         internal static extern uint GetRequestedRuntimeInfo(String pExe,
1244                                                 String pwszVersion,
1245                                                 String pConfigurationFile,
1246                                                 uint startupFlags,
1247                                                 uint runtimeInfoFlags,
1248                                                 [Out] StringBuilder pDirectory,
1249                                                 int dwDirectory,
1250                                                 out uint dwDirectoryLength,
1251                                                 [Out] StringBuilder pVersion,
1252                                                 int cchBuffer,
1253                                                 out uint dwlength);
1254 
1255         /// <summary>
1256         /// Gets the fully qualified filename of the currently executing .exe
1257         /// </summary>
1258         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1259         [DllImport(kernel32Dll, SetLastError = true, CharSet = CharSet.Unicode)]
GetModuleFileName( HandleRef hModule, [Out] StringBuilder buffer, int length)1260         internal static extern int GetModuleFileName(
1261 #if FEATURE_HANDLEREF
1262             HandleRef hModule,
1263 #else
1264             IntPtr hModule,
1265 #endif
1266             [Out] StringBuilder buffer, int length);
1267 
1268         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1269         [DllImport("kernel32.dll")]
GetStdHandle(int nStdHandle)1270         internal static extern IntPtr GetStdHandle(int nStdHandle);
1271 
1272         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1273         [DllImport("kernel32.dll")]
GetFileType(IntPtr hFile)1274         internal static extern uint GetFileType(IntPtr hFile);
1275 
1276         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1277         [SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api", Justification = "Using unmanaged equivalent for performance reasons")]
1278         [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
GetCurrentDirectory(int nBufferLength, [Out] StringBuilder lpBuffer)1279         internal static extern int GetCurrentDirectory(int nBufferLength, [Out] StringBuilder lpBuffer);
1280 
1281         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1282         [SuppressMessage("Microsoft.Usage", "CA2205:UseManagedEquivalentsOfWin32Api", Justification = "Using unmanaged equivalent for performance reasons")]
1283         [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode, EntryPoint = "SetCurrentDirectory")]
1284         [return: MarshalAs(UnmanagedType.Bool)]
SetCurrentDirectoryWindows(string path)1285         internal static extern bool SetCurrentDirectoryWindows(string path);
1286 
SetCurrentDirectory(string path)1287         internal static bool SetCurrentDirectory(string path)
1288         {
1289             if (IsWindows)
1290             {
1291                 return SetCurrentDirectoryWindows(path);
1292             }
1293 
1294             // Make sure this does not throw
1295             try
1296             {
1297                 Directory.SetCurrentDirectory(path);
1298             }
1299             catch
1300             {
1301             }
1302             return true;
1303         }
1304 
1305         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1306         [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
GetFullPathName(string target, int bufferLength, char* buffer, IntPtr mustBeZero)1307         internal static unsafe extern int GetFullPathName(string target, int bufferLength, char* buffer, IntPtr mustBeZero);
1308 
1309         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1310         [DllImport("KERNEL32.DLL")]
OpenProcess(eDesiredAccess dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId)1311         private static extern SafeProcessHandle OpenProcess(eDesiredAccess dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, int dwProcessId);
1312 
1313         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1314         [DllImport("NTDLL.DLL")]
NtQueryInformationProcess(SafeProcessHandle hProcess, PROCESSINFOCLASS pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, ref int pSize)1315         private static extern int NtQueryInformationProcess(SafeProcessHandle hProcess, PROCESSINFOCLASS pic, ref PROCESS_BASIC_INFORMATION pbi, int cb, ref int pSize);
1316 
1317         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1318         [return: MarshalAs(UnmanagedType.Bool)]
1319         [DllImport("kernel32.dll", CharSet = AutoOrUnicode, SetLastError = true)]
GlobalMemoryStatusEx([In, Out] MemoryStatus lpBuffer)1320         private static extern bool GlobalMemoryStatusEx([In, Out] MemoryStatus lpBuffer);
1321 
1322         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1323         [DllImport("kernel32.dll", CharSet = CharSet.Unicode, BestFitMapping = false)]
GetShortPathName(string path, [Out] StringBuilder fullpath, [In] int length)1324         internal static extern int GetShortPathName(string path, [Out] StringBuilder fullpath, [In] int length);
1325 
1326         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1327         [DllImport("kernel32.dll", CharSet = CharSet.Unicode, BestFitMapping = false)]
GetLongPathName([In] string path, [Out] StringBuilder fullpath, [In] int length)1328         internal static extern int GetLongPathName([In] string path, [Out] StringBuilder fullpath, [In] int length);
1329 
1330         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1331         [DllImport("kernel32.dll", CharSet = AutoOrUnicode, SetLastError = true)]
CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SecurityAttributes lpPipeAttributes, int nSize)1332         internal static extern bool CreatePipe(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, SecurityAttributes lpPipeAttributes, int nSize);
1333 
1334         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1335         [DllImport("kernel32.dll", CharSet = AutoOrUnicode, SetLastError = true)]
ReadFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped)1336         internal static extern bool ReadFile(SafeFileHandle hFile, byte[] lpBuffer, uint nNumberOfBytesToRead, out uint lpNumberOfBytesRead, IntPtr lpOverlapped);
1337 
1338         /// <summary>
1339         /// CoWaitForMultipleHandles allows us to wait in an STA apartment and still service RPC requests from other threads.
1340         /// VS needs this in order to allow the in-proc compilers to properly initialize, since they will make calls from the
1341         /// build thread which the main thread (blocked on BuildSubmission.Execute) must service.
1342         /// </summary>
1343         [SuppressMessage("Microsoft.Design", "CA1060:MovePInvokesToNativeMethodsClass", Justification = "Class name is NativeMethodsShared for increased clarity")]
1344         [DllImport("ole32.dll")]
CoWaitForMultipleHandles(COWAIT_FLAGS dwFlags, int dwTimeout, int cHandles, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] pHandles, out int pdwIndex)1345         public static extern int CoWaitForMultipleHandles(COWAIT_FLAGS dwFlags, int dwTimeout, int cHandles, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] pHandles, out int pdwIndex);
1346 
1347         internal const uint GENERIC_READ = 0x80000000;
1348         internal const uint FILE_SHARE_READ = 0x1;
1349         internal const uint FILE_ATTRIBUTE_NORMAL = 0x80;
1350         internal const uint FILE_FLAG_OPEN_REPARSE_POINT = 0x00200000;
1351         internal const uint OPEN_EXISTING = 3;
1352 
1353         [DllImport("kernel32.dll", CharSet = AutoOrUnicode, CallingConvention = CallingConvention.StdCall,
1354             SetLastError = true)]
CreateFile( string lpFileName, uint dwDesiredAccess, uint dwShareMode, IntPtr lpSecurityAttributes, uint dwCreationDisposition, uint dwFlagsAndAttributes, IntPtr hTemplateFile )1355         internal static extern SafeFileHandle CreateFile(
1356             string lpFileName,
1357             uint dwDesiredAccess,
1358             uint dwShareMode,
1359             IntPtr lpSecurityAttributes,
1360             uint dwCreationDisposition,
1361             uint dwFlagsAndAttributes,
1362             IntPtr hTemplateFile
1363             );
1364 
1365         [DllImport("kernel32.dll", SetLastError = true)]
GetFileTime( SafeFileHandle hFile, out FILETIME lpCreationTime, out FILETIME lpLastAccessTime, out FILETIME lpLastWriteTime )1366         internal static extern bool GetFileTime(
1367             SafeFileHandle hFile,
1368             out FILETIME lpCreationTime,
1369             out FILETIME lpLastAccessTime,
1370             out FILETIME lpLastWriteTime
1371             );
1372 
1373         [DllImport("kernel32.dll", SetLastError = true)]
1374         [return: MarshalAs(UnmanagedType.Bool)]
1375 
CloseHandle(IntPtr hObject)1376         internal static extern bool CloseHandle(IntPtr hObject);
1377 
1378 #endregion
1379 
1380 #region Extensions
1381 
1382         /// <summary>
1383         /// Waits while pumping APC messages.  This is important if the waiting thread is an STA thread which is potentially
1384         /// servicing COM calls from other threads.
1385         /// </summary>
1386         [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Scope = "member", Target = "Microsoft.Build.Shared.NativeMethodsShared.#MsgWaitOne(System.Threading.WaitHandle,System.Int32)", Justification = "This is necessary and it has been used for a long time. No need to change it now.")]
MsgWaitOne(this WaitHandle handle)1387         internal static bool MsgWaitOne(this WaitHandle handle)
1388         {
1389             return handle.MsgWaitOne(Timeout.Infinite);
1390         }
1391 
1392         /// <summary>
1393         /// Waits while pumping APC messages.  This is important if the waiting thread is an STA thread which is potentially
1394         /// servicing COM calls from other threads.
1395         /// </summary>
MsgWaitOne(this WaitHandle handle, TimeSpan timeout)1396         internal static bool MsgWaitOne(this WaitHandle handle, TimeSpan timeout)
1397         {
1398             return MsgWaitOne(handle, (int)timeout.TotalMilliseconds);
1399         }
1400 
1401         /// <summary>
1402         /// Waits while pumping APC messages.  This is important if the waiting thread is an STA thread which is potentially
1403         /// servicing COM calls from other threads.
1404         /// </summary>
1405         [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Runtime.InteropServices.SafeHandle.DangerousGetHandle", Justification = "Necessary to avoid pumping")]
MsgWaitOne(this WaitHandle handle, int timeout)1406         internal static bool MsgWaitOne(this WaitHandle handle, int timeout)
1407         {
1408             // CoWaitForMultipleHandles allows us to wait in an STA apartment and still service RPC requests from other threads.
1409             // VS needs this in order to allow the in-proc compilers to properly initialize, since they will make calls from the
1410             // build thread which the main thread (blocked on BuildSubmission.Execute) must service.
1411             int waitIndex;
1412 #if FEATURE_HANDLE_SAFEWAITHANDLE
1413             IntPtr handlePtr = handle.SafeWaitHandle.DangerousGetHandle();
1414 #else
1415             IntPtr handlePtr = handle.GetSafeWaitHandle().DangerousGetHandle();
1416 #endif
1417             int returnValue = CoWaitForMultipleHandles(COWAIT_FLAGS.COWAIT_NONE, timeout, 1, new IntPtr[] { handlePtr }, out waitIndex);
1418             ErrorUtilities.VerifyThrow(returnValue == 0 || ((uint)returnValue == RPC_S_CALLPENDING && timeout != Timeout.Infinite), "Received {0} from CoWaitForMultipleHandles, but expected 0 (S_OK)", returnValue);
1419             return returnValue == 0;
1420         }
1421 
1422 #endregion
1423 
1424 #region helper methods
1425 
FileExists(string path)1426         internal static bool FileExists(string path)
1427         {
1428             WIN32_FILE_ATTRIBUTE_DATA data = new WIN32_FILE_ATTRIBUTE_DATA();
1429             return GetFileAttributesEx(path, 0, ref data);
1430         }
1431 
1432 #endregion
1433     }
1434 }
1435