#include "clrprivbindercoreclr.h"
#include "clrprivbinderassemblyloadcontext.h"
// Helper function in the VM, invoked by the Binder, to invoke the host assembly resolver
-extern HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly);
+extern HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly);
// Helper to check if we have a host assembly resolver set
extern BOOL RuntimeCanUseAppPathAssemblyResolver(DWORD adid);
#endif //CROSSGEN_COMPILE
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
-HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin,
+HRESULT AssemblyBinder::BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin,
/* in */ AssemblyName *pAssemblyName,
/* in */ IAssemblyName *pIAssemblyName,
/* out */ Assembly **ppAssembly)
HRESULT hr = E_FAIL;
BINDER_LOG_ENTER(W("AssemblyBinder::BindUsingHostAssemblyResolver"));
- _ASSERTE(pLoadContextToBindWithin != NULL);
-
- // Get the application context within which the assembly will be bound and loaded
- ApplicationContext *pApplicationContext = pLoadContextToBindWithin->GetAppContext();
- _ASSERTE(pApplicationContext != NULL);
+ _ASSERTE(pManagedAssemblyLoadContextToBindWithin != NULL);
// Call into the VM to use the HostAssemblyResolver and load the assembly
ICLRPrivAssembly *pLoadedAssembly = NULL;
- hr = RuntimeInvokeHostAssemblyResolver(pLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly);
+ hr = RuntimeInvokeHostAssemblyResolver(pManagedAssemblyLoadContextToBindWithin, pIAssemblyName, &pLoadedAssembly);
if (SUCCEEDED(hr))
{
_ASSERTE(pLoadedAssembly != NULL);
// Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call
// of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly
// that has been loaded.
- hr = AssemblyBinder::BindUsingHostAssemblyResolver(this, pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly);
+ hr = AssemblyBinder::BindUsingHostAssemblyResolver(GetManagedAssemblyLoadContext(), pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly);
if (SUCCEEDED(hr))
{
// We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.
IF_FAIL_GO(pAssemblyName->Init(pIAssemblyName));
hr = BindAssemblyByNameWorker(pAssemblyName, &pCoreCLRFoundAssembly, false /* excludeAppPaths */);
+
+#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
+ if ((hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND)) ||
+ (hr == FUSION_E_APP_DOMAIN_LOCKED) || (hr == FUSION_E_REF_DEF_MISMATCH))
+ {
+ // If we are here, one of the following is possible:
+ //
+ // 1) The assembly has not been found in the current binder's application context (i.e. it has not already been loaded), OR
+ // 2) An assembly with the same simple name was already loaded in the context of the current binder but we ran into a Ref/Def
+ // mismatch (either due to version difference or strong-name difference).
+ //
+ // Thus, if default binder has been overridden, then invoke it in an attempt to perform the binding for it make the call
+ // of what to do next. The host-overridden binder can either fail the bind or return reference to an existing assembly
+ // that has been loaded.
+
+ // Attempt to resolve the assembly via managed TPA ALC instance if one exists
+ INT_PTR pManagedAssemblyLoadContext = GetManagedAssemblyLoadContext();
+ if (pManagedAssemblyLoadContext != NULL)
+ {
+ hr = AssemblyBinder::BindUsingHostAssemblyResolver(pManagedAssemblyLoadContext, pAssemblyName, pIAssemblyName, &pCoreCLRFoundAssembly);
+ if (SUCCEEDED(hr))
+ {
+ // We maybe returned an assembly that was bound to a different AssemblyLoadContext instance.
+ // In such a case, we will not overwrite the binding context (which would be wrong since it would not
+ // be present in the cache of the current binding context).
+ if (pCoreCLRFoundAssembly->GetBinder() == NULL)
+ {
+ pCoreCLRFoundAssembly->SetBinder(this);
+ }
+ }
+ }
+ }
+#endif // defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
+
IF_FAIL_GO(hr);
-
+
*ppAssembly = pCoreCLRFoundAssembly.Extract();
Exit:;
/* in */ LPCTSTR szMDAssemblyPath = NULL);
#if defined(FEATURE_HOST_ASSEMBLY_RESOLVER) && !defined(DACCESS_COMPILE) && !defined(CROSSGEN_COMPILE) && !defined(MDILNIGEN)
- static HRESULT BindUsingHostAssemblyResolver (/* in */ CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin,
+ static HRESULT BindUsingHostAssemblyResolver (/* in */ INT_PTR pManagedAssemblyLoadContextToBindWithin,
/* in */ AssemblyName *pAssemblyName,
/* in */ IAssemblyName *pIAssemblyName,
/* out */ Assembly **ppAssembly);
BINDER_SPACE::Assembly **ppCoreCLRFoundAssembly,
bool excludeAppPaths);
- INT_PTR GetManagedTPABinderInstance()
+ INT_PTR GetManagedAssemblyLoadContext()
{
return m_ptrManagedAssemblyLoadContext;
}
- void SetManagedTPABinderInstance(INT_PTR ptrManagedTPABinderInstance)
+ void SetManagedAssemblyLoadContext(INT_PTR ptrManagedTPABinderInstance)
{
_ASSERTE(m_ptrManagedAssemblyLoadContext == NULL);
<Member Name="get_Default" />
<Member Name="SetProfileOptimizationRoot(System.String)" />
<Member Name="StartProfileOptimization(System.String)" />
+ <Member MemberType="Event" Name="Resolving" />
</Type>
<Type Name="System.Reflection.Metadata.AssemblyExtensions">
<Member Name="TryGetRawMetadata(System.Reflection.Assembly,System.Byte*@,System.Int32@)"/>
GCHandle gchALC = GCHandle.Alloc(this);
IntPtr ptrALC = GCHandle.ToIntPtr(gchALC);
m_pNativeAssemblyLoadContext = InitializeAssemblyLoadContext(ptrALC, fRepresentsTPALoadContext);
+
+ // Initialize the resolve event handler to be null by default
+ Resolving = null;
}
[DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
return context.LoadFromAssemblyName(assemblyName);
}
+ private Assembly GetFirstResolvedAssembly(AssemblyName assemblyName)
+ {
+ Assembly resolvedAssembly = null;
+
+ Func<AssemblyLoadContext, AssemblyName, Assembly> assemblyResolveHandler = Resolving;
+
+ if (assemblyResolveHandler != null)
+ {
+ // Loop through the event subscribers and return the first non-null Assembly instance
+ Delegate [] arrSubscribers = assemblyResolveHandler.GetInvocationList();
+ for(int i = 0; i < arrSubscribers.Length; i++)
+ {
+ resolvedAssembly = ((Func<AssemblyLoadContext, AssemblyName, Assembly>)arrSubscribers[i])(this, assemblyName);
+ if (resolvedAssembly != null)
+ {
+ break;
+ }
+ }
+ }
+
+ return resolvedAssembly;
+ }
+
public Assembly LoadFromAssemblyName(AssemblyName assemblyName)
{
// AssemblyName is mutable. Cache the expected name before anybody gets a chance to modify it.
Assembly assembly = Load(assemblyName);
if (assembly == null)
{
+ // Invoke the AssemblyResolve event callbacks if wired up
+ assembly = GetFirstResolvedAssembly(assemblyName);
+ }
+
+ if (assembly == null)
+ {
throw new FileNotFoundException(Environment.GetResourceString("IO.FileLoad"), requestedSimpleName);
}
#endif // FEATURE_MULTICOREJI
}
+ public event Func<AssemblyLoadContext, AssemblyName, Assembly> Resolving;
+
// Contains the reference to VM's representation of the AssemblyLoadContext
private IntPtr m_pNativeAssemblyLoadContext;
[System.Security.SecuritySafeCritical]
protected override Assembly Load(AssemblyName assemblyName)
{
- return Assembly.Load(assemblyName);
+ // We were loading an assembly into TPA ALC that was not found on TPA list. As a result we are here.
+ // Returning null will result in the AssemblyResolve event subscribers to be invoked to help resolve the assembly.
+ return null;
}
}
}
}
// Returns S_OK if the assembly was successfully loaded
-HRESULT RuntimeInvokeHostAssemblyResolver(CLRPrivBinderAssemblyLoadContext *pLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly)
+HRESULT RuntimeInvokeHostAssemblyResolver(INT_PTR pManagedAssemblyLoadContextToBindWithin, IAssemblyName *pIAssemblyName, ICLRPrivAssembly **ppLoadedAssembly)
{
CONTRACTL
{
GCPROTECT_BEGIN(_gcRefs);
- // Get the pointer to the managed assembly load context
- INT_PTR ptrManagedAssemblyLoadContext = pLoadContextToBindWithin->GetManagedAssemblyLoadContext();
-
// Prepare to invoke System.Runtime.Loader.AssemblyLoadContext.Resolve method.
//
// First, initialize an assembly spec for the requested assembly
// Setup the arguments for the call
ARG_SLOT args[2] =
{
- PtrToArgSlot(ptrManagedAssemblyLoadContext), // IntPtr for managed assembly load context instance
+ PtrToArgSlot(pManagedAssemblyLoadContextToBindWithin), // IntPtr for managed assembly load context instance
ObjToArgSlot(_gcRefs.oRefAssemblyName), // AssemblyName instance
};
{
// We are initializing the managed instance of Assembly Load Context that would represent the TPA binder.
// First, confirm we do not have an existing managed ALC attached to the TPA binder.
- INT_PTR ptrTPAAssemblyLoadContext = pTPABinderContext->GetManagedTPABinderInstance();
+ INT_PTR ptrTPAAssemblyLoadContext = pTPABinderContext->GetManagedAssemblyLoadContext();
if ((ptrTPAAssemblyLoadContext != NULL) && (ptrTPAAssemblyLoadContext != ptrManagedAssemblyLoadContext))
{
COMPlusThrow(kInvalidOperationException, IDS_HOST_ASSEMBLY_RESOLVER_INCOMPATIBLE_TPA_BINDING_CONTEXT);
}
// Attach the managed TPA binding context with the native one.
- pTPABinderContext->SetManagedTPABinderInstance(ptrManagedAssemblyLoadContext);
+ pTPABinderContext->SetManagedAssemblyLoadContext(ptrManagedAssemblyLoadContext);
ptrNativeAssemblyLoadContext = reinterpret_cast<INT_PTR>(pTPABinderContext);
}