File: net\System\Net\_AutoWebProxyScriptEngine.cs
Project: ndp\fx\src\System.csproj (System)
//------------------------------------------------------------------------------
// <copyright fieldInfole="_AutoWebProxyScriptEngine.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//------------------------------------------------------------------------------
 
namespace System.Net
{
#if !FEATURE_PAL
    using System.Net.NetworkInformation;
    using System.Security.Principal;
#endif
    using System.Collections.Generic;
    using System.Globalization;
    using System.Net.Configuration;
    using System.Security.Permissions;
    using System.Threading;
 
    // This class (and its helper classes implementing IWebRequestFinder interface) are responsible for
    // determining the location of the PAC file, download and execute it, in order to retrieve proxy 
    // information.
    // This class also monitors the Registry and re-reads proxy configuration settings, if the corresponding
    // Registry values change.
    internal class AutoWebProxyScriptEngine
    {
        private bool automaticallyDetectSettings;
        private Uri automaticConfigurationScript;
 
        private WebProxy webProxy;
        private IWebProxyFinder webProxyFinder;
 
        private bool executeWinHttpGetProxyForUrl = false;
        private Timer retryWinHttpGetProxyForUrlTimer;
 
        // Used by abortable lock.
        private bool m_LockHeld;
 
        private bool m_UseRegistry;
 
#if !FEATURE_PAL
        // Used to get notifications of network changes and do AutoDetection (which are global).
        private int m_NetworkChangeStatus;
        private AutoDetector m_AutoDetector;
 
        // This has to hold on to the creating user's registry hive and impersonation context.
        private SafeRegistryHandle hkcu;
        private WindowsIdentity m_Identity;
#endif // !FEATURE_PAL
 
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)]
        internal AutoWebProxyScriptEngine(WebProxy proxy, bool useRegistry)
        {
            GlobalLog.Assert(proxy != null, "'proxy' must be assigned.");
            webProxy = proxy;
            m_UseRegistry = useRegistry;
 
#if !FEATURE_PAL
            m_AutoDetector = AutoDetector.CurrentAutoDetector;
            m_NetworkChangeStatus = m_AutoDetector.NetworkChangeStatus;
 
            SafeRegistryHandle.RegOpenCurrentUser(UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out hkcu);
            if (m_UseRegistry)
            {
                ListenForRegistry();
 
                // Keep track of the identity we used to read the registry, in case we need to read it again later.
                m_Identity = WindowsIdentity.GetCurrent();
            }
 
#endif // !FEATURE_PAL
 
            // In Win2003 winhttp added a Windows Service handling the auto-proxy discovery. In XP using winhttp
            // APIs will load, compile and execute the wpad file in-process. This will also load COM, since
            // WinHttp requires COM to compile the file. For these reasons, we don't use WinHttp on XP, but
            // only on newer OS versions where the "WinHTTP Web Proxy Auto-Discovery Service" exists.
            webProxyFinder = new HybridWebProxyFinder(this);
        }
 
        // AutoWebProxyScriptEngine has special abortable locking.  No one should ever lock (this) except the locking helper methods below.
        private static class SyncStatus
        {
            internal const int Unlocked = 0;
            internal const int Locking = 1;
            internal const int LockOwner = 2;
            internal const int AbortedLocked = 3;
            internal const int Aborted = 4;
        }
 
        private void EnterLock(ref int syncStatus)
        {
            if (syncStatus == SyncStatus.Unlocked)
            {
                lock (this)
                {
                    if (syncStatus != SyncStatus.Aborted)
                    {
                        syncStatus = SyncStatus.Locking;
                        while (true)
                        {
                            if (!m_LockHeld)
                            {
                                syncStatus = SyncStatus.LockOwner;
                                m_LockHeld = true;
                                return;
                            }
                            Monitor.Wait(this);
                            if (syncStatus == SyncStatus.Aborted)
                            {
                                Monitor.Pulse(this);  // This is to ensure that a Pulse meant to let someone take the lock isn't lost.
                                return;
                            }
                        }
                    }
                }
            }
        }
 
        private void ExitLock(ref int syncStatus)
        {
            if (syncStatus != SyncStatus.Unlocked && syncStatus != SyncStatus.Aborted)
            {
                lock (this)
                {
                    m_LockHeld = false;
                    if (syncStatus == SyncStatus.AbortedLocked)
                    {
                        webProxyFinder.Reset();
                        syncStatus = SyncStatus.Aborted;
                    }
                    else
                    {
                        syncStatus = SyncStatus.Unlocked;
                    }
                    Monitor.Pulse(this);
                }
            }
        }
 
        internal void Abort(ref int syncStatus)
        {
            lock (this)
            {
                switch (syncStatus)
                {
                    case SyncStatus.Unlocked:
                        syncStatus = SyncStatus.Aborted;
                        break;
 
                    case SyncStatus.Locking:
                        syncStatus = SyncStatus.Aborted;
                        Monitor.PulseAll(this);
                        break;
 
                    case SyncStatus.LockOwner:
                        syncStatus = SyncStatus.AbortedLocked;
                        webProxyFinder.Abort();
                        break;
                }
            }
        }
        // End of locking helper methods.
 
 
        // The lock is always held while these three are modified.
        internal bool AutomaticallyDetectSettings
        {
            get
            {
                return automaticallyDetectSettings;
            }
            set
            {
                if (automaticallyDetectSettings != value)
                {
                    automaticallyDetectSettings = value;
                    webProxyFinder.Reset();
                }
            }
        }
 
        internal Uri AutomaticConfigurationScript
        {
            get
            {
                return automaticConfigurationScript;
            }
            set
            {
                if (!object.Equals(automaticConfigurationScript, value))
                {
                    automaticConfigurationScript = value;
                    webProxyFinder.Reset();
                }
            }
        }
 
        internal ICredentials Credentials
        {
            get
            {
                return webProxy.Credentials;
            }
        }
 
        internal bool GetProxies(Uri destination, out IList<string> proxyList)
        {
            int syncStatus = SyncStatus.Unlocked;
            return GetProxies(destination, out proxyList, ref syncStatus);
        }
 
        internal bool GetProxies(Uri destination, out IList<string> proxyList, ref int syncStatus)
        {
            GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::GetProxies()");
 
            proxyList = null;
 
#if !FEATURE_PAL
            // See if we need to reinitialize based on registry or other changes.
#if !AUTOPROXY_SKIP_CHECK
            CheckForChanges(ref syncStatus);
#endif
#endif // !FEATURE_PAL
 
            if (!webProxyFinder.IsValid)
            {
                // If a zero value is specified in the configuration setting for AutoConfigUrlRetryInterval, the retry feature will be opted-out.
                if (retryWinHttpGetProxyForUrlTimer == null && SettingsSectionInternal.Section.AutoConfigUrlRetryInterval != 0)
                {
                    long retryIntervalInMilliseconds = SettingsSectionInternal.Section.AutoConfigUrlRetryInterval * 1000;
 
                    retryWinHttpGetProxyForUrlTimer = new Timer(s =>
                    {
                        var wr = (WeakReference<AutoWebProxyScriptEngine>)s;
                        AutoWebProxyScriptEngine thisRef;
                        if (wr.TryGetTarget(out thisRef))
                        {
                            thisRef.executeWinHttpGetProxyForUrl = true;
                        }
                    }, new WeakReference<AutoWebProxyScriptEngine>(this),
                    retryIntervalInMilliseconds, retryIntervalInMilliseconds);
                }
                
                if (executeWinHttpGetProxyForUrl)
                {
                    // Reset the value and continue execution.
                    executeWinHttpGetProxyForUrl = false;
                }
                else
                {
                    // This is to improve performance on e.g. home networks, where auto-detect will always
                    // fail, but IE settings turn auto-detect ON by default. I.e. in home networks on each
                    // call we would try to retrieve the PAC location.
                    // To retry retrieving the PAC file, need to wait for the next signal from retryWinHttpGetProxyForUrlTimer.
                    return false;
                }
            }
            else
            {
                if (retryWinHttpGetProxyForUrlTimer != null)
                {
                    retryWinHttpGetProxyForUrlTimer.Dispose();
                    retryWinHttpGetProxyForUrlTimer = null;
                }
            }
 
            // This whole thing has to be locked, both to prevent simultaneous downloading / compilation, and
            // because the script isn't threadsafe.
            try
            {
                EnterLock(ref syncStatus);
                if (syncStatus != SyncStatus.LockOwner)
                {
                    // This is typically because a download got aborted.
                    return false;
                }
 
                return webProxyFinder.GetProxies(destination, out proxyList);
            }
            finally
            {
                ExitLock(ref syncStatus);
                GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::GetProxies() proxies:" + ValidationHelper.ToString(proxyList));
            }
        }
 
        internal WebProxyData GetWebProxyData()
        {
            // PS DevDiv bug #217205 / TFS Dev10 #588370: use winhttp.WinhttpGetIEProxyConfigForCurrentUser
            WebProxyDataBuilder builder = null;
 
            if (ComNetOS.IsWin7orLater)
            {
                builder = new WinHttpWebProxyBuilder();
            }
            else
            {
                builder = new RegBlobWebProxyDataBuilder(m_AutoDetector.Connectoid, hkcu);
            }
 
            return builder.Build();
        }
 
        internal void Close()
        {
#if !FEATURE_PAL
            // m_AutoDetector is always set up in the constructor, use it to lock
            if (m_AutoDetector != null)
            {
                int syncStatus = SyncStatus.Unlocked;
                try
                {
                    EnterLock(ref syncStatus);
                    GlobalLog.Assert(syncStatus == SyncStatus.LockOwner, "AutoWebProxyScriptEngine#{0}::Close()|Failed to acquire lock.", ValidationHelper.HashString(this));
 
                    if (m_AutoDetector != null)
                    {
                        registrySuppress = true;
                        if (registryChangeEventPolicy != null)
                        {
                            registryChangeEventPolicy.Close();
                            registryChangeEventPolicy = null;
                        }
                        if (registryChangeEventLM != null)
                        {
                            registryChangeEventLM.Close();
                            registryChangeEventLM = null;
                        }
                        if (registryChangeEvent != null)
                        {
                            registryChangeEvent.Close();
                            registryChangeEvent = null;
                        }
 
                        if (regKeyPolicy != null && !regKeyPolicy.IsInvalid)
                        {
                            regKeyPolicy.Close();
                        }
                        if (regKeyLM != null && !regKeyLM.IsInvalid)
                        {
                            regKeyLM.Close();
                        }
                        if (regKey != null && !regKey.IsInvalid)
                        {
                            regKey.Close();
                        }
 
                        if (hkcu != null)
                        {
                            hkcu.RegCloseKey();
                            hkcu = null;
                        }
 
                        if (m_Identity != null)
                        {
                            m_Identity.Dispose();
                            m_Identity = null;
                        }
 
                        webProxyFinder.Dispose();
 
                        m_AutoDetector = null;
                    }
                }
                finally
                {
                    ExitLock(ref syncStatus);
                }
            }
#endif // !FEATURE_PAL
        }
 
#if !FEATURE_PAL
        private SafeRegistryHandle regKey;
        private SafeRegistryHandle regKeyLM;
        private SafeRegistryHandle regKeyPolicy;
        private AutoResetEvent registryChangeEvent;
        private AutoResetEvent registryChangeEventLM;
        private AutoResetEvent registryChangeEventPolicy;
        private bool registryChangeDeferred;
        private bool registryChangeLMDeferred;
        private bool registryChangePolicyDeferred;
        private bool needRegistryUpdate;
        private bool needConnectoidUpdate;
        private bool registrySuppress;
 
        internal void ListenForRegistry()
        {
            GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry()");
            if (!registrySuppress)
            {
                if (registryChangeEvent == null)
                {
                    GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKCU.");
                    ListenForRegistryHelper(ref regKey, ref registryChangeEvent, IntPtr.Zero,
                        RegBlobWebProxyDataBuilder.ProxyKey);
                }
                if (registryChangeEventLM == null)
                {
                    GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKLM.");
                    ListenForRegistryHelper(ref regKeyLM, ref registryChangeEventLM,
                        UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.ProxyKey);
                }
                if (registryChangeEventPolicy == null)
                {
                    GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() hooking HKLM/Policies.");
                    ListenForRegistryHelper(ref regKeyPolicy, ref registryChangeEventPolicy,
                        UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE, RegBlobWebProxyDataBuilder.PolicyKey);
                }
 
                // If any succeeded, we should monitor it.
                if (registryChangeEvent == null && registryChangeEventLM == null && registryChangeEventPolicy == null)
                {
                    registrySuppress = true;
                }
            }
        }
 
        private void ListenForRegistryHelper(ref SafeRegistryHandle key, ref AutoResetEvent changeEvent, IntPtr baseKey, string subKey)
        {
            uint errorCode = 0;
 
            // First time through?
            if (key == null || key.IsInvalid)
            {
                if (baseKey == IntPtr.Zero)
                {
                    // Impersonation requires extra effort.
                    GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenCurrentUser() using hkcu:" + hkcu.DangerousGetHandle().ToString("x"));
                    if (hkcu != null)
                    {
                        errorCode = hkcu.RegOpenKeyEx(subKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key);
                        GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenKeyEx() returned errorCode:" + errorCode + " key:" + key.DangerousGetHandle().ToString("x"));
                    }
                    else
                    {
                        errorCode = UnsafeNclNativeMethods.ErrorCodes.ERROR_NOT_FOUND;
                    }
                }
                else
                {
                    errorCode = SafeRegistryHandle.RegOpenKeyEx(baseKey, subKey, 0, UnsafeNclNativeMethods.RegistryHelper.KEY_READ, out key);
                    //GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegOpenKeyEx() returned errorCode:" + errorCode + " key:" + key.DangerousGetHandle().ToString("x"));
                }
                if (errorCode == 0)
                {
                    changeEvent = new AutoResetEvent(false);
                }
            }
            if (errorCode == 0)
            {
                // accessing Handle is protected by a link demand, OK for System.dll
                errorCode = key.RegNotifyChangeKeyValue(true, UnsafeNclNativeMethods.RegistryHelper.REG_NOTIFY_CHANGE_LAST_SET, changeEvent.SafeWaitHandle, true);
                GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegNotifyChangeKeyValue() returned errorCode:" + errorCode);
            }
            if (errorCode != 0)
            {
                if (key != null && !key.IsInvalid)
                {
                    try
                    {
                        errorCode = key.RegCloseKey();
                    }
                    catch (Exception exception)
                    {
                        if (NclUtilities.IsFatal(exception)) throw;
                    }
                    GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ListenForRegistry() RegCloseKey() returned errorCode:" + errorCode);
                }
                key = null;
                if (changeEvent != null)
                {
                    changeEvent.Close();
                    changeEvent = null;
                }
            }
        }
 
        private void RegistryChanged()
        {
            GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::RegistryChanged()");
            if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_system_setting_update));
 
            // always refresh settings because they might have changed
            WebProxyData webProxyData;
            using (m_Identity.Impersonate())
            {
                webProxyData = GetWebProxyData();
            }
            webProxy.Update(webProxyData);
        }
 
        private void ConnectoidChanged()
        {
            GlobalLog.Print("AutoWebProxyScriptEngine#" + ValidationHelper.HashString(this) + "::ConnectoidChanged()");
            if (Logging.On) Logging.PrintWarning(Logging.Web, SR.GetString(SR.net_log_proxy_update_due_to_ip_config_change));
 
            // Get the new connectoid/detector.  Only do this after detecting a change, to avoid ----s with other people detecting changes.
            // (We don't want to end up using a detector/connectoid that doesn't match what we read from the registry.)
            m_AutoDetector = AutoDetector.CurrentAutoDetector;
 
            if (m_UseRegistry)
            {
                // update the engine and proxy
                WebProxyData webProxyData;
                using (m_Identity.Impersonate())
                {
                    webProxyData = GetWebProxyData();
                }
                webProxy.Update(webProxyData);
            }
 
            // Always uninitialized if the connectoid/address changed and we are autodetecting.
            if (automaticallyDetectSettings)
                webProxyFinder.Reset();
        }
 
        internal void CheckForChanges()
        {
            int syncStatus = SyncStatus.Unlocked;
            CheckForChanges(ref syncStatus);
        }
 
        [SecurityPermission(SecurityAction.Assert, Flags = SecurityPermissionFlag.ControlPrincipal)]
        private void CheckForChanges(ref int syncStatus)
        {
            // Catch ObjectDisposedException instead of synchronizing with Close().
            try
            {
                bool changed = AutoDetector.CheckForNetworkChanges(ref m_NetworkChangeStatus);
                bool ignoreRegistryChange = false;
                if (changed || needConnectoidUpdate)
                {
                    try
                    {
                        EnterLock(ref syncStatus);
                        if (changed || needConnectoidUpdate)   // Make sure no one else took care of it before we got the lock.
                        {
                            needConnectoidUpdate = syncStatus != SyncStatus.LockOwner;
                            if (!needConnectoidUpdate)
                            {
                                ConnectoidChanged();
 
                                // We usually get a registry change at the same time.  Since the connectoid change does more,
                                // we can skip reading the registry info twice.
                                ignoreRegistryChange = true;
                            }
                        }
                    }
                    finally
                    {
                        ExitLock(ref syncStatus);
                    }
                }
 
                if (!m_UseRegistry)
                {
                    return;
                }
 
                bool forReal = false;
                AutoResetEvent tempEvent = registryChangeEvent;
                if (registryChangeDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false))))
                {
                    try
                    {
                        EnterLock(ref syncStatus);
                        if (forReal || registryChangeDeferred)  // Check if someone else handled it before I got the lock.
                        {
                            registryChangeDeferred = syncStatus != SyncStatus.LockOwner;
                            if (!registryChangeDeferred && registryChangeEvent != null)
                            {
                                try
                                {
                                    using (m_Identity.Impersonate())
                                    {
                                        ListenForRegistryHelper(ref regKey, ref registryChangeEvent, IntPtr.Zero,
                                            RegBlobWebProxyDataBuilder.ProxyKey);
                                    }
                                }
                                catch
                                {
                                    throw;
                                }
                                needRegistryUpdate = true;
                            }
                        }
                    }
                    finally
                    {
                        ExitLock(ref syncStatus);
                    }
                }
 
                forReal = false;
                tempEvent = registryChangeEventLM;
                if (registryChangeLMDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false))))
                {
                    try
                    {
                        EnterLock(ref syncStatus);
                        if (forReal || registryChangeLMDeferred)  // Check if someone else handled it before I got the lock.
                        {
                            registryChangeLMDeferred = syncStatus != SyncStatus.LockOwner;
                            if (!registryChangeLMDeferred && registryChangeEventLM != null)
                            {
                                try
                                {
                                    using (m_Identity.Impersonate())
                                    {
                                        ListenForRegistryHelper(ref regKeyLM, ref registryChangeEventLM,
                                            UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE,
                                            RegBlobWebProxyDataBuilder.ProxyKey);
                                    }
                                }
                                catch
                                {
                                    throw;
                                }
                                needRegistryUpdate = true;
                            }
                        }
                    }
                    finally
                    {
                        ExitLock(ref syncStatus);
                    }
                }
 
                forReal = false;
                tempEvent = registryChangeEventPolicy;
                if (registryChangePolicyDeferred || (forReal = (tempEvent != null && tempEvent.WaitOne(0, false))))
                {
                    try
                    {
                        EnterLock(ref syncStatus);
                        if (forReal || registryChangePolicyDeferred)  // Check if someone else handled it before I got the lock.
                        {
                            registryChangePolicyDeferred = syncStatus != SyncStatus.LockOwner;
                            if (!registryChangePolicyDeferred && registryChangeEventPolicy != null)
                            {
                                try
                                {
                                    using (m_Identity.Impersonate())
                                    {
                                        ListenForRegistryHelper(ref regKeyPolicy, ref registryChangeEventPolicy,
                                            UnsafeNclNativeMethods.RegistryHelper.HKEY_LOCAL_MACHINE,
                                            RegBlobWebProxyDataBuilder.PolicyKey);
                                    }
                                }
                                catch
                                {
                                    throw;
                                }
                                needRegistryUpdate = true;
                            }
                        }
                    }
                    finally
                    {
                        ExitLock(ref syncStatus);
                    }
                }
 
                if (needRegistryUpdate)
                {
                    try
                    {
                        EnterLock(ref syncStatus);
                        if (needRegistryUpdate && syncStatus == SyncStatus.LockOwner)
                        {
                            needRegistryUpdate = false;
 
                            // We don't need to process this now if we just did it for the connectoid.
                            if (!ignoreRegistryChange)
                            {
                                RegistryChanged();
                            }
                        }
                    }
                    finally
                    {
                        ExitLock(ref syncStatus);
                    }
                }
            }
            catch (ObjectDisposedException) { }
        }
 
        private class AutoDetector
        {
            private static volatile NetworkAddressChangePolled s_AddressChange;
            private static volatile UnsafeNclNativeMethods.RasHelper s_RasHelper;
 
            private static int s_CurrentVersion = 0;
            private volatile static AutoDetector s_CurrentAutoDetector;
            private volatile static bool s_Initialized;
            private static object s_LockObject;
 
            static AutoDetector()
            {
                s_LockObject = new object();
            }
 
            private static void Initialize()
            {
                if (!s_Initialized)
                {
                    lock (s_LockObject)
                    {
                        if (!s_Initialized)
                        {
                            s_CurrentAutoDetector = new AutoDetector(UnsafeNclNativeMethods.RasHelper.GetCurrentConnectoid(), 1);
                            if (NetworkChange.CanListenForNetworkChanges)
                            {
                                s_AddressChange = new NetworkAddressChangePolled();
                            }
                            if (UnsafeNclNativeMethods.RasHelper.RasSupported)
                            {
                                s_RasHelper = new UnsafeNclNativeMethods.RasHelper();
                            }
                            s_CurrentVersion = 1;
                            s_Initialized = true;
                        }
                    }
                }
            }
 
            internal static bool CheckForNetworkChanges(ref int changeStatus)
            {
                Initialize();
                CheckForChanges();
                int oldStatus = changeStatus;
                changeStatus = Volatile.Read(ref s_CurrentVersion);
                return oldStatus != changeStatus;
            }
 
            private static void CheckForChanges()
            {
                bool changed = false;
                if (s_RasHelper != null && s_RasHelper.HasChanged)
                {
                    s_RasHelper.Reset();
                    changed = true;
                }
                if (s_AddressChange != null && s_AddressChange.CheckAndReset())
                {
                    changed = true;
                }
                if (changed)
                {
                    int currentVersion = Interlocked.Increment(ref s_CurrentVersion);
                    s_CurrentAutoDetector = new AutoDetector(UnsafeNclNativeMethods.RasHelper.GetCurrentConnectoid(), currentVersion);
                }
            }
 
            internal static AutoDetector CurrentAutoDetector
            {
                get
                {
                    Initialize();
                    return s_CurrentAutoDetector;
                }
            }
 
 
            private readonly string m_Connectoid;
            private readonly int m_CurrentVersion;
 
            private AutoDetector(string connectoid, int currentVersion)
            {
                m_Connectoid = connectoid;
                m_CurrentVersion = currentVersion;
            }
 
            internal string Connectoid
            {
                get
                {
                    return m_Connectoid;
                }
            }
 
            internal int NetworkChangeStatus
            {
                get
                {
                    return m_CurrentVersion;
                }
            }
        }
#endif
    }
}