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