SharedUtils.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / untmp / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / Services / Monitoring / system / Diagnosticts / SharedUtils.cs / 1305376 / SharedUtils.cs

                            //------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

namespace System.Diagnostics { 
    using System.Security.Permissions; 
    using System.Security;
    using System.Threading; 
    using System.Text;
    using Microsoft.Win32;
    using System.Globalization;
    using System.ComponentModel; 
    using System.Security.Principal;
    using System.Security.AccessControl; 
    using System.Runtime.Versioning; 
    using System.Runtime.CompilerServices;
    using System.Runtime.ConstrainedExecution; 
    using System.Runtime.InteropServices;
    using Microsoft.Win32.SafeHandles;
    using System.Diagnostics.CodeAnalysis;
 
    internal static class SharedUtils {
 
        internal const int UnknownEnvironment = 0; 
        internal const int W2kEnvironment = 1;
        internal const int NtEnvironment = 2; 
        internal const int NonNtEnvironment = 3;
        private static int environment = UnknownEnvironment;

        private static Object s_InternalSyncObject; 
        private static Object InternalSyncObject {
            get { 
                if (s_InternalSyncObject == null) { 
                    Object o = new Object();
                    Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); 
                }
                return s_InternalSyncObject;
            }
        } 

        internal static Win32Exception CreateSafeWin32Exception() { 
            return CreateSafeWin32Exception(0); 
        }
 
        internal static Win32Exception CreateSafeWin32Exception(int error) {
            Win32Exception newException = null;
            // Need to assert SecurtiyPermission, otherwise Win32Exception
            // will not be able to get the error message. At this point the right 
            // permissions have already been demanded.
            SecurityPermission securityPermission = new SecurityPermission(PermissionState.Unrestricted); 
            securityPermission.Assert(); 
            try {
                if (error == 0) 
                    newException = new Win32Exception();
                else
                    newException = new Win32Exception(error);
            } 
            finally {
                SecurityPermission.RevertAssert(); 
            } 

            return newException; 
        }

        internal static int CurrentEnvironment {
            get { 
                if (environment == UnknownEnvironment) {
                    lock (InternalSyncObject) { 
                        if (environment == UnknownEnvironment) { 
                            // Need to assert Environment permissions here
                            // the environment check is not exposed as a public method 
                            if (Environment.OSVersion.Platform == PlatformID.Win32NT)  {
                                if (Environment.OSVersion.Version.Major >= 5)
                                    environment = W2kEnvironment;
                                else 
                                    environment = NtEnvironment;
                            } 
                            else 
                                environment = NonNtEnvironment;
                        } 
                    }
                }

                return environment; 
            }
        } 
 
        internal static void CheckEnvironment() {
            if (CurrentEnvironment == NonNtEnvironment) 
                throw new PlatformNotSupportedException(SR.GetString(SR.WinNTRequired));
        }

        internal static void CheckNtEnvironment() { 
            if (CurrentEnvironment == NtEnvironment)
                throw new PlatformNotSupportedException(SR.GetString(SR.Win2000Required)); 
        } 

        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        internal static void EnterMutex(string name, ref Mutex mutex) {
            string mutexName = null;
            if (CurrentEnvironment == W2kEnvironment) 
                mutexName = "Global\\" +  name;
            else 
                mutexName = name; 

            EnterMutexWithoutGlobal(mutexName, ref mutex); 
        }

        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)] 
        [SecurityPermission(SecurityAction.Assert, ControlPrincipal = true)]
        [SuppressMessage("Microsoft.Security", "CA2106:SecureAsserts", Justification = "[....]: We pass fixed data into sec.AddAccessRule")] 
        internal static void EnterMutexWithoutGlobal(string mutexName, ref Mutex mutex) { 
            bool createdNew;
            MutexSecurity sec = new MutexSecurity(); 
            SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
            sec.AddAccessRule(new MutexAccessRule(everyoneSid, MutexRights.Synchronize | MutexRights.Modify, AccessControlType.Allow));

            Mutex tmpMutex = new Mutex(false, mutexName, out createdNew, sec); 

            SafeWaitForMutex(tmpMutex, ref mutex); 
        } 

        // We need to atomically attempt to acquire the mutex and record whether we took it (because we require thread affinity 
        // 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
        // to hold a CER over a call to WaitOne (this could cause deadlocks). The ---- is to provide a new API out of
        // 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
        // API out of mscorlib, so we'll build our own solution. 
        // 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
        // an unlimited time (we do this in an infinite loop so the semantic of acquiring the mutex is unchanged, the timeout is 
        // just to allow us to poll for abort). A limitation of CERs in Whidbey (and part of the problem that put us in this 
        // position in the first place) is that a CER root in a method will cause the entire method to delay thread aborts. So we
        // need to carefully partition the real CER part of out logic in a sub-method (and ensure the jit doesn't inline on us). 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private static bool SafeWaitForMutex(Mutex mutexIn, ref Mutex mutexOut)
        { 
            Debug.Assert(mutexOut == null, "You must pass in a null ref Mutex");
 
            // Wait as long as necessary for the mutex. 
            while (true) {
 
                // Attempt to acquire the mutex but timeout quickly if we can't.
                if (!SafeWaitForMutexOnce(mutexIn, ref mutexOut))
                    return false;
                if (mutexOut != null) 
                    return true;
 
                // 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. 
                // But the abort logic itself is poll based (in the this case) so we really need to check for a pending abort
                // explicitly else the two timing windows will virtually never line up and we'll still end up stalling the abort 
                // attempt. Thread.Sleep checks for pending abort for us.
                Thread.Sleep(0);
            }
        } 

        // The portion of SafeWaitForMutex that runs under a CER and thus must not block for a arbitrary period of time. 
        // This method must not be inlined (to stop the CER accidently spilling into the calling method). 
        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)] 
        [MethodImplAttribute(MethodImplOptions.NoInlining)]
        private static bool SafeWaitForMutexOnce(Mutex mutexIn, ref Mutex mutexOut)
        {
            bool ret; 

            RuntimeHelpers.PrepareConstrainedRegions(); 
            try {} finally { 
                // Wait for the mutex for half a second (long enough to gain the mutex in most scenarios and short enough to avoid
                // impacting a thread abort for too long). 
                // Holding a mutex requires us to keep thread affinity and announce ourselves as a critical region.
                Thread.BeginCriticalRegion();
                Thread.BeginThreadAffinity();
                int result = WaitForSingleObjectDontCallThis(mutexIn.SafeWaitHandle, 500); 
                switch (result) {
 
                case NativeMethods.WAIT_OBJECT_0: 
                case NativeMethods.WAIT_ABANDONED:
                    // Mutex was obtained, atomically record that fact. 
                    mutexOut = mutexIn;
                    ret = true;
                    break;
 
                case NativeMethods.WAIT_TIMEOUT:
                    // Couldn't get mutex yet, simply return and we'll try again later. 
                    ret = true; 
                    break;
 
                default:
                    // Some sort of failure return immediately all the way to the caller of SafeWaitForMutex.
                    ret = false;
                    break; 
                }
 
                // If we're not leaving with the Mutex we don't require thread affinity and we're not a critical region any more. 
                if (mutexOut == null) {
                    Thread.EndThreadAffinity(); 
                    Thread.EndCriticalRegion();
                }
            }
 
            return ret;
        } 
 
        // P/Invoke for the methods above. Don't call this from anywhere else.
        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        [System.Security.SuppressUnmanagedCodeSecurity]
        [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
        [DllImport(ExternDll.Kernel32, ExactSpelling=true, SetLastError=true, EntryPoint="WaitForSingleObject")] 
        private static extern int WaitForSingleObjectDontCallThis(SafeWaitHandle handle, int timeout);
 
        [ResourceExposure(ResourceScope.Machine)]  // This is scoped to a Fx build dir. 
        [ResourceConsumption(ResourceScope.Machine)]
            // What if an app is locked back?  Why would we use this? 
        internal static string GetLatestBuildDllDirectory(string machineName) {
            string dllDir = "";
            RegistryKey baseKey = null;
            RegistryKey complusReg = null; 

            //This property is retrieved only when creationg a new category, 
            //                          the calling code already demanded PerformanceCounterPermission. 
            //                          Therefore the assert below is safe.
 
            //This property is retrieved only when creationg a new log,
            //                          the calling code already demanded EventLogPermission.
            //                          Therefore the assert below is safe.
 
            RegistryPermission registryPermission = new RegistryPermission(PermissionState.Unrestricted);
            registryPermission.Assert(); 
 
            try {
                if (machineName.Equals(".")) { 
                    return GetLocalBuildDirectory();
                }
                else {
                    baseKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName); 
                }
                if (baseKey == null) 
                    throw new InvalidOperationException(SR.GetString(SR.RegKeyMissingShort, "HKEY_LOCAL_MACHINE", machineName)); 

                complusReg = baseKey.OpenSubKey("SOFTWARE\\Microsoft\\.NETFramework"); 
                if (complusReg != null) {
                    string installRoot = (string)complusReg.GetValue("InstallRoot");
                    if (installRoot != null && installRoot != String.Empty) {
                        // the "policy" subkey contains a v{major}.{minor} subkey for each version installed.  There are also 
                        // some extra subkeys like "standards" and "upgrades" we want to ignore.
 
                        // first we figure out what version we are... 
                        string versionPrefix = "v" + Environment.Version.Major + "." + Environment.Version.Minor;
                        RegistryKey policyKey = complusReg.OpenSubKey("policy"); 

                        // This is the full version string of the install on the remote machine we want to use (for example "v2.0.50727")
                        string version = null;
 
                        if (policyKey != null) {
                            try { 
 
                                // First check to see if there is a version of the runtime with the same minor and major number:
                                RegistryKey bestKey = policyKey.OpenSubKey(versionPrefix); 

                                if (bestKey != null) {
                                    try {
                                        version = versionPrefix + "." + GetLargestBuildNumberFromKey(bestKey); 
                                    } finally {
                                        bestKey.Close(); 
                                    } 
                                } else {
                                    // There isn't an exact match for our version, so we will look for the largest version 
                                    // installed.
                                    string[] majorVersions = policyKey.GetSubKeyNames();
                                    int[] largestVersion = new int[] { -1, -1, -1 };
                                    for (int i = 0; i < majorVersions.Length; i++) { 

                                        string majorVersion = majorVersions[i]; 
 
                                        // If this looks like a key of the form v{something}.{something}, we should see if it's a usable build.
                                        if (majorVersion.Length > 1 && majorVersion[0] == 'v' && majorVersion.Contains(".")) { 
                                            int[] currentVersion = new int[] { -1, -1, -1 };

                                            string[] splitVersion = majorVersion.Substring(1).Split('.');
 
                                            if(splitVersion.Length != 2) {
                                                continue; 
                                            } 

                                            if (!Int32.TryParse(splitVersion[0], out currentVersion[0]) || !Int32.TryParse(splitVersion[1], out currentVersion[1])) { 
                                                continue;
                                            }

                                            RegistryKey k = policyKey.OpenSubKey(majorVersion); 
                                            if (k == null) {
                                                // We may be able to use another subkey 
                                                continue; 
                                            }
                                            try { 
                                                currentVersion[2] = GetLargestBuildNumberFromKey(k);

                                                if (currentVersion[0] > largestVersion[0]
                                                    || ((currentVersion[0] == largestVersion[0]) && (currentVersion[1] > largestVersion[1]))) { 
                                                    largestVersion = currentVersion;
                                                } 
                                            } finally { 
                                                k.Close();
                                            } 
                                        }
                                    }

                                    version = "v" + largestVersion[0] + "." + largestVersion[1] + "." + largestVersion[2]; 
                                }
                            } finally { 
                                policyKey.Close(); 
                            }
 
                            if (version != null && version != String.Empty) {
                                StringBuilder installBuilder = new StringBuilder();
                                installBuilder.Append(installRoot);
                                if (!installRoot.EndsWith("\\", StringComparison.Ordinal)) 
                                    installBuilder.Append("\\");
                                installBuilder.Append(version); 
                                dllDir = installBuilder.ToString(); 
                            }
                        } 
                    }
                }
            }
            catch { 
                // ignore
            } 
            finally { 
                if (complusReg != null)
                    complusReg.Close(); 

                if (baseKey != null)
                    baseKey.Close();
 
                RegistryPermission.RevertAssert();
            } 
 
            return dllDir;
        } 

        [ResourceExposure(ResourceScope.Machine)]
        [ResourceConsumption(ResourceScope.Machine)]
        private static int GetLargestBuildNumberFromKey(RegistryKey rootKey) { 
            int largestBuild = -1;
 
            string[] minorVersions = rootKey.GetValueNames(); 
            for (int i = 0; i < minorVersions.Length; i++) {
                int o; 
                if (Int32.TryParse(minorVersions[i], out o)) {
                    largestBuild = (largestBuild > o) ? largestBuild : o;
                }
            } 

            return largestBuild; 
        } 

        [ResourceExposure(ResourceScope.Machine)] 
        [ResourceConsumption(ResourceScope.Machine)]
        private static string GetLocalBuildDirectory() {
            return RuntimeEnvironment.GetRuntimeDirectory();
        } 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK