private static T AdaptToHost <T>(AddInToken pipeline, IContract addInContract) { if (addInContract == null) { throw new ArgumentNullException("addInContract"); } System.Diagnostics.Contracts.Contract.EndContractBlock(); ContractComponent contract = pipeline._contract; HostAdapter hostAdapter = pipeline._hostAdapter; Type hostAdapterType; Type contractType; LoadContractAndHostAdapter(contract, hostAdapter, out contractType, out hostAdapterType); int?temporaryToken = null; try { temporaryToken = addInContract.AcquireLifetimeToken(); // This assembly resolve event exists to: // 1) To upgrade the contract assembly from the LoadFrom context to // the default loader context ResolverHelper resolver = new ResolverHelper(pipeline); ResolveEventHandler assemblyResolver = new ResolveEventHandler(resolver.ResolveAssemblyForHostAdapter); AppDomain.CurrentDomain.AssemblyResolve += assemblyResolver; // Create the Host Adapter, which is a subclass of the Host Add-In View. // Pass the contract implementation to this constructor, using a // transparent proxy to the real type. Detect common errors here. InvokerDelegate myInvokerDelegate = CreateConsInvoker(hostAdapterType, contractType); T hav; try { hav = (T)myInvokerDelegate(addInContract); } catch (ArgumentException e) { CheckForLoaderContextProblems(contract, pipeline._addinAdapter, e); throw; } finally { AppDomain.CurrentDomain.AssemblyResolve -= assemblyResolver; } return(hav); } finally { if (temporaryToken != null && addInContract != null) { addInContract.RevokeLifetimeToken((int)temporaryToken); } } }
internal PartialToken(HostAdapter hostAdapter, ContractComponent contract, AddInAdapter addinAdapter, AddInBase addinBase) { System.Diagnostics.Contracts.Contract.Requires(hostAdapter != null); System.Diagnostics.Contracts.Contract.Requires(contract != null); System.Diagnostics.Contracts.Contract.Requires(addinAdapter != null); System.Diagnostics.Contracts.Contract.Requires(addinBase != null); _hostAdapter = hostAdapter; _contract = contract; _addinAdapter = addinAdapter; _addinBase = addinBase; }
internal PartialToken(HostAdapter hostAdapter, ContractComponent contract, AddInAdapter addinAdapter, AddInBase addinBase) { System.Diagnostics.Contracts.Contract.Requires(hostAdapter != null); System.Diagnostics.Contracts.Contract.Requires(contract != null); System.Diagnostics.Contracts.Contract.Requires(addinAdapter != null); System.Diagnostics.Contracts.Contract.Requires(addinBase != null); _hostAdapter = hostAdapter; _contract = contract; _addinAdapter = addinAdapter; _addinBase = addinBase; }
internal static T ActivateHostAdapter <T>(PartialToken pipeline, IContract addIn) { if (pipeline == null) { throw new ArgumentNullException("pipeline"); } if (addIn == null) { throw new ArgumentNullException("addIn"); } System.Diagnostics.Contracts.Contract.EndContractBlock(); ContractComponent contract = pipeline._contract; HostAdapter hostAdapter = pipeline._hostAdapter; Type hostAdapterType; Type contractType; LoadContractAndHostAdapter(contract, hostAdapter, out contractType, out hostAdapterType); // This assembly resolve event exists to: // 1) To upgrade the contract assembly from the LoadFrom context to // the default loader context ResolverHelper resolver = new ResolverHelper(contract); ResolveEventHandler assemblyResolver = new ResolveEventHandler(resolver.ResolveAssemblyForHostAdapter); AppDomain.CurrentDomain.AssemblyResolve += assemblyResolver; T hav; InvokerDelegate myInvokerDelegate = CreateConsInvoker(hostAdapterType, contractType); try { hav = (T)myInvokerDelegate(addIn); } catch (ArgumentException e) { CheckForLoaderContextProblems(contract, pipeline._addinAdapter, e); throw; } finally { AppDomain.CurrentDomain.AssemblyResolve -= assemblyResolver; } return(hav); }
internal static void LoadContractAndHostAdapter(ContractComponent contract, HostAdapter hostAdapter, out Type contractType, out Type hostAdapterType) { PermissionSet assertSet = new PermissionSet(PermissionState.None); assertSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, hostAdapter.Location)); assertSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, contract.Location)); assertSet.Assert(); // Explicitly load the host adapter & contract first, using LoadFrom // to ensure they are loaded in the same loader context. Assembly hostAdapterAssembly = Assembly.LoadFrom(hostAdapter.Location); Assembly contractAssembly = Assembly.LoadFrom(contract.Location); hostAdapterType = hostAdapterAssembly.GetType(hostAdapter.TypeInfo.FullName, true); contractType = contractAssembly.GetType(contract.TypeInfo.FullName, true); }
internal AddInToken(HostAdapter hostAdapter, ContractComponent contract, AddInAdapter addinAdapter, AddInBase addinBase, AddIn addin) { System.Diagnostics.Contracts.Contract.Requires(hostAdapter != null); System.Diagnostics.Contracts.Contract.Requires(contract != null); System.Diagnostics.Contracts.Contract.Requires(addinAdapter != null); System.Diagnostics.Contracts.Contract.Requires(addinBase != null); System.Diagnostics.Contracts.Contract.Requires(addin != null); _hostAddinViews = hostAdapter.HostAddinViews; _hostAdapter = hostAdapter; _contract = contract; _addinAdapter = addinAdapter; _addinBase = addinBase; _addin = addin; // _pipelineRootDir must be filled in after deserialization. }
internal InspectionResults Inspect(PipelineComponentType componentType, string assemblyFileName, string pipelineRootDirectory) { System.Diagnostics.Contracts.Contract.Requires(assemblyFileName != null); System.Diagnostics.Contracts.Contract.Requires(pipelineRootDirectory != null); _assemblyFileName = assemblyFileName; _pipelineRootDirectory = pipelineRootDirectory; // Set up the assembly resolve event. _currentComponentType = componentType; ResolveEventHandler assemblyResolver = new ResolveEventHandler(ResolveAssembly); AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve += assemblyResolver; InspectionResults retval = new InspectionResults(); retval.Components = new List <PipelineComponent>(); retval.Warnings = new Collection <String>(); Type[] publicTypes; String assemblyName = null; // Need to assert again here because we are in a new appdomain FileIOPermission permission = new FileIOPermission(FileIOPermissionAccess.PathDiscovery | FileIOPermissionAccess.Read, _pipelineRootDirectory); permission.Assert(); try { // We want to load the assembly WITHOUT REGARD OF PUBLISHER POLICY. // If the directory structure contains v1.0 of a component and v1.1 // exists in the GAC and is a security fix to v1.0, we still want to // inspect v1.0. (The reason is we have other parts of the // pipeline that were likely compiled against v1.0, not v1.1, and // we do type comparisons by comparing the fully qualified assembly // name.) LoadFrom unfortunately respects policy. Assembly's // ReflectionOnlyLoad(byte[]) doesn't. ReflectionOnlyLoadFrom(String) // does respect policy if you've set DEVPATH, but only as a bug. // We don't think setting DEVPATH is interesting. Assembly a = Assembly.ReflectionOnlyLoadFrom(_assemblyFileName); publicTypes = a.GetTypes(); assemblyName = a.FullName; } catch (FileNotFoundException fnf) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AssemblyLoadFileNotFound, fnf.Message, fnf.FileName)); return(retval); } catch (Exception e) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AssemblyLoadThrew, e.GetType().Name, e.Message, _assemblyFileName)); return(retval); } PipelineComponent component = null; String relativeFileName = Utils.MakeRelativePath(_assemblyFileName, _pipelineRootDirectory); Type lastType = null; try { // Iterate over public types, looking for the appropriate custom attributes. foreach (Type type in publicTypes) { component = null; lastType = type; switch (componentType) { case PipelineComponentType.Contract: if (!Utils.HasCustomAttribute(PipelineComponent.ContractAttributeInReflectionLoaderContext, type)) { continue; } component = new ContractComponent(new TypeInfo(type), relativeFileName); break; case PipelineComponentType.AddInAdapter: if (!Utils.HasCustomAttribute(PipelineComponent.AddInAdapterAttributeInReflectionLoaderContext, type)) { continue; } component = new AddInAdapter(new TypeInfo(type), relativeFileName); break; case PipelineComponentType.AddInBase: if (Utils.HasCustomAttribute(PipelineComponent.AddInAttributeInReflectionLoaderContext, type)) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AddInInAddInViewFolder, type.Name, _assemblyFileName)); } if (!Utils.HasCustomAttribute(PipelineComponent.AddInBaseAttributeInReflectionLoaderContext, type)) { continue; } TypeInfo[] activatableAs = null; CustomAttributeData cad = Utils.GetCustomAttributeData(PipelineComponent.AddInBaseAttributeInReflectionLoaderContext, type); foreach (CustomAttributeNamedArgument cana in cad.NamedArguments) { if (cana.MemberInfo.Name == "ActivatableAs") { CustomAttributeTypedArgument arg = cana.TypedValue; ReadOnlyCollection <CustomAttributeTypedArgument> types = (ReadOnlyCollection <CustomAttributeTypedArgument>)arg.Value; activatableAs = new TypeInfo[types.Count]; int i = 0; foreach (CustomAttributeTypedArgument subArg in types) { activatableAs[i++] = new TypeInfo((Type)subArg.Value); } } } component = new AddInBase(new TypeInfo(type), activatableAs, relativeFileName, assemblyName); break; default: System.Diagnostics.Contracts.Contract.Assert(false, "Fell through switch - unrecognized componentType in InspectionWorker.Inspect"); break; } // switch // If we found a component, make sure it satisfies all of its constraints, and give our // PipelineComponents a chance to initialize state. if (component != null) { if (component.Validate(type, retval.Warnings)) { retval.Components.Add(component); } } } // foreach type in the assembly } // try catch (FileNotFoundException fnf) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.AssemblyLoadFileNotFound, fnf.Message, fnf.FileName)); return(retval); } catch (NotImplementedException) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.NotImplementedFeatureBadCtorParamOrAssembly, _assemblyFileName, (lastType == null) ? "" : lastType.FullName)); return(retval); } catch (Exception e) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.InspectingAssemblyThrew, e.GetType().Name, e.Message, _assemblyFileName)); return(retval); } AppDomain.CurrentDomain.ReflectionOnlyAssemblyResolve -= assemblyResolver; if (retval.Components.Count == 0 && _currentComponentType != PipelineComponentType.AddIn && _currentComponentType != PipelineComponentType.AddInBase) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, Res.NoAddInModelPartsFound, componentType, _assemblyFileName)); } #if ADDIN_VERBOSE_WARNINGS foreach (PipelineComponent c in retval.Components) { retval.Warnings.Add(String.Format(CultureInfo.CurrentCulture, "Found a {0}. Name: {1} Assembly: {2}", componentType, c.SimpleName, c.AssemblySimpleName)); } #endif return(retval); }
internal ResolverHelper(ContractComponent contract) { _contract = contract; }
// Detects two somewhat easy to hit user bugs, where the contract assembly // is loaded twice in different loader contexts, or where the add-in adapter // has been loaded in the host's assembly. If either of these happens, // then the transparent proxy will not be castable to the interface type, // and the remoting code will attempt to load the addin adapter's // type in the host's appdomain. // We explicitly do not allow the add-in adapter's assembly to leak // into this appdomain. If the contract assembly exists in the same // directory as the application, or in potentially other locations, it may // get loaded twice in different loader contexts by the CLR V2's loader. // This took about a week to debug, with experts from all the affected areas. private static void CheckForLoaderContextProblems(ContractComponent contract, AddInAdapter addinAdapter, Exception inner) { String contractAsmName = contract.TypeInfo.AssemblyName; String addinAdapterAsmName = addinAdapter.TypeInfo.AssemblyName; List <Assembly> contracts = new List <Assembly>(); List <Assembly> addinAdapters = new List <Assembly>(); foreach (Assembly a in AppDomain.CurrentDomain.GetAssemblies()) { String aName = a.GetName().FullName; if (aName == contractAsmName) { contracts.Add(a); } if (aName == addinAdapterAsmName) { addinAdapters.Add(a); } } if (addinAdapters.Count > 0) { StringBuilder locations = new StringBuilder(); foreach (Assembly a in addinAdapters) { locations.Append(Environment.NewLine); locations.Append(a.CodeBase); } Exception e = new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, Res.AddInAdapterLoadedInWrongAppDomain, addinAdapter.TypeInfo.AssemblyName, addinAdapter.Location, locations.ToString()), inner); e.Data["Incorrectly loaded add-in adapters"] = addinAdapters; e.Data["Expected adapter location"] = addinAdapter.Location; if (contracts.Count > 1) { e.Data["Duplicate Contracts"] = contracts; } throw e; } if (contracts.Count > 1) { StringBuilder locations = new StringBuilder(); foreach (Assembly a in contracts) { locations.Append(Environment.NewLine); locations.Append(a.CodeBase); } Exception e = new InvalidOperationException( String.Format(CultureInfo.CurrentCulture, Res.ContractAssemblyLoadedMultipleTimes, contract.TypeInfo.AssemblyName, contract.Location, locations.ToString()), inner); e.Data["Incorrectly loaded contracts"] = contracts; e.Data["Expected contract location"] = contract.Location; throw e; } else { // If you're seeing this in a debugger on V2 of the CLR, make sure you hook the assembly resolve event. // Otherwise, see if there was an ArgumentException thrown from an HAV's constructor. System.Diagnostics.Contracts.Contract.Assert(false, "Did the AddIn Model upgrade the contract to the default loader context using an assembly resolve event? Either that, or your HAV's constructor threw an ArgumentException"); } }
private static T ActivateInAppDomain <T>(AddInToken pipeline, AppDomain domain, AddInControllerImpl controller, bool weOwn) { ContractComponent contract = pipeline._contract; HostAdapter hostAdapter = pipeline._hostAdapter; bool usingHostAppDomain = domain == AppDomain.CurrentDomain; //begin direct connect code if (AddInToken.EnableDirectConnect && !weOwn && usingHostAppDomain) { Type havType = typeof(T); TypeInfo havTypeInfo = new TypeInfo(havType); if (pipeline._addinBase.CanDirectConnectTo(havTypeInfo)) { // Connect directly for best performance. // Assert permission to the specific Addin directory only. PermissionSet permissionSet = new PermissionSet(PermissionState.None); permissionSet.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, Path.GetDirectoryName(pipeline._addin.Location))); permissionSet.Assert(); Assembly addInAssembly = Assembly.LoadFrom(pipeline._addin.Location); // Type addinType = addInAssembly.GetType(pipeline._addin.TypeInfo.FullName, true); Object addIn = addinType.GetConstructor(new Type[0]).Invoke(new Object[0]); System.Diagnostics.Contracts.Contract.Assert(addIn != null, "Bypass couldn't create the add-in"); // remember the addin directly as the HAV. Set the contract to null. controller.AssociateWithHostAddinView(addIn, null); return((T)addIn); } } //end direct connect code // Use Activator.CreateInstance instead of AppDomain.CreateInstanceAndUnwrap // because Activator will do the appropriate security asserts in the // remote appdomain. Type t = typeof(ActivationWorker); Object[] args = new Object[] { pipeline }; ObjectHandle objHandle = Activator.CreateInstance(domain, t.Assembly.FullName, t.FullName, false, BindingFlags.Instance | BindingFlags.NonPublic, null, args, null, null); ActivationWorker activationWorker = (ActivationWorker)objHandle.Unwrap(); activationWorker.UsingHostAppDomain = usingHostAppDomain; System.AddIn.Contract.IContract addInContract = null; try { addInContract = activationWorker.Activate(); } catch (Exception ex) { CheckForDuplicateAssemblyProblems(pipeline, ex); throw; } if (weOwn) { domain.SetData(ContractHandle.s_appDomainOwner, addInContract); } controller.ActivationWorker = activationWorker; T hav = AdaptToHost <T>(pipeline, addInContract); controller.AssociateWithHostAddinView(hav, addInContract); return(hav); }