e79aa3c0ed
Former-commit-id: a2155e9bd80020e49e72e86c44da02a8ac0e57a4
1377 lines
56 KiB
C#
1377 lines
56 KiB
C#
//------------------------------------------------------------------------------
|
|
// <copyright file="ProcessHost.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//------------------------------------------------------------------------------
|
|
|
|
namespace System.Web.Hosting {
|
|
using System;
|
|
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.Configuration;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.IO;
|
|
using System.Runtime.ExceptionServices;
|
|
using System.Runtime.InteropServices;
|
|
using System.Runtime.Remoting;
|
|
using System.Security;
|
|
using System.Security.Permissions;
|
|
using System.Threading;
|
|
using System.Web;
|
|
using System.Web.Configuration;
|
|
using System.Web.Util;
|
|
|
|
|
|
[ComImport, Guid("0ccd465e-3114-4ca3-ad50-cea561307e93"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IProcessHost {
|
|
|
|
void StartApplication(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)]
|
|
String appId,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)]
|
|
String appPath,
|
|
[MarshalAs(UnmanagedType.Interface)] out Object runtimeInterface);
|
|
|
|
void ShutdownApplication([In, MarshalAs(UnmanagedType.LPWStr)] String appId);
|
|
|
|
void Shutdown();
|
|
|
|
void EnumerateAppDomains( [MarshalAs(UnmanagedType.Interface)] out IAppDomainInfoEnum appDomainInfoEnum);
|
|
|
|
}
|
|
|
|
// Used by webengine4.dll for launching Helios applications via ProcessHost.
|
|
|
|
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("E2A1F244-70EB-483A-ACC8-DE6ACE5BF8B1")]
|
|
internal interface IProcessHostLite {
|
|
[return: MarshalAs(UnmanagedType.Interface)]
|
|
IObjectHandle GetCustomLoader(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string appId,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string appConfigPath,
|
|
[Out, MarshalAs(UnmanagedType.Interface)] out IProcessHostSupportFunctions supportFunctions,
|
|
[Out, MarshalAs(UnmanagedType.Interface)] out AppDomain newlyCreatedAppDomain);
|
|
|
|
void ReportCustomLoaderError(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string appId,
|
|
[In] int hr,
|
|
[In, MarshalAs(UnmanagedType.Interface)] AppDomain newlyCreatedAppDomain);
|
|
|
|
[return: MarshalAs(UnmanagedType.BStr)]
|
|
string GetFullExceptionMessage(
|
|
[In] int hr,
|
|
[In] IntPtr pErrorInfo);
|
|
}
|
|
|
|
//
|
|
// App domain protocol manager
|
|
// Note that this doesn't provide COM interop
|
|
//
|
|
|
|
public interface IAdphManager {
|
|
|
|
void StartAppDomainProtocolListenerChannel(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String appId,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String protocolId,
|
|
IListenerChannelCallback listenerChannelCallback);
|
|
|
|
void StopAppDomainProtocolListenerChannel(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String appId,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String protocolId,
|
|
int listenerChannelId,
|
|
bool immediate);
|
|
|
|
void StopAppDomainProtocol(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String appId,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String protocolId,
|
|
bool immediate);
|
|
}
|
|
|
|
[ComImport, Guid("1cc9099d-0a8d-41cb-87d6-845e4f8c4e91"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IPphManager {
|
|
|
|
void StartProcessProtocolListenerChannel(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String protocolId,
|
|
IListenerChannelCallback listenerChannelCallback);
|
|
|
|
void StopProcessProtocolListenerChannel(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String protocolId,
|
|
int listenerChannelId,
|
|
bool immediate);
|
|
|
|
void StopProcessProtocol(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] String protocolId,
|
|
bool immediate);
|
|
}
|
|
|
|
|
|
[ComImport, Guid("9d98b251-453e-44f6-9cec-8b5aed970129"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IProcessHostIdleAndHealthCheck {
|
|
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
bool IsIdle();
|
|
|
|
void Ping(IProcessPingCallback callback);
|
|
}
|
|
|
|
|
|
[ComImport, Guid("5BC9C234-6CD7-49bf-A07A-6FDB7F22DFFF"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IAppDomainInfo {
|
|
[return: MarshalAs(UnmanagedType.BStr)]
|
|
string GetId();
|
|
|
|
[return: MarshalAs(UnmanagedType.BStr)]
|
|
string GetVirtualPath();
|
|
|
|
[return: MarshalAs(UnmanagedType.BStr)]
|
|
string GetPhysicalPath();
|
|
|
|
[return: MarshalAs(UnmanagedType.I4)]
|
|
int GetSiteId();
|
|
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
bool IsIdle();
|
|
}
|
|
|
|
[ComImport, Guid("F79648FB-558B-4a09-88F1-1E3BCB30E34F"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IAppDomainInfoEnum {
|
|
[return: MarshalAs(UnmanagedType.Interface)]
|
|
IAppDomainInfo GetData();
|
|
|
|
[return: MarshalAs(UnmanagedType.I4)]
|
|
int Count();
|
|
|
|
[return: MarshalAs(UnmanagedType.Bool)]
|
|
bool MoveNext();
|
|
|
|
void Reset();
|
|
}
|
|
|
|
public class AppDomainInfoEnum : IAppDomainInfoEnum
|
|
{
|
|
private AppDomainInfo[] _appDomainInfos;
|
|
private int _curPos;
|
|
|
|
internal AppDomainInfoEnum(AppDomainInfo[] appDomainInfos)
|
|
{
|
|
_appDomainInfos = appDomainInfos;
|
|
_curPos = -1;
|
|
}
|
|
|
|
public int Count()
|
|
{
|
|
return _appDomainInfos.Length;
|
|
}
|
|
|
|
public IAppDomainInfo GetData()
|
|
{
|
|
return _appDomainInfos[_curPos];
|
|
}
|
|
|
|
public bool MoveNext()
|
|
{
|
|
_curPos++;
|
|
|
|
if (_curPos >= _appDomainInfos.Length)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public void Reset()
|
|
{
|
|
_curPos = -1;
|
|
}
|
|
}
|
|
|
|
public class AppDomainInfo : IAppDomainInfo
|
|
{
|
|
private string _id;
|
|
private string _virtualPath;
|
|
private string _physicalPath;
|
|
private int _siteId;
|
|
private bool _isIdle;
|
|
|
|
internal AppDomainInfo(string id, string vpath, string physPath, int siteId, bool isIdle)
|
|
{
|
|
_id = id;
|
|
_virtualPath = vpath;
|
|
_physicalPath = physPath;
|
|
_siteId = siteId;
|
|
_isIdle = isIdle;
|
|
}
|
|
|
|
public string GetId()
|
|
{
|
|
return _id;
|
|
}
|
|
|
|
public string GetVirtualPath()
|
|
{
|
|
return _virtualPath;
|
|
}
|
|
|
|
public string GetPhysicalPath()
|
|
{
|
|
return _physicalPath;
|
|
}
|
|
|
|
public int GetSiteId()
|
|
{
|
|
return _siteId;
|
|
}
|
|
|
|
public bool IsIdle()
|
|
{
|
|
return _isIdle;
|
|
}
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
// New for Dev10
|
|
[ComImport, Guid("AE54F424-71BC-4da5-AA2F-8C0CD53496FC"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IApplicationPreloadManager {
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Util",
|
|
Justification="Name must match IIS COM interface.")]
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="0#Util",
|
|
Justification="Name must match IIS COM interface.")]
|
|
void SetApplicationPreloadUtil(
|
|
[In, MarshalAs(UnmanagedType.Interface)] IApplicationPreloadUtil preloadUtil);
|
|
|
|
void SetApplicationPreloadState(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string context,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string appId,
|
|
[In, MarshalAs(UnmanagedType.Bool)] bool enabled);
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId="Util",
|
|
Justification="Name must match IIS COM interface.")]
|
|
[ComImport, Guid("940D8ADD-9E40-4475-9A67-2CDCDF57995C"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)]
|
|
public interface IApplicationPreloadUtil {
|
|
|
|
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId="1#",
|
|
Justification="Parameter kind must match IIS COM interface.")]
|
|
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId="2#",
|
|
Justification="Parameter kind must match IIS COM interface.")]
|
|
[SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId="3#",
|
|
Justification="Parameter kind must match IIS COM interface.")]
|
|
void GetApplicationPreloadInfo(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string context,
|
|
[Out, MarshalAs(UnmanagedType.Bool)] out bool enabled,
|
|
[Out, MarshalAs(UnmanagedType.BStr)] out string startupObjType,
|
|
[Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)] out string[] parametersForStartupObj);
|
|
|
|
void ReportApplicationPreloadFailure(
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string context,
|
|
[In, MarshalAs(UnmanagedType.U4)] int errorCode,
|
|
[In, MarshalAs(UnmanagedType.LPWStr)] string errorMessage);
|
|
}
|
|
|
|
|
|
/// <include file='doc\ProcessHost.uex' path='docs/doc[@for="ProcessHost"]/*' />
|
|
public sealed class ProcessHost : MarshalByRefObject,
|
|
IProcessHost,
|
|
IProcessHostLite,
|
|
ICustomRuntimeManager,
|
|
IAdphManager, // process protocol handlers manager
|
|
IPphManager, // appdomain protocol handlers manager
|
|
IProcessHostIdleAndHealthCheck,
|
|
IProcessSuspendListener,
|
|
IApplicationPreloadManager {
|
|
private static Object _processHostStaticLock = new Object();
|
|
private static ProcessHost _theProcessHost;
|
|
|
|
[ThreadStatic]
|
|
private static KeyValuePair<string, ExceptionDispatchInfo> _customLoaderStartupError;
|
|
|
|
private readonly CustomRuntimeManager _customRuntimeManager = new CustomRuntimeManager();
|
|
|
|
private IProcessHostSupportFunctions _functions;
|
|
private ApplicationManager _appManager;
|
|
private ProtocolsSection _protocolsConfig;
|
|
|
|
// process protocol handlers by prot id
|
|
private Hashtable _protocolHandlers = new Hashtable();
|
|
private IApplicationPreloadUtil _preloadUtil = null;
|
|
|
|
private System.Threading.Semaphore _preloadingThrottle = null;
|
|
|
|
private ProtocolsSection ProtocolsConfig {
|
|
get {
|
|
if (_protocolsConfig == null) {
|
|
lock (this) {
|
|
if (_protocolsConfig == null) {
|
|
|
|
if (HttpConfigurationSystem.IsSet) {
|
|
_protocolsConfig = RuntimeConfig.GetRootWebConfig().Protocols;
|
|
} else {
|
|
Configuration c = WebConfigurationManager.OpenWebConfiguration(null);
|
|
_protocolsConfig = (ProtocolsSection) c.GetSection("system.web/protocols");
|
|
}
|
|
|
|
}
|
|
}
|
|
}
|
|
return _protocolsConfig;
|
|
}
|
|
}
|
|
|
|
// ctor only called via GetProcessHost
|
|
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "Reading this particular registry value is safe.")]
|
|
private ProcessHost(IProcessHostSupportFunctions functions) {
|
|
try {
|
|
// remember support functions
|
|
_functions = functions;
|
|
|
|
// pass them along to the HostingEnvironment in the default domain
|
|
HostingEnvironment.SupportFunctions = functions;
|
|
|
|
// create singleton app manager
|
|
_appManager = ApplicationManager.GetApplicationManager();
|
|
|
|
// For M3 we get the throttling limit from the registry.
|
|
// Dev10\Beta1 work item 543420 is to investigate whether we need to get rid of the throttling
|
|
int maxPreloadConcurrency = (int)Misc.GetAspNetRegValue(null, "MaxPreloadConcurrency", 0);
|
|
if (maxPreloadConcurrency > 0) {
|
|
_preloadingThrottle = new System.Threading.Semaphore(maxPreloadConcurrency, maxPreloadConcurrency);
|
|
}
|
|
|
|
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[]
|
|
{ SR.GetString(SR.Cant_Create_Process_Host)});
|
|
Debug.Trace("internal", "ProcessHost::ctor failed with " + e.GetType().FullName + ": " + e.Message + "\r\n" + e.StackTrace);
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// ValidateType
|
|
//
|
|
// Validate and Get the Type that is sent in
|
|
//
|
|
// Note: Because ProtocolElement is outside of our assembly we need to do
|
|
// that here, and because of that we need to hardcode the property
|
|
// names!!
|
|
//
|
|
private Type ValidateAndGetType( ProtocolElement element,
|
|
string typeName,
|
|
Type assignableType,
|
|
string elementPropertyName ) {
|
|
Type handlerType;
|
|
|
|
try {
|
|
handlerType = Type.GetType(typeName, true /*throwOnError*/);
|
|
}
|
|
catch (Exception e) {
|
|
|
|
PropertyInformation propInfo = null;
|
|
string source = String.Empty;
|
|
int lineNum = 0;
|
|
|
|
if (element != null && null != element.ElementInformation) {
|
|
propInfo = element.ElementInformation.Properties[elementPropertyName];
|
|
|
|
if (null != propInfo) {
|
|
source = propInfo.Source;
|
|
lineNum = propInfo.LineNumber;
|
|
}
|
|
|
|
}
|
|
|
|
throw new ConfigurationErrorsException(
|
|
e.Message,
|
|
e,
|
|
source,
|
|
lineNum);
|
|
}
|
|
|
|
ConfigUtil.CheckAssignableType( assignableType, handlerType, element, elementPropertyName);
|
|
|
|
return handlerType;
|
|
}
|
|
|
|
private Type GetAppDomainProtocolHandlerType(String protocolId) {
|
|
Type t = null;
|
|
|
|
try {
|
|
// get app domaoin protocol handler type from config
|
|
ProtocolElement configEntry = ProtocolsConfig.Protocols[protocolId];
|
|
if (configEntry == null)
|
|
throw new ArgumentException(SR.GetString(SR.Unknown_protocol_id, protocolId));
|
|
|
|
t = ValidateAndGetType( configEntry,
|
|
configEntry.AppDomainHandlerType,
|
|
typeof(AppDomainProtocolHandler),
|
|
"AppDomainHandlerType" );
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Invalid_AppDomain_Prot_Type)} );
|
|
}
|
|
}
|
|
|
|
return t;
|
|
}
|
|
|
|
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags=SecurityPermissionFlag.Infrastructure)]
|
|
public override Object InitializeLifetimeService() {
|
|
return null; // never expire lease
|
|
}
|
|
|
|
// called from ProcessHostFactoryHelper to get ProcessHost
|
|
internal static ProcessHost GetProcessHost(IProcessHostSupportFunctions functions) {
|
|
if (_theProcessHost == null) {
|
|
lock (_processHostStaticLock) {
|
|
if (_theProcessHost == null) {
|
|
_theProcessHost = new ProcessHost(functions);
|
|
}
|
|
}
|
|
}
|
|
|
|
return _theProcessHost;
|
|
}
|
|
|
|
internal static ProcessHost DefaultHost {
|
|
get {
|
|
return _theProcessHost; // may be null
|
|
}
|
|
}
|
|
|
|
internal IProcessHostSupportFunctions SupportFunctions {
|
|
get {
|
|
return _functions;
|
|
}
|
|
}
|
|
|
|
//
|
|
// IProcessHostProcessProtocolManager interface implementation
|
|
//
|
|
|
|
// starts process protocol handler on demand
|
|
public void StartProcessProtocolListenerChannel(String protocolId, IListenerChannelCallback listenerChannelCallback) {
|
|
try {
|
|
if (protocolId == null)
|
|
throw new ArgumentNullException("protocolId");
|
|
|
|
// validate protocol id
|
|
ProtocolElement configEntry = ProtocolsConfig.Protocols[protocolId];
|
|
if (configEntry == null)
|
|
throw new ArgumentException(SR.GetString(SR.Unknown_protocol_id, protocolId));
|
|
|
|
ProcessProtocolHandler protocolHandler = null;
|
|
Type protocolHandlerType = null;
|
|
|
|
protocolHandlerType = ValidateAndGetType( configEntry,
|
|
configEntry.ProcessHandlerType,
|
|
typeof(ProcessProtocolHandler),
|
|
"ProcessHandlerType" );
|
|
|
|
lock (this) {
|
|
// lookup or create protocol handler
|
|
protocolHandler = _protocolHandlers[protocolId] as ProcessProtocolHandler;
|
|
|
|
if (protocolHandler == null) {
|
|
protocolHandler = (ProcessProtocolHandler)Activator.CreateInstance(protocolHandlerType);
|
|
_protocolHandlers[protocolId] = protocolHandler;
|
|
}
|
|
|
|
}
|
|
|
|
// call the handler to start listenerChannel
|
|
if (protocolHandler != null) {
|
|
protocolHandler.StartListenerChannel(listenerChannelCallback, this);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Invalid_Process_Prot_Type)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public void StopProcessProtocolListenerChannel(String protocolId, int listenerChannelId, bool immediate) {
|
|
try {
|
|
if (protocolId == null)
|
|
throw new ArgumentNullException("protocolId");
|
|
|
|
ProcessProtocolHandler protocolHandler = null;
|
|
|
|
lock (this) {
|
|
// lookup protocol handler
|
|
protocolHandler = _protocolHandlers[protocolId] as ProcessProtocolHandler;
|
|
}
|
|
|
|
// call the handler to stop listenerChannel
|
|
if (protocolHandler != null) {
|
|
protocolHandler.StopListenerChannel(listenerChannelId, immediate);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Stop_Listener_Channel)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
public void StopProcessProtocol(String protocolId, bool immediate) {
|
|
try {
|
|
if (protocolId == null)
|
|
throw new ArgumentNullException("protocolId");
|
|
|
|
ProcessProtocolHandler protocolHandler = null;
|
|
|
|
lock (this) {
|
|
// lookup and remove protocol handler
|
|
protocolHandler = _protocolHandlers[protocolId] as ProcessProtocolHandler;
|
|
|
|
if (protocolHandler != null) {
|
|
_protocolHandlers.Remove(protocolId);
|
|
}
|
|
}
|
|
|
|
if (protocolHandler != null) {
|
|
protocolHandler.StopProtocol(immediate);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Stop_Process_Prot)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
//
|
|
// IAppDomainProtocolManager
|
|
//
|
|
|
|
// starts app domain protocol handler on demand (called by process protocol handler
|
|
|
|
public void StartAppDomainProtocolListenerChannel(String appId, String protocolId, IListenerChannelCallback listenerChannelCallback) {
|
|
try {
|
|
if (appId == null)
|
|
throw new ArgumentNullException("appId");
|
|
if (protocolId == null)
|
|
throw new ArgumentNullException("protocolId");
|
|
|
|
ISAPIApplicationHost appHost = CreateAppHost(appId, null);
|
|
|
|
// get app domain protocol handler type from config
|
|
Type handlerType = GetAppDomainProtocolHandlerType(protocolId);
|
|
|
|
AppDomainProtocolHandler handler = null;
|
|
|
|
LockableAppDomainContext ac = _appManager.GetLockableAppDomainContext(appId);
|
|
|
|
lock (ac) {
|
|
HostingEnvironmentParameters hostingParameters = new HostingEnvironmentParameters();
|
|
hostingParameters.HostingFlags = HostingEnvironmentFlags.ThrowHostingInitErrors;
|
|
|
|
PreloadApplicationIfRequired(appId, appHost, hostingParameters, ac);
|
|
|
|
// call app manager to create the handler
|
|
handler = (AppDomainProtocolHandler)_appManager.CreateObjectInternal(
|
|
appId, handlerType, appHost, false /*failIfExists*/,
|
|
hostingParameters);
|
|
|
|
// create a shim object that we can use for proxy unwrapping
|
|
ListenerAdapterDispatchShim shim = (ListenerAdapterDispatchShim)
|
|
_appManager.CreateObjectInternal(
|
|
appId, typeof(ListenerAdapterDispatchShim), appHost, false /*failIfExists*/,
|
|
hostingParameters);
|
|
|
|
if (null != shim) {
|
|
shim.StartListenerChannel(handler, listenerChannelCallback);
|
|
|
|
// remove the shim
|
|
((IRegisteredObject)shim).Stop(true);
|
|
}
|
|
else {
|
|
throw new HttpException(SR.GetString(SR.Failure_Create_Listener_Shim));
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Start_AppDomain_Listener)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
public void StopAppDomainProtocolListenerChannel(String appId, String protocolId, int listenerChannelId, bool immediate) {
|
|
try {
|
|
if (appId == null)
|
|
throw new ArgumentNullException("appId");
|
|
if (protocolId == null)
|
|
throw new ArgumentNullException("protocolId");
|
|
|
|
// get app domaoin protocol handler type from config
|
|
Type handlerType = GetAppDomainProtocolHandlerType(protocolId);
|
|
|
|
AppDomainProtocolHandler handler = null;
|
|
|
|
LockableAppDomainContext ac = _appManager.GetLockableAppDomainContext(appId);
|
|
lock (ac) {
|
|
// call app manager to create the handler
|
|
handler = (AppDomainProtocolHandler)_appManager.GetObject(appId, handlerType);
|
|
}
|
|
|
|
// stop the listenerChannel
|
|
if (handler != null) {
|
|
handler.StopListenerChannel(listenerChannelId, immediate);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Stop_AppDomain_Listener)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
public void StopAppDomainProtocol(String appId, String protocolId, bool immediate) {
|
|
try {
|
|
if (appId == null)
|
|
throw new ArgumentNullException("appId");
|
|
if (protocolId == null)
|
|
throw new ArgumentNullException("protocolId");
|
|
|
|
// get app domaoin protocol handler type from config
|
|
Type handlerType = GetAppDomainProtocolHandlerType(protocolId);
|
|
|
|
AppDomainProtocolHandler handler = null;
|
|
|
|
LockableAppDomainContext ac = _appManager.GetLockableAppDomainContext(appId);
|
|
lock (ac) {
|
|
// call app manager to create the handler
|
|
handler = (AppDomainProtocolHandler)_appManager.GetObject(appId, handlerType);
|
|
}
|
|
|
|
// stop protocol
|
|
if (handler != null) {
|
|
handler.StopProtocol(immediate);
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Stop_AppDomain_Protocol)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public void StartApplication(String appId, String appPath, out Object runtimeInterface)
|
|
{
|
|
try {
|
|
if (appId == null)
|
|
throw new ArgumentNullException("appId");
|
|
if (appPath == null)
|
|
throw new ArgumentNullException("appPath");
|
|
|
|
Debug.Assert(_functions != null, "_functions != null");
|
|
|
|
runtimeInterface = null;
|
|
|
|
PipelineRuntime runtime = null;
|
|
|
|
//
|
|
// Fill app a Dictionary with 'binding rules' -- name value string pairs
|
|
// for app domain creation
|
|
//
|
|
|
|
//
|
|
|
|
|
|
if (appPath[0] == '.') {
|
|
System.IO.FileInfo file = new System.IO.FileInfo(appPath);
|
|
appPath = file.FullName;
|
|
}
|
|
|
|
if (!StringUtil.StringEndsWith(appPath, '\\')) {
|
|
appPath = appPath + "\\";
|
|
}
|
|
|
|
// Create new app host of a consistent type
|
|
IApplicationHost appHost = CreateAppHost(appId, appPath);
|
|
|
|
|
|
//
|
|
// Create the AppDomain and a registered object in it
|
|
//
|
|
LockableAppDomainContext ac = _appManager.GetLockableAppDomainContext(appId);
|
|
|
|
lock (ac) {
|
|
// #1 WOS 1690249: ASP.Net v2.0: ASP.NET stress: 2nd chance exception: Attempted to access an unloaded AppDomain.
|
|
// if an old AppDomain exists with a PipelineRuntime, remove it from
|
|
// AppManager._appDomains so that a new AppDomain will be created
|
|
// #2 WOS 1977425: ASP.NET apps continue recycling after touching machine.config once - this used to initiate shutdown,
|
|
// but that can cause us to recycle the app repeatedly if we initiate shutdown before IIS initiates shutdown of the
|
|
// previous app.
|
|
|
|
_appManager.RemoveFromTableIfRuntimeExists(appId, typeof(PipelineRuntime));
|
|
|
|
|
|
// Preload (if required) the App Domain before letting the first request to be processed
|
|
PreloadApplicationIfRequired(appId, appHost, null, ac);
|
|
|
|
try {
|
|
runtime = (PipelineRuntime)_appManager.CreateObjectInternal(
|
|
appId,
|
|
typeof(PipelineRuntime),
|
|
appHost,
|
|
true /* failIfExists */,
|
|
null /* default */ );
|
|
}
|
|
catch (AppDomainUnloadedException) {
|
|
// munch it so we can retry again
|
|
}
|
|
|
|
if (null != runtime) {
|
|
runtime.SetThisAppDomainsIsapiAppId(appId);
|
|
runtime.StartProcessing();
|
|
runtimeInterface = new ObjectHandle(runtime);
|
|
}
|
|
}
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Start_Integrated_App)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
|
|
public void ShutdownApplication(String appId) {
|
|
try {
|
|
// call into app manager
|
|
_appManager.ShutdownApplication(appId);
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Stop_Integrated_App)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
public void Shutdown() {
|
|
try {
|
|
// collect all protocols under lock
|
|
ArrayList protocolList = new ArrayList();
|
|
int refCount = 0;
|
|
|
|
lock (this) {
|
|
// lookup protocol handler
|
|
foreach (DictionaryEntry e in _protocolHandlers) {
|
|
protocolList.Add(e.Value);
|
|
}
|
|
|
|
_protocolHandlers = new Hashtable();
|
|
}
|
|
|
|
// stop all process protocols outside of lock
|
|
foreach (ProcessProtocolHandler p in protocolList) {
|
|
p.StopProtocol(true);
|
|
}
|
|
|
|
// call into app manager to shutdown
|
|
_appManager.ShutdownAll();
|
|
|
|
|
|
// SupportFunctions interface provided by native layer
|
|
// must be released now.
|
|
// Otherwise the release of the COM object will have
|
|
// to wait for GC. Native layer assumes that after
|
|
// returning from Shutdown there is no reference
|
|
// to the native objects from ProcessHost.
|
|
//
|
|
do {
|
|
refCount = Marshal.ReleaseComObject( _functions );
|
|
} while( refCount != 0 );
|
|
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_Shutdown_ProcessHost), e.ToString()} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
[SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", Justification = "See comment for why we're calling GC.Collect.")]
|
|
IProcessResumeCallback IProcessSuspendListener.Suspend() {
|
|
object resumeState = _appManager.SuspendAllApplications();
|
|
Action customRuntimeResumeCallback = _customRuntimeManager.SuspendAllCustomRuntimes();
|
|
|
|
IProcessResumeCallback callback = new SimpleProcessResumeCallbackDispatcher(() => {
|
|
_appManager.ResumeAllApplications(resumeState);
|
|
if (customRuntimeResumeCallback != null) {
|
|
customRuntimeResumeCallback();
|
|
}
|
|
});
|
|
|
|
// Per CLR team's suggestion, we perform one final GC to try to free
|
|
// any pages that can be reclaimed. Ideally we would do this in the
|
|
// unmanaged layer, but the ICLRGCManager is unavailable to us at the
|
|
// time we would need to perform a collection. So we'll do it here
|
|
// instead.
|
|
GC.Collect();
|
|
|
|
return callback;
|
|
}
|
|
|
|
ICustomRuntimeRegistrationToken ICustomRuntimeManager.Register(ICustomRuntime customRuntime) {
|
|
Debug.Assert(customRuntime != null);
|
|
return _customRuntimeManager.Register(customRuntime);
|
|
}
|
|
|
|
public void EnumerateAppDomains( out IAppDomainInfoEnum appDomainInfoEnum )
|
|
{
|
|
try {
|
|
ApplicationManager appManager = ApplicationManager.GetApplicationManager();
|
|
AppDomainInfo [] infos;
|
|
|
|
infos = appManager.GetAppDomainInfos();
|
|
|
|
appDomainInfoEnum = new AppDomainInfoEnum(infos);
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_AppDomain_Enum)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// IProcessHostIdleAndHealthCheck interface implementation
|
|
public bool IsIdle() {
|
|
bool result = false;
|
|
|
|
try {
|
|
result = _appManager.IsIdle();
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_PMH_Idle)} );
|
|
}
|
|
throw;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
public void Ping(IProcessPingCallback callback) {
|
|
try {
|
|
if (callback != null)
|
|
_appManager.Ping(callback);
|
|
}
|
|
catch (Exception e) {
|
|
using (new ProcessImpersonationContext()) {
|
|
Misc.ReportUnhandledException(e, new string[] {
|
|
SR.GetString(SR.Failure_PMH_Ping)} );
|
|
}
|
|
throw;
|
|
}
|
|
}
|
|
|
|
// Users cannot provide any call stack that eventually leads to this method, as it will fail at some
|
|
// point with a NullReferenceException. The ASP.NET runtime is the only entity that can call this
|
|
// without something failing, and those call sites are safe.
|
|
[SuppressMessage("Microsoft.Security", "CA2122:DoNotIndirectlyExposeMethodsWithLinkDemands", Justification = "See comment above.")]
|
|
private ISAPIApplicationHost CreateAppHost(string appId, string appPath) {
|
|
|
|
//
|
|
// if we have a null physical path, we need
|
|
// to use the PMH to resolve it
|
|
//
|
|
if (String.IsNullOrEmpty(appPath)) {
|
|
string virtualPath;
|
|
string physicalPath;
|
|
string siteName;
|
|
string siteID;
|
|
|
|
_functions.GetApplicationProperties(
|
|
appId,
|
|
out virtualPath,
|
|
out physicalPath,
|
|
out siteName,
|
|
out siteID);
|
|
|
|
//
|
|
// make sure physical app path ends with '\\' and virtual does not
|
|
//
|
|
if (!StringUtil.StringEndsWith(physicalPath, '\\')) {
|
|
physicalPath = physicalPath + "\\";
|
|
}
|
|
|
|
Debug.Assert( !String.IsNullOrEmpty(physicalPath), "!String.IsNullOrEmpty(physicalPath)");
|
|
appPath = physicalPath;
|
|
}
|
|
|
|
//
|
|
// Create a new application host
|
|
// This needs to be a coherent type across all
|
|
// protocol types so that we get a consistent
|
|
// environment regardless of which protocol initializes first
|
|
//
|
|
ISAPIApplicationHost appHost = new
|
|
ISAPIApplicationHost(
|
|
appId,
|
|
appPath,
|
|
false, /* validatePhysicalPath */
|
|
_functions
|
|
);
|
|
|
|
|
|
return appHost;
|
|
}
|
|
|
|
public void SetApplicationPreloadUtil(IApplicationPreloadUtil applicationPreloadUtil) {
|
|
|
|
// Do not allow setting PreloadUtil again if it has already has been set
|
|
if (_preloadUtil != null) {
|
|
throw new InvalidOperationException(SR.GetString(SR.Failure_ApplicationPreloadUtil_Already_Set));
|
|
}
|
|
|
|
_preloadUtil = applicationPreloadUtil;
|
|
}
|
|
|
|
public void SetApplicationPreloadState(string context, string appId, bool enabled) {
|
|
// Check params
|
|
if (String.IsNullOrEmpty(context)) {
|
|
throw ExceptionUtil.ParameterNullOrEmpty("context");
|
|
}
|
|
if (String.IsNullOrEmpty(appId)) {
|
|
throw ExceptionUtil.ParameterNullOrEmpty("appId");
|
|
}
|
|
|
|
// _preloadUtil must be not null if we have an application preload enabled
|
|
if (enabled && _preloadUtil == null) {
|
|
throw new ArgumentException(SR.GetString(SR.Invalid_Enabled_Preload_Parameter), "enabled");
|
|
}
|
|
|
|
LockableAppDomainContext ac = _appManager.GetLockableAppDomainContext(appId);
|
|
|
|
lock (ac) {
|
|
ac.PreloadContext = context;
|
|
if (enabled) {
|
|
PreloadApplicationIfRequired(appId, null, null, ac);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal static void PreloadApplicationIfNotShuttingdown (string appId, LockableAppDomainContext ac) {
|
|
// If GL_STOP_LISTENING wasn't triggered, the reset is likely due to a configuration change.
|
|
if (ProcessHost.DefaultHost != null && !UnsafeIISMethods.MgdIsAppPoolShuttingDown()) {
|
|
// Start the new app on another thread instead of hijacking the current app unloading thread
|
|
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object o) {
|
|
lock (ac) {
|
|
try {
|
|
// NOTE: we don't know what HostingEnvironmentParameters were passed to our previous application instance
|
|
// so we pass null (default for HTTP activation). We could have cached it in ApplicationContext if needed
|
|
ProcessHost.DefaultHost.PreloadApplicationIfRequired(appId, null, null, ac);
|
|
}
|
|
catch (Exception e) {
|
|
ProcessHost.DefaultHost.ReportApplicationPreloadFailureWithAssert(
|
|
ac.PreloadContext,
|
|
HResults.E_FAIL,
|
|
Misc.FormatExceptionMessage(
|
|
e, new string[] {
|
|
SR.GetString(SR.Failure_Preload_Application_Initialization)}));
|
|
}
|
|
}
|
|
}));
|
|
}
|
|
}
|
|
|
|
// New for Dev10.
|
|
// creates a new AppDomain, preloads and calls user code in it
|
|
internal void PreloadApplicationIfRequired(
|
|
string appId,
|
|
IApplicationHost appHostParameter,
|
|
HostingEnvironmentParameters hostingParameters,
|
|
LockableAppDomainContext ac) {
|
|
|
|
// NOTE1: Must never be called under lock (_appManager)
|
|
// NOTE2: Must always be called under lock (ac)
|
|
|
|
// We only need to preload if auto start is enabled and we have not already preloaded (i.e. HostingEnvironment doesn't exist)
|
|
if (_preloadUtil == null || ac.PreloadContext == null || ac.HostEnv != null) {
|
|
return;
|
|
}
|
|
|
|
// Get and verify the preload parameters
|
|
string preloadObjTypeName;
|
|
string[] paramsForStartupObj;
|
|
bool stillEnabled;
|
|
|
|
GetApplicationPreloadInfoWithAssert(ac.PreloadContext, out stillEnabled, out preloadObjTypeName, out paramsForStartupObj);
|
|
|
|
// Dev10: 782385 ASP.NET autostart implementation should be tolerant of empty string for the provider type
|
|
if (!stillEnabled || String.IsNullOrEmpty(preloadObjTypeName)) {
|
|
return;
|
|
}
|
|
|
|
// Ready to load the App Domain
|
|
if (_preloadingThrottle != null) {
|
|
// Throttle the number of simultaneously created appdomains
|
|
_preloadingThrottle.WaitOne();
|
|
}
|
|
|
|
try {
|
|
|
|
// Create the app-host and start a new App Domain
|
|
IApplicationHost appHost = (appHostParameter == null) ? CreateAppHost(appId, null) : appHostParameter;
|
|
|
|
// call app manager to create the PreloadHost
|
|
PreloadHost preloadHostObj = (PreloadHost)_appManager.CreateObjectInternal(
|
|
appId,
|
|
typeof(PreloadHost),
|
|
appHost,
|
|
true /*failIfExists*/,
|
|
hostingParameters);
|
|
|
|
// Dev10 858421: File sharing violations on config files cause unnecessary process shutdown in autostart mode
|
|
//
|
|
// There are race conditions between whoever modifies the config files
|
|
// and the application config system that reads from the config files
|
|
// These file sharing violation lead to random application initialization failures
|
|
// Service auto-start mode is more vulnerable to these sharing violations because
|
|
// it starts a new app domain as soon as the file change notification is received
|
|
// and when an error occurs IIS recycles the whole app pool rather than a particular app
|
|
//
|
|
// In most cases that we see in stress the inner most exception is System.IO.IOException
|
|
// so if we see this exception we will give it another try before
|
|
// reporting the errors to IIS and recycling the process
|
|
//
|
|
|
|
// Check for I/O exceptions during initialization and retry one time
|
|
Exception appInitEx = preloadHostObj.InitializationException;
|
|
if (GetInnerMostException(appInitEx) is IOException) {
|
|
try {
|
|
// prevent ApplicationManager.HostingEnvironmentShutdownInitiated from attempting to preload again
|
|
ac.RetryingPreload = true;
|
|
// shutdown old hosting environment
|
|
ac.HostEnv.InitiateShutdownInternal();
|
|
}
|
|
finally {
|
|
ac.RetryingPreload = false;
|
|
}
|
|
|
|
// Create the app-host and start a new App Domain
|
|
appHost = (appHostParameter == null) ? CreateAppHost(appId, null) : appHostParameter;
|
|
|
|
// call app manager to create the PreloadHost
|
|
preloadHostObj = (PreloadHost)_appManager.CreateObjectInternal(
|
|
appId,
|
|
typeof(PreloadHost),
|
|
appHost,
|
|
true /*failIfExists*/,
|
|
hostingParameters);
|
|
|
|
appInitEx = preloadHostObj.InitializationException;
|
|
}
|
|
|
|
// Check again for initialization exception and tell IIS to recycle the process
|
|
if (appInitEx != null) {
|
|
ReportApplicationPreloadFailureWithAssert(
|
|
ac.PreloadContext,
|
|
HResults.E_FAIL,
|
|
Misc.FormatExceptionMessage(
|
|
appInitEx, new string[] {
|
|
SR.GetString(SR.Failure_Preload_Application_Initialization)} ));
|
|
|
|
// we must throw if preload fails because we cannot allow the normal
|
|
// startup path to continue and attempt to create a HostingEnvironment
|
|
throw appInitEx;
|
|
}
|
|
|
|
// Call preload code in the App Domain
|
|
try {
|
|
preloadHostObj.CreateIProcessHostPreloadClientInstanceAndCallPreload(preloadObjTypeName, paramsForStartupObj);
|
|
}
|
|
catch (Exception e) {
|
|
// report errors
|
|
ReportApplicationPreloadFailureWithAssert(
|
|
ac.PreloadContext,
|
|
HResults.E_FAIL,
|
|
Misc.FormatExceptionMessage(
|
|
e, new string[] {
|
|
SR.GetString(SR.Failure_Calling_Preload_Provider)} ).ToString());
|
|
|
|
throw;
|
|
}
|
|
}
|
|
finally {
|
|
if (_preloadingThrottle != null) {
|
|
_preloadingThrottle.Release();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
private static Exception GetInnerMostException(Exception e) {
|
|
if (e == null) {
|
|
return null;
|
|
}
|
|
while (e.InnerException != null) {
|
|
e = e.InnerException;
|
|
}
|
|
return e;
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
private void GetApplicationPreloadInfoWithAssert(
|
|
string context, out bool enabled, out string startupObjType, out string[] parametersForStartupObj) {
|
|
_preloadUtil.GetApplicationPreloadInfo(context, out enabled, out startupObjType, out parametersForStartupObj);
|
|
}
|
|
|
|
[PermissionSet(SecurityAction.Assert, Unrestricted = true)]
|
|
private void ReportApplicationPreloadFailureWithAssert(string context, int errorCode, string errorMessage) {
|
|
_preloadUtil.ReportApplicationPreloadFailure(context, errorCode, errorMessage);
|
|
}
|
|
|
|
private sealed class SimpleProcessResumeCallbackDispatcher : IProcessResumeCallback {
|
|
private readonly Action _callback;
|
|
public SimpleProcessResumeCallbackDispatcher(Action callback) {
|
|
Debug.Assert(callback != null);
|
|
_callback = callback;
|
|
}
|
|
|
|
public void Resume() {
|
|
_callback();
|
|
}
|
|
}
|
|
|
|
// The methods below are for propagating error information to ApplicationManager
|
|
// so that we can display a YSOD when the application starts. We rely on the
|
|
// fact that webengine4.dll will immediately call IProcessHost.StartApplication
|
|
// if GetCustomLoader returns null, so TLS is appropriate.
|
|
|
|
internal static ExceptionDispatchInfo GetExistingCustomLoaderFailureAndClear(string appId) {
|
|
var copiedError = _customLoaderStartupError;
|
|
if (String.Equals(copiedError.Key, appId, StringComparison.OrdinalIgnoreCase)) {
|
|
_customLoaderStartupError = default(KeyValuePair<string, ExceptionDispatchInfo>);
|
|
return copiedError.Value;
|
|
}
|
|
else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
private static void SetCustomLoaderFailure(string appId, ExceptionDispatchInfo error) {
|
|
_customLoaderStartupError = new KeyValuePair<string, ExceptionDispatchInfo>(appId, error);
|
|
}
|
|
|
|
IObjectHandle IProcessHostLite.GetCustomLoader(string appId, string appConfigPath, out IProcessHostSupportFunctions supportFunctions, out AppDomain newlyCreatedAppDomain) {
|
|
supportFunctions = null;
|
|
newlyCreatedAppDomain = null;
|
|
CustomLoaderHelperFunctions helperFunctions = new CustomLoaderHelperFunctions(_functions, appId);
|
|
string appVirtualPath = helperFunctions.AppVirtualPath;
|
|
|
|
try {
|
|
string customLoaderAssemblyPhysicalPath = helperFunctions.MapPath("bin/AspNet.Loader.dll");
|
|
|
|
if (!File.Exists(customLoaderAssemblyPhysicalPath)) {
|
|
return null; // no custom loader is in use; fall back to legacy hosting logic
|
|
}
|
|
|
|
// Technically there is a race condition between the file existence check above
|
|
// and the assembly load that will take place shortly. We won't worry too much
|
|
// about this since the window is very short and the application shouldn't be
|
|
// modified during this process anyway.
|
|
|
|
string appRootPhysicalPath = helperFunctions.AppPhysicalPath;
|
|
string webConfigPhysicalPath = helperFunctions.MapPath("Web.config");
|
|
bool webConfigFileExists = File.Exists(webConfigPhysicalPath);
|
|
|
|
// The CustomLoaderHelper class is defined in System.Web.ApplicationServices.dll
|
|
// so that OOB frameworks don't need to take a hardcoded System.Web.dll dependency.
|
|
// There might be weird issues if we try to load a GACed System.Web.dll into the
|
|
// same AppDomain as a bin-deployed System.Web.dll.
|
|
supportFunctions = _functions;
|
|
return CustomLoaderHelper.GetCustomLoader(
|
|
helperFunctions: helperFunctions,
|
|
appConfigMetabasePath: appConfigPath,
|
|
configFilePath: (webConfigFileExists) ? webConfigPhysicalPath : null,
|
|
customLoaderPhysicalPath: customLoaderAssemblyPhysicalPath,
|
|
newlyCreatedAppDomain: out newlyCreatedAppDomain);
|
|
}
|
|
catch (Exception ex) {
|
|
SetCustomLoaderFailure(appId, ExceptionDispatchInfo.Capture(ex));
|
|
return null;
|
|
}
|
|
}
|
|
|
|
void IProcessHostLite.ReportCustomLoaderError(string appId, int hr, AppDomain newlyCreatedAppDomain) {
|
|
try {
|
|
try {
|
|
// If the failure originated in managed code (GetCustomLoader), this will actually
|
|
// result in the original managed exception being rethrown, which is convenient
|
|
// for us.
|
|
Marshal.ThrowExceptionForHR(hr);
|
|
}
|
|
finally {
|
|
// AD wasn't unloaded by CustomLoaderHelper, so kill it here.
|
|
AppDomain.Unload(newlyCreatedAppDomain);
|
|
}
|
|
}
|
|
catch (Exception ex) {
|
|
SetCustomLoaderFailure(appId, ExceptionDispatchInfo.Capture(ex));
|
|
}
|
|
}
|
|
|
|
// Used to extract the full message of an exception, including class name and stack trace.
|
|
string IProcessHostLite.GetFullExceptionMessage(int hr, IntPtr pErrorInfo) {
|
|
// If no IErrorInfo is explicitly specified, provide -1 to suppress the automated
|
|
// GetErrorInfo lookup, as it may have been overwritten and might be irrelevant.
|
|
Exception ex = Marshal.GetExceptionForHR(hr, (pErrorInfo != IntPtr.Zero) ? pErrorInfo : (IntPtr)(-1));
|
|
|
|
if (ex != null) {
|
|
return ex.ToString();
|
|
} else {
|
|
// Should never hit this case, but just in case, return a dummy value.
|
|
Debug.Fail("The provided HRESULT should've represented a failure.");
|
|
return String.Empty;
|
|
}
|
|
}
|
|
|
|
private sealed class CustomLoaderHelperFunctions : ICustomLoaderHelperFunctions {
|
|
private static readonly bool? _isEnabled = GetIsEnabledValueFromRegistry();
|
|
|
|
private readonly IProcessHostSupportFunctions _supportFunctions;
|
|
|
|
internal CustomLoaderHelperFunctions(IProcessHostSupportFunctions supportFunctions, string appId) {
|
|
_supportFunctions = supportFunctions;
|
|
|
|
string appVirtualPath, appPhysicalPath, siteName, siteId;
|
|
_supportFunctions.GetApplicationProperties(appId, out appVirtualPath, out appPhysicalPath, out siteName, out siteId);
|
|
|
|
AppId = appId;
|
|
AppVirtualPath = appVirtualPath;
|
|
AppPhysicalPath = appPhysicalPath;
|
|
}
|
|
|
|
public string AppId { get; private set; }
|
|
public string AppPhysicalPath { get; private set; }
|
|
public string AppVirtualPath { get; private set; }
|
|
public bool? CustomLoaderIsEnabled { get { return _isEnabled; } } // true = always enabled, false = always disabled, null = check trust level
|
|
|
|
private static bool? GetIsEnabledValueFromRegistry() {
|
|
bool? isEnabled = null;
|
|
try {
|
|
int valueInRegistry = (int)Misc.GetAspNetRegValue(null, "CustomLoaderEnabled", -1);
|
|
if (valueInRegistry == 1) {
|
|
// explicitly enabled
|
|
isEnabled = true;
|
|
}
|
|
else if (valueInRegistry == 0) {
|
|
// explicitly disabled
|
|
isEnabled = false;
|
|
}
|
|
}
|
|
catch {
|
|
// We don't care about errors, as we'll just query fallback logic.
|
|
}
|
|
|
|
return isEnabled;
|
|
}
|
|
|
|
public string GetTrustLevel(string appConfigMetabasePath) {
|
|
object retVal;
|
|
int hr = UnsafeIISMethods.MgdGetConfigProperty(appConfigMetabasePath, "system.web/trust", "level", out retVal);
|
|
Marshal.ThrowExceptionForHR(hr);
|
|
return (string)retVal;
|
|
}
|
|
|
|
public string MapPath(string relativePath) {
|
|
return _supportFunctions.MapPathInternal(AppId, AppVirtualPath, relativePath);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal sealed class ListenerAdapterDispatchShim : MarshalByRefObject, IRegisteredObject {
|
|
|
|
void IRegisteredObject.Stop(bool immediate) {
|
|
HostingEnvironment.UnregisterObject(this);
|
|
}
|
|
|
|
// this should run in an Hosted app domain (not in the default domain)
|
|
internal void StartListenerChannel( AppDomainProtocolHandler handler, IListenerChannelCallback listenerCallback ) {
|
|
Debug.Assert( HostingEnvironment.IsHosted, "HostingEnvironment.IsHosted" );
|
|
Debug.Assert( null != handler, "null != handler" );
|
|
|
|
IListenerChannelCallback unwrappedProxy = MarshalComProxy(listenerCallback);
|
|
|
|
Debug.Assert(null != unwrappedProxy, "null != unwrappedProxy");
|
|
if (null != unwrappedProxy && null != handler) {
|
|
handler.StartListenerChannel(unwrappedProxy);
|
|
}
|
|
}
|
|
|
|
internal IListenerChannelCallback MarshalComProxy(IListenerChannelCallback defaultDomainCallback) {
|
|
IListenerChannelCallback localProxy = null;
|
|
|
|
// get the underlying COM object
|
|
IntPtr pUnk = Marshal.GetIUnknownForObject(defaultDomainCallback);
|
|
|
|
// this object isn't a COM object
|
|
if (IntPtr.Zero == pUnk) {
|
|
return null;
|
|
}
|
|
|
|
IntPtr ppv = IntPtr.Zero;
|
|
try {
|
|
// QI it for the interface
|
|
Guid g = typeof(IListenerChannelCallback).GUID;
|
|
|
|
int hresult = Marshal.QueryInterface(pUnk, ref g, out ppv);
|
|
if (hresult < 0) {
|
|
Marshal.ThrowExceptionForHR(hresult);
|
|
}
|
|
|
|
// create a RCW we can hold onto in this domain
|
|
// this bumps the ref count so we can drop our refs on the raw interfaces
|
|
localProxy = (IListenerChannelCallback)Marshal.GetObjectForIUnknown(ppv);
|
|
}
|
|
finally {
|
|
// drop our explicit refs and keep the managed instance
|
|
if (IntPtr.Zero != ppv) {
|
|
Marshal.Release(ppv);
|
|
}
|
|
if (IntPtr.Zero != pUnk) {
|
|
Marshal.Release(pUnk);
|
|
}
|
|
}
|
|
|
|
return localProxy;
|
|
}
|
|
|
|
}
|
|
}
|
|
|