// 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 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(InterlockedCompareExchangePointer( (PVOID volatile *)(destination), (PVOID)(exchange), (PVOID)(comparand))); #endif // __UtilCode_h__ else } // ---PlacementNewDeleteHelper------------------------------------------------------------- template class PlacementNewDeleteHelper; template class PlacementNewDeleteHelper { 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 class PlacementNewDeleteHelper { public: static void Construct(TYPE const & value, void *pvWhere) { *reinterpret_cast(pvWhere) = value; } static void Destruct(TYPE &) { } }; // ---HOLDERBASE--------------------------------------------------------------------------- template 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(&m_value[0]); } inline TYPE & GetPtr() { return reinterpret_cast(&m_value[0]); } inline void Construct(TYPE const & value) { PlacementNewDeleteHelper::Construct(value, (void *)&m_value[0]); } inline void Destruct() { PlacementNewDeleteHelper::Destruct(GetRef()); } }; // ---HOLDER------------------------------------------------------------------------------- template class Holder : public HolderBase { 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 class ZeroInitGlobalHolder : public HolderBase { 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 inline void DoNothingHelper(TYPE & value) { UNUSED(value); } // ---RELEASEHELPER------------------------------------------------------------------------ template inline void ReleaseHelper(TYPE & value) { value->Release(); } // ---RELEASEHOLDER------------------------------------------------------------------------ template class ReleaseHolder : public Holder< TYPE, &DoNothingHelper, &ReleaseHelper > { public: inline ReleaseHolder(TYPE & value) : Holder< TYPE, &DoNothingHelper, &ReleaseHelper >(value) {} ReleaseHolder() : Holder< TYPE, &DoNothingHelper, &ReleaseHelper >() {} }; // ---ZEROINITGLOBALRELEASEHOLDER---------------------------------------------------------- template class ZeroInitGlobalReleaseHolder : public ZeroInitGlobalHolder< TYPE, &DoNothingHelper, &ReleaseHelper > { }; // ---FREELIBRARYHELPER-------------------------------------------------------------------- inline void FreeLibraryHelper(HMODULE & hMod) { FreeLibrary(hMod); } // ---HMODULEHOLDER------------------------------------------------------------------------ class HMODULEHolder : public Holder< HMODULE, &DoNothingHelper, &FreeLibraryHelper > { public: inline HMODULEHolder(HMODULE value) : Holder< HMODULE, &DoNothingHelper, &FreeLibraryHelper >(value) {} HMODULEHolder() : Holder< HMODULE, &DoNothingHelper, &FreeLibraryHelper >() {} }; // ---ZEROINITHMODULEHOLDER---------------------------------------------------------------- class ZeroInitGlobalHMODULEHolder : public ZeroInitGlobalHolder< HMODULE, &DoNothingHelper, &FreeLibraryHelper > { }; // ---DELAYLOADFUNCTOR--------------------------------------------------------------------- // T must be a function typedef. // For example, "typedef int X(short i); DelayLoadFunctor pfnX;" template 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(::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(::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 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( &g_hModMscoree, hModMscoree, NULL) == NULL) { g_hModMscoreeHolder.ClearUnsafe(); g_hModMscoreeHolder.Assign(g_hModMscoree); hModMscoreeHolder.SuppressRelease(); } } *pMscoree = g_hModMscoree; return S_OK; } // ---MSCOREEFUNCTOR----------------------------------------------------------------------- template class MscoreeFunctor : public DelayLoadFunctor { public: HRESULT Init(LPCSTR szProcName) { HRESULT hr = S_OK; HMODULE hModMscoree = NULL; IfHrFailRet(GetMSCOREE(&hModMscoree)); return DelayLoadFunctor::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(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 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(&pMetaHost))); ReleaseHolder hMetaHost(pMetaHost); // // Great - we got an ICLRMetaHost. Now publish this to // g_pCLRMetaHost in a thread-safe way. // if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT( &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 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(&pMetaHostPolicy))); ReleaseHolder hMetaHostPolicy(pMetaHostPolicy); // // Great - we got an ICLRMetaHostPolicy. Now publish this to // g_pCLRMetaHostPolicy in a thread-safe way. // if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT( &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 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(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_USE_PROCESS_IMAGE_PATH | METAHOST_POLICY_APPLY_UPGRADE_POLICY), pEXE, pIStream, // (is NULL) _wszVersion, &_cchVersion, wszImageVersion, pcchImageVersion, &dwConfigFlags, IID_ICLRRuntimeInfo, reinterpret_cast(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 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(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(&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 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(&pStrongName))); // // Great - we got an ICLRStrongName. Now publish this to // g_pCLRStrongName in a thread-safe way. // if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT( &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 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(&pStrongName))); // // Great - we got an ICLRStrongName2. Now publish this to // g_pCLRStrongName2 in a thread-safe way. // if (LegacyActivationShim::Util::InterlockedCompareExchangePointerT( &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( &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(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 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__