diff options
Diffstat (limited to 'src/inc/legacyactivationshimutil.h')
-rw-r--r-- | src/inc/legacyactivationshimutil.h | 1116 |
1 files changed, 1116 insertions, 0 deletions
diff --git a/src/inc/legacyactivationshimutil.h b/src/inc/legacyactivationshimutil.h new file mode 100644 index 0000000000..4e6cdf0d25 --- /dev/null +++ b/src/inc/legacyactivationshimutil.h @@ -0,0 +1,1116 @@ +// 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. +// +// LegacyActivationShim.h +// +// This file allows simple migration from .NET Runtime v2 Host Activation APIs +// to the .NET Runtime v4 Host Activation APIs through simple shim functions. + +#ifndef __LEGACYACTIVATIONSHIMUTIL_H__ +#define __LEGACYACTIVATIONSHIMUTIL_H__ + +// To minimize how much we perturb sources that we are included in, we make sure that +// all macros we define/redefine are restored at the end of the header. +#pragma push_macro("SELECTANY") +#pragma push_macro("_TEXT_ENCODE") +#pragma push_macro("countof") +#pragma push_macro("UNUSED") + +#ifndef _MSC_VER +#error "LegacyActivationShim.h cannot be used on non-MS compilers" +#endif + +// ---SELECTANY------------------------------------------------------------------------------------ +#undef SELECTANY +#define SELECTANY extern __declspec(selectany) + +// Allow users of these headers to provide custom 'LoadLibrary' implementation (e.g. WszLoadLibrary). +// Example of usage is in ndp\clr\src\fusion\tools\viewer. +#ifndef LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY +#define LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY ::LoadLibrary +#endif + +// _T macro alternative to make strings ASCII/UNICODE +#undef _TEXT_ENCODE +#ifdef UNICODE +#define _TEXT_ENCODE(str) L ## str +#else //!UNICODE +#define _TEXT_ENCODE(str) str +#endif //!UNICODE + +// countof ... number of items in an array +#ifndef countof +#define countof(x) (sizeof(x) / sizeof(x[0])) +#endif countof + +#ifndef UNUSED +#define UNUSED(var) ((void)(var)) +#endif //UNUSED + +#ifndef __LEGACYACTIVATIONSHIM_H__ + #error Error: Include LegacyActivationShim.h or LegacyActivationShimDelayLoad.h instead of directly including LegacyActivationShimUtil.h +#endif // __LEGACYACTIVATIONSHIM_H__ + +// ---PLACEMENT NEW-------------------------------------------------------------------------------- +#ifndef __PLACEMENT_NEW_INLINE +#define __PLACEMENT_NEW_INLINE +// Inline placement new +inline void* operator new(size_t, void *_Where) +{ // construct array with placement at _Where + return (_Where); +} + +// delete if placement new fails +inline void operator delete(void *, void *) +{ +} +#endif __PLACEMENT_NEW_INLINE + +// ---LEGACYACTIVATON NAMESPACE-------------------------------------------------------------------- +namespace LegacyActivationShim +{ + // ---UTIL NAMESPACE--------------------------------------------------------------------------- + namespace Util + { + // ---INTERLOCKEDCOMPAREEXCHANGEPOINTERT--------------------------------------------------- + // Variation on InterlockedCompareExchangePointer that adds some type safety. + // Added 'T' to end of name because an identical name to the original function + // confuses GCC + template <typename T> + inline + T InterlockedCompareExchangePointerT( + T volatile* destination, + T exchange, + T comparand) + { +#ifdef __UtilCode_h__ + // Utilcode has redefined InterlockedCompareExchangePointer + return ::InterlockedCompareExchangeT(destination, exchange, comparand); +#else // __UtilCode_h__ + return reinterpret_cast<T>(InterlockedCompareExchangePointer( + (PVOID volatile *)(destination), + (PVOID)(exchange), + (PVOID)(comparand))); +#endif // __UtilCode_h__ else + } + + // ---PlacementNewDeleteHelper------------------------------------------------------------- + template <typename TYPE, bool IS_CLASS> + class PlacementNewDeleteHelper; + + template <typename TYPE> + class PlacementNewDeleteHelper<TYPE, true> + { + public: + // Some environments #define New and Delete, so name these functions + // Construct and Destruct to keep them unique. + static void Construct(TYPE const & value, void *pvWhere) + { new (pvWhere) TYPE(value); } + + static void Destruct(TYPE & value) + { value.~TYPE(); } + }; + + template <typename TYPE> + class PlacementNewDeleteHelper<TYPE, false> + { + public: + static void Construct(TYPE const & value, void *pvWhere) + { *reinterpret_cast<TYPE *>(pvWhere) = value; } + + static void Destruct(TYPE &) + { } + }; + + // ---HOLDERBASE--------------------------------------------------------------------------- + template <typename TYPE> + class HolderBase + { + public: + // Relies on implicit default constructor, which permits zero-init static + // object declaration. Do not define one. + // HolderBase() {} + + protected: + char m_value[sizeof(TYPE)]; + + inline + TYPE & GetRef() + { return *reinterpret_cast<TYPE *>(&m_value[0]); } + + inline + TYPE & GetPtr() + { return reinterpret_cast<TYPE *>(&m_value[0]); } + + inline + void Construct(TYPE const & value) + { PlacementNewDeleteHelper<TYPE, __is_class(TYPE)>::Construct(value, (void *)&m_value[0]); } + + inline + void Destruct() + { PlacementNewDeleteHelper<TYPE, __is_class(TYPE)>::Destruct(GetRef()); } + }; + + // ---HOLDER------------------------------------------------------------------------------- + template <typename TYPE, void (*ASSIGNF)(TYPE &), void (*RELEASEF)(TYPE &)> + class Holder : public HolderBase<TYPE> + { + protected: + bool m_assigned; + bool m_suppressed; + + public: + inline + Holder() : m_assigned(false), m_suppressed(false) + {} + + inline + Holder(TYPE const & value) : m_assigned(false), m_suppressed(false) + { Assign(value); } + + inline + ~Holder() + { Release(); } + + inline + void Assign(TYPE const & value) + { + Release(); + Construct(value); + m_assigned = true; + (*ASSIGNF)(GetValue()); + } + + inline + void Release() + { + if (m_assigned) + { + if (!m_suppressed) + { + (*RELEASEF)(GetValue()); + } + m_assigned = false; + m_suppressed = false; + Destruct(); + } + } + + inline + void SuppressRelease() + { + m_suppressed = m_assigned; + } + + inline + TYPE & GetValue() + { + // _ASSERTE(m_assigned); + return GetRef(); + } + + inline + bool IsAssigned() + { return m_assigned; } + }; + + // ---ZEROINITGLOBALHOLDER----------------------------------------------------------------- + // This class should ONLY be used for global (file scope) variables. It relies on zero + // initialized data in the image. This will fail miserably for other scenarios, as the + // memory used for the object may not be zero-initialized, which will result in incorrect + // behaviour. + template <typename TYPE, void (*ASSIGNF)(TYPE &), void (*RELEASEF)(TYPE &)> + class ZeroInitGlobalHolder : public HolderBase<TYPE> + { + protected: + bool m_assigned; + + public: + // Relies on implicit default constructor, which permits zero-init static + // field declaration. Do not define an explicit constructor. + // ZeroInitGlobalHolder() {} + + inline + ~ZeroInitGlobalHolder() + { Release(); } + + inline + void Assign(TYPE const & value) + { + Release(); + Construct(value); + m_assigned = true; + (*ASSIGNF)(GetValue()); + } + + inline + void Release() + { + if (m_assigned) + { + (*RELEASEF)(GetValue()); + m_assigned = false; + Destruct(); + } + } + + inline + TYPE & GetValue() + { + // _ASSERTE(m_assigned); + return GetRef(); + } + + inline + bool IsAssigned() + { return m_assigned; } + + inline + void ClearUnsafe() + { m_assigned = false; } + }; + + // ---DONOTHINGHELPER---------------------------------------------------------------------- + template <typename TYPE> + inline + void DoNothingHelper(TYPE & value) + { UNUSED(value); } + + // ---RELEASEHELPER------------------------------------------------------------------------ + template <typename TYPE> + inline + void ReleaseHelper(TYPE & value) + { value->Release(); } + + // ---RELEASEHOLDER------------------------------------------------------------------------ + template <typename TYPE> + class ReleaseHolder + : public Holder< TYPE, &DoNothingHelper<TYPE>, &ReleaseHelper<TYPE> > + { + public: + inline + ReleaseHolder(TYPE & value) + : Holder< TYPE, &DoNothingHelper<TYPE>, &ReleaseHelper<TYPE> >(value) + {} + + ReleaseHolder() + : Holder< TYPE, &DoNothingHelper<TYPE>, &ReleaseHelper<TYPE> >() + {} + }; + + // ---ZEROINITGLOBALRELEASEHOLDER---------------------------------------------------------- + template <typename TYPE> + class ZeroInitGlobalReleaseHolder + : public ZeroInitGlobalHolder< TYPE, &DoNothingHelper<TYPE>, &ReleaseHelper<TYPE> > + { + }; + + // ---FREELIBRARYHELPER-------------------------------------------------------------------- + inline + void FreeLibraryHelper(HMODULE & hMod) + { + FreeLibrary(hMod); + } + + // ---HMODULEHOLDER------------------------------------------------------------------------ + class HMODULEHolder + : public Holder< HMODULE, &DoNothingHelper<HMODULE>, &FreeLibraryHelper > + { + public: + inline + HMODULEHolder(HMODULE value) + : Holder< HMODULE, &DoNothingHelper<HMODULE>, &FreeLibraryHelper >(value) + {} + + HMODULEHolder() + : Holder< HMODULE, &DoNothingHelper<HMODULE>, &FreeLibraryHelper >() + {} + }; + + // ---ZEROINITHMODULEHOLDER---------------------------------------------------------------- + class ZeroInitGlobalHMODULEHolder + : public ZeroInitGlobalHolder< HMODULE, &DoNothingHelper<HMODULE>, &FreeLibraryHelper > + { + }; + + // ---DELAYLOADFUNCTOR--------------------------------------------------------------------- + // T must be a function typedef. + // For example, "typedef int X(short i); DelayLoadFunctor<X> pfnX;" + template <typename T> + class DelayLoadFunctor + { + private: + HMODULEHolder m_hModHolder; + T * m_proc; + + public: + HRESULT Init(LPCTSTR wzDllName, LPCSTR szProcName) + { + // Load module + HMODULE hMod = LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY(wzDllName); + if (hMod == NULL) + return HRESULT_FROM_WIN32(::GetLastError()); + HMODULEHolder hModHolder(hMod); + + // Load proc address + T * proc = reinterpret_cast<T *>(::GetProcAddress(hMod, szProcName)); + if (proc == NULL) + return HRESULT_FROM_WIN32(::GetLastError()); + + // Store results + hModHolder.SuppressRelease(); + m_hModHolder.Assign(hMod); + m_proc = proc; + + return S_OK; + } + + HRESULT Init(HMODULE hMod, LPCSTR szProcName) + { + // Load proc address + T * proc = reinterpret_cast<T *>(::GetProcAddress(hMod, szProcName)); + if (proc == NULL) + return HRESULT_FROM_WIN32(::GetLastError()); + + // Store result + m_proc = proc; + + // Success + return S_OK; + } + + T& operator()() + { + return *m_proc; + } + }; + + // ---ZEROINITGLOBALSPINLOCK---------------------------------------------------------------- + class ZeroInitGlobalSpinLock + { + private: + enum LOCK_STATE + { + UNLOCKED = 0, + LOCKED = 1 + }; + + LONG volatile m_lock; + + static inline void Lock(ZeroInitGlobalSpinLock*& lock) + { + while (InterlockedExchange(&lock->m_lock, LOCKED) == LOCKED) + { + ::SwitchToThread(); + } + } + + static inline void Unlock(ZeroInitGlobalSpinLock*& lock) + { InterlockedExchange(&lock->m_lock, UNLOCKED); } + + public: + typedef LegacyActivationShim::Util::Holder<ZeroInitGlobalSpinLock*, + &ZeroInitGlobalSpinLock::Lock, + &ZeroInitGlobalSpinLock::Unlock> + Holder; + }; + + // ---MSCOREEDATA-------------------------------------------------------------------------- + SELECTANY HMODULE g_hModMscoree = NULL; + SELECTANY ZeroInitGlobalHMODULEHolder g_hModMscoreeHolder; + + // ---GETMSCOREE--------------------------------------------------------------------------- + inline + HRESULT GetMSCOREE(HMODULE *pMscoree) + { + if (g_hModMscoree == NULL) + { + HMODULE hModMscoree = LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY(_TEXT_ENCODE("mscoree.dll")); + if (hModMscoree == NULL) + return HRESULT_FROM_WIN32(GetLastError()); + HMODULEHolder hModMscoreeHolder(hModMscoree); + + if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT<HMODULE>( + &g_hModMscoree, hModMscoree, NULL) == NULL) + { + g_hModMscoreeHolder.ClearUnsafe(); + g_hModMscoreeHolder.Assign(g_hModMscoree); + hModMscoreeHolder.SuppressRelease(); + } + } + + *pMscoree = g_hModMscoree; + return S_OK; + } + + // ---MSCOREEFUNCTOR----------------------------------------------------------------------- + template <typename T> + class MscoreeFunctor : public DelayLoadFunctor<T> + { + public: + HRESULT Init(LPCSTR szProcName) + { + HRESULT hr = S_OK; + HMODULE hModMscoree = NULL; + IfHrFailRet(GetMSCOREE(&hModMscoree)); + + return DelayLoadFunctor<T>::Init(hModMscoree, szProcName); + } + }; + + // ---CALLCLRCREATEINSTANCE------------------------------------------------------------------ + inline + HRESULT CallCLRCreateInstance( + REFCLSID clsid, + REFIID riid, + LPVOID *ppInterface) + { + HRESULT hr = S_OK; + HMODULE hMscoree = NULL; + IfHrFailRet(GetMSCOREE(&hMscoree)); + + typedef HRESULT (__stdcall *CLRCreateInstance_pfn) ( + REFCLSID clsid, + REFIID riid, + LPVOID *ppInterface); + + CLRCreateInstance_pfn pfnCLRCreateInstance = + reinterpret_cast<CLRCreateInstance_pfn>(GetProcAddress(hMscoree, "CLRCreateInstance")); + + if (pfnCLRCreateInstance == NULL) + return HRESULT_FROM_WIN32(GetLastError()); + + return (*pfnCLRCreateInstance)( + clsid, + riid, + ppInterface); + } + + // ---CLRMETAHOST INTERFACE DATA----------------------------------------------------------- + SELECTANY ICLRMetaHost* g_pCLRMetaHost = NULL; + SELECTANY ZeroInitGlobalReleaseHolder<ICLRMetaHost*> g_hCLRMetaHost; + + // ---GETCLRMETAHOST----------------------------------------------------------------------- + // NOTE: Does not AddRef returned interface pointer. + inline + HRESULT GetCLRMetaHost( + /*out*/ ICLRMetaHost **ppCLRMetaHost) + { + HRESULT hr = S_OK; + + if (g_pCLRMetaHost == NULL) + { + ICLRMetaHost *pMetaHost = NULL; + IfHrFailRet(CallCLRCreateInstance(CLSID_CLRMetaHost, + IID_ICLRMetaHost, + reinterpret_cast<LPVOID *>(&pMetaHost))); + ReleaseHolder<ICLRMetaHost*> hMetaHost(pMetaHost); + + // + // Great - we got an ICLRMetaHost. Now publish this to + // g_pCLRMetaHost in a thread-safe way. + // + + if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT<ICLRMetaHost *>( + &g_pCLRMetaHost, pMetaHost, NULL) == NULL) + { + // Successful publish. In this case, we also assign to the + // holder to ensure that the interface is released when the + // image is unloaded. + g_hCLRMetaHost.ClearUnsafe(); + g_hCLRMetaHost.Assign(g_pCLRMetaHost); + hMetaHost.SuppressRelease(); // Keep it AddRef'ed for the g_hCLRMetaHost + } + } + + *ppCLRMetaHost = g_pCLRMetaHost; + + return hr; + } + + // ---HasNewActivationAPIs----------------------------------------------------------------- + SELECTANY ULONG g_fHasNewActivationAPIs = ULONG(-1); + + inline + bool HasNewActivationAPIs() + { + if (g_fHasNewActivationAPIs == ULONG(-1)) + { + ICLRMetaHost *pMetaHost = NULL; + HRESULT hr = GetCLRMetaHost(&pMetaHost); + InterlockedCompareExchange((LONG volatile *)&g_fHasNewActivationAPIs, (LONG)(SUCCEEDED(hr)), ULONG(-1)); + } + + return g_fHasNewActivationAPIs != 0; + } + + // ---CLRMETAHOSTPOLICY INTERFACE DATA----------------------------------------------------- + SELECTANY ICLRMetaHostPolicy* g_pCLRMetaHostPolicy = NULL; + SELECTANY ZeroInitGlobalReleaseHolder<ICLRMetaHostPolicy*> g_hCLRMetaHostPolicy; + + // ---GETCLRMETAHOSTPOLICY----------------------------------------------------------------- + // NOTE: Does not AddRef returned interface pointer. + inline + HRESULT GetCLRMetaHostPolicy( + /*out*/ ICLRMetaHostPolicy **ppICLRMetaHostPolicy) + { + HRESULT hr = S_OK; + + if (g_pCLRMetaHostPolicy == NULL) + { + ICLRMetaHostPolicy *pMetaHostPolicy = NULL; + IfHrFailRet(CallCLRCreateInstance(CLSID_CLRMetaHostPolicy, + IID_ICLRMetaHostPolicy, + reinterpret_cast<LPVOID *>(&pMetaHostPolicy))); + ReleaseHolder<ICLRMetaHostPolicy*> hMetaHostPolicy(pMetaHostPolicy); + + // + // Great - we got an ICLRMetaHostPolicy. Now publish this to + // g_pCLRMetaHostPolicy in a thread-safe way. + // + + if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT<ICLRMetaHostPolicy*>( + &g_pCLRMetaHostPolicy, pMetaHostPolicy, NULL) == NULL) + { + // Successful publish. In this case, we also assign to the + // holder to ensure that the interface is released when the + // image is unloaded. + g_hCLRMetaHostPolicy.ClearUnsafe(); + g_hCLRMetaHostPolicy.Assign(g_pCLRMetaHostPolicy); + hMetaHostPolicy.SuppressRelease(); + } + } + + *ppICLRMetaHostPolicy = g_pCLRMetaHostPolicy; + + return hr; + } + + // ---RUNTIMEINFO DATA--------------------------------------------------------------------- + struct RuntimeInfo + { + ICLRRuntimeInfo *m_pRuntimeInfo; + + DWORD m_cchImageVersion; + WCHAR m_wszImageVersion[512]; + + inline + void Init() + { + m_pRuntimeInfo = NULL; + m_cchImageVersion = countof(m_wszImageVersion); + m_wszImageVersion[0] = L'\0'; + } + + inline + void Release() + { + if (m_pRuntimeInfo != NULL) + { + m_pRuntimeInfo->Release(); + m_pRuntimeInfo = NULL; + } + } + }; + + SELECTANY LONG g_runtimeInfoIsInitialized = FALSE; + SELECTANY RuntimeInfo g_runtimeInfo; + SELECTANY ZeroInitGlobalSpinLock g_runtimeInfoLock; + SELECTANY ZeroInitGlobalReleaseHolder<RuntimeInfo*> g_hRuntimeInfo; + + // ---GETCLRRUNTIMEINFOHELPER-------------------------------------------------------------- + // Logic: + // 1. Try to bind using ICLRMetaHostPolicy::GetRequestedRuntime and incoming arguments. + // 2. Try to bind using ICLRMetaHostPolicy::GetRequestedRuntime and "v4.0.0" and + // upgrade policy. + // 3. Try to bind to latest using GetRequestedRuntimeInfo. + + inline + HRESULT GetCLRRuntimeInfoHelper( + /*out*/ ICLRRuntimeInfo **ppCLRRuntimeInfo, + LPCWSTR pEXE = NULL, + IStream *pIStream = NULL, + __inout_ecount_opt(*pcchVersion) LPWSTR wszVersion = NULL, + DWORD *pcchVersion = NULL, + __out_ecount_opt(*pcchImageVersion) LPWSTR wszImageVersion = NULL, + DWORD *pcchImageVersion = NULL) + { + HRESULT hr = S_OK; + + // + // 1. Try policy-based binding first, which will incorporate config files and such. + // + + ICLRMetaHostPolicy *pMetaHostPolicy = NULL; + IfHrFailRet(GetCLRMetaHostPolicy(&pMetaHostPolicy)); + + DWORD dwConfigFlags = 0; + + hr = pMetaHostPolicy->GetRequestedRuntime( + METAHOST_POLICY_USE_PROCESS_IMAGE_PATH, + pEXE, + pIStream, + wszVersion, + pcchVersion, + wszImageVersion, + pcchImageVersion, + &dwConfigFlags, + IID_ICLRRuntimeInfo, + reinterpret_cast<LPVOID *>(ppCLRRuntimeInfo)); + + if (hr != S_OK && + pEXE == NULL && + pIStream == NULL && + wszVersion == NULL) + { // + // 2. Try to bind using ICLRMetaHostPolicy::GetRequestedRuntime and "v4.0.0" and upgrade policy. + // + + WCHAR _wszVersion[256]; // We can't use new in this header, so just pick an obscenely long version string length of 256 + DWORD _cchVersion = countof(_wszVersion); + wcscpy_s(_wszVersion, _cchVersion, L"v4.0.0"); + hr = pMetaHostPolicy->GetRequestedRuntime( + static_cast<METAHOST_POLICY_FLAGS>(METAHOST_POLICY_USE_PROCESS_IMAGE_PATH | + METAHOST_POLICY_APPLY_UPGRADE_POLICY), + pEXE, + pIStream, // (is NULL) + _wszVersion, + &_cchVersion, + wszImageVersion, + pcchImageVersion, + &dwConfigFlags, + IID_ICLRRuntimeInfo, + reinterpret_cast<LPVOID *>(ppCLRRuntimeInfo)); + } + + if (hr != S_OK && + pEXE == NULL && + pIStream == NULL && + wszVersion == NULL) + { // + // 3. Try to bind using GetRequestedRuntimeInfo(NULL) + // + + typedef HRESULT __stdcall GetRequestedRuntimeInfo_t( + LPCWSTR pExe, + LPCWSTR pwszVersion, + LPCWSTR pConfigurationFile, + DWORD startupFlags, + DWORD runtimeInfoFlags, + LPWSTR pDirectory, + DWORD dwDirectory, + DWORD *dwDirectoryLength, + LPWSTR pVersion, + DWORD cchBuffer, + DWORD* dwlength); + + HMODULE hMscoree = NULL; + IfHrFailRet(GetMSCOREE(&hMscoree)); + + // We're using GetRequestedRuntimeInfo here because it is the only remaining API + // that will not be Whidbey-capped and will allow "bind to latest" semantics. This + // is cheating a bit, but should work for now. The alternative is to use + // ICLRMetaHost::EnumerateRuntimes to achieve the same result. + DelayLoadFunctor<GetRequestedRuntimeInfo_t> GetRequestedRuntimeInfoFN; + IfHrFailRet(GetRequestedRuntimeInfoFN.Init(hMscoree, "GetRequestedRuntimeInfo")); + + WCHAR szDir_[_MAX_PATH]; + DWORD cchDir_ = countof(szDir_); + WCHAR szVersion_[_MAX_PATH]; + DWORD cchVersion_ = countof(szVersion_); + DWORD dwInfoFlags_ = RUNTIME_INFO_UPGRADE_VERSION + | RUNTIME_INFO_DONT_SHOW_ERROR_DIALOG; + + IfHrFailRet(GetRequestedRuntimeInfoFN()( + NULL, + NULL, + NULL, + 0, + dwInfoFlags_, + szDir_, + cchDir_, + &cchDir_, + szVersion_, + cchVersion_, + &cchVersion_)); + + // Unable to get a version to try to load. + if (hr != S_OK) + { + return CLR_E_SHIM_RUNTIMELOAD; + } + + ICLRMetaHost *pMetaHost = NULL; + IfHrFailRet(GetCLRMetaHost(&pMetaHost)); + + hr = pMetaHost->GetRuntime(szVersion_, + IID_ICLRRuntimeInfo, + reinterpret_cast<LPVOID *>(ppCLRRuntimeInfo)); + + if (hr != S_OK) + { + return CLR_E_SHIM_RUNTIMELOAD; + } + + if (wszImageVersion != NULL) + { + wcsncpy_s(wszImageVersion, *pcchImageVersion, szVersion_, cchVersion_); + *pcchImageVersion = cchVersion_; + } + } + + if (hr == S_OK && + (dwConfigFlags & METAHOST_CONFIG_FLAGS_LEGACY_V2_ACTIVATION_POLICY_MASK) == + METAHOST_CONFIG_FLAGS_LEGACY_V2_ACTIVATION_POLICY_TRUE) + { // If the config requested that the runtime be bound as the legacy runtime. + IfHrFailRet((*ppCLRRuntimeInfo)->BindAsLegacyV2Runtime()); + } + + return hr; + } + + // ---GETRUNTIMEINFO----------------------------------------------------------------------- + inline + HRESULT GetRuntimeInfo( + /*out*/ RuntimeInfo **ppRuntimeInfo, + LPCWSTR pEXE = NULL, + IStream *pIStream = NULL, + __inout_ecount_opt(*pcchVersion) LPWSTR wszVersion = NULL, + DWORD *pcchVersion = NULL) + { + HRESULT hr = S_OK; + + if (!g_runtimeInfoIsInitialized) + { + ZeroInitGlobalSpinLock::Holder lock(&g_runtimeInfoLock); + if (!g_runtimeInfoIsInitialized) + { + g_runtimeInfo.Init(); + + IfHrFailRet(GetCLRRuntimeInfoHelper( + &g_runtimeInfo.m_pRuntimeInfo, + pEXE, + pIStream, + wszVersion, + pcchVersion, + g_runtimeInfo.m_wszImageVersion, + &g_runtimeInfo.m_cchImageVersion)); + + // + // Initialized - now publish. + // + + g_hRuntimeInfo.ClearUnsafe(); + g_hRuntimeInfo.Assign(&g_runtimeInfo); + InterlockedExchange(&g_runtimeInfoIsInitialized, TRUE); + } + } + + // + // Return the struct + // + + *ppRuntimeInfo = &g_runtimeInfo; + return hr; + } + + // --------BINDTOV4------------------------------------------------------------------------ + // Used by hosted DLLs that require the use of v4 for all their + // LegacyActivationShim calls. Can (and should) be called from DllMain, + // provided the DLL has a static (non-delayload) dependency on mscoree.dll. + inline + HRESULT BindToV4() + { + HRESULT hr = E_FAIL; + + if (!g_runtimeInfoIsInitialized) + { + ZeroInitGlobalSpinLock::Holder lock(&g_runtimeInfoLock); + if (!g_runtimeInfoIsInitialized) + { + ICLRMetaHostPolicy *pMetaHostPolicy = NULL; + IfHrFailRet(GetCLRMetaHostPolicy(&pMetaHostPolicy)); + + g_runtimeInfo.Init(); + + // + // Try to bind using ICLRMetaHostPolicy::GetRequestedRuntime and "v4.0.0" and upgrade policy. + // + + WCHAR _wszVersion[256]; // We can't use new in this header, so just pick an obscenely long version string length of 256 + DWORD _cchVersion = countof(_wszVersion); + wcscpy_s(_wszVersion, _cchVersion, L"v4.0.0"); + + IfHrFailRet(pMetaHostPolicy->GetRequestedRuntime( + METAHOST_POLICY_APPLY_UPGRADE_POLICY, + NULL, // image path + NULL, // config stream + _wszVersion, + &_cchVersion, + g_runtimeInfo.m_wszImageVersion, + &g_runtimeInfo.m_cchImageVersion, + NULL, // config flags + IID_ICLRRuntimeInfo, + reinterpret_cast<LPVOID *>(&g_runtimeInfo.m_pRuntimeInfo))); + + // + // Initialized - now publish. + // + + g_hRuntimeInfo.ClearUnsafe(); + g_hRuntimeInfo.Assign(&g_runtimeInfo); + InterlockedExchange(&g_runtimeInfoIsInitialized, TRUE); + + hr = S_OK; + } + } + + return hr; + } + + // ---GETCLRRUNTIMEINFO-------------------------------------------------------------------- + inline + HRESULT GetCLRRuntimeInfo( + /*out*/ ICLRRuntimeInfo **ppCLRRuntimeInfo, + LPCWSTR pEXE = NULL, + IStream *pIStream = NULL, + __inout_ecount_opt(*pcchVersion) LPWSTR wszVersion = NULL, + DWORD *pcchVersion = NULL) + { + HRESULT hr = S_OK; + + RuntimeInfo *pRuntimeInfo = NULL; + IfHrFailRet(GetRuntimeInfo(&pRuntimeInfo, pEXE, pIStream, wszVersion, pcchVersion)); + + *ppCLRRuntimeInfo = pRuntimeInfo->m_pRuntimeInfo; + return hr; + } + + // ---GetConfigImageVersion---------------------------------------------------------------- + inline + HRESULT GetConfigImageVersion( + __out_ecount(*pcchBuffer) LPWSTR wzBuffer, + DWORD *pcchBuffer) + { + HRESULT hr = S_OK; + + RuntimeInfo *pRuntimeInfo = NULL; + IfHrFailRet(GetRuntimeInfo(&pRuntimeInfo)); + + DWORD cchBuffer = *pcchBuffer; + *pcchBuffer = pRuntimeInfo->m_cchImageVersion; + + if (cchBuffer <= pRuntimeInfo->m_cchImageVersion) + { + wcsncpy_s( + wzBuffer, + cchBuffer, + pRuntimeInfo->m_wszImageVersion, + pRuntimeInfo->m_cchImageVersion); + } + else + { + IfHrFailRet(HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER)); + } + + return hr; + } + + // ---ICLRSTRONGNAME INTERFACE DATA-------------------------------------------------------- + SELECTANY ICLRStrongName* g_pCLRStrongName = NULL; + SELECTANY ZeroInitGlobalReleaseHolder<ICLRStrongName*> g_hCLRStrongName; + + // ---GETCLRSTRONGNAME--------------------------------------------------------------------- + // NOTE: Does not AddRef returned interface pointer. + inline + HRESULT GetCLRStrongName( + /*out*/ ICLRStrongName **ppCLRStrongName) + { + HRESULT hr = S_OK; + + if (g_pCLRStrongName == NULL) + { + ICLRRuntimeInfo *pInfo = NULL; + IfHrFailRet(GetCLRRuntimeInfo(&pInfo)); + + ICLRStrongName *pStrongName; + + IfHrFailRet(pInfo->GetInterface( + CLSID_CLRStrongName, + IID_ICLRStrongName, + reinterpret_cast<LPVOID *>(&pStrongName))); + + // + // Great - we got an ICLRStrongName. Now publish this to + // g_pCLRStrongName in a thread-safe way. + // + + if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT<ICLRStrongName *>( + &g_pCLRStrongName, pStrongName, NULL) == NULL) + { + // Successful publish. In this case, we also assign to the + // holder to ensure that the interface is released when the + // image is unloaded. + g_hCLRStrongName.ClearUnsafe(); + g_hCLRStrongName.Assign(g_pCLRStrongName); + } + else + { + // We were beat to the punch, don't publish this interface + // and make sure we use the published value for consistency. + pStrongName->Release(); + } + } + + *ppCLRStrongName = g_pCLRStrongName; + return hr; + } + + // ---ICLRSTRONGNAME2 INTERFACE DATA-------------------------------------------------------- + SELECTANY ICLRStrongName2* g_pCLRStrongName2 = NULL; + SELECTANY ZeroInitGlobalReleaseHolder<ICLRStrongName2*> g_hCLRStrongName2; + + // ---GETCLRSTRONGNAME2--------------------------------------------------------------------- + // NOTE: Does not AddRef returned interface pointer. + inline + HRESULT GetCLRStrongName2( + /*out*/ ICLRStrongName2 **ppCLRStrongName2) + { + HRESULT hr = S_OK; + + if (g_pCLRStrongName2 == NULL) + { + ICLRRuntimeInfo *pInfo = NULL; + IfHrFailRet(GetCLRRuntimeInfo(&pInfo)); + + ICLRStrongName2 *pStrongName; + + IfHrFailRet(pInfo->GetInterface( + CLSID_CLRStrongName, + IID_ICLRStrongName2, + reinterpret_cast<LPVOID *>(&pStrongName))); + + // + // Great - we got an ICLRStrongName2. Now publish this to + // g_pCLRStrongName2 in a thread-safe way. + // + + if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT<ICLRStrongName2 *>( + &g_pCLRStrongName2, pStrongName, NULL) == NULL) + { + // Successful publish. In this case, we also assign to the + // holder to ensure that the interface is released when the + // image is unloaded. + g_hCLRStrongName2.ClearUnsafe(); + g_hCLRStrongName2.Assign(g_pCLRStrongName2); + } + else + { + // We were beat to the punch, don't publish this interface + // and make sure we use the published value for consistency. + pStrongName->Release(); + } + } + + *ppCLRStrongName2 = g_pCLRStrongName2; + return hr; + } + + // ---AddStartupFlags------------------------------------------------------------------------------ + inline + HRESULT AddStartupFlags( + ICLRRuntimeInfo *pInfo, + LPCWSTR wszBuildFlavor, + DWORD dwStartupFlags, + LPCWSTR wszHostConfigFile) + { + if (wszBuildFlavor != NULL && + (wszBuildFlavor[0] == L's' || wszBuildFlavor[0] == L'S') && + (wszBuildFlavor[1] == L'v' || wszBuildFlavor[1] == L'V') && + (wszBuildFlavor[2] == L'r' || wszBuildFlavor[2] == L'R') && + wszBuildFlavor[3] == 0) + { + dwStartupFlags |= STARTUP_SERVER_GC; + } + + HRESULT hr = S_OK; + + DWORD dwEffectiveStartupFlags = 0; + IfHrFailRet(pInfo->GetDefaultStartupFlags(&dwEffectiveStartupFlags, NULL, NULL)); + + // Startup flags at this point are either default (i.e. STARTUP_CONCURRENT_GC) + // or have been set based on a config file. We want to clear the concurrent + // GC flag because we are supplying non-defaults, and combine them with the + // user supplied flags. Note that STARTUP_CONCURRENT_GC is never set as part + // of reading a config so we are not losing any information here. + + dwEffectiveStartupFlags &= ~STARTUP_CONCURRENT_GC; + dwEffectiveStartupFlags |= dwStartupFlags; + + return pInfo->SetDefaultStartupFlags(dwEffectiveStartupFlags, wszHostConfigFile); + } + + // ------------------------------------------------------------------------------------------------ + SELECTANY HMODULE g_hShlwapi = NULL; + SELECTANY ZeroInitGlobalHMODULEHolder g_hShlwapiHolder; + + // ------------------------------------------------------------------------------------------------ + inline + HRESULT CreateIStreamFromFile( + LPCWSTR wszFilePath, + IStream **ppIStream) + { + HRESULT hr = S_OK; + *ppIStream = NULL; + + if (g_hShlwapi == NULL) + { + HMODULE hShlwapi = LEGACY_ACTIVATION_SHIM_LOAD_LIBRARY(_TEXT_ENCODE("shlwapi.dll")); + if (hShlwapi == NULL) + return HRESULT_FROM_WIN32(GetLastError()); + HMODULEHolder hShlwapiHolder(hShlwapi); + + if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT<HMODULE>( + &g_hShlwapi, hShlwapi, NULL) == NULL) + { + g_hShlwapiHolder.ClearUnsafe(); + g_hShlwapiHolder.Assign(hShlwapi); + hShlwapiHolder.SuppressRelease(); + } + } + + typedef HRESULT (__stdcall * SHCreateStreamOnFile_pfn)( + LPCWSTR wszFile, + DWORD grfMode, + IStream **ppstm); + + SHCreateStreamOnFile_pfn pCreateStreamOnFile = + reinterpret_cast<SHCreateStreamOnFile_pfn>(GetProcAddress(g_hShlwapi, "SHCreateStreamOnFileW")); + + if (pCreateStreamOnFile == NULL) + return HRESULT_FROM_WIN32(GetLastError()); + + //_ASSERTE(pCreateStreamOnFile != NULL); + + // Create IStream + IStream* pStream(NULL); + IfHrFailRet((*pCreateStreamOnFile)(wszFilePath, 0 /*STGM_READ*/, &pStream)); + ReleaseHolder<IStream*> hStream(pStream); + + // Success, prevent release and assign IStream to out parameter + *ppIStream = pStream; + hStream.SuppressRelease(); + + return S_OK; + } + }; // namespace Util +}; // namespace LegacyActivationShim + +#pragma pop_macro("UNUSED") +#pragma pop_macro("countof") +#pragma pop_macro("_TEXT_ENCODE") +#pragma pop_macro("SELECTANY") + +#endif // __LEGACYACTIVATIONSHIMUTIL_H__ + |