File: Hosting\ISAPIRuntime.cs
Project: ndp\fx\src\xsp\system\Web\System.Web.csproj (System.Web)
//------------------------------------------------------------------------------
// <copyright file="ISAPIRuntime.cs" company="Microsoft">
//     Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>                                                                
//------------------------------------------------------------------------------
 
/*
 * The ASP.NET runtime services
 * 
 * Copyright (c) 1998 Microsoft Corporation
 */
 
namespace System.Web.Hosting {
    using System.Runtime.InteropServices;       
    using System.Collections;
    using System.Reflection;
    using System.Threading;
    using System.Web;
    using System.Web.Management;
    using System.Web.Util;
    using System.Globalization;
    using System.Security.Permissions;
    
 
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    /// <internalonly/>
    [ComImport, Guid("08a2c56f-7c16-41c1-a8be-432917a1a2d1"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IISAPIRuntime {
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
        void StartProcessing();
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
        void StopProcessing();
 
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
        [return: MarshalAs(UnmanagedType.I4)]
        int ProcessRequest(
                          [In]
                          IntPtr ecb, 
                          [In, MarshalAs(UnmanagedType.I4)]
                          int useProcessModel);
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        [SecurityPermission(SecurityAction.InheritanceDemand, Unrestricted = true)]
        void DoGCCollect();
    }
 
    // DevDiv #195200: Adding link demands to IISAPIRuntime causes partial trust to stop
    // working. Ideally that type should have been internal, but we can't do that since
    // that would be a breaking API change. So instead we introduce a parallel internal
    // type with a different COM GUID. Existing public methods on ISAPIRuntime will be
    // protected by a link demand, but our native layer will go through this specific
    // interface which can bypass the link demands.
    [ComImport, Guid("15eb8d20-d4ed-4855-a276-91a75a696955"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
    internal interface IISAPIRuntime2 {
 
        void StartProcessing();
 
        void StopProcessing();
 
        [return: MarshalAs(UnmanagedType.I4)]
        int ProcessRequest([In]IntPtr ecb, [In, MarshalAs(UnmanagedType.I4)]int useProcessModel);
 
        void DoGCCollect();
 
    }
 
    /// <devdoc>
    ///    <para>[To be supplied.]</para>
    /// </devdoc>
    /// <internalonly/>
    public sealed class ISAPIRuntime : MarshalByRefObject, IISAPIRuntime, IISAPIRuntime2, IRegisteredObject {
 
        // WARNING: do not modify without making corresponding changes in appdomains.h
        private const int WORKER_REQUEST_TYPE_IN_PROC            = 0x0;
        private const int WORKER_REQUEST_TYPE_OOP                = 0x1;
        private const int WORKER_REQUEST_TYPE_IN_PROC_VERSION_2  = 0x2;
 
        // to control removal from unmanaged table (to it only once)
        private static int _isThisAppDomainRemovedFromUnmanagedTable;
 
        [SecurityPermission(SecurityAction.Demand, Unrestricted = true)]
        public ISAPIRuntime() {
            HostingEnvironment.RegisterObject(this);
        }
 
 
        public override Object InitializeLifetimeService() {
            return null; // never expire lease
        }
 
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        public void StartProcessing() {
            Debug.Trace("ISAPIRuntime", "StartProcessing");
        }
 
        void IISAPIRuntime2.StartProcessing() {
            StartProcessing();
        }
 
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        public void StopProcessing() {
            Debug.Trace("ISAPIRuntime", "StopProcessing");
            HostingEnvironment.UnregisterObject(this);
        }
 
        void IISAPIRuntime2.StopProcessing() {
            StopProcessing();
        }
 
        /*
         * Process one ISAPI request
         *
         * @param ecb ECB
         * @param useProcessModel flag set to true when out-of-process
         */
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        public int ProcessRequest(IntPtr ecb, int iWRType) {
            IntPtr pHttpCompletion = IntPtr.Zero;
            if (iWRType == WORKER_REQUEST_TYPE_IN_PROC_VERSION_2) {
                pHttpCompletion = ecb;
                ecb = UnsafeNativeMethods.GetEcb(pHttpCompletion);
            } 
            ISAPIWorkerRequest wr = null;
            try {
                bool useOOP = (iWRType == WORKER_REQUEST_TYPE_OOP);
                wr = ISAPIWorkerRequest.CreateWorkerRequest(ecb, useOOP);
                wr.Initialize();
 
                // check if app path matches (need to restart app domain?)                
                String wrPath = wr.GetAppPathTranslated();
                String adPath = HttpRuntime.AppDomainAppPathInternal;                
                
                if (adPath == null ||
                    StringUtil.EqualsIgnoreCase(wrPath, adPath)) {
                    
                    HttpRuntime.ProcessRequestNoDemand(wr);
                    return 0;
                }
                else {
                    // need to restart app domain
                    HttpRuntime.ShutdownAppDomain(ApplicationShutdownReason.PhysicalApplicationPathChanged,
                                                  SR.GetString(SR.Hosting_Phys_Path_Changed,
                                                                                   adPath,
                                                                                   wrPath));
                    return 1;
                }
            }
            catch(Exception e) {
                try {
                    WebBaseEvent.RaiseRuntimeError(e, this);
                } catch {}
                
                // Have we called HSE_REQ_DONE_WITH_SESSION?  If so, don't re-throw.
                if (wr != null && wr.Ecb == IntPtr.Zero) {
                    if (pHttpCompletion != IntPtr.Zero) {
                        UnsafeNativeMethods.SetDoneWithSessionCalled(pHttpCompletion);
                    }
                    // if this is a thread abort exception, cancel the abort
                    if (e is ThreadAbortException) {
                        Thread.ResetAbort();
                    }                    
                    // IMPORTANT: if this thread is being aborted because of an AppDomain.Unload,
                    // the CLR will still throw an AppDomainUnloadedException. The native caller
                    // must special case COR_E_APPDOMAINUNLOADED(0x80131014) and not
                    // call HSE_REQ_DONE_WITH_SESSION more than once.
                    return 0;
                }
                
                // re-throw if we have not called HSE_REQ_DONE_WITH_SESSION
                throw;
            }
        }
 
        int IISAPIRuntime2.ProcessRequest(IntPtr ecb, int iWRType) {
            return ProcessRequest(ecb, iWRType);
        }
 
        /// <devdoc>
        ///    <para>[To be supplied.]</para>
        /// </devdoc>
        [SecurityPermission(SecurityAction.LinkDemand, Unrestricted = true)] // DevDiv #180492
        public void DoGCCollect() {
            for (int c = 10; c > 0; c--) {
                System.GC.Collect();
            }
        }
 
        void IISAPIRuntime2.DoGCCollect() {
            DoGCCollect();
        }
 
 
        /// <internalonly/>
        void IRegisteredObject.Stop(bool immediate) {
            RemoveThisAppDomainFromUnmanagedTable();
            HostingEnvironment.UnregisterObject(this);
        }
 
 
        internal static void RemoveThisAppDomainFromUnmanagedTable() {
            if (Interlocked.Exchange(ref _isThisAppDomainRemovedFromUnmanagedTable, 1) != 0) {
                return;
            }
 
            try {
                String appId = HttpRuntime.AppDomainAppId;
                if (appId != null ) {
                    Debug.Trace("ISAPIRuntime", "Calling UnsafeNativeMethods.AppDomainRestart appId=" + appId);
 
                    UnsafeNativeMethods.AppDomainRestart(appId);
                }
 
                HttpRuntime.AddAppDomainTraceMessage(SR.GetString(SR.App_Domain_Restart));
            }
            catch {
            }
        }
    }
}