当CLR实例化 PEAssembly 的时候,会调用 PEAssemblyHolder pFile(pDomain->BindAssemblySpec(this, fThrowOnFileNotFound, fRaisePrebindEvents, pCallerStackMark));
实际上分为三步:
1.通过 pSpec->Bind()获取到bindResult,然后调用PEAssembly::Open new 一个 PEAssembly实例,参数就是bindReuslt
2.pspec 的bind函数会返回pPrivAsm。代码为:
hr = pTPABinder->Bind(assemblyDisplayName,
m_wszCodeBase,
GetParentAssembly()? GetParentAssembly()->GetFile():NULL,
fNgenExplicitBind,
fExplicitBindToNativeImage,
&pPrivAsm);
当返回pprivasm之后, 会将pprivasm通过函数GetAssemblyFromPrivAssemblyFast, result = BINDER_SPACE::GetAssemblyFromPrivAssemblyFast(pPrivAsm.Extract());
最后 pResult->Init(result,fIsInGAC, fIsOnTpaList);返回 上一步的bindResult
3.ptabinder->bind函数会调用获取pasm程序集句柄,这个句柄实际上为 首先调用 binderresult,然后通过binderresult获取到hostbinderresult,最后通过hostbinderresult返回程序pasm
4.当获取binderresult 的时候,他会实例化peimage 和 pasm,然后设置binderresult。
具体就是以上四个步骤:
部分代码如下:
if (pSpec->HasUniqueIdentity())
{
HRESULT hrBindResult = S_OK;
PEAssemblyHolder result;
EX_TRY
{
if (!IsCached(pSpec))
{
{
bool fAddFileToCache = false;
BOOL fIsWellKnown = FALSE;
// Use CoreClr's fusion alternative
CoreBindResult bindResult;
pSpec->Bind(this, fThrowOnFileNotFound, &bindResult, FALSE /* fNgenExplicitBind */, FALSE /* fExplicitBindToNativeImage */, pCallerStackMark);
hrBindResult = bindResult.GetHRBindResult();
if (bindResult.Found())
{
if (SystemDomain::SystemFile() && bindResult.IsMscorlib())
{
// Avoid rebinding to another copy of mscorlib
result = SystemDomain::SystemFile();
result.SuppressRelease(); // Didn't get a refcount
}
else
{
// IsSystem on the PEFile should be false, even for mscorlib satellites
result = PEAssembly::Open(&bindResult,
FALSE, pSpec->IsIntrospectionOnly());
}
fAddFileToCache = true;
// Setup the reference to the binder, which performed the bind, into the AssemblySpec
ICLRPrivBinder* pBinder = result->GetBindingContext();
_ASSERTE(pBinder != NULL);
pSpec->SetBindingContext(pBinder);
}
if (fAddFileToCache)
{
if (pSpec->CanUseWithBindingCache() && result->CanUseWithBindingCache())
{
// Failure to add simply means someone else beat us to it. In that case
// the FindCachedFile call below (after catch block) will update result
// to the cached value.
AddFileToCache(pSpec, result, TRUE /*fAllowFailure*/);
}
}
else if (!fIsWellKnown)
{
_ASSERTE(fThrowOnFileNotFound == FALSE);
// Don't trigger the resolve event for the CoreLib satellite assembly. A misbehaving resolve event may
// return an assembly that does not match, and this can cause recursive resource lookups during error
// reporting. The CoreLib satellite assembly is loaded from relative locations based on the culture, see
// AssemblySpec::Bind().
if (!pSpec->IsMscorlibSatellite())
{
// Trigger the resolve event also for non-throw situation.
// However, this code path will behave as if the resolve handler has thrown,
// that is, not trigger an MDA.
AssemblySpec NewSpec(this);
AssemblySpec *pFailedSpec = NULL;
fForceReThrow = TRUE; // Managed resolve event handler can throw
// Purposly ignore return value
PostBindResolveAssembly(pSpec, &NewSpec, hrBindResult, &pFailedSpec);
}
}
}
}
}
EX_CATCH
{
Exception *ex = GET_EXCEPTION();
AssemblySpec NewSpec(this);
AssemblySpec *pFailedSpec = NULL;
// Let transient exceptions or managed resolve event handler exceptions propagate
if (ex->IsTransient() || fForceReThrow)
{
EX_RETHROW;
}
{
// This is not executed for SO exceptions so we need to disable the backout
// stack validation to prevent false violations from being reported.
DISABLE_BACKOUT_STACK_VALIDATION;
BOOL fFailure = PostBindResolveAssembly(pSpec, &NewSpec, ex->GetHR(), &pFailedSpec);
if (fFailure)
{
BOOL bFileNotFoundException =
(EEFileLoadException::GetFileLoadKind(ex->GetHR()) == kFileNotFoundException);
if (!bFileNotFoundException)
{
fFailure = AddExceptionToCache(pFailedSpec, ex);
} // else, fFailure stays TRUE
// Effectively, fFailure == bFileNotFoundException || AddExceptionToCache(pFailedSpec, ex)
// Only throw this exception if we are the first in the cache
if (fFailure)
{
//
// If the BindingFailure MDA is enabled, trigger one for this failure
// Note: TryResolveAssembly() can also throw if an AssemblyResolve event subscriber throws
// and the MDA isn't sent in this case (or for transient failure cases)
//
#ifdef MDA_SUPPORTED
MdaBindingFailure* pProbe = MDA_GET_ASSISTANT(BindingFailure);
if (pProbe)
{
// Transition to cooperative GC mode before using any OBJECTREFs.
GCX_COOP();
OBJECTREF exceptionObj = GET_THROWABLE();
GCPROTECT_BEGIN(exceptionObj)
{
pProbe->BindFailed(pFailedSpec, &exceptionObj);
}
GCPROTECT_END();
}
#endif
// In the same cases as for the MDA, store the failure information for DAC to read
if (IsDebuggerAttached()) {
FailedAssembly *pFailed = new FailedAssembly();
pFailed->Initialize(pFailedSpec, ex);
IfFailThrow(m_failedAssemblies.Append(pFailed));
}
if (!bFileNotFoundException || fThrowOnFileNotFound)
{
// V1.1 App-compatibility workaround. See VSW530166 if you want to whine about it.
//
// In Everett, if we failed to download an assembly because of a broken network cable,
// we returned a FileNotFoundException with a COR_E_FILENOTFOUND hr embedded inside
// (which would be exposed when marshaled to native.)
//
// In Whidbey, we now set the more appropriate INET_E_RESOURCE_NOT_FOUND hr. But
// the online/offline switch code in VSTO for Everett hardcoded a check for
// COR_E_FILENOTFOUND.
//
// So now, to keep that code from breaking, we have to remap INET_E_RESOURCE_NOT_FOUND
// back to COR_E_FILENOTFOUND. We're doing it here rather down in Fusion so as to affect
// the least number of callers.
if (ex->GetHR() == INET_E_RESOURCE_NOT_FOUND)
{
EEFileLoadException::Throw(pFailedSpec, COR_E_FILENOTFOUND, ex);
}
if (EEFileLoadException::CheckType(ex))
{
if (pFailedSpec == pSpec)
{
EX_RETHROW; //preserve the information
}
else
{
StackSString exceptionDisplayName, failedSpecDisplayName;
((EEFileLoadException*)ex)->GetName(exceptionDisplayName);
pFailedSpec->GetFileOrDisplayName(0, failedSpecDisplayName);
if (exceptionDisplayName.CompareCaseInsensitive(failedSpecDisplayName) == 0)
{
EX_RETHROW; // Throw the original exception. Otherwise, we'd throw an exception that contains the same message twice.
}
}
}
EEFileLoadException::Throw(pFailedSpec, ex->GetHR(), ex);
}
}
}
}
}
EX_END_CATCH(RethrowTerminalExceptions);
// Now, if it's a cacheable bind we need to re-fetch the result from the cache, as we may have been racing with another
// thread to store our result. Note that we may throw from here, if there is a cached exception.
// This will release the refcount of the current result holder (if any), and will replace
// it with a non-addref'ed result
if (pSpec->CanUseWithBindingCache() && (result== NULL || result->CanUseWithBindingCache()))
{
result = FindCachedFile(pSpec);
if (result != NULL)
result->AddRef();
}
return result.Extract();
}