summaryrefslogtreecommitdiff
path: root/src/vm/clrprivbinderappx.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/vm/clrprivbinderappx.cpp')
-rw-r--r--src/vm/clrprivbinderappx.cpp1057
1 files changed, 1057 insertions, 0 deletions
diff --git a/src/vm/clrprivbinderappx.cpp b/src/vm/clrprivbinderappx.cpp
new file mode 100644
index 0000000000..b55ece4556
--- /dev/null
+++ b/src/vm/clrprivbinderappx.cpp
@@ -0,0 +1,1057 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+
+
+#include "common.h" // precompiled header
+#include "assemblyusagelogmanager.h"
+
+//=====================================================================================================================
+#include "clrprivbinderappx.h"
+//CLRPrivBinderAppX * CLRPrivBinderAppX::s_pSingleton = nullptr;
+SPTR_IMPL_INIT(CLRPrivBinderAppX, CLRPrivBinderAppX, s_pSingleton, nullptr);
+
+#ifndef DACCESS_COMPILE
+//=====================================================================================================================
+#include "appxutil.h"
+#include "clrprivbinderutil.h"
+#include "fusionlogging.h"
+#include "clrprivtypecachewinrt.h"
+#include "fusionp.h"
+
+using namespace CLRPrivBinderUtil;
+
+//=====================================================================================================================
+CLRPrivBinderAppX::CLRPrivBinderAppX(LPCWSTR * rgwzAltPath, UINT cAltPaths)
+ : m_MapReadLock(CrstCLRPrivBinderMaps,
+ static_cast<CrstFlags>(CRST_DEBUG_ONLY_CHECK_FORBID_SUSPEND_THREAD |
+ CRST_GC_NOTRIGGER_WHEN_TAKEN |
+ CRST_DEBUGGER_THREAD |
+ // FindAssemblyBySpec complicates matters, which needs to take the m_MapReadLock.
+ // But FindAssemblyBySpec cannot switch to preemptive mode, as that would trigger
+ // a GC. Since this is a leaf lock, and since it does not make any calls out of
+ // the runtime, this lock can be taken in cooperative mode if the locked scope is
+ // also marked as ForbidSuspend (since FindAssemblyBySpec can be called by
+ // the debugger and the profiler). TODO: it would be nice to be able to specify
+ // this flag for just the specific places where it is necessary rather than for
+ // the lock as a whole.
+ CRST_UNSAFE_ANYMODE)),
+ m_MapWriteLock(CrstCLRPrivBinderMapsAdd, CRST_DEFAULT),
+ m_cAltPaths(cAltPaths),
+ m_fCanUseNativeImages(TRUE),
+ m_pParentBinder(nullptr),
+ m_pFusionBinder(nullptr),
+ m_pWinRTBinder(nullptr),
+
+ // Note: the first CLRPrivBinderAppX object is created prior to runtime startup, so this code cannot call
+ // AppX::IsAppXDesignMode; however, FindAssemblyBySpec cannot call IsAppXDesignMode either because that would
+ // cause a GC_TRIGGERS violation for the GetAssemblyIfLoaded scenario. However this doesn't matter because
+ // the assembly map will be empty until at least the first call to BindAssemblyByName is made, at which point
+ // a call to IsAppXDesignMode can be made. Thus, we default to the most conversative setting and overwrite this
+ // value in BindAssemblyByName.
+ m_fusionBindingScope(CLRPrivBinderFusion::kBindingScope_FrameworkSubset)
+{
+ STANDARD_VM_CONTRACT;
+
+ // Copy altpaths
+ if (cAltPaths > 0)
+ {
+ m_rgAltPathsHolder = new NewArrayHolder<WCHAR>[cAltPaths];
+ m_rgAltPaths = new WCHAR *[cAltPaths];
+
+ for (UINT iAltPath = 0; iAltPath < cAltPaths; iAltPath++)
+ {
+ size_t cchAltPath = wcslen(rgwzAltPath[iAltPath]);
+ m_rgAltPathsHolder[iAltPath] = m_rgAltPaths[iAltPath] = new WCHAR[cchAltPath + 1];
+ wcscpy_s(m_rgAltPaths[iAltPath], cchAltPath + 1, rgwzAltPath[iAltPath]);
+ }
+ }
+
+#ifdef FEATURE_FUSION
+ IfFailThrow(RuntimeCreateCachingILFingerprintFactory(&m_pFingerprintFactory));
+#endif
+}
+
+//=====================================================================================================================
+CLRPrivBinderFusion::BindingScope CLRPrivBinderAppX::GetFusionBindingScope()
+{
+ WRAPPER_NO_CONTRACT;
+
+ m_fusionBindingScope = (AppX::IsAppXDesignMode() || (m_pParentBinder != nullptr))
+ ? CLRPrivBinderFusion::kBindingScope_FrameworkAll
+ : CLRPrivBinderFusion::kBindingScope_FrameworkSubset;
+
+ return m_fusionBindingScope;
+}
+
+//=====================================================================================================================
+CLRPrivBinderAppX::~CLRPrivBinderAppX()
+{
+ WRAPPER_NO_CONTRACT;
+
+ m_NameToAssemblyMap.RemoveAll();
+ AssemblyUsageLogManager::UnRegisterBinderFromUsageLog((UINT_PTR)this);
+
+ clr::SafeRelease(m_pWinRTBinder);
+}
+
+//=====================================================================================================================
+CLRPrivBinderAppX *
+CLRPrivBinderAppX::GetOrCreateBinder()
+{
+ STANDARD_VM_CONTRACT;
+ HRESULT hr = S_OK;
+
+ if (s_pSingleton == nullptr)
+ {
+ ReleaseHolder<IAssemblyUsageLog> pNewUsageLog;
+ IfFailThrow(AssemblyUsageLogManager::GetUsageLogForContext(W("App"), AppX::GetHeadPackageMoniker(), &pNewUsageLog));
+
+ ReleaseHolder<CLRPrivBinderAppX> pBinder;
+ pBinder = clr::SafeAddRef(new CLRPrivBinderAppX(nullptr, 0));
+
+ pBinder->m_pFusionBinder = clr::SafeAddRef(new CLRPrivBinderFusion());
+
+ CLRPrivTypeCacheWinRT * pWinRtTypeCache = CLRPrivTypeCacheWinRT::GetOrCreateTypeCache();
+ pBinder->m_pWinRTBinder = clr::SafeAddRef(new CLRPrivBinderWinRT(
+ pBinder,
+ pWinRtTypeCache,
+ nullptr, // rgwzAltPath
+ 0, // cAltPaths
+ CLRPrivBinderWinRT::NamespaceResolutionKind_WindowsAPI,
+ TRUE // fCanUseNativeImages
+ ));
+
+ if (InterlockedCompareExchangeT<decltype(s_pSingleton)>(&s_pSingleton, pBinder, nullptr) == nullptr)
+ pBinder.SuppressRelease();
+
+ // Register binder with usagelog infrastructure.
+ UINT_PTR binderId;
+ IfFailThrow(pBinder->GetBinderID(&binderId));
+ IfFailThrow(AssemblyUsageLogManager::RegisterBinderWithUsageLog(binderId, pNewUsageLog));
+
+ // Create and register WinRT usage log
+ ReleaseHolder<IAssemblyUsageLog> pNewWinRTUsageLog;
+ IfFailThrow(AssemblyUsageLogManager::GetUsageLogForContext(W("WinRT"), AppX::GetHeadPackageMoniker(), &pNewWinRTUsageLog));
+
+ UINT_PTR winRTBinderId;
+ IfFailThrow(pBinder->m_pWinRTBinder->GetBinderID(&winRTBinderId));
+ IfFailThrow(AssemblyUsageLogManager::RegisterBinderWithUsageLog(winRTBinderId, pNewWinRTUsageLog));
+ }
+
+ return s_pSingleton;
+}
+
+//=====================================================================================================================
+// Used only for designer binding context
+CLRPrivBinderAppX * CLRPrivBinderAppX::CreateParentedBinder(
+ ICLRPrivBinder * pParentBinder,
+ CLRPrivTypeCacheWinRT * pWinRtTypeCache,
+ LPCWSTR * rgwzAltPath,
+ UINT cAltPaths,
+ BOOL fCanUseNativeImages)
+{
+ STANDARD_VM_CONTRACT;
+ HRESULT hr = S_OK;
+
+ ReleaseHolder<CLRPrivBinderAppX> pBinder;
+ pBinder = clr::SafeAddRef(new CLRPrivBinderAppX(rgwzAltPath, cAltPaths));
+
+ pBinder->m_pParentBinder = clr::SafeAddRef(pParentBinder);
+ pBinder->m_fCanUseNativeImages = fCanUseNativeImages;
+
+ // We want to share FusionBinder with pParentBinder (which bubbles up through the chain of binders to the global AppXBinder code:s_pSingleton)
+ // Ideally we would get the FusionBinder from pParentBinder (via casting to a new interface). It is much easier just to fetch it from
+ // the global AppX binder directly
+ pBinder->m_pFusionBinder = clr::SafeAddRef(s_pSingleton->GetFusionBinder());
+
+ if (cAltPaths > 0)
+ {
+ pBinder->m_pWinRTBinder = clr::SafeAddRef(new CLRPrivBinderWinRT(
+ pBinder,
+ pWinRtTypeCache,
+ rgwzAltPath,
+ cAltPaths,
+ CLRPrivBinderWinRT::NamespaceResolutionKind_WindowsAPI,
+ fCanUseNativeImages));
+ }
+
+ pBinder.SuppressRelease();
+ return pBinder;
+}
+
+//=====================================================================================================================
+HRESULT CLRPrivBinderAppX::BindAppXAssemblyByNameWorker(
+ IAssemblyName * pIAssemblyName,
+ DWORD dwAppXBindFlags,
+ CLRPrivAssemblyAppX ** ppAssembly)
+{
+ STANDARD_VM_CONTRACT;
+ HRESULT hr = S_OK;
+
+ fusion::logging::StatusScope logStatus(0, ID_FUSLOG_BINDING_STATUS_IMMERSIVE, &hr);
+
+ VALIDATE_ARG_RET(pIAssemblyName != nullptr);
+ VALIDATE_ARG_RET((dwAppXBindFlags & ABF_BindIL) == ABF_BindIL);
+ VALIDATE_ARG_RET(ppAssembly != nullptr);
+
+ DWORD dwContentType = AssemblyContentType_Default;
+ IfFailRet(hr = fusion::util::GetProperty(pIAssemblyName, ASM_NAME_CONTENT_TYPE, &dwContentType));
+ if ((hr == S_OK) && (dwContentType != AssemblyContentType_Default))
+ {
+ IfFailRet(CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT);
+ }
+
+ ReleaseHolder<CLRPrivAssemblyAppX> pAssembly;
+
+ // Get the simple name.
+ WCHAR wzSimpleName[_MAX_PATH];
+ DWORD cchSimpleName = _MAX_PATH;
+ IfFailRet(pIAssemblyName->GetName(&cchSimpleName, wzSimpleName));
+
+ { // Look for previous successful bind. Host callouts are now forbidden.
+ ForbidSuspendThreadCrstHolder lock(&m_MapReadLock);
+ pAssembly = clr::SafeAddRef(m_NameToAssemblyMap.Lookup(wzSimpleName));
+ }
+
+ if (pAssembly == nullptr)
+ {
+ ReleaseHolder<ICLRPrivResource> pResourceIL;
+ ReleaseHolder<ICLRPrivResource> pResourceNI;
+
+ // Create assembly identity using the simple name. For successful binds this will be updated
+ // with the full assembly identity in the VerifyBind callback.
+ NewHolder<AssemblyIdentity> pIdentity = new AssemblyIdentity();
+ IfFailRet(pIdentity->Initialize(wzSimpleName));
+
+ //
+ // Check the head package first to see if this matches an EXE, then check
+ // all packages to see if this matches a DLL.
+ //
+ WCHAR wzFilePath[_MAX_PATH];
+ {
+ hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
+
+ if (FAILED(hr))
+ {
+ // Create simple name with .EXE extension
+ WCHAR wzSimpleFileName[_MAX_PATH];
+ wcscpy_s(wzSimpleFileName, NumItems(wzSimpleFileName), wzSimpleName);
+ wcscat_s(wzSimpleFileName, NumItems(wzSimpleFileName), W(".EXE"));
+
+ // Search for the file using AppX::FileFileInCurrentPackage helper.
+ UINT32 cchFilePath = NumItems(wzFilePath);
+ hr = AppX::FindFileInCurrentPackage(
+ wzSimpleFileName,
+ &cchFilePath,
+ wzFilePath,
+ PACKAGE_FILTER_CLR_DEFAULT,
+ (PCWSTR *)(void *)m_rgAltPaths,
+ m_cAltPaths,
+ m_pParentBinder != NULL ? AppX::FindFindInPackageFlags_SkipCurrentPackageGraph : AppX::FindFindInPackageFlags_None);
+ }
+
+ if (FAILED(hr))
+ {
+ // Create simple name with .DLL extension
+ WCHAR wzSimpleFileName[_MAX_PATH];
+ wcscpy_s(wzSimpleFileName, NumItems(wzSimpleFileName), wzSimpleName);
+ wcscat_s(wzSimpleFileName, NumItems(wzSimpleFileName), W(".DLL"));
+
+ // Search for the file using AppX::FileFileInCurrentPackage helper
+ UINT32 cchFilePath = NumItems(wzFilePath);
+ hr = AppX::FindFileInCurrentPackage(
+ wzSimpleFileName,
+ &cchFilePath,
+ wzFilePath,
+ PACKAGE_FILTER_CLR_DEFAULT,
+ (PCWSTR *)(void *)m_rgAltPaths,
+ m_cAltPaths,
+ m_pParentBinder != NULL ? AppX::FindFindInPackageFlags_SkipCurrentPackageGraph : AppX::FindFindInPackageFlags_None);
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ fusion::logging::LogMessage(0, ID_FUSLOG_BINDING_STATUS_FOUND, wzFilePath);
+ }
+ else
+ {
+ // Cache the bind failure result before returning. Careful not to overwrite the bind result with the cache insertion result.
+ HRESULT hrResult = hr;
+ IfFailRet(CacheBindResult(pIdentity, hr));
+ if (hr == S_OK)
+ { // Cache now owns identity object lifetime.
+ pIdentity.SuppressRelease();
+ }
+ hr = hrResult;
+ }
+ IfFailRet(hr);
+ }
+
+ NewHolder<CLRPrivResourcePathImpl> pResourcePath = new CLRPrivResourcePathImpl(wzFilePath);
+ IfFailRet(pResourcePath->QueryInterface(__uuidof(ICLRPrivResource), (LPVOID*)&pResourceIL));
+ pResourcePath.SuppressRelease();
+
+ // Create an IBindResult and provide it to the new CLRPrivAssemblyAppX object.
+ ReleaseHolder<IBindResult> pIBindResult = ToInterface<IBindResult>(
+ new CLRPrivAssemblyBindResultWrapper(pIAssemblyName, wzFilePath, m_pFingerprintFactory));
+
+
+ // Create the new CLRPrivAssemblyAppX object.
+ NewHolder<CLRPrivAssemblyAppX> pAssemblyObj =
+ new CLRPrivAssemblyAppX(pIdentity, this, pResourceIL, pIBindResult);
+
+ //
+ // Check cache. If someone beat us then use it instead; otherwise add new ICLRPrivAssembly.
+ //
+ do
+ {
+ // Because the read lock must be taken within a ForbidSuspend region, use AddInPhases.
+ if (m_NameToAssemblyMap.CheckAddInPhases<ForbidSuspendThreadCrstHolder, CrstHolder>(
+ pAssemblyObj, m_MapReadLock, m_MapWriteLock, pAssemblyObj.GetValue()))
+ {
+ { // Careful not to allow the cache insertion result to overwrite the bind result.
+ HRESULT hrResult = hr;
+ IfFailRet(CacheBindResult(pIdentity, hr));
+ if (hr == S_OK)
+ { // Cache now owns identity object lifetime, but ~CLRPrivBinderAssembly
+ // can also remove the identity from the cache prior to cache deletion.
+ pIdentity.SuppressRelease();
+ }
+ hr = hrResult;
+ }
+
+ pAssembly = pAssemblyObj.Extract();
+ }
+ else
+ {
+ ForbidSuspendThreadCrstHolder lock(&m_MapReadLock);
+ pAssembly = clr::SafeAddRef(m_NameToAssemblyMap.Lookup(wzSimpleName));
+ }
+ }
+ while (pAssembly == nullptr); // Keep looping until we find the existing one, or add a new one
+ }
+
+ _ASSERTE(pAssembly != nullptr);
+
+ if (((dwAppXBindFlags & ABF_BindNI) == ABF_BindNI) &&
+ m_fCanUseNativeImages)
+ {
+ //
+ // Look to see if there's a native image available.
+ //
+
+ // Fire BindingNgenPhaseStart ETW event if enabled.
+ {
+ InlineSString<128> ssAssemblyName;
+ FireEtwBindingNgenPhaseStart(
+ (AppDomain::GetCurrentDomain()->GetId().m_dwId),
+ LOADCTX_TYPE_HOSTED,
+ ETWFieldUnused,
+ ETWLoaderLoadTypeNotAvailable,
+ NULL,
+ FusionBind::GetAssemblyNameDisplayName(pIAssemblyName, ssAssemblyName, ASM_DISPLAYF_FULL).GetUnicode(),
+ GetClrInstanceId());
+ }
+
+ ReleaseHolder<IBindResult> pIBindResultIL;
+ IfFailRet(pAssembly->GetIBindResult(&pIBindResultIL));
+ _ASSERTE(pIBindResultIL != nullptr);
+
+ NewArrayHolder<WCHAR> wzZapSet = DuplicateStringThrowing(g_pConfig->ZapSet());
+ NativeConfigData cfgData = {
+ wzZapSet,
+ PEFile::GetNativeImageConfigFlags()
+ };
+
+ IfFailRet(BindToNativeAssembly(
+ pIBindResultIL, &cfgData, static_cast<IBindContext*>(this), fusion::logging::GetCurrentFusionBindLog()));
+
+ // Ensure that the native image found above in BindToNativeAssembly is reported as existing in the CLRPrivAssembly object
+ if (hr == S_OK)
+ {
+ ReleaseHolder<ICLRPrivResource> pNIImageResource;
+ // This will make GetAvailableImageTypes return that a native image exists.
+ IfFailRet(pAssembly->GetImageResource(ASSEMBLY_IMAGE_TYPE_NATIVE, NULL, &pNIImageResource));
+#ifdef _DEBUG
+ DWORD dwImageTypes;
+
+ _ASSERTE(SUCCEEDED(pAssembly->GetAvailableImageTypes(&dwImageTypes)));
+ _ASSERTE((dwImageTypes & ASSEMBLY_IMAGE_TYPE_NATIVE) == ASSEMBLY_IMAGE_TYPE_NATIVE);
+#endif
+ }
+
+ // Fire BindingNgenPhaseEnd ETW event if enabled.
+ {
+ InlineSString<128> ssAssemblyName;
+ FireEtwBindingNgenPhaseEnd(
+ (AppDomain::GetCurrentDomain()->GetId().m_dwId),
+ LOADCTX_TYPE_HOSTED,
+ ETWFieldUnused,
+ ETWLoaderLoadTypeNotAvailable,
+ NULL,
+ FusionBind::GetAssemblyNameDisplayName(pIAssemblyName, ssAssemblyName, ASM_DISPLAYF_FULL).GetUnicode(),
+ GetClrInstanceId());
+ }
+
+ // BindToNativeAssembly can return S_FALSE, but this could be misleading.
+ if (hr == S_FALSE)
+ hr = S_OK;
+ }
+
+ if (SUCCEEDED(hr))
+ {
+ *ppAssembly = pAssembly.Extract();
+ }
+
+ return hr;
+}
+
+//=====================================================================================================================
+HRESULT CLRPrivBinderAppX::BindAppXAssemblyByName(
+ IAssemblyName * pIAssemblyName,
+ DWORD dwAppXBindFlags,
+ ICLRPrivAssembly ** ppPrivAssembly)
+{
+ STANDARD_VM_CONTRACT;
+ HRESULT hr = S_OK;
+
+ ReleaseHolder<CLRPrivAssemblyAppX> pAppXAssembly;
+ IfFailRet(BindAppXAssemblyByNameWorker(pIAssemblyName, dwAppXBindFlags, &pAppXAssembly));
+ IfFailRet(pAppXAssembly->QueryInterface(__uuidof(ICLRPrivAssembly), (LPVOID*)ppPrivAssembly));
+
+ return hr;
+}
+
+//=====================================================================================================================
+HRESULT CLRPrivBinderAppX::PreBindAppXAssemblyByName(
+ IAssemblyName * pIAssemblyName,
+ DWORD dwAppXBindFlags,
+ IBindResult ** ppIBindResult)
+{
+ STANDARD_VM_CONTRACT;
+ HRESULT hr = S_OK;
+
+ VALIDATE_ARG_RET(pIAssemblyName != nullptr);
+ VALIDATE_ARG_RET(ppIBindResult != nullptr);
+
+
+ ReleaseHolder<CLRPrivAssemblyAppX> pAppXAssembly;
+ IfFailRet(BindAppXAssemblyByNameWorker(pIAssemblyName, dwAppXBindFlags, &pAppXAssembly));
+ IfFailRet(pAppXAssembly->GetIBindResult(ppIBindResult));
+
+ return hr;
+}
+
+//=====================================================================================================================
+HRESULT CLRPrivBinderAppX::FindAssemblyBySpec(
+ LPVOID pvAppDomain,
+ LPVOID pvAssemblySpec,
+ HRESULT * pResult,
+ ICLRPrivAssembly ** ppAssembly)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ CAN_TAKE_LOCK;
+ }
+ CONTRACTL_END
+
+ HRESULT hr = S_OK;
+
+ AppDomain* pAppDomain = reinterpret_cast<AppDomain*>(pvAppDomain);
+ AssemblySpec* pAssemblySpec = reinterpret_cast<AssemblySpec*>(pvAssemblySpec);
+ VALIDATE_PTR_RET(pAppDomain);
+ VALIDATE_PTR_RET(pAssemblySpec);
+ VALIDATE_PTR_RET(pResult);
+ VALIDATE_PTR_RET(ppAssembly);
+
+ //
+ // Follow the same order as a bind.
+ //
+
+ hr = CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT;
+
+ if (FAILED(hr))
+ {
+ _ASSERTE(m_pFusionBinder != nullptr);
+ hr = m_pFusionBinder->FindFusionAssemblyBySpec(pAppDomain, pAssemblySpec, m_fusionBindingScope, pResult, ppAssembly);
+ }
+
+ // See comment in code:CLRPrivBinderAppX::BindAssemblyByName for explanation of this conditional.
+ if (hr == CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT)
+ {
+ if (FAILED(hr) && (m_pWinRTBinder != nullptr))
+ {
+ hr = m_pWinRTBinder->FindWinRTAssemblyBySpec(pAppDomain, pAssemblySpec, pResult, ppAssembly);
+ }
+
+ if (FAILED(hr))
+ {
+ AssemblyIdentity refId;
+ IfFailRet(refId.Initialize(pAssemblySpec));
+ bool fCheckParent = false;
+
+ { // Check for a previously-recorded bind. Host callouts are now forbidden.
+ ForbidSuspendThreadCrstHolder lock(&m_MapReadLock);
+ BindingRecordMap::element_t const * pKeyVal = m_BindingRecordMap.LookupPtr(&refId);
+
+ if (pKeyVal != nullptr)
+ {
+ //
+ // Previous bind occurred. If a failure result is cached then the binder would
+ // have tried the parent binder (if available) before returning.
+ //
+
+ AssemblyIdentity const & defId(*pKeyVal->Key());
+ BindingRecord const & record(pKeyVal->Value());
+
+ *ppAssembly = nullptr;
+ *pResult = record.hr;
+
+ if (SUCCEEDED(*pResult))
+ {
+ //
+ // Previous bind succeeded. Get the corresponding ICLRPrivAssembly.
+ //
+
+ // Check this binder for a match. Host callouts are now forbidden.
+ CLRPrivAssemblyAppX* pPrivAssembly = m_NameToAssemblyMap.Lookup(defId.Name);
+
+ if (pPrivAssembly == nullptr)
+ {
+ _ASSERTE_MSG(false, "Should never see success value and a null CLRPrivAssemblyAppX pointer.");
+ return (*pResult = E_UNEXPECTED);
+ }
+
+ _ASSERTE(pPrivAssembly->m_pIdentity != nullptr);
+ _ASSERTE(pPrivAssembly->m_pIdentity == &defId);
+
+ // Now check that the version and PKT values are compatible.
+ *pResult = CLRPrivBinderUtil::VerifyBind(refId, *pPrivAssembly->m_pIdentity);
+
+ if (SUCCEEDED(*pResult))
+ {
+ VERIFY(SUCCEEDED(pPrivAssembly->QueryInterface(__uuidof(ICLRPrivAssembly), (LPVOID*)ppAssembly)));
+ }
+
+ return S_OK;
+ }
+ else
+ {
+ //
+ // Previous bind failed. Check the parent binder (if available), but do it outside of this binder's lock.
+ //
+
+ fCheckParent = true;
+ }
+ }
+ else
+ {
+ //
+ // No previous bind occurred. Do not check the parent binder since this could result
+ // in an incorrect bind (if this binder would have bound to a different assembly).
+ //
+
+ return E_FAIL;
+ }
+ }
+
+ if (fCheckParent && m_pParentBinder != nullptr)
+ { // Check the parent (shared designer context) for a match.
+ hr = m_pParentBinder->FindAssemblyBySpec(pAppDomain, pAssemblySpec, pResult, ppAssembly);
+ }
+ }
+ }
+
+ // There are three possibilities upon exit:
+ // 1. Cache lookup failed, in which case FAILED(hr) == true
+ // 2. A binding failure was cached, in which case (1) == false && FAILED(*pResult) == true
+ // 3. A binding success was cached, in which case we must find an assembly:
+ // (1) == false && (2) == false && *ppAssembly != nullptr
+ _ASSERTE(FAILED(hr) || FAILED(*pResult) || *ppAssembly != nullptr);
+ return hr;
+}
+
+//=====================================================================================================================
+// Record the binding result to support cache-based lookups (using ICLRPrivCachedBinder::FindAssemblyBySpec).
+
+HRESULT CLRPrivBinderAppX::CacheBindResult(
+ AssemblyIdentity * pIdentity, // On success, will assume object lifetime ownership.
+ HRESULT hrResult)
+{
+ STANDARD_VM_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ VALIDATE_PTR_RET(pIdentity);
+
+ // Initialize the binding record.
+ BindingRecord rec = { hrResult };
+ BindingRecordMap::element_t newEntry(pIdentity, rec);
+
+ // Because the read lock must be taken within a ForbidSusped region, use CheckAddInPhases.
+ if (m_BindingRecordMap.CheckAddInPhases<ForbidSuspendThreadCrstHolder, CrstHolder>(
+ newEntry, m_MapReadLock, m_MapWriteLock))
+ {
+ // Indicates that this identity object was cached.
+ // Caller relinquishes object ownership.
+ return S_OK;
+ }
+ else
+ {
+ // Pre-existing entry was found.
+
+#ifdef _DEBUG
+ ForbidSuspendThreadCrstHolder lock(&m_MapReadLock);
+ auto pExistingEntry = m_BindingRecordMap.LookupPtr(pIdentity);
+ if (pExistingEntry != nullptr)
+ {
+ // It's possible for racing threads to try to cache their results;
+ // just make sure that they got the same HRESULT.
+ _ASSERTE(pExistingEntry->Value().hr == rec.hr);
+ }
+#endif
+
+ // Indicates that previous entry existed, and this identity object was not cached.
+ // Caller retains object ownership.
+ hr = S_FALSE;
+ }
+
+ return hr;
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivBinder::BindAssemblyByName
+
+HRESULT CLRPrivBinderAppX::BindAssemblyByName(
+ IAssemblyName * pAssemblyName,
+ ICLRPrivAssembly ** ppAssembly)
+{
+ STANDARD_BIND_CONTRACT;
+ BinderHRESULT hr = S_OK;
+ ReleaseHolder<ICLRPrivAssembly> pResult;
+
+ VALIDATE_ARG_RET(pAssemblyName != nullptr && ppAssembly != nullptr);
+
+ EX_TRY
+ {
+ hr = CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT;
+
+ if (FAILED(hr))
+ {
+ _ASSERTE(m_pFusionBinder != nullptr);
+ hr = m_pFusionBinder->BindFusionAssemblyByName(pAssemblyName, GetFusionBindingScope(), &pResult);
+ }
+
+ //
+ // The fusion binder returns CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT only if it did not
+ // recognize pAssemblyName as a FX assembly. Only then should other binders be consulted
+ // (otherwise applications would be able to copy arbitrary FX assemblies into their AppX
+ // package and use them directly).
+ //
+
+ if (hr == CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT)
+ {
+ if (FAILED(hr) && (m_pWinRTBinder != nullptr))
+ {
+ hr = m_pWinRTBinder->BindWinRTAssemblyByName(pAssemblyName, &pResult);
+ }
+
+ if (FAILED(hr))
+ {
+ hr = BindAppXAssemblyByName(pAssemblyName, ABF_Default, &pResult);
+ }
+
+ if (FAILED(hr) && (m_pParentBinder != nullptr))
+ {
+ hr = m_pParentBinder->BindAssemblyByName(pAssemblyName, &pResult);
+ }
+
+ _ASSERTE(FAILED(hr) || pResult != nullptr);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ // Return if either the bind or the bind cache fails.
+ IfFailRet(hr);
+
+ // Success.
+ *ppAssembly = pResult.Extract();
+ return hr;
+}
+
+//=====================================================================================================================
+// Implements code:IBindContext::PreBind
+HRESULT CLRPrivBinderAppX::PreBind(
+ IAssemblyName * pIAssemblyName,
+ DWORD dwPreBindFlags,
+ IBindResult ** ppIBindResult)
+{
+ STANDARD_BIND_CONTRACT;
+ BinderHRESULT hr = S_OK;
+
+ VALIDATE_ARG_RET((dwPreBindFlags & ~(PRE_BIND_APPLY_POLICY)) == 0);
+ VALIDATE_ARG_RET(pIAssemblyName != nullptr && ppIBindResult != nullptr);
+
+ // Assert that we are only working with binder that supports Native Images context bits.
+ _ASSERTE(m_fCanUseNativeImages);
+
+ EX_TRY
+ {
+ hr = CLR_E_BIND_UNRECOGNIZED_IDENTITY_FORMAT;
+
+ if (FAILED(hr))
+ {
+ hr = m_pFusionBinder->PreBindFusionAssemblyByName(pIAssemblyName, dwPreBindFlags, ppIBindResult);
+ }
+
+ if (FAILED(hr) && (m_pWinRTBinder != nullptr))
+ {
+ hr = m_pWinRTBinder->BindWinRTAssemblyByName(pIAssemblyName, ppIBindResult, TRUE);
+ }
+
+ if (FAILED(hr))
+ {
+ hr = PreBindAppXAssemblyByName(pIAssemblyName, ABF_BindIL, ppIBindResult);
+ }
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (FAILED(hr) && (m_pParentBinder != nullptr))
+ {
+ ReleaseHolder<IBindContext> pParentBindContext;
+ hr = m_pParentBinder->QueryInterface(__uuidof(IBindContext), (LPVOID *)&pParentBindContext);
+ if (SUCCEEDED(hr))
+ {
+ hr = pParentBindContext->PreBind(pIAssemblyName, dwPreBindFlags, ppIBindResult);
+ }
+ }
+
+ return hr;
+}
+
+//=====================================================================================================================
+UINT_PTR CLRPrivBinderAppX::GetBinderID()
+{
+ LIMITED_METHOD_CONTRACT;
+ return reinterpret_cast<UINT_PTR>(this);
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivBinder::GetBinderID
+HRESULT CLRPrivBinderAppX::GetBinderID(
+ UINT_PTR *pBinderId)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ *pBinderId = GetBinderID();
+ return S_OK;
+}
+
+//=====================================================================================================================
+// Implements code:IBindContext::IsDefaultContext
+HRESULT CLRPrivBinderAppX::IsDefaultContext()
+{
+ LIMITED_METHOD_CONTRACT;
+ return S_OK;
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivWinRtTypeBinder::FindAssemblyForWinRtTypeIfLoaded
+// Finds Assembly * for type in AppDomain * if it is loaded.
+// Returns NULL if assembly is not loaded or type is not found.
+//
+void *
+CLRPrivBinderAppX::FindAssemblyForWinRtTypeIfLoaded(
+ void * pAppDomain,
+ LPCUTF8 szNamespace,
+ LPCUTF8 szClassName)
+{
+ CONTRACTL
+ {
+ NOTHROW;
+ GC_NOTRIGGER;
+ FORBID_FAULT;
+ MODE_ANY;
+ }
+ CONTRACTL_END
+
+ void * pAssembly = nullptr;
+ if (m_pWinRTBinder != nullptr)
+ {
+ pAssembly = (void *)m_pWinRTBinder->FindAssemblyForTypeIfLoaded(
+ dac_cast<PTR_AppDomain>((AppDomain *)pAppDomain),
+ szNamespace,
+ szClassName);
+ }
+
+ if ((pAssembly == nullptr) && (m_pParentBinder != nullptr))
+ {
+ ReleaseHolder<ICLRPrivWinRtTypeBinder> pParentBinder =
+ ToInterface_NoThrow<ICLRPrivWinRtTypeBinder>(m_pParentBinder.GetValue());
+ // Parent binder should be another instance of code:CLRPrivBinderAppX class that implements the interface
+ _ASSERTE(pParentBinder != nullptr);
+
+ pAssembly = pParentBinder->FindAssemblyForWinRtTypeIfLoaded(
+ pAppDomain,
+ szNamespace,
+ szClassName);
+ }
+
+ return pAssembly;
+}
+
+//=====================================================================================================================
+CLRPrivAssemblyAppX::CLRPrivAssemblyAppX(
+ CLRPrivBinderUtil::AssemblyIdentity * pIdentity,
+ CLRPrivBinderAppX *pBinder,
+ ICLRPrivResource *pIResourceIL,
+ IBindResult * pIBindResult)
+ : m_pIdentity(pIdentity),
+ m_pBinder(nullptr),
+ m_pIResourceIL(nullptr),
+ m_pIResourceNI(nullptr),
+ m_pIBindResult(nullptr)
+{
+ STANDARD_VM_CONTRACT;
+
+ VALIDATE_PTR_THROW(pIdentity);
+ VALIDATE_PTR_THROW(pBinder);
+ VALIDATE_PTR_THROW(pIResourceIL);
+ VALIDATE_PTR_THROW(pIBindResult);
+
+ m_pBinder = clr::SafeAddRef(pBinder);
+ m_pIResourceIL = clr::SafeAddRef(pIResourceIL);
+ m_pIBindResult = clr::SafeAddRef(pIBindResult);
+}
+
+//=====================================================================================================================
+CLRPrivAssemblyAppX::~CLRPrivAssemblyAppX()
+{
+ LIMITED_METHOD_CONTRACT;
+ clr::SafeRelease(m_pIResourceNI);
+}
+
+//=====================================================================================================================
+LPCWSTR CLRPrivAssemblyAppX::GetSimpleName() const
+{
+ LIMITED_METHOD_CONTRACT;
+ return m_pIdentity->Name;
+}
+
+//=====================================================================================================================
+// Implements code:IUnknown::Release
+ULONG CLRPrivAssemblyAppX::Release()
+{
+ LIMITED_METHOD_CONTRACT;
+ STATIC_CONTRACT_CAN_TAKE_LOCK;
+ _ASSERTE(m_cRef > 0);
+
+ ULONG cRef;
+
+ {
+ // To achieve proper lifetime semantics, the name to assembly map elements' CLRPrivAssemblyAppX
+ // instances are not ref counted. We cannot allow discovery of the object via m_NameToAssemblyMap
+ // when the ref count is 0 (to prevent another thread to AddRef and Release it back to 0 in parallel).
+ // All uses of the map are guarded by the map lock, so we have to decrease the ref count under that
+ // lock (to avoid the chance that 2 threads are running Release to ref count 0 at once).
+ // Host callouts are now forbidden.
+ ForbidSuspendThreadCrstHolder lock(&m_pBinder->m_MapReadLock);
+
+ cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ {
+ m_pBinder->m_NameToAssemblyMap.Remove(GetSimpleName());
+ m_pBinder->m_BindingRecordMap.Remove(m_pIdentity);
+ }
+ }
+
+ if (cRef == 0)
+ {
+ delete this;
+ }
+
+ return cRef;
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivBinder::BindAssemblyByName
+HRESULT CLRPrivAssemblyAppX::BindAssemblyByName(
+ IAssemblyName * pAssemblyName,
+ ICLRPrivAssembly ** ppAssembly)
+{
+ WRAPPER_NO_CONTRACT;
+
+ return m_pBinder->BindAssemblyByName(
+ pAssemblyName,
+ ppAssembly);
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivBinder::GetBinderID
+HRESULT CLRPrivAssemblyAppX::GetBinderID(
+ UINT_PTR *pBinderId)
+{
+ WRAPPER_NO_CONTRACT;
+ return m_pBinder->GetBinderID(
+ pBinderId);
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivAssembly::IsShareable
+HRESULT CLRPrivAssemblyAppX::IsShareable(
+ BOOL * pbIsShareable)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ VALIDATE_ARG_RET(pbIsShareable != nullptr);
+
+ *pbIsShareable = FALSE;
+ return S_OK;
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivAssembly::GetAvailableImageTypes
+HRESULT CLRPrivAssemblyAppX::GetAvailableImageTypes(
+ LPDWORD pdwImageTypes)
+{
+ LIMITED_METHOD_CONTRACT;
+
+ VALIDATE_ARG_RET(pdwImageTypes != nullptr);
+
+ *pdwImageTypes = 0;
+
+ if (m_pIResourceIL != nullptr)
+ *pdwImageTypes |= ASSEMBLY_IMAGE_TYPE_IL;
+
+ if (m_pIResourceNI != nullptr)
+ *pdwImageTypes |= ASSEMBLY_IMAGE_TYPE_NATIVE;
+
+ return S_OK;
+}
+
+//=====================================================================================================================
+static ICLRPrivResource* GetResourceForBindResult(
+ IBindResult * pIBindResult)
+{
+ STANDARD_VM_CONTRACT;
+ VALIDATE_ARG_THROW(pIBindResult != nullptr);
+
+ WCHAR wzPath[_MAX_PATH];
+ DWORD cchPath = NumItems(wzPath);
+ ReleaseHolder<IAssemblyLocation> pIAssemLoc;
+ IfFailThrow(pIBindResult->GetAssemblyLocation(&pIAssemLoc));
+ IfFailThrow(pIAssemLoc->GetPath(wzPath, &cchPath));
+ return ToInterface<ICLRPrivResource>(new CLRPrivResourcePathImpl(wzPath));
+}
+
+//=====================================================================================================================
+// Implements code:ICLRPrivAssembly::GetImageResource
+HRESULT CLRPrivAssemblyAppX::GetImageResource(
+ DWORD dwImageType,
+ DWORD * pdwImageType,
+ ICLRPrivResource ** ppIResource)
+{
+ STANDARD_BIND_CONTRACT;
+ HRESULT hr = S_OK;
+
+ VALIDATE_ARG_RET(ppIResource != nullptr && m_pIBindResult != nullptr);
+
+ EX_TRY
+ {
+ DWORD _dwImageType;
+ if (pdwImageType == nullptr)
+ pdwImageType = &_dwImageType;
+
+ if ((dwImageType & ASSEMBLY_IMAGE_TYPE_NATIVE) == ASSEMBLY_IMAGE_TYPE_NATIVE)
+ {
+ ReleaseHolder<IBindResult> pIBindResultNI;
+ if (m_pIResourceNI == nullptr)
+ {
+ if (SUCCEEDED(hr = m_pIBindResult->GetNativeImage(&pIBindResultNI, nullptr)) && pIBindResultNI != nullptr)
+ {
+ ReleaseHolder<ICLRPrivResource> pResourceNI = GetResourceForBindResult(pIBindResultNI);
+ if (InterlockedCompareExchangeT<ICLRPrivResource *>(&m_pIResourceNI, pResourceNI, nullptr) == nullptr)
+ pResourceNI.SuppressRelease();
+ }
+ else
+ {
+ IfFailGo(CLR_E_BIND_IMAGE_UNAVAILABLE);
+ }
+ }
+
+ *ppIResource = clr::SafeAddRef(m_pIResourceNI);
+ *pdwImageType = ASSEMBLY_IMAGE_TYPE_NATIVE;
+ }
+ else if ((dwImageType & ASSEMBLY_IMAGE_TYPE_IL) == ASSEMBLY_IMAGE_TYPE_IL)
+ {
+ *ppIResource = clr::SafeAddRef(m_pIResourceIL);
+ *pdwImageType = ASSEMBLY_IMAGE_TYPE_IL;
+ }
+ else
+ {
+ hr = CLR_E_BIND_IMAGE_UNAVAILABLE;
+ }
+
+ ErrExit:
+ ;
+ }
+ EX_CATCH_HRESULT(hr);
+
+ return hr;
+}
+
+
+//=====================================================================================================================
+// Implements code:ICLRPrivBinder::VerifyBind
+HRESULT CLRPrivBinderAppX::VerifyBind(
+ IAssemblyName *pAssemblyName,
+ ICLRPrivAssembly *pAssembly,
+ ICLRPrivAssemblyInfo *pAssemblyInfo)
+{
+ STANDARD_BIND_CONTRACT;
+
+ HRESULT hr = S_OK;
+
+ VALIDATE_ARG_RET(pAssemblyName!= nullptr && pAssemblyInfo != nullptr);
+
+ UINT_PTR binderID;
+ IfFailRet(pAssembly->GetBinderID(&binderID));
+
+ if (binderID != GetBinderID())
+ {
+ return pAssembly->VerifyBind(pAssemblyName, pAssembly, pAssemblyInfo);
+ }
+
+ return CLRPrivBinderUtil::VerifyBind(pAssemblyName, pAssemblyInfo);
+}
+
+//=====================================================================================================================
+/*static*/
+LPCWSTR CLRPrivBinderAppX::NameToAssemblyMapTraits::GetKey(CLRPrivAssemblyAppX *pAssemblyAppX)
+{
+ WRAPPER_NO_CONTRACT;
+ _ASSERT(pAssemblyAppX != nullptr);
+ return pAssemblyAppX->GetSimpleName();
+}
+
+//=====================================================================================================================
+HRESULT CLRPrivAssemblyAppX::GetIBindResult(
+ IBindResult ** ppIBindResult)
+{
+ STANDARD_VM_CONTRACT;
+
+ VALIDATE_ARG_RET(ppIBindResult != nullptr);
+ VALIDATE_CONDITION(m_pIBindResult != nullptr, return E_UNEXPECTED);
+
+ *ppIBindResult = clr::SafeAddRef(m_pIBindResult);
+
+ return S_OK;
+}
+
+#endif // !DACCESS_COMPILE