BuildManagerHost.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ FX-1434 / FX-1434 / 1.0 / untmp / whidbey / REDBITS / ndp / fx / src / xsp / System / Web / Compilation / BuildManagerHost.cs / 3 / 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;

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

    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.ApplicationHost;
        }
    } 

    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;
    } 
 
    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(); 
        }
    }

    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(); 

        ArrayList assemblyList = new ArrayList();
        try {
            // Treat it as relative to the app root 
            virtualPath.CombineWithAppRoot();
 
            CompilationSection compConfig = RuntimeConfig.GetConfig(virtualPath).Compilation; 

            // 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(Util.GetAssemblyCodeBase(assemblies[i]));
                    } 
                } 
            }
        } finally { 
            RemovePendingCall();
        }

        return (String[])assemblyList.ToArray(typeof(String)); 
    }
 
    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();
        } 
    }

    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 = RuntimeConfig.GetConfig(virtualPath).Compilation; 
 
        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 = RuntimeConfig.GetConfig(virtualPath).Compilation;
 
        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; 

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

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