summaryrefslogtreecommitdiff
path: root/src/inc/legacyactivationshimutil.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/inc/legacyactivationshimutil.h')
-rw-r--r--src/inc/legacyactivationshimutil.h1116
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__
+