BuildManagerHost.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ 4.0 / 4.0 / DEVDIV_TFS / Dev10 / Releases / RTMRel / ndp / fx / src / xsp / System / Web / Compilation / BuildManagerHost.cs / 1305376 / BuildManagerHost.cs

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

/************************************************************************************************************/ 
 

namespace System.Web.Compilation { 

using System;
using System.Collections;
using System.Collections.Generic; 
using System.Collections.Specialized;
using System.Diagnostics; 
using System.Globalization; 
using System.IO;
using System.Text; 
using System.Threading;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler; 
using System.Web;
using System.Web.Caching; 
using System.Web.Util; 
using System.Web.UI;
using System.Web.Hosting; 
using Debug=System.Web.Util.Debug;
using System.Web.Configuration;
using System.Security.Permissions;
 

// 
// Instances of this class are created in the ASP.NET app domain.  The class 
// methods are called by ClientBuildManager (cross app domain)
// 
internal class BuildManagerHost : MarshalByRefObject, IRegisteredObject {

    private ClientBuildManager _client;
    private BuildManager _buildManager; 

    private int _pendingCallsCount; 
 
    private EventHandler _onAppDomainUnload;
 
    private bool _ignorePendingCalls;
    private IDictionary _assemblyCollection;

    private object _lock = new object(); 

    private static bool _inClientBuildManager; 
 
    internal static bool InClientBuildManager {
        get { return _inClientBuildManager; } 
        set { _inClientBuildManager = true; }
    }

    ///  
    /// Returns whether there is support for multitargeting. This is usually only true in VS as they will supply a
    /// TypeDescriptionProvider that is required for correctly reflecting over types in the target framework. 
    /// When running in the 4.0 runtime application pool, or when running from aspnet_compiler, this value 
    /// will be false.
    ///  
    internal static bool SupportsMultiTargeting {
        get; set;
    }
 
    private ClientVirtualPathProvider _virtualPathProvider;
 
    public BuildManagerHost() { 
        HostingEnvironment.RegisterObject(this);
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.ResolveAssembly); 
    }

    void IRegisteredObject.Stop(bool immediate) {
 
        // Make sure all the pending calls complete
        WaitForPendingCallsToFinish(); 
 
        HostingEnvironment.UnregisterObject(this);
    } 

    internal IApplicationHost ApplicationHost {
        get {
            return HostingEnvironment.ApplicationHostInternal; 
        }
    } 
 
    internal string CodeGenDir {
        get { 
            // Add a pending call to make sure our thread doesn't get killed
            AddPendingCall();

            try { 
                return HttpRuntime.CodegenDirInternal;
            } 
            finally { 
                RemovePendingCall();
            } 
        }
    }

    internal void RegisterAssembly(String assemblyName, String assemblyLocation) { 
        Debug.Trace("BuildManagerHost", "RegisterAssembly '" + assemblyName + "','" + assemblyLocation + "'");
 
        if (_assemblyCollection == null) { 
            lock (_lock) {
                if (_assemblyCollection == null) { 
                    _assemblyCollection = Hashtable.Synchronized(new Hashtable());
                }
            }
        } 

        AssemblyName asmName = new AssemblyName(assemblyName); 
        _assemblyCollection[asmName.FullName] = assemblyLocation; 
    }
 
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    private Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
        Debug.Trace("BuildManagerHost", "ResolveAssembly '" + e.Name + "'");
        if (_assemblyCollection == null) 
            return null;
 
        String assemblyLocation = (String)_assemblyCollection[e.Name]; 
        if (assemblyLocation == null)
            return null; 

        Debug.Trace("BuildManagerHost", "ResolveAssembly: found");

        return Assembly.LoadFrom(assemblyLocation); 
    }
 
    /* 
     * Make sure all the (non-request based) pending calls complete
     */ 
    private void WaitForPendingCallsToFinish() {
        for (;;) {
            if (_pendingCallsCount <= 0 || _ignorePendingCalls)
                break; 

            Thread.Sleep(250); 
        } 
    }
 
    internal void AddPendingCall() {
        Interlocked.Increment(ref _pendingCallsCount);
    }
 
    internal void RemovePendingCall() {
        Interlocked.Decrement(ref _pendingCallsCount); 
    } 

    private void OnAppDomainShutdown(object o, BuildManagerHostUnloadEventArgs args) { 
        _client.OnAppDomainShutdown(args.Reason);
    }

    internal void CompileApplicationDependencies() { 
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 
 
        try {
            _buildManager.EnsureTopLevelFilesCompiled(); 
        }
        finally {
            RemovePendingCall();
        } 
    }
 
    internal void PrecompileApp(ClientBuildManagerCallback callback) { 
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 

        try {
            _buildManager.PrecompileApp(callback);
        } 
        finally {
            RemovePendingCall(); 
        } 
    }
 
    internal IDictionary GetBrowserDefinitions() {
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall();
 
        try {
            return BrowserCapabilitiesCompiler.BrowserCapabilitiesFactory.InternalGetBrowserElements(); 
        } 
        finally {
            RemovePendingCall(); 
        }
    }

    internal string[] GetVirtualCodeDirectories() { 
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 
 
        try {
            return _buildManager.GetCodeDirectories(); 
        }
        finally {
            RemovePendingCall();
        } 
    }
 
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)] 
    internal void GetCodeDirectoryInformation(VirtualPath virtualCodeDir,
        out Type codeDomProviderType, out CompilerParameters compParams, 
        out string generatedFilesDir) {

        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 

        try { 
            BuildManager.SkipTopLevelCompilationExceptions = true; 
            _buildManager.EnsureTopLevelFilesCompiled();
 
            // Treat it as relative to the app root
            virtualCodeDir = virtualCodeDir.CombineWithAppRoot();

            _buildManager.GetCodeDirectoryInformation(virtualCodeDir, 
                out codeDomProviderType, out compParams, out generatedFilesDir);
        } 
        finally { 
            BuildManager.SkipTopLevelCompilationExceptions = false;
            RemovePendingCall(); 
        }
    }

    internal void GetCompilerParams(VirtualPath virtualPath, out Type codeDomProviderType, 
        out CompilerParameters compParams) {
 
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall();
 
        try {
            BuildManager.SkipTopLevelCompilationExceptions = true;
            _buildManager.EnsureTopLevelFilesCompiled();
 
            // Ignore the BuildProvider return value
            GetCompilerParamsAndBuildProvider(virtualPath, out codeDomProviderType, out compParams); 
 
            // This is the no-compile case
            if (compParams == null) 
                return;

            FixupReferencedAssemblies(virtualPath, compParams);
        } 
        finally {
            BuildManager.SkipTopLevelCompilationExceptions = false; 
            RemovePendingCall(); 
        }
    } 

    internal string[] GetCompiledTypeAndAssemblyName(VirtualPath virtualPath, ClientBuildManagerCallback callback) {

        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall();
 
        try { 
            // Treat it as relative to the app root
            virtualPath.CombineWithAppRoot(); 

            Type t = BuildManager.GetCompiledType(virtualPath, callback);

            if (t == null) return null; 

            string assemblyPath = Util.GetAssemblyPathFromType(t); 
            return new string[] { t.FullName, assemblyPath }; 
        }
        finally { 
            RemovePendingCall();
        }
    }
 
    internal string GetGeneratedSourceFile(VirtualPath virtualPath) {
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall(); 

        Type codeDomProviderType; 
        CompilerParameters compilerParameters;
        string generatedFilesDir;

        try { 
            if (!virtualPath.DirectoryExists()) {
                throw new ArgumentException(SR.GetString(SR.GetGeneratedSourceFile_Directory_Only, 
                    virtualPath.VirtualPathString), "virtualPath"); 
            }
 
            // Calls GetCodeDirectoryInformation to ensure the source files are created for the
            // directory specified by virtualPath
            GetCodeDirectoryInformation(virtualPath,
                out codeDomProviderType, out compilerParameters, 
                out generatedFilesDir);
 
            return BuildManager.GenerateFileTable[virtualPath.VirtualPathStringNoTrailingSlash]; 
        }
        finally { 
            RemovePendingCall();
        }
    }
 
    internal string GetGeneratedFileVirtualPath(string filePath) {
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall(); 

        try { 
            // Performs reverse hashtable lookup to find the filePath in the Value collection.
            Dictionary.Enumerator e = BuildManager.GenerateFileTable.GetEnumerator();
            while (e.MoveNext()) {
                KeyValuePair pair = e.Current; 
                if (filePath.Equals(pair.Value, StringComparison.Ordinal)) {
                    return pair.Key; 
                } 
            }
 
            return null;
        }
        finally {
            RemovePendingCall(); 
        }
    } 
 
    /*
     * Returns an array of the assemblies defined in the bin and assembly reference config section 
     */
    internal String[] GetTopLevelAssemblyReferences(VirtualPath virtualPath) {
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 

        List assemblyList = new List(); 
        try { 
            // Treat it as relative to the app root
            virtualPath.CombineWithAppRoot(); 

            CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);

            // Add all the config assemblies to the list 
            foreach (AssemblyInfo assemblyInfo in compConfig.Assemblies) {
                Assembly[] assemblies = assemblyInfo.AssemblyInternal; 
                for (int i = 0; i < assemblies.Length; i++) { 
                    if (assemblies[i] != null) {
                        assemblyList.Add(assemblies[i]); 
                    }
                }
            }
        } finally { 
            RemovePendingCall();
        } 
        StringCollection paths = new StringCollection(); 
        Util.AddAssembliesToStringCollection(assemblyList, paths);
        string[] references = new string[paths.Count]; 
        paths.CopyTo(references, 0);
        return references;
    }
 
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    internal string GenerateCode( 
        VirtualPath virtualPath, string virtualFileString, out IDictionary linePragmasTable) { 

        AddPendingCall(); 
        try {
            string code = null;
            Type codeDomProviderType;
            CompilerParameters compilerParameters; 

            CodeCompileUnit ccu = GenerateCodeCompileUnit(virtualPath, virtualFileString, out codeDomProviderType, 
                out compilerParameters, out linePragmasTable); 

            if (ccu != null && codeDomProviderType != null) { 
                CodeDomProvider codeProvider = CompilationUtil.CreateCodeDomProvider(codeDomProviderType);

                CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
                codeGeneratorOptions.BlankLinesBetweenMembers = false; 
                codeGeneratorOptions.IndentString = string.Empty;
 
                StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); 
                codeProvider.GenerateCodeFromCompileUnit(ccu, sw, codeGeneratorOptions);
 
                code = sw.ToString();
            }

            return code; 
        }
        finally { 
            RemovePendingCall(); 
        }
    } 

    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    internal CodeCompileUnit GenerateCodeCompileUnit(
        VirtualPath virtualPath, string virtualFileString, out Type codeDomProviderType, 
        out CompilerParameters compilerParameters, out IDictionary linePragmasTable) {
 
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall();
 
        try {
            BuildManager.SkipTopLevelCompilationExceptions = true;
            _buildManager.EnsureTopLevelFilesCompiled();
 
            // Get the virtual file content so that we can use the correct hash code.
            if (virtualFileString == null) { 
                using (Stream stream = virtualPath.OpenFile()) { 
                    TextReader reader = Util.ReaderFromStream(stream, virtualPath);
                    virtualFileString = reader.ReadToEnd(); 
                }
            }

            _virtualPathProvider.RegisterVirtualFile(virtualPath, virtualFileString); 

            string cacheKey = BuildManager.GetCacheKeyFromVirtualPath(virtualPath) + "_CBMResult"; 
            BuildResultCodeCompileUnit result = (BuildResultCodeCompileUnit)BuildManager.GetBuildResultFromCache(cacheKey, virtualPath); 

            if (result == null) { 
                lock (_lock) {
                    // Don't need to check the result again since it's very unlikely in CBM scenarios.
                    DateTime utcStart = DateTime.UtcNow;
 
                    BuildProvider internalBuildProvider = GetCompilerParamsAndBuildProvider(
                        virtualPath, out codeDomProviderType, out compilerParameters); 
 
                    // This is the no-compile case
                    if (internalBuildProvider == null) { 
                        linePragmasTable = null;
                        return null;
                    }
 
                    CodeCompileUnit ccu = internalBuildProvider.GetCodeCompileUnit(out linePragmasTable);
 
                    result = new BuildResultCodeCompileUnit(codeDomProviderType, ccu, compilerParameters, linePragmasTable); 
                    result.VirtualPath = virtualPath;
                    result.SetCacheKey(cacheKey); 

                    FixupReferencedAssemblies(virtualPath, compilerParameters);

                    // CodeCompileUnit could be null, do not try to fix referenced assemblies. 
                    // This happens for example when an .asmx file does not contain any code.
                    if (ccu != null) { 
                        // VSWhidbey 501260 Add all the referenced assemblies to the CodeCompileUnit 
                        // in case the CodeDom provider needs them for code generation
                        foreach (String assemblyString in compilerParameters.ReferencedAssemblies) { 
                            ccu.ReferencedAssemblies.Add(assemblyString);
                        }
                    }
 
                    // Add all the dependencies, so that the ccu gets cached correctly (VSWhidbey 275091)
                    ICollection dependencies = internalBuildProvider.VirtualPathDependencies; 
                    if (dependencies != null) 
                        result.AddVirtualPathDependencies(dependencies);
 
                    BuildManager.CacheBuildResult(cacheKey, result, utcStart);

                    return ccu;
                } 
            }
 
            codeDomProviderType = result.CodeDomProviderType; 
            compilerParameters = result.CompilerParameters;
            linePragmasTable = result.LinePragmasTable; 

            FixupReferencedAssemblies(virtualPath, compilerParameters);

            return result.CodeCompileUnit; 
        }
        finally { 
            if (virtualFileString != null) { 
                _virtualPathProvider.RevertVirtualFile(virtualPath);
            } 
            BuildManager.SkipTopLevelCompilationExceptions = false;

            RemovePendingCall();
        } 
    }
 
    internal bool IsCodeAssembly(string assemblyName) { 
        return BuildManager.GetNormalizedCodeAssemblyName(assemblyName) != null;
    } 

    // Add the referenced assemblies into the compileParameters. Notice that buildProviders do not have
    // the correct referenced assemblies and we don't cache them since the assemblies could change
    // between appdomains. (removing assemblies from bin, etc) 
    private void FixupReferencedAssemblies(VirtualPath virtualPath, CompilerParameters compilerParameters) {
        CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath); 
 
        ICollection referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig);
        Util.AddAssembliesToStringCollection(referencedAssemblies, compilerParameters.ReferencedAssemblies); 
    }

    private BuildProvider GetCompilerParamsAndBuildProvider(VirtualPath virtualPath,
        out Type codeDomProviderType, out CompilerParameters compilerParameters) { 

        virtualPath.CombineWithAppRoot(); 
 
        CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);
 
        ICollection referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig);

        // Create the buildprovider for the passed in virtualPath
        BuildProvider buildProvider = null; 

        // Special case global asax build provider here since we do not want to compile every files with ".asax" extension. 
        if (StringUtil.EqualsIgnoreCase(virtualPath.VirtualPathString, BuildManager.GlobalAsaxVirtualPath.VirtualPathString)) { 
            ApplicationBuildProvider provider = new ApplicationBuildProvider();
            provider.SetVirtualPath(virtualPath); 
            provider.SetReferencedAssemblies(referencedAssemblies);
            buildProvider = provider;
        }
        else { 
            buildProvider = BuildManager.CreateBuildProvider(virtualPath, compConfig,
            referencedAssemblies, true /*failIfUnknown*/); 
        } 

        // DevDiv 69017 
        // The methods restricted to internalBuildProvider have been moved up to BuildProvider
        // to allow WCFBuildProvider to support .svc syntax highlighting.

        // Ignore parse errors, since they should not break the designer 
        buildProvider.IgnoreParseErrors = true;
 
        // Ignore all control properties, since we do not generate code for the properties 
        buildProvider.IgnoreControlProperties = true;
 
        // Process as many errors as possible, do not rethrow on first error
        buildProvider.ThrowOnFirstParseError = false;

        // Get the language (causes the file to be parsed) 
        CompilerType compilerType = buildProvider.CodeCompilerType;
 
        // compilerType could be null in the no-compile case (VSWhidbey 221749) 
        if (compilerType == null) {
            codeDomProviderType = null; 
            compilerParameters = null;
            return null;
        }
 
        // Return the provider type and compiler params
        codeDomProviderType = compilerType.CodeDomProviderType; 
        compilerParameters = compilerType.CompilerParameters; 

        IAssemblyDependencyParser parser = buildProvider.AssemblyDependencyParser; 

        // Add all the assemblies that the page depends on (e.g. user controls)
        if (parser != null && parser.AssemblyDependencies != null) {
            Util.AddAssembliesToStringCollection(parser.AssemblyDependencies, 
                compilerParameters.ReferencedAssemblies);
        } 
 
        // Make any fix up adjustments to the CompilerParameters to work around some issues
        AssemblyBuilder.FixUpCompilerParameters(codeDomProviderType, compilerParameters); 

        return buildProvider;
    }
 
    public override Object InitializeLifetimeService() {
        return null; // never expire lease 
    } 

    internal void Configure(ClientBuildManager client) { 

        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall();
 
        try {
            _virtualPathProvider = new ClientVirtualPathProvider(); 
            HostingEnvironment.RegisterVirtualPathProviderInternal(_virtualPathProvider); 

            _client = client; 

            // Type description provider required for multitargeting compilation support in VS.
            if (_client.CBMTypeDescriptionProviderBridge != null) {
                TargetFrameworkUtil.CBMTypeDescriptionProviderBridge = _client.CBMTypeDescriptionProviderBridge; 
            }
 
            // start watching for app domain unloading 
            _onAppDomainUnload = new EventHandler(OnAppDomainUnload);
            Thread.GetDomain().DomainUnload += _onAppDomainUnload; 

            _buildManager = BuildManager.TheBuildManager;

            // Listen to appdomain shutdown. 
            HttpRuntime.AppDomainShutdown += new BuildManagerHostUnloadEventHandler(this.OnAppDomainShutdown);
        } 
        finally { 
            RemovePendingCall();
        } 
    }

    internal Exception InitializationException {
        get { 
            return HostingEnvironment.InitializationException;
        } 
    } 

    private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) { 
        Thread.GetDomain().DomainUnload -= _onAppDomainUnload;

        if (_client != null) {
            _client.OnAppDomainUnloaded(HttpRuntime.ShutdownReason); 
            _client = null;
        } 
    } 

    internal bool UnloadAppDomain() { 
        _ignorePendingCalls = true;

        // Make sure HttpRuntime does not ignore the appdomain shutdown.
        HttpRuntime.SetUserForcedShutdown(); 

        // Force unload the appdomain when called from client 
        return HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.UnloadAppDomainCalled, "CBM called UnloadAppDomain"); 
    }
 
    // This provider is created in the hosted appdomain for faster access of the virtual file content.
    // Note this is used both in CBM and the aspnet precompilation tool
    internal class ClientVirtualPathProvider : VirtualPathProvider {
        private IDictionary _stringDictionary; 

        internal ClientVirtualPathProvider() { 
            _stringDictionary = new HybridDictionary(true); 
        }
 
        public override bool FileExists(string virtualPath) {
            if (_stringDictionary.Contains(virtualPath)) {
                return true;
            } 

            return base.FileExists(virtualPath); 
        } 

        public override CacheDependency GetCacheDependency(string virtualPath, 
            IEnumerable virtualPathDependencies, DateTime utcStart) {

            if (virtualPath != null) {
                virtualPath = UrlPath.MakeVirtualPathAppAbsolute(virtualPath); 
                // Return now so the build result will be invalidated based on hashcode.
                // This is for the case that Venus passed in the file content so we don't 
                // get file change notification 
                if (_stringDictionary.Contains(virtualPath)) {
                    return null; 
                }
            }

            // otherwise creates a cachedependency using MapPathBasedVirtualPathProvider 
            return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        } 
 
        public override VirtualFile GetFile(string virtualPath) {
            String _virtualFileString = (String)_stringDictionary[virtualPath]; 

            return _virtualFileString != null? new ClientVirtualFile(virtualPath, _virtualFileString) : base.GetFile(virtualPath);
        }
 
        public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) {
            HashCodeCombiner hashCodeCombiner = null; 
 
            ArrayList clonedPaths = new ArrayList();
            foreach (string virtualDependency in virtualPathDependencies) { 
                // if the virtual path is previously cached, add the actual content to
                // the hash code combiner.
                if (_stringDictionary.Contains(virtualDependency)) {
                    if (hashCodeCombiner == null) { 
                        hashCodeCombiner = new HashCodeCombiner();
                    } 
 
                    hashCodeCombiner.AddInt(_stringDictionary[virtualDependency].GetHashCode());
                    continue; 
                }

                // Otherwise move it to the cloned collection and use the base class (previous provider)
                // to get the hash code. 
                clonedPaths.Add(virtualDependency);
            } 
 
            if (hashCodeCombiner == null) {
                return base.GetFileHash(virtualPath, virtualPathDependencies); 
            }
            hashCodeCombiner.AddObject(base.GetFileHash(virtualPath, clonedPaths));

            return hashCodeCombiner.CombinedHashString; 
        }
 
        internal void RegisterVirtualFile(VirtualPath virtualPath, String virtualFileString) { 
            _stringDictionary[virtualPath.VirtualPathString] = virtualFileString;
        } 

        internal void RevertVirtualFile(VirtualPath virtualPath) {
            _stringDictionary.Remove(virtualPath.VirtualPathString);
        } 

        internal class ClientVirtualFile : VirtualFile { 
            String _virtualFileString; 

            internal ClientVirtualFile(string virtualPath, String virtualFileString) : base(virtualPath) { 
                _virtualFileString = virtualFileString;
            }

            public override Stream Open() { 
                Stream stream = new MemoryStream();
                StreamWriter writer = new StreamWriter(stream, Encoding.Unicode); 
 
                writer.Write(_virtualFileString);
                writer.Flush(); 
                stream.Seek(0, SeekOrigin.Begin);

                return stream;
            } 
        }
    } 
} 
}

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//------------------------------------------------------------------------------ 
// 
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// 
//----------------------------------------------------------------------------- 

/************************************************************************************************************/ 
 

namespace System.Web.Compilation { 

using System;
using System.Collections;
using System.Collections.Generic; 
using System.Collections.Specialized;
using System.Diagnostics; 
using System.Globalization; 
using System.IO;
using System.Text; 
using System.Threading;
using System.Reflection;
using System.CodeDom;
using System.CodeDom.Compiler; 
using System.Web;
using System.Web.Caching; 
using System.Web.Util; 
using System.Web.UI;
using System.Web.Hosting; 
using Debug=System.Web.Util.Debug;
using System.Web.Configuration;
using System.Security.Permissions;
 

// 
// Instances of this class are created in the ASP.NET app domain.  The class 
// methods are called by ClientBuildManager (cross app domain)
// 
internal class BuildManagerHost : MarshalByRefObject, IRegisteredObject {

    private ClientBuildManager _client;
    private BuildManager _buildManager; 

    private int _pendingCallsCount; 
 
    private EventHandler _onAppDomainUnload;
 
    private bool _ignorePendingCalls;
    private IDictionary _assemblyCollection;

    private object _lock = new object(); 

    private static bool _inClientBuildManager; 
 
    internal static bool InClientBuildManager {
        get { return _inClientBuildManager; } 
        set { _inClientBuildManager = true; }
    }

    ///  
    /// Returns whether there is support for multitargeting. This is usually only true in VS as they will supply a
    /// TypeDescriptionProvider that is required for correctly reflecting over types in the target framework. 
    /// When running in the 4.0 runtime application pool, or when running from aspnet_compiler, this value 
    /// will be false.
    ///  
    internal static bool SupportsMultiTargeting {
        get; set;
    }
 
    private ClientVirtualPathProvider _virtualPathProvider;
 
    public BuildManagerHost() { 
        HostingEnvironment.RegisterObject(this);
        AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(this.ResolveAssembly); 
    }

    void IRegisteredObject.Stop(bool immediate) {
 
        // Make sure all the pending calls complete
        WaitForPendingCallsToFinish(); 
 
        HostingEnvironment.UnregisterObject(this);
    } 

    internal IApplicationHost ApplicationHost {
        get {
            return HostingEnvironment.ApplicationHostInternal; 
        }
    } 
 
    internal string CodeGenDir {
        get { 
            // Add a pending call to make sure our thread doesn't get killed
            AddPendingCall();

            try { 
                return HttpRuntime.CodegenDirInternal;
            } 
            finally { 
                RemovePendingCall();
            } 
        }
    }

    internal void RegisterAssembly(String assemblyName, String assemblyLocation) { 
        Debug.Trace("BuildManagerHost", "RegisterAssembly '" + assemblyName + "','" + assemblyLocation + "'");
 
        if (_assemblyCollection == null) { 
            lock (_lock) {
                if (_assemblyCollection == null) { 
                    _assemblyCollection = Hashtable.Synchronized(new Hashtable());
                }
            }
        } 

        AssemblyName asmName = new AssemblyName(assemblyName); 
        _assemblyCollection[asmName.FullName] = assemblyLocation; 
    }
 
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    private Assembly ResolveAssembly(object sender, ResolveEventArgs e) {
        Debug.Trace("BuildManagerHost", "ResolveAssembly '" + e.Name + "'");
        if (_assemblyCollection == null) 
            return null;
 
        String assemblyLocation = (String)_assemblyCollection[e.Name]; 
        if (assemblyLocation == null)
            return null; 

        Debug.Trace("BuildManagerHost", "ResolveAssembly: found");

        return Assembly.LoadFrom(assemblyLocation); 
    }
 
    /* 
     * Make sure all the (non-request based) pending calls complete
     */ 
    private void WaitForPendingCallsToFinish() {
        for (;;) {
            if (_pendingCallsCount <= 0 || _ignorePendingCalls)
                break; 

            Thread.Sleep(250); 
        } 
    }
 
    internal void AddPendingCall() {
        Interlocked.Increment(ref _pendingCallsCount);
    }
 
    internal void RemovePendingCall() {
        Interlocked.Decrement(ref _pendingCallsCount); 
    } 

    private void OnAppDomainShutdown(object o, BuildManagerHostUnloadEventArgs args) { 
        _client.OnAppDomainShutdown(args.Reason);
    }

    internal void CompileApplicationDependencies() { 
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 
 
        try {
            _buildManager.EnsureTopLevelFilesCompiled(); 
        }
        finally {
            RemovePendingCall();
        } 
    }
 
    internal void PrecompileApp(ClientBuildManagerCallback callback) { 
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 

        try {
            _buildManager.PrecompileApp(callback);
        } 
        finally {
            RemovePendingCall(); 
        } 
    }
 
    internal IDictionary GetBrowserDefinitions() {
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall();
 
        try {
            return BrowserCapabilitiesCompiler.BrowserCapabilitiesFactory.InternalGetBrowserElements(); 
        } 
        finally {
            RemovePendingCall(); 
        }
    }

    internal string[] GetVirtualCodeDirectories() { 
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 
 
        try {
            return _buildManager.GetCodeDirectories(); 
        }
        finally {
            RemovePendingCall();
        } 
    }
 
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)] 
    internal void GetCodeDirectoryInformation(VirtualPath virtualCodeDir,
        out Type codeDomProviderType, out CompilerParameters compParams, 
        out string generatedFilesDir) {

        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 

        try { 
            BuildManager.SkipTopLevelCompilationExceptions = true; 
            _buildManager.EnsureTopLevelFilesCompiled();
 
            // Treat it as relative to the app root
            virtualCodeDir = virtualCodeDir.CombineWithAppRoot();

            _buildManager.GetCodeDirectoryInformation(virtualCodeDir, 
                out codeDomProviderType, out compParams, out generatedFilesDir);
        } 
        finally { 
            BuildManager.SkipTopLevelCompilationExceptions = false;
            RemovePendingCall(); 
        }
    }

    internal void GetCompilerParams(VirtualPath virtualPath, out Type codeDomProviderType, 
        out CompilerParameters compParams) {
 
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall();
 
        try {
            BuildManager.SkipTopLevelCompilationExceptions = true;
            _buildManager.EnsureTopLevelFilesCompiled();
 
            // Ignore the BuildProvider return value
            GetCompilerParamsAndBuildProvider(virtualPath, out codeDomProviderType, out compParams); 
 
            // This is the no-compile case
            if (compParams == null) 
                return;

            FixupReferencedAssemblies(virtualPath, compParams);
        } 
        finally {
            BuildManager.SkipTopLevelCompilationExceptions = false; 
            RemovePendingCall(); 
        }
    } 

    internal string[] GetCompiledTypeAndAssemblyName(VirtualPath virtualPath, ClientBuildManagerCallback callback) {

        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall();
 
        try { 
            // Treat it as relative to the app root
            virtualPath.CombineWithAppRoot(); 

            Type t = BuildManager.GetCompiledType(virtualPath, callback);

            if (t == null) return null; 

            string assemblyPath = Util.GetAssemblyPathFromType(t); 
            return new string[] { t.FullName, assemblyPath }; 
        }
        finally { 
            RemovePendingCall();
        }
    }
 
    internal string GetGeneratedSourceFile(VirtualPath virtualPath) {
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall(); 

        Type codeDomProviderType; 
        CompilerParameters compilerParameters;
        string generatedFilesDir;

        try { 
            if (!virtualPath.DirectoryExists()) {
                throw new ArgumentException(SR.GetString(SR.GetGeneratedSourceFile_Directory_Only, 
                    virtualPath.VirtualPathString), "virtualPath"); 
            }
 
            // Calls GetCodeDirectoryInformation to ensure the source files are created for the
            // directory specified by virtualPath
            GetCodeDirectoryInformation(virtualPath,
                out codeDomProviderType, out compilerParameters, 
                out generatedFilesDir);
 
            return BuildManager.GenerateFileTable[virtualPath.VirtualPathStringNoTrailingSlash]; 
        }
        finally { 
            RemovePendingCall();
        }
    }
 
    internal string GetGeneratedFileVirtualPath(string filePath) {
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall(); 

        try { 
            // Performs reverse hashtable lookup to find the filePath in the Value collection.
            Dictionary.Enumerator e = BuildManager.GenerateFileTable.GetEnumerator();
            while (e.MoveNext()) {
                KeyValuePair pair = e.Current; 
                if (filePath.Equals(pair.Value, StringComparison.Ordinal)) {
                    return pair.Key; 
                } 
            }
 
            return null;
        }
        finally {
            RemovePendingCall(); 
        }
    } 
 
    /*
     * Returns an array of the assemblies defined in the bin and assembly reference config section 
     */
    internal String[] GetTopLevelAssemblyReferences(VirtualPath virtualPath) {
        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall(); 

        List assemblyList = new List(); 
        try { 
            // Treat it as relative to the app root
            virtualPath.CombineWithAppRoot(); 

            CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);

            // Add all the config assemblies to the list 
            foreach (AssemblyInfo assemblyInfo in compConfig.Assemblies) {
                Assembly[] assemblies = assemblyInfo.AssemblyInternal; 
                for (int i = 0; i < assemblies.Length; i++) { 
                    if (assemblies[i] != null) {
                        assemblyList.Add(assemblies[i]); 
                    }
                }
            }
        } finally { 
            RemovePendingCall();
        } 
        StringCollection paths = new StringCollection(); 
        Util.AddAssembliesToStringCollection(assemblyList, paths);
        string[] references = new string[paths.Count]; 
        paths.CopyTo(references, 0);
        return references;
    }
 
    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    internal string GenerateCode( 
        VirtualPath virtualPath, string virtualFileString, out IDictionary linePragmasTable) { 

        AddPendingCall(); 
        try {
            string code = null;
            Type codeDomProviderType;
            CompilerParameters compilerParameters; 

            CodeCompileUnit ccu = GenerateCodeCompileUnit(virtualPath, virtualFileString, out codeDomProviderType, 
                out compilerParameters, out linePragmasTable); 

            if (ccu != null && codeDomProviderType != null) { 
                CodeDomProvider codeProvider = CompilationUtil.CreateCodeDomProvider(codeDomProviderType);

                CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
                codeGeneratorOptions.BlankLinesBetweenMembers = false; 
                codeGeneratorOptions.IndentString = string.Empty;
 
                StringWriter sw = new StringWriter(CultureInfo.InvariantCulture); 
                codeProvider.GenerateCodeFromCompileUnit(ccu, sw, codeGeneratorOptions);
 
                code = sw.ToString();
            }

            return code; 
        }
        finally { 
            RemovePendingCall(); 
        }
    } 

    [PermissionSet(SecurityAction.Assert, Unrestricted = true)]
    internal CodeCompileUnit GenerateCodeCompileUnit(
        VirtualPath virtualPath, string virtualFileString, out Type codeDomProviderType, 
        out CompilerParameters compilerParameters, out IDictionary linePragmasTable) {
 
        // Add a pending call to make sure our thread doesn't get killed 
        AddPendingCall();
 
        try {
            BuildManager.SkipTopLevelCompilationExceptions = true;
            _buildManager.EnsureTopLevelFilesCompiled();
 
            // Get the virtual file content so that we can use the correct hash code.
            if (virtualFileString == null) { 
                using (Stream stream = virtualPath.OpenFile()) { 
                    TextReader reader = Util.ReaderFromStream(stream, virtualPath);
                    virtualFileString = reader.ReadToEnd(); 
                }
            }

            _virtualPathProvider.RegisterVirtualFile(virtualPath, virtualFileString); 

            string cacheKey = BuildManager.GetCacheKeyFromVirtualPath(virtualPath) + "_CBMResult"; 
            BuildResultCodeCompileUnit result = (BuildResultCodeCompileUnit)BuildManager.GetBuildResultFromCache(cacheKey, virtualPath); 

            if (result == null) { 
                lock (_lock) {
                    // Don't need to check the result again since it's very unlikely in CBM scenarios.
                    DateTime utcStart = DateTime.UtcNow;
 
                    BuildProvider internalBuildProvider = GetCompilerParamsAndBuildProvider(
                        virtualPath, out codeDomProviderType, out compilerParameters); 
 
                    // This is the no-compile case
                    if (internalBuildProvider == null) { 
                        linePragmasTable = null;
                        return null;
                    }
 
                    CodeCompileUnit ccu = internalBuildProvider.GetCodeCompileUnit(out linePragmasTable);
 
                    result = new BuildResultCodeCompileUnit(codeDomProviderType, ccu, compilerParameters, linePragmasTable); 
                    result.VirtualPath = virtualPath;
                    result.SetCacheKey(cacheKey); 

                    FixupReferencedAssemblies(virtualPath, compilerParameters);

                    // CodeCompileUnit could be null, do not try to fix referenced assemblies. 
                    // This happens for example when an .asmx file does not contain any code.
                    if (ccu != null) { 
                        // VSWhidbey 501260 Add all the referenced assemblies to the CodeCompileUnit 
                        // in case the CodeDom provider needs them for code generation
                        foreach (String assemblyString in compilerParameters.ReferencedAssemblies) { 
                            ccu.ReferencedAssemblies.Add(assemblyString);
                        }
                    }
 
                    // Add all the dependencies, so that the ccu gets cached correctly (VSWhidbey 275091)
                    ICollection dependencies = internalBuildProvider.VirtualPathDependencies; 
                    if (dependencies != null) 
                        result.AddVirtualPathDependencies(dependencies);
 
                    BuildManager.CacheBuildResult(cacheKey, result, utcStart);

                    return ccu;
                } 
            }
 
            codeDomProviderType = result.CodeDomProviderType; 
            compilerParameters = result.CompilerParameters;
            linePragmasTable = result.LinePragmasTable; 

            FixupReferencedAssemblies(virtualPath, compilerParameters);

            return result.CodeCompileUnit; 
        }
        finally { 
            if (virtualFileString != null) { 
                _virtualPathProvider.RevertVirtualFile(virtualPath);
            } 
            BuildManager.SkipTopLevelCompilationExceptions = false;

            RemovePendingCall();
        } 
    }
 
    internal bool IsCodeAssembly(string assemblyName) { 
        return BuildManager.GetNormalizedCodeAssemblyName(assemblyName) != null;
    } 

    // Add the referenced assemblies into the compileParameters. Notice that buildProviders do not have
    // the correct referenced assemblies and we don't cache them since the assemblies could change
    // between appdomains. (removing assemblies from bin, etc) 
    private void FixupReferencedAssemblies(VirtualPath virtualPath, CompilerParameters compilerParameters) {
        CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath); 
 
        ICollection referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig);
        Util.AddAssembliesToStringCollection(referencedAssemblies, compilerParameters.ReferencedAssemblies); 
    }

    private BuildProvider GetCompilerParamsAndBuildProvider(VirtualPath virtualPath,
        out Type codeDomProviderType, out CompilerParameters compilerParameters) { 

        virtualPath.CombineWithAppRoot(); 
 
        CompilationSection compConfig = MTConfigUtil.GetCompilationConfig(virtualPath);
 
        ICollection referencedAssemblies = BuildManager.GetReferencedAssemblies(compConfig);

        // Create the buildprovider for the passed in virtualPath
        BuildProvider buildProvider = null; 

        // Special case global asax build provider here since we do not want to compile every files with ".asax" extension. 
        if (StringUtil.EqualsIgnoreCase(virtualPath.VirtualPathString, BuildManager.GlobalAsaxVirtualPath.VirtualPathString)) { 
            ApplicationBuildProvider provider = new ApplicationBuildProvider();
            provider.SetVirtualPath(virtualPath); 
            provider.SetReferencedAssemblies(referencedAssemblies);
            buildProvider = provider;
        }
        else { 
            buildProvider = BuildManager.CreateBuildProvider(virtualPath, compConfig,
            referencedAssemblies, true /*failIfUnknown*/); 
        } 

        // DevDiv 69017 
        // The methods restricted to internalBuildProvider have been moved up to BuildProvider
        // to allow WCFBuildProvider to support .svc syntax highlighting.

        // Ignore parse errors, since they should not break the designer 
        buildProvider.IgnoreParseErrors = true;
 
        // Ignore all control properties, since we do not generate code for the properties 
        buildProvider.IgnoreControlProperties = true;
 
        // Process as many errors as possible, do not rethrow on first error
        buildProvider.ThrowOnFirstParseError = false;

        // Get the language (causes the file to be parsed) 
        CompilerType compilerType = buildProvider.CodeCompilerType;
 
        // compilerType could be null in the no-compile case (VSWhidbey 221749) 
        if (compilerType == null) {
            codeDomProviderType = null; 
            compilerParameters = null;
            return null;
        }
 
        // Return the provider type and compiler params
        codeDomProviderType = compilerType.CodeDomProviderType; 
        compilerParameters = compilerType.CompilerParameters; 

        IAssemblyDependencyParser parser = buildProvider.AssemblyDependencyParser; 

        // Add all the assemblies that the page depends on (e.g. user controls)
        if (parser != null && parser.AssemblyDependencies != null) {
            Util.AddAssembliesToStringCollection(parser.AssemblyDependencies, 
                compilerParameters.ReferencedAssemblies);
        } 
 
        // Make any fix up adjustments to the CompilerParameters to work around some issues
        AssemblyBuilder.FixUpCompilerParameters(codeDomProviderType, compilerParameters); 

        return buildProvider;
    }
 
    public override Object InitializeLifetimeService() {
        return null; // never expire lease 
    } 

    internal void Configure(ClientBuildManager client) { 

        // Add a pending call to make sure our thread doesn't get killed
        AddPendingCall();
 
        try {
            _virtualPathProvider = new ClientVirtualPathProvider(); 
            HostingEnvironment.RegisterVirtualPathProviderInternal(_virtualPathProvider); 

            _client = client; 

            // Type description provider required for multitargeting compilation support in VS.
            if (_client.CBMTypeDescriptionProviderBridge != null) {
                TargetFrameworkUtil.CBMTypeDescriptionProviderBridge = _client.CBMTypeDescriptionProviderBridge; 
            }
 
            // start watching for app domain unloading 
            _onAppDomainUnload = new EventHandler(OnAppDomainUnload);
            Thread.GetDomain().DomainUnload += _onAppDomainUnload; 

            _buildManager = BuildManager.TheBuildManager;

            // Listen to appdomain shutdown. 
            HttpRuntime.AppDomainShutdown += new BuildManagerHostUnloadEventHandler(this.OnAppDomainShutdown);
        } 
        finally { 
            RemovePendingCall();
        } 
    }

    internal Exception InitializationException {
        get { 
            return HostingEnvironment.InitializationException;
        } 
    } 

    private void OnAppDomainUnload(Object unusedObject, EventArgs unusedEventArgs) { 
        Thread.GetDomain().DomainUnload -= _onAppDomainUnload;

        if (_client != null) {
            _client.OnAppDomainUnloaded(HttpRuntime.ShutdownReason); 
            _client = null;
        } 
    } 

    internal bool UnloadAppDomain() { 
        _ignorePendingCalls = true;

        // Make sure HttpRuntime does not ignore the appdomain shutdown.
        HttpRuntime.SetUserForcedShutdown(); 

        // Force unload the appdomain when called from client 
        return HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.UnloadAppDomainCalled, "CBM called UnloadAppDomain"); 
    }
 
    // This provider is created in the hosted appdomain for faster access of the virtual file content.
    // Note this is used both in CBM and the aspnet precompilation tool
    internal class ClientVirtualPathProvider : VirtualPathProvider {
        private IDictionary _stringDictionary; 

        internal ClientVirtualPathProvider() { 
            _stringDictionary = new HybridDictionary(true); 
        }
 
        public override bool FileExists(string virtualPath) {
            if (_stringDictionary.Contains(virtualPath)) {
                return true;
            } 

            return base.FileExists(virtualPath); 
        } 

        public override CacheDependency GetCacheDependency(string virtualPath, 
            IEnumerable virtualPathDependencies, DateTime utcStart) {

            if (virtualPath != null) {
                virtualPath = UrlPath.MakeVirtualPathAppAbsolute(virtualPath); 
                // Return now so the build result will be invalidated based on hashcode.
                // This is for the case that Venus passed in the file content so we don't 
                // get file change notification 
                if (_stringDictionary.Contains(virtualPath)) {
                    return null; 
                }
            }

            // otherwise creates a cachedependency using MapPathBasedVirtualPathProvider 
            return base.GetCacheDependency(virtualPath, virtualPathDependencies, utcStart);
        } 
 
        public override VirtualFile GetFile(string virtualPath) {
            String _virtualFileString = (String)_stringDictionary[virtualPath]; 

            return _virtualFileString != null? new ClientVirtualFile(virtualPath, _virtualFileString) : base.GetFile(virtualPath);
        }
 
        public override string GetFileHash(string virtualPath, IEnumerable virtualPathDependencies) {
            HashCodeCombiner hashCodeCombiner = null; 
 
            ArrayList clonedPaths = new ArrayList();
            foreach (string virtualDependency in virtualPathDependencies) { 
                // if the virtual path is previously cached, add the actual content to
                // the hash code combiner.
                if (_stringDictionary.Contains(virtualDependency)) {
                    if (hashCodeCombiner == null) { 
                        hashCodeCombiner = new HashCodeCombiner();
                    } 
 
                    hashCodeCombiner.AddInt(_stringDictionary[virtualDependency].GetHashCode());
                    continue; 
                }

                // Otherwise move it to the cloned collection and use the base class (previous provider)
                // to get the hash code. 
                clonedPaths.Add(virtualDependency);
            } 
 
            if (hashCodeCombiner == null) {
                return base.GetFileHash(virtualPath, virtualPathDependencies); 
            }
            hashCodeCombiner.AddObject(base.GetFileHash(virtualPath, clonedPaths));

            return hashCodeCombiner.CombinedHashString; 
        }
 
        internal void RegisterVirtualFile(VirtualPath virtualPath, String virtualFileString) { 
            _stringDictionary[virtualPath.VirtualPathString] = virtualFileString;
        } 

        internal void RevertVirtualFile(VirtualPath virtualPath) {
            _stringDictionary.Remove(virtualPath.VirtualPathString);
        } 

        internal class ClientVirtualFile : VirtualFile { 
            String _virtualFileString; 

            internal ClientVirtualFile(string virtualPath, String virtualFileString) : base(virtualPath) { 
                _virtualFileString = virtualFileString;
            }

            public override Stream Open() { 
                Stream stream = new MemoryStream();
                StreamWriter writer = new StreamWriter(stream, Encoding.Unicode); 
 
                writer.Write(_virtualFileString);
                writer.Flush(); 
                stream.Seek(0, SeekOrigin.Begin);

                return stream;
            } 
        }
    } 
} 
}

// 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