1 //------------------------------------------------------------------------------
2 // <copyright file="SharedUtils.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 //------------------------------------------------------------------------------
7 namespace System
.Diagnostics
{
8 using System
.Security
.Permissions
;
10 using System
.Threading
;
12 using Microsoft
.Win32
;
13 using System
.Globalization
;
14 using System
.ComponentModel
;
15 using System
.Security
.Principal
;
16 using System
.Security
.AccessControl
;
17 using System
.Runtime
.Versioning
;
18 using System
.Runtime
.CompilerServices
;
19 using System
.Runtime
.ConstrainedExecution
;
20 using System
.Runtime
.InteropServices
;
21 using Microsoft
.Win32
.SafeHandles
;
22 using System
.Diagnostics
.CodeAnalysis
;
24 internal static class SharedUtils
{
26 internal const int UnknownEnvironment
= 0;
27 internal const int W2kEnvironment
= 1;
28 internal const int NtEnvironment
= 2;
29 internal const int NonNtEnvironment
= 3;
30 private static volatile int environment
= UnknownEnvironment
;
32 private static Object s_InternalSyncObject
;
33 private static Object InternalSyncObject
{
35 if (s_InternalSyncObject
== null) {
36 Object o
= new Object();
37 Interlocked
.CompareExchange(ref s_InternalSyncObject
, o
, null);
39 return s_InternalSyncObject
;
43 internal static Win32Exception
CreateSafeWin32Exception() {
44 return CreateSafeWin32Exception(0);
47 internal static Win32Exception
CreateSafeWin32Exception(int error
) {
48 Win32Exception newException
= null;
49 // Need to assert SecurtiyPermission, otherwise Win32Exception
50 // will not be able to get the error message. At this point the right
51 // permissions have already been demanded.
52 SecurityPermission securityPermission
= new SecurityPermission(PermissionState
.Unrestricted
);
53 securityPermission
.Assert();
56 newException
= new Win32Exception();
58 newException
= new Win32Exception(error
);
61 SecurityPermission
.RevertAssert();
67 internal static int CurrentEnvironment
{
69 if (environment
== UnknownEnvironment
) {
70 lock (InternalSyncObject
) {
71 if (environment
== UnknownEnvironment
) {
72 // Need to assert Environment permissions here
73 // the environment check is not exposed as a public method
74 if (Environment
.OSVersion
.Platform
== PlatformID
.Win32NT
) {
75 if (Environment
.OSVersion
.Version
.Major
>= 5)
76 environment
= W2kEnvironment
;
78 environment
= NtEnvironment
;
81 environment
= NonNtEnvironment
;
90 internal static void CheckEnvironment() {
91 if (CurrentEnvironment
== NonNtEnvironment
)
92 throw new PlatformNotSupportedException(SR
.GetString(SR
.WinNTRequired
));
95 internal static void CheckNtEnvironment() {
96 if (CurrentEnvironment
== NtEnvironment
)
97 throw new PlatformNotSupportedException(SR
.GetString(SR
.Win2000Required
));
100 [ResourceExposure(ResourceScope
.Machine
)]
101 [ResourceConsumption(ResourceScope
.Machine
)]
102 internal static void EnterMutex(string name
, ref Mutex mutex
) {
103 string mutexName
= null;
104 if (CurrentEnvironment
== W2kEnvironment
)
105 mutexName
= "Global\\" + name
;
109 EnterMutexWithoutGlobal(mutexName
, ref mutex
);
112 [ResourceExposure(ResourceScope
.Machine
)]
113 [ResourceConsumption(ResourceScope
.Machine
)]
114 [SecurityPermission(SecurityAction
.Assert
, ControlPrincipal
= true)]
115 [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification
= "Microsoft: We pass fixed data into sec.AddAccessRule")]
116 internal static void EnterMutexWithoutGlobal(string mutexName
, ref Mutex mutex
) {
118 MutexSecurity sec
= new MutexSecurity();
119 SecurityIdentifier everyoneSid
= new SecurityIdentifier(WellKnownSidType
.AuthenticatedUserSid
, null);
120 sec
.AddAccessRule(new MutexAccessRule(everyoneSid
, MutexRights
.Synchronize
| MutexRights
.Modify
, AccessControlType
.Allow
));
122 Mutex tmpMutex
= new Mutex(false, mutexName
, out createdNew
, sec
);
124 SafeWaitForMutex(tmpMutex
, ref mutex
);
127 // We need to atomically attempt to acquire the mutex and record whether we took it (because we require thread affinity
128 // while the mutex is held and the two states must be kept in lock step). We can get atomicity with a CER, but we don't want
129 // to hold a CER over a call to WaitOne (this could cause deadlocks). The ---- is to provide a new API out of
130 // mscorlib that performs the wait and lets us know if it succeeded. But at this late stage we don't want to expose a new
131 // API out of mscorlib, so we'll build our own solution.
132 // We'll P/Invoke out to the WaitForSingleObject inside a CER, but use a timeout to ensure we can't block a thread abort for
133 // an unlimited time (we do this in an infinite loop so the semantic of acquiring the mutex is unchanged, the timeout is
134 // just to allow us to poll for abort). A limitation of CERs in Whidbey (and part of the problem that put us in this
135 // position in the first place) is that a CER root in a method will cause the entire method to delay thread aborts. So we
136 // need to carefully partition the real CER part of out logic in a sub-method (and ensure the jit doesn't inline on us).
137 [ResourceExposure(ResourceScope
.Machine
)]
138 [ResourceConsumption(ResourceScope
.Machine
)]
139 private static bool SafeWaitForMutex(Mutex mutexIn
, ref Mutex mutexOut
)
141 Debug
.Assert(mutexOut
== null, "You must pass in a null ref Mutex");
143 // Wait as long as necessary for the mutex.
146 // Attempt to acquire the mutex but timeout quickly if we can't.
147 if (!SafeWaitForMutexOnce(mutexIn
, ref mutexOut
))
149 if (mutexOut
!= null)
152 // We come out here to the outer method every so often so we're not in a CER and a thread abort can interrupt us.
153 // But the abort logic itself is poll based (in the this case) so we really need to check for a pending abort
154 // explicitly else the two timing windows will virtually never line up and we'll still end up stalling the abort
155 // attempt. Thread.Sleep checks for pending abort for us.
160 // The portion of SafeWaitForMutex that runs under a CER and thus must not block for a arbitrary period of time.
161 // This method must not be inlined (to stop the CER accidently spilling into the calling method).
162 [ResourceExposure(ResourceScope
.Machine
)]
163 [ResourceConsumption(ResourceScope
.Machine
)]
164 [MethodImplAttribute(MethodImplOptions
.NoInlining
)]
165 private static bool SafeWaitForMutexOnce(Mutex mutexIn
, ref Mutex mutexOut
)
169 RuntimeHelpers
.PrepareConstrainedRegions();
171 // Wait for the mutex for half a second (long enough to gain the mutex in most scenarios and short enough to avoid
172 // impacting a thread abort for too long).
173 // Holding a mutex requires us to keep thread affinity and announce ourselves as a critical region.
174 Thread
.BeginCriticalRegion();
175 Thread
.BeginThreadAffinity();
176 int result
= WaitForSingleObjectDontCallThis(mutexIn
.SafeWaitHandle
, 500);
179 case NativeMethods
.WAIT_OBJECT_0
:
180 case NativeMethods
.WAIT_ABANDONED
:
181 // Mutex was obtained, atomically record that fact.
186 case NativeMethods
.WAIT_TIMEOUT
:
187 // Couldn't get mutex yet, simply return and we'll try again later.
192 // Some sort of failure return immediately all the way to the caller of SafeWaitForMutex.
197 // If we're not leaving with the Mutex we don't require thread affinity and we're not a critical region any more.
198 if (mutexOut
== null) {
199 Thread
.EndThreadAffinity();
200 Thread
.EndCriticalRegion();
207 // P/Invoke for the methods above. Don't call this from anywhere else.
208 [ResourceExposure(ResourceScope
.Machine
)]
209 [ResourceConsumption(ResourceScope
.Machine
)]
210 [System
.Security
.SuppressUnmanagedCodeSecurity
]
211 [ReliabilityContract(Consistency
.WillNotCorruptState
, Cer
.MayFail
)]
212 [DllImport(ExternDll
.Kernel32
, ExactSpelling
=true, SetLastError
=true, EntryPoint
="WaitForSingleObject")]
213 private static extern int WaitForSingleObjectDontCallThis(SafeWaitHandle handle
, int timeout
);
215 [ResourceExposure(ResourceScope
.Machine
)] // This is scoped to a Fx build dir.
216 [ResourceConsumption(ResourceScope
.Machine
)]
217 // What if an app is locked back? Why would we use this?
218 internal static string GetLatestBuildDllDirectory(string machineName
) {
220 RegistryKey baseKey
= null;
221 RegistryKey complusReg
= null;
223 //This property is retrieved only when creationg a new category,
224 // the calling code already demanded PerformanceCounterPermission.
225 // Therefore the assert below is safe.
227 //This property is retrieved only when creationg a new log,
228 // the calling code already demanded EventLogPermission.
229 // Therefore the assert below is safe.
231 RegistryPermission registryPermission
= new RegistryPermission(PermissionState
.Unrestricted
);
232 registryPermission
.Assert();
235 if (machineName
.Equals(".")) {
236 return GetLocalBuildDirectory();
239 baseKey
= RegistryKey
.OpenRemoteBaseKey(RegistryHive
.LocalMachine
, machineName
);
242 throw new InvalidOperationException(SR
.GetString(SR
.RegKeyMissingShort
, "HKEY_LOCAL_MACHINE", machineName
));
244 complusReg
= baseKey
.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework");
245 if (complusReg
!= null) {
246 string installRoot
= (string)complusReg
.GetValue("InstallRoot");
247 if (installRoot
!= null && installRoot
!= String
.Empty
) {
248 // the "policy" subkey contains a v{major}.{minor} subkey for each version installed. There are also
249 // some extra subkeys like "standards" and "upgrades" we want to ignore.
251 // first we figure out what version we are...
252 string versionPrefix
= "v" + Environment
.Version
.Major
+ "." + Environment
.Version
.Minor
;
253 RegistryKey policyKey
= complusReg
.OpenSubKey("policy");
255 // This is the full version string of the install on the remote machine we want to use (for example "v2.0.50727")
256 string version
= null;
258 if (policyKey
!= null) {
261 // First check to see if there is a version of the runtime with the same minor and major number:
262 RegistryKey bestKey
= policyKey
.OpenSubKey(versionPrefix
);
264 if (bestKey
!= null) {
266 version
= versionPrefix
+ "." + GetLargestBuildNumberFromKey(bestKey
);
271 // There isn't an exact match for our version, so we will look for the largest version
273 string[] majorVersions
= policyKey
.GetSubKeyNames();
274 int[] largestVersion
= new int[] { -1, -1, -1 }
;
275 for (int i
= 0; i
< majorVersions
.Length
; i
++) {
277 string majorVersion
= majorVersions
[i
];
279 // If this looks like a key of the form v{something}.{something}, we should see if it's a usable build.
280 if (majorVersion
.Length
> 1 && majorVersion
[0] == 'v' && majorVersion
.Contains(".")) {
281 int[] currentVersion
= new int[] { -1, -1, -1 }
;
283 string[] splitVersion
= majorVersion
.Substring(1).Split('.');
285 if(splitVersion
.Length
!= 2) {
289 if (!Int32
.TryParse(splitVersion
[0], out currentVersion
[0]) || !Int32
.TryParse(splitVersion
[1], out currentVersion
[1])) {
293 RegistryKey k
= policyKey
.OpenSubKey(majorVersion
);
295 // We may be able to use another subkey
299 currentVersion
[2] = GetLargestBuildNumberFromKey(k
);
301 if (currentVersion
[0] > largestVersion
[0]
302 || ((currentVersion
[0] == largestVersion
[0]) && (currentVersion
[1] > largestVersion
[1]))) {
303 largestVersion
= currentVersion
;
311 version
= "v" + largestVersion
[0] + "." + largestVersion
[1] + "." + largestVersion
[2];
317 if (version
!= null && version
!= String
.Empty
) {
318 StringBuilder installBuilder
= new StringBuilder();
319 installBuilder
.Append(installRoot
);
320 if (!installRoot
.EndsWith("\\", StringComparison
.Ordinal
))
321 installBuilder
.Append("\\");
322 installBuilder
.Append(version
);
323 dllDir
= installBuilder
.ToString();
333 if (complusReg
!= null)
339 RegistryPermission
.RevertAssert();
345 [ResourceExposure(ResourceScope
.Machine
)]
346 [ResourceConsumption(ResourceScope
.Machine
)]
347 private static int GetLargestBuildNumberFromKey(RegistryKey rootKey
) {
348 int largestBuild
= -1;
350 string[] minorVersions
= rootKey
.GetValueNames();
351 for (int i
= 0; i
< minorVersions
.Length
; i
++) {
353 if (Int32
.TryParse(minorVersions
[i
], out o
)) {
354 largestBuild
= (largestBuild
> o
) ? largestBuild
: o
;
361 [ResourceExposure(ResourceScope
.Machine
)]
362 [ResourceConsumption(ResourceScope
.Machine
)]
363 private static string GetLocalBuildDirectory() {
364 return RuntimeEnvironment
.GetRuntimeDirectory();