diff options
Diffstat (limited to 'src/vm/corhost.cpp')
-rw-r--r-- | src/vm/corhost.cpp | 8852 |
1 files changed, 8852 insertions, 0 deletions
diff --git a/src/vm/corhost.cpp b/src/vm/corhost.cpp new file mode 100644 index 0000000000..c229a0ee07 --- /dev/null +++ b/src/vm/corhost.cpp @@ -0,0 +1,8852 @@ +// 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. +//***************************************************************************** +// CorHost.cpp +// +// Implementation for the meta data dispenser code. +// + +//***************************************************************************** + +#include "common.h" + +#include "mscoree.h" +#include "corhost.h" +#include "excep.h" +#include "threads.h" +#include "jitinterface.h" +#include "eeconfig.h" +#include "dbginterface.h" +#include "ceemain.h" +#include "rwlock.h" +#include "hosting.h" +#include "eepolicy.h" +#include "clrex.h" +#ifdef FEATURE_IPCMAN +#include "ipcmanagerinterface.h" +#endif // FEATURE_IPCMAN +#include "comcallablewrapper.h" +#include "hostexecutioncontext.h" +#include "invokeutil.h" +#include "appdomain.inl" +#include "vars.hpp" +#include "comdelegate.h" +#include "dllimportcallback.h" +#include "eventtrace.h" + +#include "win32threadpool.h" +#include "eventtrace.h" +#include "finalizerthread.h" +#include "threadsuspend.h" + +#ifndef FEATURE_PAL +#include "dwreport.h" +#endif // !FEATURE_PAL + +#include "stringarraylist.h" + +#ifdef FEATURE_COMINTEROP +#include "winrttypenameconverter.h" +#endif + +#if defined(FEATURE_APPX_BINDER) +#include "clrprivbinderappx.h" +#include "clrprivtypecachewinrt.h" +#endif + +GVAL_IMPL_INIT(DWORD, g_fHostConfig, 0); + +#ifdef FEATURE_IMPLICIT_TLS +#ifndef __llvm__ +EXTERN_C __declspec(thread) ThreadLocalInfo gCurrentThreadInfo; +#else // !__llvm__ +EXTERN_C __thread ThreadLocalInfo gCurrentThreadInfo; +#endif // !__llvm__ +#ifndef FEATURE_PAL +EXTERN_C UINT32 _tls_index; +#else // FEATURE_PAL +UINT32 _tls_index = 0; +#endif // FEATURE_PAL +SVAL_IMPL_INIT(DWORD, CExecutionEngine, TlsIndex, _tls_index); +#else +SVAL_IMPL_INIT(DWORD, CExecutionEngine, TlsIndex, TLS_OUT_OF_INDEXES); +#endif + + +#if defined(FEATURE_INCLUDE_ALL_INTERFACES) || defined(FEATURE_WINDOWSPHONE) +SVAL_IMPL_INIT(ECustomDumpFlavor, CCLRErrorReportingManager, g_ECustomDumpFlavor, DUMP_FLAVOR_Default); +#endif + +#ifndef DACCESS_COMPILE + +extern void STDMETHODCALLTYPE EEShutDown(BOOL fIsDllUnloading); +extern HRESULT STDMETHODCALLTYPE CoInitializeEE(DWORD fFlags); +extern void PrintToStdOutA(const char *pszString); +extern void PrintToStdOutW(const WCHAR *pwzString); +extern BOOL g_fEEHostedStartup; + +INT64 g_PauseTime; // Total time in millisecond the CLR has been paused +Volatile<BOOL> g_IsPaused; // True if the runtime is paused (FAS) +CLREventStatic g_ClrResumeEvent; // Event that is fired at FAS Resuming +#ifndef FEATURE_CORECLR +CLREventStatic g_PauseCompletedEvent; // Set when Pause has completed its work on another thread. +#endif + +#if defined(FEATURE_CORECLR) +extern BYTE g_rbTestKeyBuffer[]; +#endif + +#if !defined(FEATURE_CORECLR) +//****************************************************************************** +// <TODO>TODO: ICorThreadpool: Move this into a separate file CorThreadpool.cpp +// after the move to VBL </TODO> +//****************************************************************************** + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorRegisterWaitForSingleObject(PHANDLE phNewWaitObject, + HANDLE hWaitObject, + WAITORTIMERCALLBACK Callback, + PVOID Context, + ULONG timeout, + BOOL executeOnlyOnce, + BOOL* pResult) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + + BEGIN_ENTRYPOINT_NOTHROW; + + ULONG flag = executeOnlyOnce ? WAIT_SINGLE_EXECUTION : 0; + *pResult = FALSE; + EX_TRY + { + *pResult = ThreadpoolMgr::RegisterWaitForSingleObject(phNewWaitObject, + hWaitObject, + Callback, + Context, + timeout, + flag); + + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorBindIoCompletionCallback(HANDLE fileHandle, + LPOVERLAPPED_COMPLETION_ROUTINE callback) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + BOOL ret = FALSE; + DWORD errCode = 0; + + EX_TRY + { + ret = ThreadpoolMgr::BindIoCompletionCallback(fileHandle,callback,0, errCode); + hr = (ret ? S_OK : HRESULT_FROM_WIN32(errCode)); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorUnregisterWait(HANDLE hWaitObject, + HANDLE CompletionEvent, + BOOL* pResult) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + + BEGIN_ENTRYPOINT_NOTHROW; + + *pResult = FALSE; + EX_TRY + { + + *pResult = ThreadpoolMgr::UnregisterWaitEx(hWaitObject,CompletionEvent); + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + + return hr; + +} + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorQueueUserWorkItem(LPTHREAD_START_ROUTINE Function, + PVOID Context,BOOL executeOnlyOnce, + BOOL* pResult ) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + *pResult = FALSE; + EX_TRY + { + *pResult = ThreadpoolMgr::QueueUserWorkItem(Function,Context,QUEUE_ONLY); + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorCallOrQueueUserWorkItem(LPTHREAD_START_ROUTINE Function, + PVOID Context, + BOOL* pResult ) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + *pResult = FALSE; + EX_TRY + { + *pResult = ThreadpoolMgr::QueueUserWorkItem(Function,Context,CALL_OR_QUEUE); + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorCreateTimer(PHANDLE phNewTimer, + WAITORTIMERCALLBACK Callback, + PVOID Parameter, + DWORD DueTime, + DWORD Period, + BOOL* pResult) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + *pResult = FALSE; + EX_TRY + { + *pResult = ThreadpoolMgr::CreateTimerQueueTimer(phNewTimer,Callback,Parameter,DueTime,Period,0); + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorDeleteTimer(HANDLE Timer, HANDLE CompletionEvent, BOOL* pResult) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + *pResult = FALSE; + EX_TRY + { + *pResult = ThreadpoolMgr::DeleteTimerQueueTimer(Timer,CompletionEvent); + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorChangeTimer(HANDLE Timer, + ULONG DueTime, + ULONG Period, + BOOL* pResult) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + *pResult = FALSE; + EX_TRY + { + //CONTRACT_VIOLATION(ThrowsViolation); + *pResult = ThreadpoolMgr::ChangeTimerQueueTimer(Timer,DueTime,Period); + hr = (*pResult ? S_OK : HRESULT_FROM_GetLastError()); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorSetMaxThreads(DWORD MaxWorkerThreads, + DWORD MaxIOCompletionThreads) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + BOOL result = FALSE; + EX_TRY + { + result = ThreadpoolMgr::SetMaxThreads(MaxWorkerThreads, MaxIOCompletionThreads); + hr = (result ? S_OK : E_FAIL); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorGetMaxThreads(DWORD *MaxWorkerThreads, + DWORD *MaxIOCompletionThreads) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + BOOL result = FALSE; + EX_TRY + { + result = ThreadpoolMgr::GetMaxThreads(MaxWorkerThreads, MaxIOCompletionThreads); + hr = (result ? S_OK : E_FAIL); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +HRESULT STDMETHODCALLTYPE CorThreadpool::CorGetAvailableThreads(DWORD *AvailableWorkerThreads, + DWORD *AvailableIOCompletionThreads) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + BEGIN_ENTRYPOINT_NOTHROW; + + BOOL result = FALSE; + EX_TRY + { + result = ThreadpoolMgr::GetAvailableThreads(AvailableWorkerThreads, AvailableIOCompletionThreads); + hr = (result ? S_OK : E_FAIL); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} +#endif // !defined(FEATURE_CORECLR) +//*************************************************************************** + +ULONG CorRuntimeHostBase::m_Version = 0; + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +static CCLRDebugManager s_CLRDebugManager; +#endif // FEATURE_INCLUDE_ALL_INTERFACES + +#if defined(FEATURE_INCLUDE_ALL_INTERFACES) || defined(FEATURE_WINDOWSPHONE) +CCLRErrorReportingManager g_CLRErrorReportingManager; +#endif // defined(FEATURE_INCLUDE_ALL_INTERFACES) || defined(FEATURE_WINDOWSPHONE) + +#ifdef FEATURE_IPCMAN +static CCLRSecurityAttributeManager s_CLRSecurityAttributeManager; +#endif // FEATURE_IPCMAN + +#endif // !DAC + +typedef DPTR(CONNID) PTR_CONNID; + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +// Hash table to keep track <connection, name> for SQL fiber support +class ConnectionNameTable : CHashTableAndData<CNewDataNoThrow> +{ + friend class CCLRDebugManager; +public: + + // Key to match is connection ID. + // Returns true if the given HASHENTRY has the same key as the requested key. + BOOL Cmp(SIZE_T requestedKey, const HASHENTRY * pEntry) + { + SUPPORTS_DAC; + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + CONNID keyRequested = (CONNID)requestedKey; + CONNID keySearch = dac_cast<PTR_ConnectionNameHashEntry>(pEntry)->m_dwConnectionId; + return keyRequested != keySearch; + } + + // Hash function + ULONG Hash(CONNID dwConnectionId) + { + SUPPORTS_DAC; + LIMITED_METHOD_CONTRACT; + + return (ULONG)(dwConnectionId); + } + +#ifndef DACCESS_COMPILE + // constructor + ConnectionNameTable( + ULONG iBuckets) : // # of chains we are hashing into. + CHashTableAndData<CNewDataNoThrow>(iBuckets) + {LIMITED_METHOD_CONTRACT;} + + // destructor + ~ConnectionNameTable() + { + CONTRACTL + { + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + NOTHROW; + } + CONTRACTL_END; + HASHFIND hashFind; + ConnectionNameHashEntry *pNameEntry; + + pNameEntry = (ConnectionNameHashEntry *)FindFirstEntry(&hashFind); + while (pNameEntry != NULL) + { + if (pNameEntry->m_pwzName) + { + delete pNameEntry->m_pwzName; + pNameEntry->m_pwzName = NULL; + } + + if (pNameEntry->m_CLRTaskCount != 0) + { + _ASSERTE(pNameEntry->m_ppCLRTaskArray != NULL); + for (UINT i = 0; i < pNameEntry->m_CLRTaskCount; i++) + { + pNameEntry->m_ppCLRTaskArray[i]->Release(); + } + delete [] pNameEntry->m_ppCLRTaskArray; + pNameEntry->m_ppCLRTaskArray = NULL; + pNameEntry->m_CLRTaskCount = 0; + } + pNameEntry = (ConnectionNameHashEntry *)FindNextEntry(&hashFind); + } + } + + // Add a new connection into hash table. + // This function does not throw but return NULL when memory allocation fails. + ConnectionNameHashEntry *AddConnection( + CONNID dwConnectionId, + __in_z WCHAR *pwzName) // We should review this in the future. This API is + // public and callable by a host. This SAL annotation + // is the best we can do now. + { + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + } + CONTRACTL_END; + + ULONG iHash = Hash(dwConnectionId); + + size_t len = wcslen(pwzName) + 1; + WCHAR *pConnName = new (nothrow) WCHAR[len]; + if (pConnName == NULL) + return NULL; + + ConnectionNameHashEntry *pRecord = (ConnectionNameHashEntry *)Add(iHash); + if (pRecord) + { + pRecord->m_dwConnectionId = dwConnectionId; + pRecord->m_pwzName = pConnName; + wcsncpy_s(pRecord->m_pwzName, len, pwzName, len); + pRecord->m_CLRTaskCount = 0; + pRecord->m_ppCLRTaskArray = NULL; + } + else + { + if (pConnName) + delete [] pConnName; + } + + return pRecord; + } + + // Delete a hash entry given a connection id + void DeleteConnection(CONNID dwConnectionId) + { + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + } + CONTRACTL_END; + + ULONG iHash; + iHash = Hash(dwConnectionId); + ConnectionNameHashEntry * pRecord = + reinterpret_cast<ConnectionNameHashEntry *>(Find(iHash, (SIZE_T)dwConnectionId)); + if (pRecord == NULL) + { + return; + } + + _ASSERTE(pRecord->m_CLRTaskCount == 0 && pRecord->m_ppCLRTaskArray == NULL); + if (pRecord->m_pwzName) + { + delete pRecord->m_pwzName; + pRecord->m_pwzName = NULL; + } + Delete(iHash, (HASHENTRY *)pRecord); + } + + // return NULL if the given connection id cannot be found. + ConnectionNameHashEntry *FindConnection(CONNID dwConnectionId) + { + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + } + CONTRACTL_END; + + ULONG iHash; + iHash = Hash(dwConnectionId); + return reinterpret_cast<ConnectionNameHashEntry *>(Find(iHash, (SIZE_T)dwConnectionId)); + } +#endif // !DAC +}; +#endif //FEATURE_INCLUDE_ALL_INTERFACES + + +// Keep track connection id and name +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +SPTR_IMPL(ConnectionNameTable, CCLRDebugManager, m_pConnectionNameHash); +CrstStatic CCLRDebugManager::m_lockConnectionNameTable; +#endif // FEATURE_INCLUDE_ALL_INTERFACES + +#ifndef DACCESS_COMPILE + + +#if !defined(FEATURE_CORECLR) // simple hosting +//***************************************************************************** +// ICorRuntimeHost +//***************************************************************************** +extern BOOL g_singleVersionHosting; + +// *** ICorRuntimeHost methods *** +// Returns an object for configuring the runtime prior to +// it starting. If the runtime has been initialized this +// routine returns an error. See ICorConfiguration. +HRESULT CorHost::GetConfiguration(ICorConfiguration** pConfiguration) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + HRESULT hr=E_FAIL; + BEGIN_ENTRYPOINT_NOTHROW; + if (CorHost::GetHostVersion() != 1) + { + hr=HOST_E_INVALIDOPERATION; + } + else + if (!pConfiguration) + hr= E_POINTER; + else + if (!m_Started) + { + *pConfiguration = (ICorConfiguration *) this; + AddRef(); + hr=S_OK; + } + END_ENTRYPOINT_NOTHROW; + // Cannot obtain configuration after the runtime is started + return hr; +} + +STDMETHODIMP CorHost::Start(void) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + hr = CorRuntimeHostBase::Start(); + + END_ENTRYPOINT_NOTHROW; + + if (hr == S_FALSE) + { + // This is to keep v1 behavior. + hr = S_OK; + } + return(hr); +} +#endif // !defined(FEATURE_CORECLR) + + +// *** ICorRuntimeHost methods *** +#ifndef FEATURE_CORECLR +// Returns an object for configuring the runtime prior to +// it starting. If the runtime has been initialized this +// routine returns an error. See ICorConfiguration. +HRESULT CorHost2::GetConfiguration(ICorConfiguration** pConfiguration) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + if (!pConfiguration) + return E_POINTER; + HRESULT hr=E_FAIL; + BEGIN_ENTRYPOINT_NOTHROW; + if (!m_Started) + { + *pConfiguration = (ICorConfiguration *) this; + AddRef(); + hr=S_OK; + } + END_ENTRYPOINT_NOTHROW; + // Cannot obtain configuration after the runtime is started + return hr; +} +#endif // FEATURE_CORECLR + +extern BOOL g_fWeOwnProcess; + +CorHost2::CorHost2() +{ + LIMITED_METHOD_CONTRACT; + +#ifdef FEATURE_CORECLR + m_fStarted = FALSE; + m_fFirstToLoadCLR = FALSE; + m_fAppDomainCreated = FALSE; +#endif // FEATURE_CORECLR +} + +static DangerousNonHostedSpinLock lockOnlyOneToInvokeStart; + +STDMETHODIMP CorHost2::Start() +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + }CONTRACTL_END; + + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + +#ifdef FEATURE_CORECLR + // Ensure that only one thread at a time gets in here + DangerousNonHostedSpinLockHolder lockHolder(&lockOnlyOneToInvokeStart); + + // To provide the complete semantic of Start/Stop in context of a given host, we check m_fStarted and let + // them invoke the Start only if they have not already. Likewise, they can invoke the Stop method + // only if they have invoked Start prior to that. + // + // This prevents a host from invoking Stop twice and hitting the refCount to zero, when another + // host is using the CLR, as CLR instance sharing across hosts is a scenario for CoreCLR. + + if (g_fEEStarted) + { + hr = S_OK; + // CoreCLR is already running - but was Start already invoked by this host? + if (m_fStarted) + { + // This host had already invoked the Start method - return them an error + hr = HOST_E_INVALIDOPERATION; + } + else + { + // Increment the global (and dynamic) refCount... + FastInterlockIncrement(&m_RefCount); + + // And set our flag that this host has invoked the Start... + m_fStarted = TRUE; + } + } + else +#endif // FEATURE_CORECLR + { + // Using managed C++ libraries, its possible that when the runtime is already running, + // MC++ will use CorBindToRuntimeEx to make callbacks into specific appdomain of its + // choice. Now, CorBindToRuntimeEx results in CorHost2::CreateObject being invoked + // that will set runtime hosted flag "g_fHostConfig |= CLRHOSTED". + // + // For the case when managed code started without CLR hosting and MC++ does a + // CorBindToRuntimeEx, setting the CLR hosted flag is incorrect. + // + // Thus, before we attempt to start the runtime, we save the status of it being + // already running or not. Next, if we are able to successfully start the runtime + // and ONLY if it was not started earlier will we set the hosted flag below. + if (!g_fEEStarted) + { + g_fHostConfig |= CLRHOSTED; + } + + hr = CorRuntimeHostBase::Start(); + if (SUCCEEDED(hr)) + { +#ifdef FEATURE_CORECLR + // Set our flag that this host invoked the Start method. + m_fStarted = TRUE; + + // And they also loaded the CoreCLR DLL in the memory (for this version). + // This is a special flag as the host that has got this flag set will be allowed + // to repeatedly invoke Stop method (without corresponding Start method invocations). + // This is to support scenarios like that of Office where they need to bring down + // the CLR at any cost. + // + // So, if you want to do that, just make sure you are the first host to load the + // specific version of CLR in memory AND start it. + m_fFirstToLoadCLR = TRUE; +#endif // FEATURE_CORECLR + if (FastInterlockIncrement(&m_RefCount) != 1) + { + } + else + { + if (g_fWeOwnProcess) + { + // Runtime is started by a managed exe. Bump the ref-count, so that + // matching Start/Stop does not stop runtime. + FastInterlockIncrement(&m_RefCount); + } + } + } + } + + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// Starts the runtime. This is equivalent to CoInitializeEE(); +HRESULT CorRuntimeHostBase::Start() +{ + CONTRACTL + { + NOTHROW; + DISABLED(GC_TRIGGERS); + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + { + m_Started = TRUE; +#ifdef FEATURE_EVENT_TRACE + g_fEEHostedStartup = TRUE; +#endif // FEATURE_EVENT_TRACE + hr = InitializeEE(COINITEE_DEFAULT); + } + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#if !defined(FEATURE_CORECLR) // simple hosting +HRESULT CorHost::Stop() +{ + CONTRACTL + { + NOTHROW; + ENTRY_POINT; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + } + CONTRACTL_END; + + // This must remain this way (that is doing nothing) for backwards compat reasons. + return S_OK; +} +#endif // !defined(FEATURE_CORECLR) + +HRESULT CorHost2::Stop() +{ + CONTRACTL + { + NOTHROW; + ENTRY_POINT; // We're bringing the EE down, so no point in probing + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + } + CONTRACTL_END; + if (!g_fEEStarted) + { + return E_UNEXPECTED; + } + HRESULT hr=S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + +#ifdef FEATURE_CORECLR + // Is this host eligible to invoke the Stop method? + if ((!m_fStarted) && (!m_fFirstToLoadCLR)) + { + // Well - since this host never invoked Start, it is not eligible to invoke Stop. + // Semantically, for such a host, CLR is not available in the process. The only + // exception to this condition is the host that first loaded this version of the + // CLR and invoked Start method. For details, refer to comments in CorHost2::Start implementation. + hr = HOST_E_CLRNOTAVAILABLE; + } + else +#endif // FEATURE_CORECLR + { + while (TRUE) + { + LONG refCount = m_RefCount; + if (refCount == 0) + { + #ifdef FEATURE_CORECLR + hr = HOST_E_CLRNOTAVAILABLE; + #else // !FEATURE_CORECLR + hr= E_UNEXPECTED; + #endif // FEATURE_CORECLR + break; + } + else + if (FastInterlockCompareExchange(&m_RefCount, refCount - 1, refCount) == refCount) + { + #ifdef FEATURE_CORECLR + // Indicate that we have got a Stop for a corresponding Start call from the + // Host. Semantically, CoreCLR has stopped for them. + m_fStarted = FALSE; + #endif // FEATURE_CORECLR + + if (refCount > 1) + { + hr=S_FALSE; + break; + } + else + { + break; + } + } + } + } +#ifndef FEATURE_CORECLR + if (hr==S_OK) + { + EPolicyAction action = GetEEPolicy()->GetDefaultAction(OPR_ProcessExit, NULL); + if (action > eExitProcess) + { + g_fFastExitProcess = 1; + } + EEShutDown(FALSE); + } +#endif // FEATURE_CORECLR + END_ENTRYPOINT_NOTHROW; + +#ifndef FEATURE_CORECLR + if (hr == S_OK) + { + if (m_HostControl) + { + m_HostControl->Release(); + m_HostControl = NULL; + } + } +#endif // FEATURE_CORECLR + + return hr; +} + +#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORECLR) + +// Creates a domain in the runtime. The identity array is +// a pointer to an array TYPE containing IIdentity objects defining +// the security identity. +HRESULT CorRuntimeHostBase::CreateDomain(LPCWSTR pwzFriendlyName, + IUnknown* pIdentityArray, // Optional + IUnknown ** pAppDomain) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_ENTRY_POINT; + + return CreateDomainEx(pwzFriendlyName, + NULL, + NULL, + pAppDomain); +} + + +// Returns the default domain. +HRESULT CorRuntimeHostBase::GetDefaultDomain(IUnknown ** pAppDomain) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_PREEMPTIVE; + ENTRY_POINT; + } CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + if (!g_fEEStarted) + return hr; + + if( pAppDomain == NULL) + return E_POINTER; + + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + if (SystemDomain::System()) { + AppDomain* pCom = SystemDomain::System()->DefaultDomain(); + if(pCom) + hr = pCom->GetComIPForExposedObject(pAppDomain); + } + + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +// Returns the default domain. +HRESULT CorRuntimeHostBase::CurrentDomain(IUnknown ** pAppDomain) +{ + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + ENTRY_POINT; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + } + CONTRACTL_END; + + HRESULT hr = E_UNEXPECTED; + if (!g_fEEStarted) + return hr; + + if( pAppDomain == NULL) return E_POINTER; + + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + AppDomain* pCom = ::GetAppDomain(); + if(pCom) + hr = pCom->GetComIPForExposedObject(pAppDomain); + + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +}; + +#endif // FEATURE_COMINTEROP && !FEATURE_CORECLR + +HRESULT CorHost2::GetCurrentAppDomainId(DWORD *pdwAppDomainId) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + // No point going further if the runtime is not running... + // We use CanRunManagedCode() instead of IsRuntimeActive() because this allows us + // to specify test using the form that does not trigger a GC. + if (!(g_fEEStarted && CanRunManagedCode(LoaderLockCheck::None)) +#ifdef FEATURE_CORECLR + || !m_fStarted +#endif + ) + { + return HOST_E_CLRNOTAVAILABLE; + } + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + if(pdwAppDomainId == NULL) + { + hr = E_POINTER; + } + else + { + Thread *pThread = GetThread(); + if (!pThread) + { + hr = E_UNEXPECTED; + } + else + { + *pdwAppDomainId = SystemDomain::GetCurrentDomain()->GetId().m_dwId; + } + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +HRESULT CorHost2::ExecuteApplication(LPCWSTR pwzAppFullName, + DWORD dwManifestPaths, + LPCWSTR *ppwzManifestPaths, + DWORD dwActivationData, + LPCWSTR *ppwzActivationData, + int *pReturnValue) +{ +#ifndef FEATURE_CORECLR + // This API should not be called when the EE has already been started. + HRESULT hr = E_UNEXPECTED; + if (g_fEEStarted) + return hr; + + // + // We will let unhandled exceptions in the activated application + // propagate all the way up, so that ClickOnce semi-trusted apps + // can participate in the Dr Watson program, etc... + // + + CONTRACTL { + THROWS; + ENTRY_POINT; + } + CONTRACTL_END; + + if (!pwzAppFullName) + IfFailGo(E_POINTER); + + // Set the information about the application to execute. + CorCommandLine::m_pwszAppFullName = (LPWSTR) pwzAppFullName; + CorCommandLine::m_dwManifestPaths = dwManifestPaths; + CorCommandLine::m_ppwszManifestPaths = (LPWSTR*) ppwzManifestPaths; + CorCommandLine::m_dwActivationData = dwActivationData; + CorCommandLine::m_ppwszActivationData = (LPWSTR*) ppwzActivationData; + + // Start up the EE. + IfFailGo(Start()); + + Thread *pThread; + pThread = GetThread(); + if (pThread == NULL) + pThread = SetupThreadNoThrow(&hr); + if (pThread == NULL) + goto ErrExit; + + _ASSERTE (!pThread->PreemptiveGCDisabled()); + + hr = S_OK; + + BEGIN_ENTRYPOINT_THROWS_WITH_THREAD(pThread); + ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD) + + SystemDomain::ActivateApplication(pReturnValue); + + END_DOMAIN_TRANSITION; + END_ENTRYPOINT_THROWS_WITH_THREAD; + +ErrExit: + return hr; +#else // FEATURE_CORECLR + return E_NOTIMPL; +#endif +} + +#ifdef FEATURE_CORECLR +/* + * This method processes the arguments sent to the host which are then used + * to invoke the main method. + * Note - + * [0] - points to the assemblyName that has been sent by the host. + * The rest are the arguments sent to the assembly. + * Also note, this might not always return the exact same identity as the cmdLine + * used to invoke the method. + * + * For example :- + * ActualCmdLine - Foo arg1 arg2. + * (Host1) - Full_path_to_Foo arg1 arg2 +*/ +void SetCommandLineArgs(LPCWSTR pwzAssemblyPath, int argc, LPCWSTR* argv) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_COOPERATIVE; + } + CONTRACTL_END; + + struct _gc + { + PTRARRAYREF cmdLineArgs; + } gc; + + ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + gc.cmdLineArgs = (PTRARRAYREF)AllocateObjectArray(argc + 1 /* arg[0] should be the exe name*/, g_pStringClass); + OBJECTREF orAssemblyPath = StringObject::NewString(pwzAssemblyPath); + gc.cmdLineArgs->SetAt(0, orAssemblyPath); + + for (int i = 0; i < argc; ++i) + { + OBJECTREF argument = StringObject::NewString(argv[i]); + gc.cmdLineArgs->SetAt(i + 1, argument); + } + + MethodDescCallSite setCmdLineArgs(METHOD__ENVIRONMENT__SET_COMMAND_LINE_ARGS); + + ARG_SLOT args[] = + { + ObjToArgSlot(gc.cmdLineArgs), + }; + setCmdLineArgs.Call(args); + + GCPROTECT_END(); +} + +HRESULT CorHost2::ExecuteAssembly(DWORD dwAppDomainId, + LPCWSTR pwzAssemblyPath, + int argc, + LPCWSTR* argv, + DWORD *pReturnValue) +{ + CONTRACTL + { + THROWS; // Throws...as we do not want it to swallow the managed exception + ENTRY_POINT; + } + CONTRACTL_END; + + // This is currently supported in default domain only + if (dwAppDomainId != DefaultADID) + return HOST_E_INVALIDOPERATION; + + // No point going further if the runtime is not running... + if (!IsRuntimeActive() || !m_fStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + if(!pwzAssemblyPath) + return E_POINTER; + + if(argc < 0) + { + return E_INVALIDARG; + } + + if(argc > 0 && argv == NULL) + { + return E_INVALIDARG; + } + + HRESULT hr = S_OK; + + AppDomain *pCurDomain = SystemDomain::GetCurrentDomain(); + + Thread *pThread = GetThread(); + if (pThread == NULL) + { + pThread = SetupThreadNoThrow(&hr); + if (pThread == NULL) + { + goto ErrExit; + } + } + + if(pCurDomain->GetId().m_dwId != DefaultADID) + { + return HOST_E_INVALIDOPERATION; + } + + INSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; + INSTALL_UNWIND_AND_CONTINUE_HANDLER; + + _ASSERTE (!pThread->PreemptiveGCDisabled()); + + Assembly *pAssembly = AssemblySpec::LoadAssembly(pwzAssemblyPath); + +#if defined(FEATURE_MULTICOREJIT) + pCurDomain->GetMulticoreJitManager().AutoStartProfile(pCurDomain); +#endif // defined(FEATURE_MULTICOREJIT) + + { + GCX_COOP(); + + // Here we call the managed method that gets the cmdLineArgs array. + SetCommandLineArgs(pwzAssemblyPath, argc, argv); + + PTRARRAYREF arguments = NULL; + GCPROTECT_BEGIN(arguments); + + arguments = (PTRARRAYREF)AllocateObjectArray(argc, g_pStringClass); + for (int i = 0; i < argc; ++i) + { + STRINGREF argument = StringObject::NewString(argv[i]); + arguments->SetAt(i, argument); + } + + DWORD retval = pAssembly->ExecuteMainMethod(&arguments, TRUE /* waitForOtherThreads */); + if (pReturnValue) + { + *pReturnValue = retval; + } + + GCPROTECT_END(); + + } + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + UNINSTALL_UNHANDLED_MANAGED_EXCEPTION_TRAP; + +ErrExit: + + return hr; +} +#endif + +HRESULT CorHost2::ExecuteInDefaultAppDomain(LPCWSTR pwzAssemblyPath, + LPCWSTR pwzTypeName, + LPCWSTR pwzMethodName, + LPCWSTR pwzArgument, + DWORD *pReturnValue) +{ + CONTRACTL + { + NOTHROW; + ENTRY_POINT; + } + CONTRACTL_END; + + // No point going further if the runtime is not running... + if (!IsRuntimeActive() +#ifdef FEATURE_CORECLR + || !m_fStarted +#endif + ) + { + return HOST_E_CLRNOTAVAILABLE; + } + + +#ifndef FEATURE_CORECLR + if(! (pwzAssemblyPath && pwzTypeName && pwzMethodName) ) + return E_POINTER; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + Thread *pThread = GetThread(); + if (pThread == NULL) + { + pThread = SetupThreadNoThrow(&hr); + if (pThread == NULL) + { + goto ErrExit; + } + } + + _ASSERTE (!pThread->PreemptiveGCDisabled()); + + EX_TRY + { + ENTER_DOMAIN_PTR(SystemDomain::System()->DefaultDomain(),ADV_DEFAULTAD) + + INSTALL_UNWIND_AND_CONTINUE_HANDLER; + + Assembly *pAssembly = AssemblySpec::LoadAssembly(pwzAssemblyPath); + + SString szTypeName(pwzTypeName); + StackScratchBuffer buff1; + const char* szTypeNameUTF8 = szTypeName.GetUTF8(buff1); + MethodTable *pMT = ClassLoader::LoadTypeByNameThrowing(pAssembly, + NULL, + szTypeNameUTF8).AsMethodTable(); + + SString szMethodName(pwzMethodName); + StackScratchBuffer buff; + const char* szMethodNameUTF8 = szMethodName.GetUTF8(buff); + MethodDesc *pMethodMD = MemberLoader::FindMethod(pMT, szMethodNameUTF8, &gsig_SM_Str_RetInt); + + if (!pMethodMD) + { + hr = COR_E_MISSINGMETHOD; + } + else + { + GCX_COOP(); + + MethodDescCallSite method(pMethodMD); + + STRINGREF sref = NULL; + GCPROTECT_BEGIN(sref); + + if (pwzArgument) + sref = StringObject::NewString(pwzArgument); + + ARG_SLOT MethodArgs[] = + { + ObjToArgSlot(sref) + }; + DWORD retval = method.Call_RetI4(MethodArgs); + if (pReturnValue) + { + *pReturnValue = retval; + } + + GCPROTECT_END(); + } + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + END_DOMAIN_TRANSITION; + } + EX_CATCH_HRESULT(hr); + +ErrExit: + + END_ENTRYPOINT_NOTHROW; + + return hr; +#else // FEATURE_CORECLR + // Ensure that code is not loaded in the Default AppDomain + return HOST_E_INVALIDOPERATION; +#endif +} + +HRESULT ExecuteInAppDomainHelper(FExecuteInAppDomainCallback pCallback, + void * cookie) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_SO_INTOLERANT; + + HRESULT hr = S_OK; + + BEGIN_SO_TOLERANT_CODE(GetThread()); + hr = pCallback(cookie); + END_SO_TOLERANT_CODE; + + return hr; +} + +HRESULT CorHost2::ExecuteInAppDomain(DWORD dwAppDomainId, + FExecuteInAppDomainCallback pCallback, + void * cookie) +{ + + // No point going further if the runtime is not running... + if (!IsRuntimeActive() +#ifdef FEATURE_CORECLR + || !m_fStarted +#endif // FEATURE_CORECLR + ) + { + return HOST_E_CLRNOTAVAILABLE; + } + +#ifdef FEATURE_CORECLR + if(!(m_dwStartupFlags & STARTUP_SINGLE_APPDOMAIN)) + { + // Ensure that code is not loaded in the Default AppDomain + if (dwAppDomainId == DefaultADID) + return HOST_E_INVALIDOPERATION; + } +#endif // FEATURE_CORECLR + + // Moved this here since no point validating the pointer + // if the basic checks [above] fail + if( pCallback == NULL) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + ENTER_DOMAIN_ID(ADID(dwAppDomainId)) + { + // We are calling an unmanaged function pointer, either an unmanaged function, or a marshaled out delegate. + // The thread should be in preemptive mode, and SO_Tolerant. + GCX_PREEMP(); + hr=ExecuteInAppDomainHelper (pCallback, cookie); + } + END_DOMAIN_TRANSITION; + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#define EMPTY_STRING_TO_NULL(s) {if(s && s[0] == 0) {s=NULL;};} + +HRESULT CorHost2::_CreateAppDomain( + LPCWSTR wszFriendlyName, + DWORD dwFlags, + LPCWSTR wszAppDomainManagerAssemblyName, + LPCWSTR wszAppDomainManagerTypeName, + int nProperties, + LPCWSTR* pPropertyNames, + LPCWSTR* pPropertyValues, +#if !defined(FEATURE_CORECLR) + ICLRPrivBinder* pBinder, +#endif + DWORD* pAppDomainID) +{ + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + HRESULT hr=S_OK; + +#ifdef FEATURE_CORECLR + //cannot call the function more than once when single appDomain is allowed + if (m_fAppDomainCreated && (m_dwStartupFlags & STARTUP_SINGLE_APPDOMAIN)) + { + return HOST_E_INVALIDOPERATION; + } +#endif + + //normalize empty strings + EMPTY_STRING_TO_NULL(wszFriendlyName); + EMPTY_STRING_TO_NULL(wszAppDomainManagerAssemblyName); + EMPTY_STRING_TO_NULL(wszAppDomainManagerTypeName); + + if(pAppDomainID==NULL) + return E_POINTER; + +#ifdef FEATURE_CORECLR + if (!m_fStarted) + return HOST_E_INVALIDOPERATION; +#endif // FEATURE_CORECLR + + if(wszFriendlyName == NULL) + return E_INVALIDARG; + + if((wszAppDomainManagerAssemblyName == NULL) != (wszAppDomainManagerTypeName == NULL)) + return E_INVALIDARG; + + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + AppDomainCreationHolder<AppDomain> pDomain; + +#ifdef FEATURE_CORECLR + // If StartupFlag specifies single appDomain then return the default domain instead of creating new one + if(m_dwStartupFlags & STARTUP_SINGLE_APPDOMAIN) + { + pDomain.Assign(SystemDomain::System()->DefaultDomain()); + } + else +#endif + { + AppDomain::CreateUnmanagedObject(pDomain); + } + + ETW::LoaderLog::DomainLoad(pDomain, (LPWSTR)wszFriendlyName); + +#ifdef FEATURE_CORECLR + if (dwFlags & APPDOMAIN_IGNORE_UNHANDLED_EXCEPTIONS) + { + pDomain->SetIgnoreUnhandledExceptions(); + } +#endif // FEATURE_CORECLR + + if (dwFlags & APPDOMAIN_SECURITY_FORBID_CROSSAD_REVERSE_PINVOKE) + pDomain->SetReversePInvokeCannotEnter(); + + if (dwFlags & APPDOMAIN_FORCE_TRIVIAL_WAIT_OPERATIONS) + pDomain->SetForceTrivialWaitOperations(); + +#if !defined(FEATURE_CORECLR) + if (pBinder != NULL) + pDomain->SetLoadContextHostBinder(pBinder); +#endif + +#ifdef PROFILING_SUPPORTED + EX_TRY +#endif + { + pDomain->SetAppDomainManagerInfo(wszAppDomainManagerAssemblyName,wszAppDomainManagerTypeName,eInitializeNewDomainFlags_None); + + GCX_COOP(); + + struct + { + STRINGREF friendlyName; + PTRARRAYREF propertyNames; + PTRARRAYREF propertyValues; + STRINGREF sandboxName; + OBJECTREF setupInfo; + OBJECTREF adSetup; + } _gc; + + ZeroMemory(&_gc,sizeof(_gc)); + + GCPROTECT_BEGIN(_gc) + _gc.friendlyName=StringObject::NewString(wszFriendlyName); + + if(nProperties>0) + { + _gc.propertyNames = (PTRARRAYREF) AllocateObjectArray(nProperties, g_pStringClass); + _gc.propertyValues= (PTRARRAYREF) AllocateObjectArray(nProperties, g_pStringClass); + for (int i=0;i< nProperties;i++) + { + STRINGREF obj = StringObject::NewString(pPropertyNames[i]); + _gc.propertyNames->SetAt(i, obj); + + obj = StringObject::NewString(pPropertyValues[i]); + _gc.propertyValues->SetAt(i, obj); + } + } + + if (dwFlags & APPDOMAIN_SECURITY_SANDBOXED) + { + _gc.sandboxName = StringObject::NewString(W("Internet")); + } + else + { + _gc.sandboxName = StringObject::NewString(W("FullTrust")); + } + + MethodDescCallSite prepareDataForSetup(METHOD__APP_DOMAIN__PREPARE_DATA_FOR_SETUP); + + ARG_SLOT args[8]; + args[0]=ObjToArgSlot(_gc.friendlyName); + args[1]=ObjToArgSlot(NULL); + args[2]=ObjToArgSlot(NULL); + args[3]=ObjToArgSlot(NULL); +#ifdef FEATURE_CORECLR + //CoreCLR shouldn't have dependencies on parent app domain. + args[4]=ObjToArgSlot(NULL); +#else + args[4]=PtrToArgSlot(GetAppDomain()->GetSecurityDescriptor()); +#endif //FEATURE_CORECLR + args[5]=ObjToArgSlot(_gc.sandboxName); + args[6]=ObjToArgSlot(_gc.propertyNames); + args[7]=ObjToArgSlot(_gc.propertyValues); + + _gc.setupInfo=prepareDataForSetup.Call_RetOBJECTREF(args); + + // + // Get the new flag values and set it to the domain + // + PTRARRAYREF handleArrayObj = (PTRARRAYREF) ObjectToOBJECTREF(_gc.setupInfo); + _gc.adSetup = ObjectToOBJECTREF(handleArrayObj->GetAt(1)); + +#ifndef FEATURE_CORECLR + // We need to setup domain sorting before any other managed code runs in the domain, since that code + // could end up caching data based on the sorting mode of the domain. + pDomain->InitializeSorting(&_gc.adSetup); + pDomain->InitializeHashing(&_gc.adSetup); +#endif + + pDomain->DoSetup(&_gc.setupInfo); + + pDomain->CacheStringsForDAC(); + + GCPROTECT_END(); + + *pAppDomainID=pDomain->GetId().m_dwId; + +#ifdef FEATURE_CORECLR + // If StartupFlag specifies single appDomain then set the flag that appdomain has already been created + if(m_dwStartupFlags & STARTUP_SINGLE_APPDOMAIN) + { + m_fAppDomainCreated = TRUE; + } +#endif + } +#ifdef PROFILING_SUPPORTED + EX_HOOK + { + // Need the first assembly loaded in to get any data on an app domain. + { + BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); + GCX_PREEMP(); + g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID)(AppDomain*) pDomain, GET_EXCEPTION()->GetHR()); + END_PIN_PROFILER(); + } + } + EX_END_HOOK; + + // Need the first assembly loaded in to get any data on an app domain. + { + BEGIN_PIN_PROFILER(CORProfilerTrackAppDomainLoads()); + GCX_PREEMP(); + g_profControlBlock.pProfInterface->AppDomainCreationFinished((AppDomainID)(AppDomain*) pDomain, S_OK); + END_PIN_PROFILER(); + } +#endif // PROFILING_SUPPORTED + + // DoneCreating releases ownership of AppDomain. After this call, there should be no access to pDomain. + pDomain.DoneCreating(); + + END_EXTERNAL_ENTRYPOINT; + + END_ENTRYPOINT_NOTHROW; + + return hr; + +}; + +HRESULT CorHost2::_CreateDelegate( + DWORD appDomainID, + LPCWSTR wszAssemblyName, + LPCWSTR wszClassName, + LPCWSTR wszMethodName, + INT_PTR* fnPtr) +{ + + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + HRESULT hr=S_OK; + + EMPTY_STRING_TO_NULL(wszAssemblyName); + EMPTY_STRING_TO_NULL(wszClassName); + EMPTY_STRING_TO_NULL(wszMethodName); + + if (fnPtr == NULL) + return E_POINTER; + *fnPtr = NULL; + + if(wszAssemblyName == NULL) + return E_INVALIDARG; + + if(wszClassName == NULL) + return E_INVALIDARG; + + if(wszMethodName == NULL) + return E_INVALIDARG; + +#ifdef FEATURE_CORECLR + if (!m_fStarted) + return HOST_E_INVALIDOPERATION; + + if(!(m_dwStartupFlags & STARTUP_SINGLE_APPDOMAIN)) + { + // Ensure that code is not loaded in the Default AppDomain + if (appDomainID == DefaultADID) + return HOST_E_INVALIDOPERATION; + } +#endif + + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + MAKE_UTF8PTR_FROMWIDE(szAssemblyName, wszAssemblyName); + MAKE_UTF8PTR_FROMWIDE(szClassName, wszClassName); + MAKE_UTF8PTR_FROMWIDE(szMethodName, wszMethodName); + + ADID id; + id.m_dwId=appDomainID; + + ENTER_DOMAIN_ID(id) + + GCX_PREEMP(); + + AssemblySpec spec; + spec.Init(szAssemblyName); + Assembly* pAsm=spec.LoadAssembly(FILE_ACTIVE); + + // we have no signature to check so allowing calling partially trusted code + // can result in an exploit + if (!pAsm->GetSecurityDescriptor()->IsFullyTrusted()) + ThrowHR(COR_E_SECURITY); + + TypeHandle th=pAsm->GetLoader()->LoadTypeByNameThrowing(pAsm,NULL,szClassName); + MethodDesc* pMD=NULL; + + if (!th.IsTypeDesc()) + { + pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), szMethodName, MemberLoader::FM_Unique); + if (pMD == NULL) + { + // try again without the FM_Unique flag (error path) + pMD = MemberLoader::FindMethodByName(th.GetMethodTable(), szMethodName, MemberLoader::FM_Default); + if (pMD != NULL) + { + // the method exists but is overloaded + ThrowHR(COR_E_AMBIGUOUSMATCH); + } + } + } + + if (pMD==NULL || !pMD->IsStatic() || pMD->ContainsGenericVariables()) + ThrowHR(COR_E_MISSINGMETHOD); + +#ifdef FEATURE_CORECLR + // the target method must be decorated with AllowReversePInvokeCallsAttribute + if (!COMDelegate::IsMethodAllowedToSinkReversePInvoke(pMD)) + ThrowHR(COR_E_SECURITY); +#endif + + UMEntryThunk *pUMEntryThunk = GetAppDomain()->GetUMEntryThunkCache()->GetUMEntryThunk(pMD); + *fnPtr = (INT_PTR)pUMEntryThunk->GetCode(); + + END_DOMAIN_TRANSITION; + + END_EXTERNAL_ENTRYPOINT; + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#ifdef FEATURE_CORECLR +HRESULT CorHost2::CreateAppDomainWithManager( + LPCWSTR wszFriendlyName, + DWORD dwFlags, + LPCWSTR wszAppDomainManagerAssemblyName, + LPCWSTR wszAppDomainManagerTypeName, + int nProperties, + LPCWSTR* pPropertyNames, + LPCWSTR* pPropertyValues, + DWORD* pAppDomainID) +{ + WRAPPER_NO_CONTRACT; + + return _CreateAppDomain( + wszFriendlyName, + dwFlags, + wszAppDomainManagerAssemblyName, + wszAppDomainManagerTypeName, + nProperties, + pPropertyNames, + pPropertyValues, + pAppDomainID); +} + +HRESULT CorHost2::CreateDelegate( + DWORD appDomainID, + LPCWSTR wszAssemblyName, + LPCWSTR wszClassName, + LPCWSTR wszMethodName, + INT_PTR* fnPtr) +{ + WRAPPER_NO_CONTRACT; + + return _CreateDelegate(appDomainID, wszAssemblyName, wszClassName, wszMethodName, fnPtr); +} + +HRESULT CorHost2::Authenticate(ULONGLONG authKey) +{ + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + // Host authentication was used by Silverlight. It is no longer relevant for CoreCLR. + return S_OK; +} + +HRESULT CorHost2::RegisterMacEHPort() +{ + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + return S_OK; +} + +HRESULT CorHost2::SetStartupFlags(STARTUP_FLAGS flag) +{ + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + if (g_fEEStarted) + { + return HOST_E_INVALIDOPERATION; + } + + m_dwStartupFlags = flag; + + return S_OK; +} + +#endif //FEATURE_CORECLR + +#ifndef FEATURE_CORECLR +void PauseOneAppDomain(AppDomainIterator* pi) +{ + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + EX_TRY { + ENTER_DOMAIN_PTR(pi->GetDomain(),ADV_ITERATOR); + + MethodDescCallSite(METHOD__APP_DOMAIN__PAUSE).Call(NULL); + + END_DOMAIN_TRANSITION; + } EX_CATCH { + } EX_END_CATCH(SwallowAllExceptions); +} + +void ResumeOneAppDomain(AppDomainIterator* pi) +{ + CONTRACTL + { + NOTHROW; + MODE_COOPERATIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + EX_TRY { + ENTER_DOMAIN_PTR(pi->GetDomain(),ADV_ITERATOR); + + MethodDescCallSite(METHOD__APP_DOMAIN__RESUME).Call(NULL); + + END_DOMAIN_TRANSITION; + } EX_CATCH { + } EX_END_CATCH(SwallowAllExceptions); +} + +// see comments in SuspendEEFromPause +DWORD WINAPI SuspendAndResumeForPause(LPVOID arg) +{ + CONTRACTL + { + NOTHROW; + MODE_ANY; + GC_TRIGGERS; + } + CONTRACTL_END; + + ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER); + + g_PauseCompletedEvent.Set(); + g_ClrResumeEvent.Wait(INFINITE, FALSE); + + ThreadSuspend::RestartEE(FALSE, TRUE); + return 0; +} + +#endif // !FEATURE_CORECLR + +HRESULT SuspendEEForPause() +{ + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + +#ifdef FEATURE_CORECLR + // In CoreCLR, we always resume from the same thread that paused. So we can simply suspend the EE from this thread, + // knowing we'll restart from the same thread. + ThreadSuspend::SuspendEE(ThreadSuspend::SUSPEND_OTHER); +#else + // In the CLR, we can resume from a different thread than the one that paused. We can't call SuspendEE directly, + // because we can't call RestartEE from another thread. So we queue a workitem to the ThreadPool to call SuspendEE + // and ResumeEE on our behalf. + + EX_TRY + { + if (!ThreadpoolMgr::QueueUserWorkItem(SuspendAndResumeForPause, NULL, QUEUE_ONLY)) + { + hr = HRESULT_FROM_GetLastError(); + } + else + { + // wait for SuspendEE to complete before returning. + g_PauseCompletedEvent.Wait(INFINITE,FALSE); + } + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); +#endif + + return hr; +} + +HRESULT RestartEEFromPauseAndSetResumeEvent() +{ + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + GC_TRIGGERS; + } + CONTRACTL_END; + + // see comments in SuspendEEFromPause +#ifdef FEATURE_CORECLR + ThreadSuspend::RestartEE(FALSE, TRUE); +#else + // setting the resume event below will restart the EE as well. We don't wait for the restart + // to complete, because we'll sync with it next time we go to cooperative mode. +#endif + + _ASSERTE(g_ClrResumeEvent.IsValid()); + g_ClrResumeEvent.Set(); + + return S_OK; +} + + + +CorExecutionManager::CorExecutionManager() + : m_dwFlags(0), m_pauseStartTime(0) +{ + LIMITED_METHOD_CONTRACT; + g_IsPaused = FALSE; + g_PauseTime = 0; +} + +HRESULT CorExecutionManager::Pause(DWORD dwAppDomainId, DWORD dwFlags) +{ + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + HRESULT hr = S_OK; + +#ifndef FEATURE_CORECLR + if (!IsRuntimeActive()) + return HOST_E_CLRNOTAVAILABLE; +#endif + + if(g_IsPaused) + return E_FAIL; + + EX_TRY + { + if(!g_ClrResumeEvent.IsValid()) + g_ClrResumeEvent.CreateManualEvent(FALSE); + else + g_ClrResumeEvent.Reset(); + +#ifndef FEATURE_CORECLR + if (!g_PauseCompletedEvent.IsValid()) + g_PauseCompletedEvent.CreateManualEvent(FALSE); + else + g_PauseCompletedEvent.Reset(); +#endif + } + EX_CATCH_HRESULT(hr); + + if (FAILED(hr)) + return hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + m_dwFlags = dwFlags; + +#ifndef FEATURE_CORECLR + if ((m_dwFlags & PAUSE_APP_DOMAINS) != 0) + { + Thread* pThread = SetupThreadNoThrow(&hr); + if (pThread != NULL) + { + GCX_COOP_THREAD_EXISTS(pThread); + + AppDomainIterator ai(/*bOnlyActive:*/ TRUE); + while (ai.Next()) + PauseOneAppDomain(&ai); + } + } +#endif + + if (SUCCEEDED(hr)) + { + g_IsPaused = TRUE; + + hr = SuspendEEForPause(); + + // Even though this is named with TickCount, it returns milliseconds + m_pauseStartTime = (INT64)CLRGetTickCount64(); + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + + +HRESULT CorExecutionManager::Resume(DWORD dwAppDomainId) +{ + CONTRACTL + { + NOTHROW; + if (GetThread()) {GC_TRIGGERS;} else {DISABLED(GC_NOTRIGGER);} + ENTRY_POINT; // This is called by a host. + } + CONTRACTL_END; + + HRESULT hr = S_OK; + +#ifndef FEATURE_CORECLR + if (!IsRuntimeActive()) + return HOST_E_CLRNOTAVAILABLE; +#endif + + if(!g_IsPaused) + return E_FAIL; + +#ifdef FEATURE_CORECLR + // GCThread is the thread that did the Pause. Resume should also happen on that same thread + Thread *pThread = GetThread(); + if(pThread != ThreadSuspend::GetSuspensionThread()) + { + _ASSERTE(!"HOST BUG: The same thread that did Pause should do the Resume"); + return E_FAIL; + } +#endif + + BEGIN_ENTRYPOINT_NOTHROW; + + // Even though this is named with TickCount, it returns milliseconds + INT64 currTime = (INT64)CLRGetTickCount64(); + _ASSERTE(currTime >= m_pauseStartTime); + _ASSERTE(m_pauseStartTime != 0); + + g_PauseTime += (currTime - m_pauseStartTime); + g_IsPaused = FALSE; + + hr = RestartEEFromPauseAndSetResumeEvent(); + +#ifndef FEATURE_CORECLR + if (SUCCEEDED(hr)) + { + if ((m_dwFlags & PAUSE_APP_DOMAINS) != 0) + { + Thread* pThread = SetupThreadNoThrow(&hr); + if (pThread != NULL) + { + GCX_COOP_THREAD_EXISTS(pThread); + + AppDomainIterator ai(/*bOnlyActive:*/ TRUE); + while (ai.Next()) + ResumeOneAppDomain(&ai); + } + } + } +#endif + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + + +#endif //!DACCESS_COMPILE + +#ifdef FEATURE_CORECLR +#ifndef DACCESS_COMPILE +SVAL_IMPL(STARTUP_FLAGS, CorHost2, m_dwStartupFlags = STARTUP_CONCURRENT_GC); +#else +SVAL_IMPL(STARTUP_FLAGS, CorHost2, m_dwStartupFlags); +#endif + +STARTUP_FLAGS CorHost2::GetStartupFlags() +{ + return m_dwStartupFlags; +} +#endif //FEATURE_CORECLR + +#ifndef DACCESS_COMPILE + +#if !defined(FEATURE_CORECLR) +/************************************************************************************* + ** ICLRPrivRuntime Methods + *************************************************************************************/ + +HRESULT CorHost2::GetInterface( + REFCLSID rclsid, + REFIID riid, + LPVOID * ppUnk) +{ + CONTRACTL { + NOTHROW; + GC_TRIGGERS; + MODE_PREEMPTIVE; + } CONTRACTL_END; + + HRESULT hr = S_OK; + + EX_TRY + { + if (rclsid == __uuidof(CLRPrivAppXBinder)) + { + CLRPrivBinderAppX * pBinder = CLRPrivBinderAppX::GetOrCreateBinder(); + hr = pBinder->QueryInterface(riid, ppUnk); + } + else + { + hr = E_NOINTERFACE; + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +HRESULT CorHost2::CreateAppDomain( + LPCWSTR pwzFriendlyName, + ICLRPrivBinder * pBinder, + LPDWORD pdwAppDomainId) +{ + return _CreateAppDomain( + pwzFriendlyName, + 0 /* default security */, + nullptr, /* domain manager */ + nullptr, /* domain manager */ + 0, /* property count */ + nullptr, /* property names */ + nullptr, /* property values */ + pBinder, + pdwAppDomainId); +} + +HRESULT CorHost2::CreateDelegate( + DWORD appDomainID, + LPCWSTR wszAssemblyName, + LPCWSTR wszClassName, + LPCWSTR wszMethodName, + LPVOID * ppvDelegate) +{ + return _CreateDelegate(appDomainID, wszAssemblyName, wszClassName, + wszMethodName, reinterpret_cast<INT_PTR*>(ppvDelegate)); +} + +// Flag indicating if the EE was started up by an managed exe. Defined in ceemain.cpp. +extern BOOL g_fEEManagedEXEStartup; + +HRESULT CorHost2::ExecuteMain( + ICLRPrivBinder * pBinder, + int * pRetVal) +{ + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_ENTRY_POINT; + + HRESULT hr = S_OK; + + // If an exception passes through here, it will cause the + // "The application has generated an unhandled exception" dialog and offer to debug. + BEGIN_ENTRYPOINT_THROWS; + + // Indicates that the EE was started up by a managed exe. + g_fEEManagedEXEStartup = TRUE; + + IfFailGo(CorCommandLine::SetArgvW(WszGetCommandLine())); + + IfFailGo(EnsureEEStarted(COINITEE_MAIN)); + + INSTALL_UNWIND_AND_CONTINUE_HANDLER; + + // + // Look for the [STAThread] or [MTAThread] attribute + // TODO delete this code when we move to the default AppDomain + // + HMODULE hMod = WszGetModuleHandle(NULL); + + PEImageHolder pTempImage(PEImage::LoadImage(hMod)); + PEFileHolder pTempFile(PEFile::Open(pTempImage.Extract())); + + // Check for CustomAttributes - Set up the DefaultDomain and the main thread + // Note that this has to be done before ExplicitBind() as it + // affects the bind + mdToken tkEntryPoint = pTempFile->GetEntryPointToken(); + // <TODO>@TODO: What if the entrypoint is in another file of the assembly?</TODO> + ReleaseHolder<IMDInternalImport> scope(pTempFile->GetMDImportWithRef()); + // In theory, we should have a valid executable image and scope should never be NULL, but we've been + // getting Watson failures for AVs here due to ISVs modifying image headers and some new OS loader + // checks (see Dev10# 718530 and Windows 7# 615596) + if (scope == NULL) + { + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + Thread::ApartmentState state = Thread::AS_Unknown; + + if((!IsNilToken(tkEntryPoint)) && (TypeFromToken(tkEntryPoint) == mdtMethodDef)) { + if (scope->IsValidToken(tkEntryPoint)) + state = SystemDomain::GetEntryPointThreadAptState(scope, tkEntryPoint); + else + ThrowHR(COR_E_BADIMAGEFORMAT); + } + + BOOL fSetGlobalSharePolicyUsingAttribute = FALSE; + + if((!IsNilToken(tkEntryPoint)) && (TypeFromToken(tkEntryPoint) == mdtMethodDef)) + { + // The global share policy needs to be set before initializing default domain + // so that it is in place for loading of appdomain manager. + fSetGlobalSharePolicyUsingAttribute = SystemDomain::SetGlobalSharePolicyUsingAttribute(scope, tkEntryPoint); + } + + // If the entry point has an explicit thread apartment state, set it + // before running the AppDomainManager initialization code. + if (state == Thread::AS_InSTA || state == Thread::AS_InMTA) + SystemDomain::SetThreadAptState(scope, state); + + // This can potentially run managed code. + SystemDomain::InitializeDefaultDomain(FALSE, pBinder); + + // If we haven't set an explicit thread apartment state, set it after the + // AppDomainManager has got a chance to go set it in InitializeNewDomain. + if (state != Thread::AS_InSTA && state != Thread::AS_InMTA) + SystemDomain::SetThreadAptState(scope, state); + + if (fSetGlobalSharePolicyUsingAttribute) + SystemDomain::System()->DefaultDomain()->SetupLoaderOptimization(g_dwGlobalSharePolicy); + + ADID adId(DefaultADID); + + GCX_COOP(); + + ENTER_DOMAIN_ID(adId) + TESTHOOKCALL(EnteredAppDomain(adId.m_dwId)); + { + GCX_PREEMP(); + + AppDomain *pDomain = GetAppDomain(); + _ASSERTE(pDomain); + + PathString wzExeFileName; + + if (WszGetModuleFileName(nullptr, wzExeFileName) == 0) + IfFailThrow(E_UNEXPECTED); + + LPWSTR wzExeSimpleFileName = nullptr; + size_t cchExeSimpleFileName = 0; + SplitPathInterior( + wzExeFileName, + nullptr, nullptr, // drive + nullptr, nullptr, // dir + (LPCWSTR*)&wzExeSimpleFileName, &cchExeSimpleFileName, // filename + nullptr, nullptr); // ext + + // Remove the extension + wzExeSimpleFileName[cchExeSimpleFileName] = W('\0'); + + ReleaseHolder<IAssemblyName> pAssemblyName; + IfFailThrow(CreateAssemblyNameObject( + &pAssemblyName, // Returned IAssemblyName + wzExeSimpleFileName, // Name of assembly + CANOF_PARSE_DISPLAY_NAME, // Parse as display name + nullptr)); // Reserved + + AssemblySpec specExe; + specExe.InitializeSpec(pAssemblyName, nullptr, false); + + PEAssemblyHolder pPEAssembly = pDomain->BindAssemblySpec(&specExe, TRUE, FALSE); + + pDomain->SetRootAssembly(pDomain->LoadAssembly(NULL, pPEAssembly, FILE_ACTIVE)); + + LOG((LF_CLASSLOADER | LF_CORDB, + LL_INFO10, + "Created domain for an executable at %p\n", + (pDomain->GetRootAssembly()? pDomain->GetRootAssembly()->Parent() : NULL))); + TESTHOOKCALL(RuntimeStarted(RTS_CALLINGENTRYPOINT)); + + // Set the friendly name to indicate that this is an immersive domain. + pDomain->SetFriendlyName(W("Immersive Application Domain"), TRUE); + + // Execute the main method + // NOTE: we call the entry point with our entry point exception filter active + // after the AppDomain transition which is a bit different from classic apps. + // this is so that we have the correct context when notifying the debugger + // or invoking WER on the main thread and mimics the behavior of classic apps. + // the assumption is that AppX entry points are always invoked post-AD transition. + ExecuteMainInner(pDomain->GetRootAssembly()); + + // Get the global latched exit code instead of the return value from ExecuteMainMethod + // because in the case of a "void Main" method the return code is always 0, + // while the latched exit code is set in either case. + *pRetVal = GetLatchedExitCode(); + } + END_DOMAIN_TRANSITION; + TESTHOOKCALL(LeftAppDomain(adId.m_dwId)); + + UNINSTALL_UNWIND_AND_CONTINUE_HANDLER; + +ErrExit: + END_ENTRYPOINT_THROWS; + + return hr; +} + +VOID CorHost2::ExecuteMainInner(Assembly* pRootAssembly) +{ + STATIC_CONTRACT_GC_TRIGGERS; + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_ENTRY_POINT; + + struct Param + { + Assembly* pRootAssembly; + } param; + + param.pRootAssembly = pRootAssembly; + + PAL_TRY(Param*, pParam, ¶m) + { + // since this is the thread 0 entry point for AppX apps we use + // the EntryPointFilter so that an unhandled exception here will + // trigger the same behavior as in classic apps. + pParam->pRootAssembly->ExecuteMainMethod(NULL, TRUE /* waitForOtherThreads */); + } + PAL_EXCEPT_FILTER(EntryPointFilter) + { + LOG((LF_STARTUP, LL_INFO10, "EntryPointFilter returned EXCEPTION_EXECUTE_HANDLER!")); + } + PAL_ENDTRY +} + +// static +HRESULT CorHost2::SetFlagsAndHostConfig(STARTUP_FLAGS dwStartupFlags, LPCWSTR pwzHostConfigFile, BOOL fFinalize) +{ + WRAPPER_NO_CONTRACT; + + HRESULT hr = E_INVALIDARG; + + if (pwzHostConfigFile == NULL) + pwzHostConfigFile = W(""); + + DangerousNonHostedSpinLockHolder lockHolder(&m_FlagsLock); + + if (m_dwFlagsFinalized) + { + // verify that flags and config file are the same + if (dwStartupFlags == m_dwStartupFlags && + _wcsicmp(pwzHostConfigFile, m_wzHostConfigFile) == 0) + { + hr = S_OK; + } + } + else + { + // overwrite the flags and config with the incoming values + if (wcslen(pwzHostConfigFile) < COUNTOF(m_wzHostConfigFile)) + { + VERIFY(wcscpy_s(m_wzHostConfigFile, COUNTOF(m_wzHostConfigFile), pwzHostConfigFile) == 0); + + // If they asked for the server gc but only have one processor, deny that option. + // Keep this in sync with shim logic in ComputeStartupFlagsAndFlavor that also switches to + // the workstation GC on uniprocessor boxes. + if (g_SystemInfo.dwNumberOfProcessors == 1 && (dwStartupFlags & STARTUP_SERVER_GC)) + dwStartupFlags = (STARTUP_FLAGS)(dwStartupFlags & ~(STARTUP_SERVER_GC | STARTUP_CONCURRENT_GC)); + + m_dwStartupFlags = dwStartupFlags; + + if (fFinalize) + m_dwFlagsFinalized = TRUE; + + hr = S_OK; + } + } + + return hr; +} + +// static +STARTUP_FLAGS CorHost2::GetStartupFlags() +{ + WRAPPER_NO_CONTRACT; + + if (!m_dwFlagsFinalized) // make sure we return consistent results + { + DangerousNonHostedSpinLockHolder lockHolder(&m_FlagsLock); + m_dwFlagsFinalized = TRUE; + } + + return m_dwStartupFlags; +} + +// static +LPCWSTR CorHost2::GetHostConfigFile() +{ + WRAPPER_NO_CONTRACT; + + if (!m_dwFlagsFinalized) // make sure we return consistent results + { + DangerousNonHostedSpinLockHolder lockHolder(&m_FlagsLock); + m_dwFlagsFinalized = TRUE; + } + + return m_wzHostConfigFile; +} + +// static +void CorHost2::GetDefaultAppDomainProperties(StringArrayList **pPropertyNames, StringArrayList **pPropertyValues) +{ + LIMITED_METHOD_CONTRACT; + + // We should only read these after the runtime has started to ensure that the host isn't modifying them + // still + _ASSERTE(g_fEEStarted || HasStarted()); + + *pPropertyNames = &s_defaultDomainPropertyNames; + *pPropertyValues = &s_defaultDomainPropertyValues; +} + +#endif // !FEATURE_CORECLR + +#ifdef FEATURE_COMINTEROP + +// Enumerate currently existing domains. +HRESULT CorRuntimeHostBase::EnumDomains(HDOMAINENUM *hEnum) +{ + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + WRAPPER(GC_TRIGGERS); + ENTRY_POINT; + } + CONTRACTL_END; + + if(hEnum == NULL) return E_POINTER; + + // Thread setup happens in BEGIN_EXTERNAL_ENTRYPOINT below. + // If the runtime has not started, we have nothing to do. + if (!g_fEEStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + HRESULT hr = E_OUTOFMEMORY; + *hEnum = NULL; + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + + AppDomainIterator *pEnum = new (nothrow) AppDomainIterator(FALSE); + if(pEnum) { + *hEnum = (HDOMAINENUM) pEnum; + hr = S_OK; + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#endif // FEATURE_COMINTEROP + +extern "C" +HRESULT GetCLRRuntimeHost(REFIID riid, IUnknown **ppUnk) +{ + WRAPPER_NO_CONTRACT; + + return CorHost2::CreateObject(riid, (void**)ppUnk); +} + +#if defined(FEATURE_COMINTEROP) && !defined(FEATURE_CORECLR) + +HRESULT NextDomainWorker(AppDomainIterator *pEnum, + IUnknown** pAppDomain) +{ + CONTRACTL + { + DISABLED(NOTHROW); // nothrow contract's fs:0 handler gets called before the C++ EH fs:0 handler which is pushed in the prolog + GC_TRIGGERS; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + Thread *pThread = GetThread(); + BEGIN_SO_INTOLERANT_CODE_NOTHROW(pThread, return COR_E_STACKOVERFLOW); + + EX_TRY + { + GCX_COOP_THREAD_EXISTS(pThread); + + if (pEnum->Next()) + { + AppDomain* pDomain = pEnum->GetDomain(); + // Need to enter the AppDomain to synchronize access to the exposed + // object properly (can't just take the system domain mutex since we + // might need to run code that uses higher ranking crsts). + ENTER_DOMAIN_PTR(pDomain,ADV_ITERATOR) + { + + hr = pDomain->GetComIPForExposedObject(pAppDomain); + } + END_DOMAIN_TRANSITION; + } + else + { + hr = S_FALSE; + } + } + EX_CATCH_HRESULT(hr); + + END_SO_INTOLERANT_CODE; + + return hr; +} + +// Returns S_FALSE when there are no more domains. A domain +// is passed out only when S_OK is returned. +HRESULT CorRuntimeHostBase::NextDomain(HDOMAINENUM hEnum, + IUnknown** pAppDomain) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + } + CONTRACTL_END; + + if(hEnum == NULL || pAppDomain == NULL) + return E_POINTER; + + // If the runtime has not started, we have nothing to do. + if (!g_fEEStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + AppDomainIterator *pEnum = (AppDomainIterator *) hEnum; + + do + { + hr = NextDomainWorker(pEnum, pAppDomain); + // Might need to look at the next appdomain if we were attempting to get at + // the exposed appdomain object and were chucked out as the result of an + // appdomain unload. + } while (hr == COR_E_APPDOMAINUNLOADED); + END_ENTRYPOINT_NOTHROW; + return hr; +} + +// Creates a domain in the runtime. The identity array is +// a pointer to an array TYPE containing IIdentity objects defining +// the security identity. +HRESULT CorRuntimeHostBase::CreateDomainEx(LPCWSTR pwzFriendlyName, + IUnknown* pSetup, // Optional + IUnknown* pEvidence, // Optional + IUnknown ** pAppDomain) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_PREEMPTIVE; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + if(!pwzFriendlyName) return E_POINTER; + if(pAppDomain == NULL) return E_POINTER; + if(!g_fEEStarted) return E_FAIL; + + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + struct _gc { + STRINGREF pName; + OBJECTREF pSetup; + OBJECTREF pEvidence; + APPDOMAINREF pDomain; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + if (FAILED(hr = EnsureComStartedNoThrow())) + goto lDone; + + GCPROTECT_BEGIN(gc); + + gc.pName = StringObject::NewString(pwzFriendlyName); + + if(pSetup) + GetObjectRefFromComIP(&gc.pSetup, pSetup); + if(pEvidence) + GetObjectRefFromComIP(&gc.pEvidence, pEvidence); + + MethodDescCallSite createDomain(METHOD__APP_DOMAIN__CREATE_DOMAIN); + + ARG_SLOT args[3] = { + ObjToArgSlot(gc.pName), + ObjToArgSlot(gc.pEvidence), + ObjToArgSlot(gc.pSetup), + }; + + gc.pDomain = (APPDOMAINREF) createDomain.Call_RetOBJECTREF(args); + + *pAppDomain = GetComIPFromObjectRef((OBJECTREF*) &gc.pDomain); + + GCPROTECT_END(); + +lDone: ; + } + END_EXTERNAL_ENTRYPOINT; + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +// Close the enumeration releasing resources +HRESULT CorRuntimeHostBase::CloseEnum(HDOMAINENUM hEnum) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + if(hEnum) { + AppDomainIterator* pEnum = (AppDomainIterator*) hEnum; + delete pEnum; + } + + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +HRESULT CorRuntimeHostBase::CreateDomainSetup(IUnknown **pAppDomainSetup) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + MODE_PREEMPTIVE; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (!pAppDomainSetup) + return E_POINTER; + + // If the runtime has not started, we have nothing to do. + if (!g_fEEStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + // Create the domain. + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + struct _gc { + OBJECTREF pSetup; + } gc; + ZeroMemory(&gc, sizeof(gc)); + MethodTable* pMT = NULL; + + hr = EnsureComStartedNoThrow(); + if (FAILED(hr)) + goto lDone; + + pMT = MscorlibBinder::GetClass(CLASS__APPDOMAIN_SETUP); + + GCPROTECT_BEGIN(gc); + gc.pSetup = AllocateObject(pMT); + *pAppDomainSetup = GetComIPFromObjectRef((OBJECTREF*) &gc.pSetup); + GCPROTECT_END(); + +lDone: ; + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +HRESULT CorRuntimeHostBase::CreateEvidence(IUnknown **pEvidence) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_PREEMPTIVE; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (!pEvidence) + return E_POINTER; + +#ifdef FEATURE_CAS_POLICY + + // If the runtime has not started, we have nothing to do. + if (!g_fEEStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + // Create the domain. + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + GCX_COOP_THREAD_EXISTS(GET_THREAD()); + + struct _gc { + OBJECTREF pEvidence; + } gc; + ZeroMemory(&gc, sizeof(gc)); + + MethodTable* pMT = NULL; + + hr = EnsureComStartedNoThrow(); + if (FAILED(hr)) + goto lDone; + + pMT = MscorlibBinder::GetClass(CLASS__EVIDENCE); + + GCPROTECT_BEGIN(gc); + gc.pEvidence = AllocateObject(pMT); + MethodDescCallSite ctor(METHOD__EVIDENCE__CTOR, &(gc.pEvidence)); + + // Call the Evidence class constructor. + ARG_SLOT CtorArgs[] = + { + ObjToArgSlot(gc.pEvidence) + }; + ctor.Call(CtorArgs); + + *pEvidence = GetComIPFromObjectRef((OBJECTREF*) &gc.pEvidence); + GCPROTECT_END(); + +lDone: ; + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; +#else // !FEATURE_CAS_POLICY + // There is no Evidence class support without CAS policy. + return E_NOTIMPL; +#endif // FEATURE_CAS_POLICY + + return hr; +} + +HRESULT CorRuntimeHostBase::UnloadDomain(IUnknown *pUnkDomain) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + FORBID_FAULT; + ENTRY_POINT; + } + CONTRACTL_END; + + if (!pUnkDomain) + return E_POINTER; + + // If the runtime has not started, we have nothing to do. + if (!g_fEEStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + CONTRACT_VIOLATION(FaultViolation); // This entire function is full of OOM potential: must fix. + + HRESULT hr = S_OK; + DWORD dwDomainId = 0; + BEGIN_ENTRYPOINT_NOTHROW; + + _ASSERTE (g_fComStarted); + + { + SystemDomain::LockHolder lh; + + ComCallWrapper* pWrap = GetCCWFromIUnknown(pUnkDomain, FALSE); + if (!pWrap) + { + hr = COR_E_APPDOMAINUNLOADED; + } + if (SUCCEEDED(hr)) + { + dwDomainId = pWrap->GetDomainID().m_dwId; + } + } + if (SUCCEEDED(hr)) + { + hr = UnloadAppDomain(dwDomainId, TRUE); + } + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#endif // FEATURE_COMINTEROP && !FEATURE_CORECLR + +STDMETHODIMP CorHost2::UnloadAppDomain(DWORD dwDomainId, BOOL fWaitUntilDone) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + +#ifdef FEATURE_CORECLR + if (!m_fStarted) + return HOST_E_INVALIDOPERATION; + + if(m_dwStartupFlags & STARTUP_SINGLE_APPDOMAIN) + { + if (!g_fEEStarted) + { + return HOST_E_CLRNOTAVAILABLE; + } + + if(!m_fAppDomainCreated) + { + return HOST_E_INVALIDOPERATION; + } + + HRESULT hr=S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + if (!m_fFirstToLoadCLR) + { + _ASSERTE(!"Not reachable"); + hr = HOST_E_CLRNOTAVAILABLE; + } + else + { + LONG refCount = m_RefCount; + if (refCount == 0) + { + hr = HOST_E_CLRNOTAVAILABLE; + } + else + if (1 == refCount) + { + // Stop coreclr on unload. + m_fStarted = FALSE; + EEShutDown(FALSE); + } + else + { + _ASSERTE(!"Not reachable"); + hr = S_FALSE; + } + } + END_ENTRYPOINT_NOTHROW; + + return hr; + } + else +#endif // FEATURE_CORECLR + + return CorRuntimeHostBase::UnloadAppDomain(dwDomainId, fWaitUntilDone); +} + +HRESULT CorRuntimeHostBase::UnloadAppDomain(DWORD dwDomainId, BOOL fSync) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_ANY; + FORBID_FAULT; // Unloading domains cannot fail due to OOM + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + // No point going further if the runtime is not running... + { + // In IsRuntimeActive, we will call CanRunManagedCode that will + // check if the current thread has taken the loader lock or not, + // if MDA is supported. To do the check, MdaLoaderLock::ReportViolation + // will be invoked that will internally end up invoking + // MdaFactory<MdaXmlElement>::GetNext that will use the "new" operator + // that has the "FAULT" contract set, resulting in FAULT_VIOLATION since + // this method has the FORBID_FAULT contract set above. + // + // However, for a thread that holds the loader lock, unloading the appDomain is + // not a supported scenario. Thus, we should not be ending up in this code + // path for the FAULT violation. + // + // Hence, the CONTRACT_VIOLATION below for overriding the FORBID_FAULT + // for this scope only. + CONTRACT_VIOLATION(FaultViolation); + if (!IsRuntimeActive() + #ifdef FEATURE_CORECLR + || !m_fStarted + #endif // FEATURE_CORECLR + ) + { + return HOST_E_CLRNOTAVAILABLE; + } + } + + BEGIN_ENTRYPOINT_NOTHROW; + + // We do not use BEGIN_EXTERNAL_ENTRYPOINT here because + // we do not want to setup Thread. Process may be OOM, and we want Unload + // to work. + hr = AppDomain::UnloadById(ADID(dwDomainId), fSync); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +//***************************************************************************** +// Fiber Methods +//***************************************************************************** +#if !defined(FEATURE_CORECLR) // simple hosting +HRESULT CorHost::CreateLogicalThreadState() +{ + CONTRACTL + { + NOTHROW; + DISABLED(GC_TRIGGERS); + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + if (CorHost::GetHostVersion() != 1) + { + hr=HOST_E_INVALIDOPERATION; + } + else + { + _ASSERTE (GetThread() == 0 || GetThread()->HasRightCacheStackBase()); + /* Thread *thread = */ SetupThreadNoThrow(&hr); + + } + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +HRESULT CorHost::DeleteLogicalThreadState() +{ + if (CorHost::GetHostVersion() != 1) + { + return HOST_E_INVALIDOPERATION; + } + + Thread *pThread = GetThread(); + if (!pThread) + return E_UNEXPECTED; + + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + } + CONTRACTL_END; + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + // We need to reset the TrapReturningThread count that was + // set when a thread is requested to be aborted. Otherwise + // every stub call is going to go through a slow path. + if (pThread->IsAbortRequested()) + pThread->UnmarkThreadForAbort(Thread::TAR_ALL); + + // see code:Thread::OnThreadTerminate#ReportDeadOnThreadTerminate + pThread->SetThreadState(Thread::TS_ReportDead); + + pThread->OnThreadTerminate(FALSE); + END_ENTRYPOINT_NOTHROW; + return hr; +} + + +HRESULT CorHost::SwitchInLogicalThreadState(DWORD *pFiberCookie) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_ENTRY_POINT; + + if (CorHost::GetHostVersion() != 1) + { + return HOST_E_INVALIDOPERATION; + } + + if (!pFiberCookie) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = ((Thread*)pFiberCookie)->SwitchIn(::GetCurrentThread()); + + END_ENTRYPOINT_NOTHROW; + return hr; + +} + +HRESULT CorHost::SwitchOutLogicalThreadState(DWORD **pFiberCookie) +{ + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_ENTRY_POINT; + + if (CorHost::GetHostVersion() != 1) + { + return HOST_E_INVALIDOPERATION; + } + + if (!pFiberCookie) + { + return E_POINTER; + } + + Thread *pThread = GetThread(); + if (!pThread) + { + return E_UNEXPECTED; + } + + pThread->InternalSwitchOut(); + *pFiberCookie = (DWORD*)pThread; + + return S_OK; +} +#endif // !defined(FEATURE_CORECLR) + +HRESULT CorRuntimeHostBase::LocksHeldByLogicalThread(DWORD *pCount) +{ + if (!pCount) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + BEGIN_ENTRYPOINT_NOTHROW; + + Thread* pThread = GetThread(); + if (pThread == NULL) + *pCount = 0; + else + *pCount = pThread->m_dwLockCount; + + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} + +//***************************************************************************** +// ICorConfiguration +//***************************************************************************** +#if !defined(FEATURE_CORECLR) +IGCThreadControl *CorConfiguration::m_CachedGCThreadControl = 0; +IGCHostControl *CorConfiguration::m_CachedGCHostControl = 0; +IDebuggerThreadControl *CorConfiguration::m_CachedDebuggerThreadControl = 0; +DWORD *CorConfiguration::m_DSTArray = 0; +DWORD CorConfiguration::m_DSTCount = 0; +DWORD CorConfiguration::m_DSTArraySize = 0; + +// *** ICorConfiguration methods *** + + +HRESULT CorConfiguration::SetGCThreadControl(IGCThreadControl *pGCThreadControl) +{ + if (!pGCThreadControl) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + BEGIN_ENTRYPOINT_NOTHROW; + + if (m_CachedGCThreadControl) + m_CachedGCThreadControl->Release(); + + m_CachedGCThreadControl = pGCThreadControl; + + if (m_CachedGCThreadControl) + m_CachedGCThreadControl->AddRef(); + + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} + +HRESULT CorConfiguration::SetGCHostControl(IGCHostControl *pGCHostControl) +{ + if (!pGCHostControl) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + BEGIN_ENTRYPOINT_NOTHROW; + + if (m_CachedGCHostControl) + m_CachedGCHostControl->Release(); + + m_CachedGCHostControl = pGCHostControl; + + if (m_CachedGCHostControl) + m_CachedGCHostControl->AddRef(); + + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} + +HRESULT CorConfiguration::SetDebuggerThreadControl(IDebuggerThreadControl *pDebuggerThreadControl) +{ + if (!pDebuggerThreadControl) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + +#ifdef DEBUGGING_SUPPORTED + // Can't change the debugger thread control object once its been set. + if (m_CachedDebuggerThreadControl != NULL) + IfFailGo(E_INVALIDARG); + + m_CachedDebuggerThreadControl = pDebuggerThreadControl; + + // If debugging is already initialized then provide this interface pointer to it. + // It will also addref the new one and release the old one. + if (g_pDebugInterface) + g_pDebugInterface->SetIDbgThreadControl(pDebuggerThreadControl); + + if (m_CachedDebuggerThreadControl) + m_CachedDebuggerThreadControl->AddRef(); + + hr = S_OK; +#else // !DEBUGGING_SUPPORTED + hr = E_NOTIMPL; +#endif // !DEBUGGING_SUPPORTED + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; + +} + + +HRESULT CorConfiguration::AddDebuggerSpecialThread(DWORD dwSpecialThreadId) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; // debugging not hardened for SO + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + +#ifdef DEBUGGING_SUPPORTED + // If it's already in the list, don't add it again. + if (IsDebuggerSpecialThread(dwSpecialThreadId)) + { + hr = S_OK; + goto ErrExit; + } + // Grow the array if necessary. + if (m_DSTCount >= m_DSTArraySize) + { + // There's probably only ever gonna be one or two of these + // things, so we'll start small. + DWORD newSize = (m_DSTArraySize == 0) ? 2 : m_DSTArraySize * 2; + + DWORD *newArray = new (nothrow) DWORD[newSize]; + IfNullGo(newArray); + + // If we're growing instead of starting, then copy the old array. + if (m_DSTArray) + { + memcpy(newArray, m_DSTArray, m_DSTArraySize * sizeof(DWORD)); + delete [] m_DSTArray; + } + + // Update to the new array and size. + m_DSTArray = newArray; + m_DSTArraySize = newSize; + } + + // Save the new thread ID. + m_DSTArray[m_DSTCount++] = dwSpecialThreadId; + + hr = (RefreshDebuggerSpecialThreadList()); +#else // !DEBUGGING_SUPPORTED + hr = E_NOTIMPL; +#endif // !DEBUGGING_SUPPORTED +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; + +} +// Helper function to update the thread list in the debugger control block +HRESULT CorConfiguration::RefreshDebuggerSpecialThreadList() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + +#ifdef DEBUGGING_SUPPORTED + HRESULT hr = S_OK; + + if (g_pDebugInterface) + { + // Inform the debugger services that this list has changed + hr = g_pDebugInterface->UpdateSpecialThreadList( + m_DSTCount, m_DSTArray); + + _ASSERTE(SUCCEEDED(hr)); + } + + return (hr); +#else // !DEBUGGING_SUPPORTED + return E_NOTIMPL; +#endif // !DEBUGGING_SUPPORTED +} + + +// Helper func that returns true if the thread is in the debugger special thread list +BOOL CorConfiguration::IsDebuggerSpecialThread(DWORD dwThreadId) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + for (DWORD i = 0; i < m_DSTCount; i++) + { + if (m_DSTArray[i] == dwThreadId) + return (TRUE); + } + + return (FALSE); +} + + +// Clean up any debugger thread control object we may be holding, called at shutdown. +void CorConfiguration::CleanupDebuggerThreadControl() +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + if (m_CachedDebuggerThreadControl != NULL) + { + // Note: we don't release the IDebuggerThreadControl object if we're cleaning up from + // our DllMain. The DLL that implements the object may already have been unloaded. + // Leaking the object is okay... the PDM doesn't care. + if (!IsAtProcessExit()) + m_CachedDebuggerThreadControl->Release(); + + m_CachedDebuggerThreadControl = NULL; + } +} +#endif // !defined(FEATURE_CORECLR) + +//***************************************************************************** +// IUnknown +//***************************************************************************** + +ULONG CorRuntimeHostBase::AddRef() +{ + CONTRACTL + { + WRAPPER(THROWS); + WRAPPER(GC_TRIGGERS); + SO_TOLERANT; + } + CONTRACTL_END; + return InterlockedIncrement(&m_cRef); +} + +#if !defined(FEATURE_CORECLR) // simple hosting +ULONG CorHost::Release() +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + ULONG cRef = InterlockedDecrement(&m_cRef); + if (!cRef) { + delete this; + } + + return (cRef); +} +#endif // !defined(FEATURE_CORECLR) + +ULONG CorHost2::Release() +{ + LIMITED_METHOD_CONTRACT; + + ULONG cRef = InterlockedDecrement(&m_cRef); + if (!cRef) { +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + // CorHost2 is allocated before host memory interface is set up. + if (GetHostMemoryManager() == NULL) +#endif // FEATURE_INCLUDE_ALL_INTERFACES + delete this; + } + + return (cRef); +} + +#if !defined(FEATURE_CORECLR) // simple hosting +HRESULT CorHost::QueryInterface(REFIID riid, void **ppUnk) +{ + if (!ppUnk) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; // no global state updates that need guarding. + } + CONTRACTL_END; + + if (ppUnk == NULL) + { + return E_POINTER; + } + + *ppUnk = 0; + + // Deliberately do NOT hand out ICorConfiguration. They must explicitly call + // GetConfiguration to obtain that interface. + if (riid == IID_IUnknown) + *ppUnk = (IUnknown *) (ICorRuntimeHost *) this; + else if (riid == IID_ICorRuntimeHost) + { + ULONG version = 1; + if (m_Version == 0) + FastInterlockCompareExchange((LONG*)&m_Version, version, 0); + + if (m_Version != version && (g_singleVersionHosting || !g_fEEStarted)) + { + return HOST_E_INVALIDOPERATION; + } + + *ppUnk = (ICorRuntimeHost *) this; + } + else if (riid == IID_ICorThreadpool) + *ppUnk = (ICorThreadpool *) this; + else if (riid == IID_IGCHost) + *ppUnk = (IGCHost *) this; + else if (riid == IID_IGCHost2) + *ppUnk = (IGCHost2 *) this; + else if (riid == IID_IValidator) + *ppUnk = (IValidator *) this; + else if (riid == IID_IDebuggerInfo) + *ppUnk = (IDebuggerInfo *) this; + else if (riid == IID_ICLRExecutionManager) + *ppUnk = (ICLRExecutionManager *) this; + else + return (E_NOINTERFACE); + AddRef(); + return (S_OK); +} +#endif // !defined(FEATURE_CORECLR) + + +HRESULT CorHost2::QueryInterface(REFIID riid, void **ppUnk) +{ + if (!ppUnk) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; // no global state updates that need guarding. + } + CONTRACTL_END; + + if (ppUnk == NULL) + { + return E_POINTER; + } + + *ppUnk = 0; + + // Deliberately do NOT hand out ICorConfiguration. They must explicitly call + // GetConfiguration to obtain that interface. + if (riid == IID_IUnknown) + *ppUnk = static_cast<IUnknown *>(static_cast<ICLRRuntimeHost *>(this)); +#ifdef FEATURE_CORECLR // CoreCLR only supports IID_ICLRRuntimeHost2 + else if (riid == IID_ICLRRuntimeHost2) + { + ULONG version = 2; + if (m_Version == 0) + FastInterlockCompareExchange((LONG*)&m_Version, version, 0); + + *ppUnk = static_cast<ICLRRuntimeHost2 *>(this); + } +#else // DesktopCLR only supports IID_ICLRRuntimeHost + else if (riid == IID_ICLRRuntimeHost) + { + ULONG version = 2; + if (m_Version == 0) + FastInterlockCompareExchange((LONG*)&m_Version, version, 0); + + *ppUnk = static_cast<ICLRRuntimeHost *>(this); + } +#endif // FEATURE_CORECLR + else if (riid == IID_ICLRExecutionManager) + { + ULONG version = 2; + if (m_Version == 0) + FastInterlockCompareExchange((LONG*)&m_Version, version, 0); + + *ppUnk = static_cast<ICLRExecutionManager *>(this); + } +#if !defined(FEATURE_CORECLR) + else if (riid == __uuidof(ICLRPrivRuntime)) + { + ULONG version = 2; + if (m_Version == 0) + FastInterlockCompareExchange((LONG*)&m_Version, version, 0); + + *ppUnk = static_cast<ICLRPrivRuntime *>(this); + } +#endif +#ifndef FEATURE_PAL + else if (riid == IID_IPrivateManagedExceptionReporting) + { + *ppUnk = static_cast<IPrivateManagedExceptionReporting *>(this); + } +#endif // !FEATURE_PAL +#ifndef FEATURE_CORECLR + else if (riid == IID_ICorThreadpool) + *ppUnk = static_cast<ICorThreadpool *>(this); + // TODO: wwl Remove this after SQL uses new interface. + else if (riid == IID_IGCHost && + GetHostVersion() == 3) + *ppUnk = static_cast<IGCHost *>(this); + else if (riid == IID_ICLRValidator) + *ppUnk = static_cast<ICLRValidator *>(this); + else if (riid == IID_IDebuggerInfo) + *ppUnk = static_cast<IDebuggerInfo *>(this); +#ifdef FEATURE_TESTHOOKS + else if (riid == IID_ICLRTestHookManager) + { + *ppUnk=CLRTestHookManager::Start(); + if(*ppUnk==NULL) + return E_OUTOFMEMORY; + } +#endif // FEATURE_TESTHOOKS +#endif // FEATURE_CORECLR + else + return (E_NOINTERFACE); + AddRef(); + return (S_OK); +} + +#ifndef FEATURE_CORECLR // CorHost isn't exposed externally +//***************************************************************************** +// Called by the class factory template to create a new instance of this object. +//***************************************************************************** +HRESULT CorHost::CreateObject(REFIID riid, void **ppUnk) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + CorHost *pCorHost = new (nothrow) CorHost(); + if (!pCorHost) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = pCorHost->QueryInterface(riid, ppUnk); + + if (FAILED(hr)) + delete pCorHost; + } + return (hr); +} +#endif // FEATURE_CORECLR + +#ifndef FEATURE_PAL +HRESULT CorHost2::GetBucketParametersForCurrentException(BucketParameters *pParams) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // To avoid confusion, clear the buckets. + memset(pParams, 0, sizeof(BucketParameters)); + + // Defer to Watson helper. + hr = ::GetBucketParametersForCurrentException(pParams); + + END_ENTRYPOINT_NOTHROW; + + return hr; +} +#endif // !FEATURE_PAL + +HRESULT CorHost2::CreateObject(REFIID riid, void **ppUnk) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_SO_INTOLERANT_CODE_NO_THROW_CHECK_THREAD(return COR_E_STACKOVERFLOW; ); + CorHost2 *pCorHost = new (nothrow) CorHost2(); + if (!pCorHost) + { + hr = E_OUTOFMEMORY; + } + else + { + hr = pCorHost->QueryInterface(riid, ppUnk); + if (FAILED(hr)) + delete pCorHost; + } + END_SO_INTOLERANT_CODE; + return (hr); +} + + +//----------------------------------------------------------------------------- +// MapFile - Maps a file into the runtime in a non-standard way +//----------------------------------------------------------------------------- + +static PEImage *MapFileHelper(HANDLE hFile) +{ + CONTRACTL + { + THROWS; + GC_TRIGGERS; + MODE_ANY; + } + CONTRACTL_END; + + GCX_PREEMP(); + + HandleHolder hFileMap(WszCreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL)); + if (hFileMap == NULL) + ThrowLastError(); + + CLRMapViewHolder base(CLRMapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0)); + if (base == NULL) + ThrowLastError(); + + DWORD dwSize = SafeGetFileSize(hFile, NULL); + if (dwSize == 0xffffffff && GetLastError() != NOERROR) + { + ThrowLastError(); + } + PEImageHolder pImage(PEImage::LoadFlat(base, dwSize)); + return pImage.Extract(); +} + +HRESULT CorRuntimeHostBase::MapFile(HANDLE hFile, HMODULE* phHandle) +{ + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + MODE_PREEMPTIVE; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + *phHandle = (HMODULE) (MapFileHelper(hFile)->GetLoadedLayout()->GetBase()); + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + + return hr; +} + +/////////////////////////////////////////////////////////////////////////////// +// IDebuggerInfo::IsDebuggerAttached +#if !defined(FEATURE_CORECLR) +HRESULT CorDebuggerInfo::IsDebuggerAttached(BOOL *pbAttached) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + if (pbAttached == NULL) + hr = E_INVALIDARG; + else +#ifdef DEBUGGING_SUPPORTED + *pbAttached = (CORDebuggerAttached() != 0); +#else + *pbAttached = FALSE; +#endif + + END_ENTRYPOINT_NOTHROW; + + return hr; +} +#endif // !defined(FEATURE_CORECLR) + +LONG CorHost2::m_RefCount = 0; + +IHostControl *CorHost2::m_HostControl = NULL; + +LPCWSTR CorHost2::s_wszAppDomainManagerAsm = NULL; +LPCWSTR CorHost2::s_wszAppDomainManagerType = NULL; +EInitializeNewDomainFlags CorHost2::s_dwDomainManagerInitFlags = eInitializeNewDomainFlags_None; + +#ifndef FEATURE_CORECLR // not supported + +StringArrayList CorHost2::s_defaultDomainPropertyNames; +StringArrayList CorHost2::s_defaultDomainPropertyValues; + +IHostMemoryManager *CorHost2::m_HostMemoryManager = NULL; +IHostMalloc *CorHost2::m_HostMalloc = NULL; +IHostTaskManager *CorHost2::m_HostTaskManager = NULL; +IHostThreadpoolManager *CorHost2::m_HostThreadpoolManager = NULL; +IHostIoCompletionManager *CorHost2::m_HostIoCompletionManager = NULL; +IHostSyncManager *CorHost2::m_HostSyncManager = NULL; +IHostAssemblyManager *CorHost2::m_HostAssemblyManager = NULL; +IHostGCManager *CorHost2::m_HostGCManager = NULL; +IHostSecurityManager *CorHost2::m_HostSecurityManager = NULL; +IHostPolicyManager *CorHost2::m_HostPolicyManager = NULL; +int CorHost2::m_HostOverlappedExtensionSize = -1; + +STARTUP_FLAGS CorHost2::m_dwStartupFlags = STARTUP_CONCURRENT_GC; +WCHAR CorHost2::m_wzHostConfigFile[_MAX_PATH] = { 0 }; + +BOOL CorHost2::m_dwFlagsFinalized = FALSE; +DangerousNonHostedSpinLock CorHost2::m_FlagsLock; + +class CCLRMemoryNotificationCallback: public ICLRMemoryNotificationCallback +{ +public: + virtual HRESULT STDMETHODCALLTYPE OnMemoryNotification(EMemoryAvailable eMemoryAvailable) { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + // We have not started runtime yet. + if (!g_fEEStarted) + return S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + switch (eMemoryAvailable) + { + case eMemoryAvailableLow: + STRESS_LOG0(LF_GC, LL_INFO100, "Host delivers memory notification: Low\n"); + break; + case eMemoryAvailableNeutral: + STRESS_LOG0(LF_GC, LL_INFO100, "Host delivers memory notification: Neutral\n"); + break; + case eMemoryAvailableHigh: + STRESS_LOG0(LF_GC, LL_INFO100, "Host delivers memory notification: High\n"); + break; + } + static DWORD lastTime = (DWORD)-1; + if (eMemoryAvailable == eMemoryAvailableLow) + { + FastInterlockIncrement ((LONG *)&g_bLowMemoryFromHost); + DWORD curTime = GetTickCount(); + if (curTime < lastTime || curTime - lastTime >= 0x2000) + { + lastTime = curTime; + FinalizerThread::EnableFinalization(); + } + } + else + { + FastInterlockExchange ((LONG *)&g_bLowMemoryFromHost, FALSE); + } + END_ENTRYPOINT_NOTHROW; + + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLRMemoryNotificationCallback && riid != IID_IUnknown) + return (E_NOINTERFACE); + *ppvObject = this; + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } +}; + +static CCLRMemoryNotificationCallback s_MemoryNotification; + +class CLRTaskManager : public ICLRTaskManager +{ +public: + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) { + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLRTaskManager && riid != IID_IUnknown) + return (E_NOINTERFACE); + *ppvObject = this; + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE CreateTask(ICLRTask **pTask) + { + CONTRACTL + { + NOTHROW; + DISABLED(GC_NOTRIGGER); + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + +#ifdef _DEBUG + _ASSERTE (!CLRTaskHosted() || GetCurrentHostTask()); +#endif + _ASSERTE (GetThread() == NULL); + Thread *pThread = NULL; + pThread = SetupThreadNoThrow(&hr); + *pTask = pThread; + + END_ENTRYPOINT_NOTHROW; + + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE GetCurrentTask(ICLRTask **pTask) + { + // This function may be called due SQL SwitchIn/Out. Contract may + // force memory allocation which is not allowed during Switch. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_ENTRY_POINT; + + *pTask = GetThread(); + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE SetUILocale(LCID lcid) + { + Thread *pThread = GetThread(); + if (pThread == NULL) + return HOST_E_INVALIDOPERATION; + + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + MODE_PREEMPTIVE; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + //BEGIN_ENTRYPOINT_NOTHROW; + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + pThread->SetCultureId(lcid,TRUE); + } + END_EXTERNAL_ENTRYPOINT; + //END_ENTRYPOINT_NOTHROW; + + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE SetLocale(LCID lcid) + { + Thread *pThread = GetThread(); + if (pThread == NULL) + return HOST_E_INVALIDOPERATION; + + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + MODE_PREEMPTIVE; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + //BEGIN_ENTRYPOINT_NOTHROW; + + BEGIN_EXTERNAL_ENTRYPOINT(&hr) + { + pThread->SetCultureId(lcid,FALSE); + } + END_EXTERNAL_ENTRYPOINT; + //END_ENTRYPOINT_NOTHROW; + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE GetCurrentTaskType(ETaskType *pTaskType) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + BEGIN_ENTRYPOINT_NOTHROW; + *pTaskType = ::GetCurrentTaskType(); + END_ENTRYPOINT_NOTHROW; + + return S_OK; + } +}; + +static CLRTaskManager s_CLRTaskManager; + +class CLRSyncManager : public ICLRSyncManager +{ +public: + virtual HRESULT STDMETHODCALLTYPE GetMonitorOwner(SIZE_T Cookie, + IHostTask **ppOwnerHostTask) + { + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + GC_NOTRIGGER; + ENTRY_POINT;; + } + CONTRACTL_END; + + BEGIN_ENTRYPOINT_NOTHROW; + + // Cookie is the SyncBlock + // <TODO>TODO: Lifetime of Cookie?</TODO> + AwareLock* pAwareLock = (AwareLock*)Cookie; + IHostTask *pTask = NULL; + Thread *pThread = pAwareLock->GetOwningThread(); + if (pThread) + { + ThreadStoreLockHolder tsLock; + pThread = pAwareLock->GetOwningThread(); + if (pThread) + { + // See if the lock is orphaned, and the Thread object has been deleted + Thread *pWalk = NULL; + while ((pWalk = ThreadStore::GetAllThreadList(pWalk, 0, 0)) != NULL) + { + if (pWalk == pThread) + { + pTask = pThread->GetHostTaskWithAddRef(); + break; + } + } + } + } + + *ppOwnerHostTask = pTask; + + END_ENTRYPOINT_NOTHROW; + + + return S_OK; + } + virtual HRESULT STDMETHODCALLTYPE CreateRWLockOwnerIterator(SIZE_T Cookie, + SIZE_T *pIterator) { + Thread *pThread = GetThread(); + + // We may open a window for GC here. + // A host should not hijack a coop thread to do deadlock detection. + if (pThread && pThread->PreemptiveGCDisabled()) + return HOST_E_INVALIDOPERATION; + + CONTRACTL + { + NOTHROW; + MODE_PREEMPTIVE; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = E_FAIL; + +#ifdef FEATURE_RWLOCK + BEGIN_ENTRYPOINT_NOTHROW; + ThreadStoreLockHolder tsLock; + // Cookie is a weak handle. We need to make sure that the object is not moving. + CRWLock *pRWLock = *(CRWLock **) Cookie; + *pIterator = NULL; + if (pRWLock == NULL) + { + hr = S_OK; + } + else + { + hr = pRWLock->CreateOwnerIterator(pIterator); + } + END_ENTRYPOINT_NOTHROW; +#endif // FEATURE_RWLOCK + + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE GetRWLockOwnerNext(SIZE_T Iterator, + IHostTask **ppOwnerHostTask) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + +#ifdef FEATURE_RWLOCK + BEGIN_ENTRYPOINT_NOTHROW; + CRWLock::GetNextOwner(Iterator,ppOwnerHostTask); + END_ENTRYPOINT_NOTHROW; +#endif // FEATURE_RWLOCK + + return S_OK; + } + + virtual HRESULT STDMETHODCALLTYPE DeleteRWLockOwnerIterator(SIZE_T Iterator) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + +#ifdef FEATURE_RWLOCK + BEGIN_ENTRYPOINT_NOTHROW; + CRWLock::DeleteOwnerIterator(Iterator); + END_ENTRYPOINT_NOTHROW; +#endif // FEATURE_RWLOCK + + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLRSyncManager && riid != IID_IUnknown) + return (E_NOINTERFACE); + *ppvObject = this; + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } +}; + +static CLRSyncManager s_CLRSyncManager; + +extern void HostIOCompletionCallback(DWORD ErrorCode, + DWORD numBytesTransferred, + LPOVERLAPPED lpOverlapped); +class CCLRIoCompletionManager :public ICLRIoCompletionManager +{ +public: + virtual HRESULT STDMETHODCALLTYPE OnComplete(DWORD dwErrorCode, + DWORD NumberOfBytesTransferred, + void* pvOverlapped) + { + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_ENTRY_POINT; + + if (pvOverlapped) + { + BEGIN_ENTRYPOINT_NOTHROW; + HostIOCompletionCallback (dwErrorCode, NumberOfBytesTransferred, (LPOVERLAPPED)pvOverlapped); + END_ENTRYPOINT_NOTHROW; + } + + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + STATIC_CONTRACT_SO_TOLERANT; + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + STATIC_CONTRACT_SO_TOLERANT; + LIMITED_METHOD_CONTRACT; + return 1; + } + BEGIN_INTERFACE HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject) + { + STATIC_CONTRACT_SO_TOLERANT; + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLRIoCompletionManager && riid != IID_IUnknown) + return (E_NOINTERFACE); + *ppvObject = this; + return S_OK; + } +}; + +static CCLRIoCompletionManager s_CLRIoCompletionManager; +#endif // FEATURE_CORECLR + +#ifdef _DEBUG +extern void ValidateHostInterface(); +#endif + +// fusion's global copy of host assembly manager stuff +BOOL g_bFusionHosted = FALSE; +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +ICLRAssemblyReferenceList *g_pHostAsmList = NULL; +IHostAssemblyStore *g_pHostAssemblyStore = NULL; +#endif // FEATURE_INCLUDE_ALL_INTERFACES + +/*static*/ BOOL CorHost2::IsLoadFromBlocked() // LoadFrom, LoadFile and Load(byte[]) are blocked in certain hosting scenarios +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + return (g_bFusionHosted && (g_pHostAsmList != NULL)); +#else // !FEATURE_INCLUDE_ALL_INTERFACES + return FALSE; // as g_pHostAsmList is not defined for CoreCLR; hence above expression will be FALSE. +#endif // FEATURE_INCLUDE_ALL_INTERFACES +} + +static Volatile<BOOL> fOneOnly = 0; + +/////////////////////////////////////////////////////////////////////////////// +// ICLRRuntimeHost::SetHostControl +/////////////////////////////////////////////////////////////////////////////// +HRESULT CorHost2::SetHostControl(IHostControl* pHostControl) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + if (m_Version < 2) + // CLR is hosted with v1 hosting interface. Some part of v2 hosting API are disabled. + return HOST_E_INVALIDOPERATION; + + if (pHostControl == 0) + return E_INVALIDARG; + + // If Runtime has been started, do not allow setting HostMemoryManager + if (g_fEEStarted) + return E_ACCESSDENIED; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + DWORD dwSwitchCount = 0; + + while (FastInterlockExchange((LONG*)&fOneOnly, 1) == 1) + { + #ifndef FEATURE_CORECLR + if (m_HostTaskManager != NULL) + { + m_HostTaskManager->SwitchToTask(0); + } + else + { + IHostTaskManager *pHostTaskManager = NULL; + if (pHostControl->GetHostManager(IID_IHostTaskManager, (void**)&pHostTaskManager) == S_OK && + pHostTaskManager != NULL) + { + pHostTaskManager->SwitchToTask(0); + pHostTaskManager->Release(); + } + else + { + __SwitchToThread(0, ++dwSwitchCount); + } + } + #else + __SwitchToThread(0, ++dwSwitchCount); + #endif // FEATURE_CORECLR + } + +#ifndef FEATURE_CORECLR + +#ifdef _DEBUG + ValidateHostInterface(); +#endif + +#ifdef _DEBUG + DWORD dbg_HostManagerConfig = CLRConfig::GetConfigValue(CLRConfig::INTERNAL_HostManagerConfig); +#endif + + IHostMemoryManager *memoryManager = NULL; + IHostTaskManager *taskManager = NULL; + IHostThreadpoolManager *threadpoolManager = NULL; + IHostIoCompletionManager *ioCompletionManager = NULL; + IHostSyncManager *syncManager = NULL; + IHostAssemblyManager *assemblyManager = NULL; + IHostGCManager *gcManager = NULL; + IHostSecurityManager *securityManager = NULL; + IHostPolicyManager *policyManager = NULL; + + if (m_HostMemoryManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRMEMORYHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostMemoryManager,(void**)&memoryManager) == S_OK && + memoryManager != NULL) { + if (m_HostMalloc == NULL) + { + hr = memoryManager->CreateMalloc (MALLOC_THREADSAFE, &m_HostMalloc); + if (hr == S_OK) + { + memoryManager->RegisterMemoryNotificationCallback(&s_MemoryNotification); + } + else + { + memoryManager->Release(); + IfFailGo(E_UNEXPECTED); + } + } + m_HostMemoryManager = memoryManager; + g_fHostConfig |= CLRMEMORYHOSTED; + } + + if (m_HostTaskManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRTASKHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostTaskManager,(void**)&taskManager) == S_OK && + taskManager != NULL) { +#ifdef _TARGET_ARM_ // @ARMTODO: re-enable once we support hosted p/invokes. + IfFailGo(E_NOTIMPL); +#endif + m_HostTaskManager = taskManager; + m_HostTaskManager->SetCLRTaskManager(&s_CLRTaskManager); + g_fHostConfig |= CLRTASKHOSTED; + } + + if (m_HostThreadpoolManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRTHREADPOOLHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostThreadpoolManager,(void**)&threadpoolManager) == S_OK && + threadpoolManager != NULL) { + m_HostThreadpoolManager = threadpoolManager; + g_fHostConfig |= CLRTHREADPOOLHOSTED; + } + + if (m_HostIoCompletionManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRIOCOMPLETIONHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostIoCompletionManager,(void**)&ioCompletionManager) == S_OK && + ioCompletionManager != NULL) { + DWORD hostSize; + hr = ioCompletionManager->GetHostOverlappedSize(&hostSize); + if (FAILED(hr)) + { + ioCompletionManager->Release(); + IfFailGo(E_UNEXPECTED); + } + m_HostOverlappedExtensionSize = (int)hostSize; + m_HostIoCompletionManager = ioCompletionManager; + m_HostIoCompletionManager->SetCLRIoCompletionManager(&s_CLRIoCompletionManager); + g_fHostConfig |= CLRIOCOMPLETIONHOSTED; + } + + if (m_HostSyncManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRSYNCHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostSyncManager,(void**)&syncManager) == S_OK && + syncManager != NULL) { + m_HostSyncManager = syncManager; + m_HostSyncManager->SetCLRSyncManager(&s_CLRSyncManager); + g_fHostConfig |= CLRSYNCHOSTED; + } + + if (m_HostAssemblyManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRASSEMBLYHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostAssemblyManager,(void**)&assemblyManager) == S_OK && + assemblyManager != NULL) { + + assemblyManager->GetAssemblyStore(&g_pHostAssemblyStore); + + hr = assemblyManager->GetNonHostStoreAssemblies(&g_pHostAsmList); + if (FAILED(hr)) + { + assemblyManager->Release(); + IfFailGo(hr); + } + + if (g_pHostAssemblyStore || g_pHostAsmList) + g_bFusionHosted = TRUE; + m_HostAssemblyManager = assemblyManager; + g_fHostConfig |= CLRASSEMBLYHOSTED; + } + + if (m_HostGCManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRGCHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostGCManager, + (void**)&gcManager) == S_OK && + gcManager != NULL) { + m_HostGCManager = gcManager; + g_fHostConfig |= CLRGCHOSTED; + } + + if (m_HostSecurityManager == NULL && +#ifdef _DEBUG + (dbg_HostManagerConfig & CLRSECURITYHOSTED) && +#endif + pHostControl->GetHostManager(IID_IHostSecurityManager, + (void**)&securityManager) == S_OK && + securityManager != NULL) { + g_fHostConfig |= CLRSECURITYHOSTED; + m_HostSecurityManager = securityManager; +#ifdef FEATURE_CAS_POLICY + HostExecutionContextManager::InitializeRestrictedContext(); +#endif // #ifdef FEATURE_CAS_POLICY + } + + if (m_HostPolicyManager == NULL && + pHostControl->GetHostManager(IID_IHostPolicyManager, + (void**)&policyManager) == S_OK && + policyManager != NULL) { + m_HostPolicyManager = policyManager; + } +#endif //!FEATURE_CORECLR + + if (m_HostControl == NULL) + { + m_HostControl = pHostControl; + m_HostControl->AddRef(); + } + + goto ErrExit; + +ErrExit: + fOneOnly = 0; + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +class CCLRPolicyManager: public ICLRPolicyManager +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetDefaultAction(EClrOperation operation, + EPolicyAction action) + { + LIMITED_METHOD_CONTRACT; +#ifndef FEATURE_CORECLR + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + hr = GetEEPolicy()->SetDefaultAction(operation, action); + END_ENTRYPOINT_NOTHROW; + return hr; +#else // FEATURE_CORECLR + return E_NOTIMPL; +#endif // !FEATURE_CORECLR + } + + virtual HRESULT STDMETHODCALLTYPE SetTimeout(EClrOperation operation, + DWORD dwMilliseconds) + { + LIMITED_METHOD_CONTRACT; +#ifndef FEATURE_CORECLR + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + hr = GetEEPolicy()->SetTimeout(operation,dwMilliseconds); + END_ENTRYPOINT_NOTHROW; + return hr; +#else // FEATURE_CORECLR + return E_NOTIMPL; +#endif // !FEATURE_CORECLR + } + + virtual HRESULT STDMETHODCALLTYPE SetActionOnTimeout(EClrOperation operation, + EPolicyAction action) + { + LIMITED_METHOD_CONTRACT; +#ifndef FEATURE_CORECLR + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + hr = GetEEPolicy()->SetActionOnTimeout(operation,action); + END_ENTRYPOINT_NOTHROW; + return hr; +#else // FEATURE_CORECLR + return E_NOTIMPL; +#endif // !FEATURE_CORECLR + } + + virtual HRESULT STDMETHODCALLTYPE SetTimeoutAndAction(EClrOperation operation, DWORD dwMilliseconds, + EPolicyAction action) + { + LIMITED_METHOD_CONTRACT; +#ifndef FEATURE_CORECLR + STATIC_CONTRACT_SO_TOLERANT; + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + hr = GetEEPolicy()->SetTimeoutAndAction(operation,dwMilliseconds,action); + END_ENTRYPOINT_NOTHROW; + return hr; +#else // FEATURE_CORECLR + return E_NOTIMPL; +#endif // !FEATURE_CORECLR + } + + virtual HRESULT STDMETHODCALLTYPE SetActionOnFailure(EClrFailure failure, + EPolicyAction action) + { + // This is enabled for CoreCLR since a host can use this to + // specify action for handling AV. + STATIC_CONTRACT_ENTRY_POINT; + LIMITED_METHOD_CONTRACT; + HRESULT hr; +#ifdef FEATURE_CORECLR + // For CoreCLR, this method just supports FAIL_AccessViolation as a valid + // failure input arg. The validation of the specified action for the failure + // will be done in EEPolicy::IsValidActionForFailure. + if (failure != FAIL_AccessViolation) + { + return E_INVALIDARG; + } +#endif // FEATURE_CORECLR + BEGIN_ENTRYPOINT_NOTHROW; + hr = GetEEPolicy()->SetActionOnFailure(failure,action); + END_ENTRYPOINT_NOTHROW; + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE SetUnhandledExceptionPolicy(EClrUnhandledException policy) + { + LIMITED_METHOD_CONTRACT; +#ifndef FEATURE_CORECLR + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr; + BEGIN_ENTRYPOINT_NOTHROW; + hr = GetEEPolicy()->SetUnhandledExceptionPolicy(policy); + END_ENTRYPOINT_NOTHROW; + return hr; +#else // FEATURE_CORECLR + return E_NOTIMPL; +#endif // !FEATURE_CORECLR + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return 1; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return 1; + } + + BEGIN_INTERFACE HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, + void **ppvObject) + { + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + if (riid != IID_ICLRPolicyManager && riid != IID_IUnknown) + return (E_NOINTERFACE); + + // Ensure that the out going pointer is not null + if (ppvObject == NULL) + return E_POINTER; + + *ppvObject = this; + return S_OK; + } +}; + +static CCLRPolicyManager s_PolicyManager; + +#ifndef FEATURE_CORECLR // not supported +class CCLROnEventManager: public ICLROnEventManager +{ +public: + virtual HRESULT STDMETHODCALLTYPE RegisterActionOnEvent(EClrEvent event, + IActionOnCLREvent *pAction) + { + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + ENTRY_POINT; + + // This function is always called from outside the Runtime. So, we assert that we either don't have a + // managed thread, or if we do, that we're in preemptive GC mode. + PRECONDITION((GetThread() == NULL) || !GetThread()->PreemptiveGCDisabled()); + } + CONTRACTL_END; + + if (event >= MaxClrEvent || pAction == NULL || event < (EClrEvent)0) + return E_INVALIDARG; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // Note: its only safe to use a straight ReleaseHolder from within the VM directory when we know we're + // called from outside the Runtime. We assert that above, just to be sure. + ReleaseHolder<IActionOnCLREvent> actionHolder(pAction); + pAction->AddRef(); + + CrstHolderWithState ch(m_pLock); + + DWORD dwSwitchCount = 0; + while (m_ProcessEvent != 0) + { + ch.Release(); + __SwitchToThread(0, ++dwSwitchCount); + ch.Acquire(); + } + + if (m_pAction[event] == NULL) + { + m_pAction[event] = new (nothrow)ActionNode; + if (m_pAction[event] == NULL) + hr = E_OUTOFMEMORY; + } + + if (SUCCEEDED(hr)) + { + ActionNode *walk = m_pAction[event]; + while (TRUE) + { + int n = 0; + for ( ; n < ActionNode::ActionArraySize; n ++) + { + if (walk->pAction[n] == NULL) + { + walk->pAction[n] = pAction; + actionHolder.SuppressRelease(); + hr = S_OK; + break; + } + } + if (n < ActionNode::ActionArraySize) + { + break; + } + if (walk->pNext == NULL) + { + walk->pNext = new (nothrow) ActionNode; + if (walk->pNext == NULL) + { + hr = E_OUTOFMEMORY; + break; + } + } + walk = walk->pNext; + } + } + + END_ENTRYPOINT_NOTHROW; + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE UnregisterActionOnEvent(EClrEvent event, + IActionOnCLREvent *pAction) + { + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + ENTRY_POINT; + } + CONTRACTL_END; + + if (event == Event_StackOverflow) + { + // We don't want to take a lock when we process StackOverflow event, because we may + // not have enough stack to do it. + // So we do not release our cache of the callback in order to avoid race. + return HOST_E_INVALIDOPERATION; + } + + HRESULT hr = S_OK; + + ActionNode *walk = NULL; + ActionNode *prev = NULL; + + + BEGIN_ENTRYPOINT_NOTHROW; + + CrstHolderWithState ch(m_pLock); + + DWORD dwSwitchCount = 0; + while (m_ProcessEvent != 0) + { + ch.Release(); + __SwitchToThread(0, ++dwSwitchCount); + ch.Acquire(); + } + + if (m_pAction[event] == NULL) + IfFailGo(HOST_E_INVALIDOPERATION); + + walk = m_pAction[event]; + while (walk) + { + BOOL fInUse = FALSE; + for (int n = 0; n < ActionNode::ActionArraySize; n ++) + { + if (prev && !fInUse && walk->pAction[n]) + fInUse = TRUE; + if (walk->pAction[n] == pAction) + { + walk->pAction[n] = NULL; + ch.Release(); + pAction->Release(); + hr = S_OK; + goto ErrExit; + } + } + if (prev && !fInUse) + { + prev->pNext = walk->pNext; + delete walk; + walk = prev; + } + prev = walk; + walk = walk->pNext; + } + hr = HOST_E_INVALIDOPERATION; +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + STATIC_CONTRACT_SO_TOLERANT; + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppUnk) + { + STATIC_CONTRACT_SO_TOLERANT; + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLROnEventManager && riid != IID_IUnknown) + return (E_NOINTERFACE); + *ppUnk = this; + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + STATIC_CONTRACT_SO_TOLERANT; + LIMITED_METHOD_CONTRACT; + return 1; + } + + // This function is to work around an issue in scan.exe. + // scan.exe is not smart to handle that if (){} else {}. + void ProcessSOEvent(void *data) + { + STATIC_CONTRACT_SO_TOLERANT; + WRAPPER_NO_CONTRACT; + + if (m_pLock == NULL) + return; + + ActionNode *walk = m_pAction[Event_StackOverflow]; + + while (walk) + { + for (int n = 0; n < ActionNode::ActionArraySize; n ++) + { + if (walk->pAction[n]) + { + walk->pAction[n]->OnEvent(Event_StackOverflow,data); + } + } + walk = walk->pNext; + } + } + + void ProcessEvent(EClrEvent event, void *data) + { + WRAPPER_NO_CONTRACT; + + if (m_pLock == NULL) + { + return; + } + + _ASSERTE (event != Event_StackOverflow); + + { + CrstHolder ch(m_pLock); + + if (event == Event_ClrDisabled) + { + if (m_CLRDisabled) + { + return; + } + m_CLRDisabled = TRUE; + } + m_ProcessEvent ++; + + // Release the lock around the call into the host. Is this correct? + // It seems that we need to hold the lock except for the actual callback itself. + } + + BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread()); + { + ActionNode *walk = m_pAction[event]; + while (walk) + { + for (int n = 0; n < ActionNode::ActionArraySize; n ++) + { + if (walk->pAction[n]) + { + walk->pAction[n]->OnEvent(event,data); + } + } + walk = walk->pNext; + } + } + END_SO_TOLERANT_CODE_CALLING_HOST; + + { + CrstHolder ch(m_pLock); + m_ProcessEvent --; + } + } + + BOOL IsActionRegisteredForEvent(EClrEvent event) + { + WRAPPER_NO_CONTRACT; + + // Check to see if the event manager has been set up. + if (m_pLock == NULL) + return FALSE; + + CrstHolder ch(m_pLock); + + ActionNode *walk = m_pAction[event]; + while (walk) + { + for (int n = 0; n < ActionNode::ActionArraySize; n ++) + { + if (walk->pAction[n] != NULL) + { + // We found an action registered for this event. + return TRUE; + } + } + walk = walk->pNext; + } + + // There weren't any actions registered. + return FALSE; + } + + HRESULT Init() + { + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_SO_TOLERANT; + + HRESULT hr = S_OK; + if (m_pLock == NULL) + { + EX_TRY + { + BEGIN_SO_INTOLERANT_CODE(GetThread()); + { + InitHelper(); + } + END_SO_INTOLERANT_CODE; + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + } + + return hr; + } + +#if 0 + // We do not need this one. We have one instance of this class + // and it is static. + CCLROnEventManager() + { + LIMITED_METHOD_CONTRACT; + for (int n = 0; n < MaxClrEvent; n ++) + m_pAction[n] = NULL; + } +#endif + +private: + struct ActionNode + { + static const int ActionArraySize = 8; + + IActionOnCLREvent *pAction[ActionArraySize]; + ActionNode *pNext; + + ActionNode () + : pNext(NULL) + { + LIMITED_METHOD_CONTRACT; + + for (int n = 0; n < ActionArraySize; n ++) + pAction[n] = 0; + } + }; + ActionNode *m_pAction[MaxClrEvent]; + + Crst* m_pLock; + + BOOL m_CLRDisabled; + + // We can not call out into host while holding the lock. At the same time + // we need to make our data consistent. Therefore, m_ProcessEvent is a marker + // to forbid touching the data structure from Register and UnRegister. + DWORD m_ProcessEvent; + + void InitHelper() + { + CONTRACTL + { + GC_NOTRIGGER; + THROWS; + MODE_ANY; + } + CONTRACTL_END; + + m_ProcessEvent = 0; + + Crst* tmp = new Crst(CrstOnEventManager, CrstFlags(CRST_DEFAULT | CRST_DEBUGGER_THREAD)); + if (FastInterlockCompareExchangePointer(&m_pLock, tmp, NULL) != NULL) + delete tmp; + } +}; + +static CCLROnEventManager s_OnEventManager; +#endif // FEATURE_CORECLR + + +void ProcessEventForHost(EClrEvent event, void *data) +{ +#ifndef FEATURE_CORECLR + WRAPPER_NO_CONTRACT; + + _ASSERTE (event != Event_StackOverflow); + + GCX_PREEMP(); + + s_OnEventManager.ProcessEvent(event,data); +#endif // FEATURE_CORECLR +} + +// We do not call ProcessEventForHost for stack overflow, since we have limit stack +// and we should avoid calling GCX_PREEMPT +void ProcessSOEventForHost(EXCEPTION_POINTERS *pExceptionInfo, BOOL fInSoTolerant) +{ +#ifndef FEATURE_CORECLR + WRAPPER_NO_CONTRACT; + + StackOverflowInfo soInfo; + if (fInSoTolerant) + { + soInfo.soType = SO_Managed; + } + else if (pExceptionInfo == NULL || IsIPInModule(g_pMSCorEE, GetIP(pExceptionInfo->ContextRecord))) + { + soInfo.soType = SO_ClrEngine; + } + else + { + soInfo.soType = SO_Other; + } + + soInfo.pExceptionInfo = pExceptionInfo; + s_OnEventManager.ProcessSOEvent(&soInfo); +#endif // FEATURE_CORECLR +} + +BOOL IsHostRegisteredForEvent(EClrEvent event) +{ + WRAPPER_NO_CONTRACT; +#ifdef FEATURE_CORECLR + return FALSE; +#else // FEATURE_CORECLR + return s_OnEventManager.IsActionRegisteredForEvent(event); +#endif // FEATURE_CORECLR +} + +inline size_t SizeInKBytes(size_t cbSize) +{ + LIMITED_METHOD_CONTRACT; + size_t cb = (cbSize % 1024) ? 1 : 0; + return ((cbSize / 1024) + cb); +} + +SIZE_T Host_SegmentSize = 0; +SIZE_T Host_MaxGen0Size = 0; +BOOL Host_fSegmentSizeSet = FALSE; +BOOL Host_fMaxGen0SizeSet = FALSE; + +void UpdateGCSettingFromHost () +{ + WRAPPER_NO_CONTRACT; + _ASSERTE (g_pConfig); + if (Host_fSegmentSizeSet) + { + g_pConfig->SetSegmentSize(Host_SegmentSize); + } + if (Host_fMaxGen0SizeSet) + { + g_pConfig->SetGCgen0size(Host_MaxGen0Size); + } +} + +#if !defined(FEATURE_CORECLR) || defined(FEATURE_WINDOWSPHONE) +class CCLRGCManager: public ICLRGCManager2 +{ +public: + virtual HRESULT STDMETHODCALLTYPE Collect(LONG Generation) + { + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (Generation > (int) GCHeap::GetGCHeap()->GetMaxGeneration()) + hr = E_INVALIDARG; + + if (SUCCEEDED(hr)) + { + // Set up a Thread object if this is called on a native thread. + Thread *pThread; + pThread = GetThread(); + if (pThread == NULL) + pThread = SetupThreadNoThrow(&hr); + if (pThread != NULL) + { + BEGIN_ENTRYPOINT_NOTHROW_WITH_THREAD(pThread); + GCX_COOP(); + + EX_TRY + { + STRESS_LOG0(LF_GC, LL_INFO100, "Host triggers GC\n"); + hr = GCHeap::GetGCHeap()->GarbageCollect(Generation); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + + END_ENTRYPOINT_NOTHROW_WITH_THREAD; + } + } + + return (hr); + } + + virtual HRESULT STDMETHODCALLTYPE GetStats(COR_GC_STATS *pStats) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + #if defined(ENABLE_PERF_COUNTERS) + + Perf_GC *pgc = &GetPerfCounters().m_GC; + + if (!pStats) + IfFailGo(E_INVALIDARG); + + if (pStats->Flags & COR_GC_COUNTS) + { + pStats->ExplicitGCCount = pgc->cInducedGCs; + + for (int idx=0; idx<3; idx++) + pStats->GenCollectionsTaken[idx] = pgc->cGenCollections[idx]; + } + + if (pStats->Flags & COR_GC_MEMORYUSAGE) + { + pStats->CommittedKBytes = SizeInKBytes(pgc->cTotalCommittedBytes); + pStats->ReservedKBytes = SizeInKBytes(pgc->cTotalReservedBytes); + pStats->Gen0HeapSizeKBytes = SizeInKBytes(pgc->cGenHeapSize[0]); + pStats->Gen1HeapSizeKBytes = SizeInKBytes(pgc->cGenHeapSize[1]); + pStats->Gen2HeapSizeKBytes = SizeInKBytes(pgc->cGenHeapSize[2]); + pStats->LargeObjectHeapSizeKBytes = SizeInKBytes(pgc->cLrgObjSize); + pStats->KBytesPromotedFromGen0 = SizeInKBytes(pgc->cbPromotedMem[0]); + pStats->KBytesPromotedFromGen1 = SizeInKBytes(pgc->cbPromotedMem[1]); + } + hr = S_OK; +ErrExit: + #else + hr = E_NOTIMPL; + #endif // ENABLE_PERF_COUNTERS + + END_ENTRYPOINT_NOTHROW; + return hr; + } + virtual HRESULT STDMETHODCALLTYPE SetGCStartupLimits( + DWORD SegmentSize, + DWORD MaxGen0Size) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // Set default overrides if specified by caller. + if (SegmentSize != (DWORD) ~0 && SegmentSize > 0) + { + hr = _SetGCSegmentSize(SegmentSize); + } + + if (SUCCEEDED(hr) && MaxGen0Size != (DWORD) ~0 && MaxGen0Size > 0) + { + hr = _SetGCMaxGen0Size(MaxGen0Size); + } + + END_ENTRYPOINT_NOTHROW; + + return (hr); + } + + virtual HRESULT STDMETHODCALLTYPE SetGCStartupLimitsEx( + SIZE_T SegmentSize, + SIZE_T MaxGen0Size) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // Set default overrides if specified by caller. + if (SegmentSize != (SIZE_T) ~0 && SegmentSize > 0) + { + hr = _SetGCSegmentSize(SegmentSize); + } + + if (SUCCEEDED(hr) && MaxGen0Size != (SIZE_T) ~0 && MaxGen0Size > 0) + { + hr = _SetGCMaxGen0Size(MaxGen0Size); + } + + END_ENTRYPOINT_NOTHROW; + + return (hr); + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, OUT PVOID *ppUnk) + { + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLRGCManager && riid != IID_ICLRGCManager2 && riid != IID_IUnknown) + return (E_NOINTERFACE); + *ppUnk = this; + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } +private: + HRESULT _SetGCSegmentSize(SIZE_T SegmentSize); + HRESULT _SetGCMaxGen0Size(SIZE_T MaxGen0Size); +}; + + +HRESULT CCLRGCManager::_SetGCSegmentSize(SIZE_T SegmentSize) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + // Sanity check the value, it must be a power of two and big enough. + if (!GCHeap::IsValidSegmentSize(SegmentSize)) + { + hr = E_INVALIDARG; + } + else + { + Host_SegmentSize = SegmentSize; + Host_fSegmentSizeSet = TRUE; + } + + return (hr); +} + +HRESULT CCLRGCManager::_SetGCMaxGen0Size(SIZE_T MaxGen0Size) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + // Sanity check the value is at least large enough. + if (!GCHeap::IsValidGen0MaxSize(MaxGen0Size)) + { + hr = E_INVALIDARG; + } + else + { + Host_MaxGen0Size = MaxGen0Size; + Host_fMaxGen0SizeSet = TRUE; + } + + return (hr); +} + +static CCLRGCManager s_GCManager; +#endif // !FEATURE_CORECLR || FEATURE_WINDOWSPHONE + +#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING +class CCLRAppDomainResourceMonitor : public ICLRAppDomainResourceMonitor +{ +public: + virtual HRESULT STDMETHODCALLTYPE GetCurrentAllocated(DWORD dwAppDomainId, + ULONGLONG* pBytesAllocated) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + SystemDomain::LockHolder lh; + AppDomainFromIDHolder pAppDomain((ADID)dwAppDomainId, TRUE, AppDomainFromIDHolder::SyncType_ADLock); + + if (!pAppDomain.IsUnloaded()) + { + if (pBytesAllocated) + { + *pBytesAllocated = pAppDomain->GetAllocBytes(); + } + } + else + { + hr = COR_E_APPDOMAINUNLOADED; + } + + END_ENTRYPOINT_NOTHROW; + + return (hr); + } + + virtual HRESULT STDMETHODCALLTYPE GetCurrentSurvived(DWORD dwAppDomainId, + ULONGLONG* pAppDomainBytesSurvived, + ULONGLONG* pTotalBytesSurvived) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + SystemDomain::LockHolder lh; + AppDomainFromIDHolder pAppDomain((ADID)dwAppDomainId, TRUE, AppDomainFromIDHolder::SyncType_ADLock); + + if (pAppDomain.IsUnloaded()) + { + hr = COR_E_APPDOMAINUNLOADED; + } + else + { + if (pAppDomainBytesSurvived) + { + *pAppDomainBytesSurvived = pAppDomain->GetSurvivedBytes(); + } + if (pTotalBytesSurvived) + { + *pTotalBytesSurvived = SystemDomain::GetTotalSurvivedBytes(); + } + } + + END_ENTRYPOINT_NOTHROW; + + return (hr); + } + + virtual HRESULT STDMETHODCALLTYPE GetCurrentCpuTime(DWORD dwAppDomainId, + ULONGLONG* pMilliseconds) + { + CONTRACTL + { + NOTHROW; + GC_TRIGGERS; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + { + SystemDomain::LockHolder lh; + + { + AppDomainFromIDHolder pAppDomain((ADID)dwAppDomainId, TRUE, AppDomainFromIDHolder::SyncType_ADLock); + + if (!pAppDomain.IsUnloaded()) + { + if (pMilliseconds) + { + *pMilliseconds = pAppDomain->QueryProcessorUsage() / 10000; + } + } + else + { + hr = COR_E_APPDOMAINUNLOADED; + } + } + } + + END_ENTRYPOINT_NOTHROW; + + return hr; + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, OUT PVOID *ppUnk) + { + LIMITED_METHOD_CONTRACT; + *ppUnk = NULL; + if (riid == IID_IUnknown) + *ppUnk = (IUnknown*)this; + else if (riid == IID_ICLRAppDomainResourceMonitor) + *ppUnk = (ICLRAppDomainResourceMonitor*)this; + else + return E_NOINTERFACE; + return S_OK; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } +}; +static CCLRAppDomainResourceMonitor s_Arm; +#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING + +#ifdef FEATURE_APTCA +class CLRDomainManager : public ICLRDomainManager +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetAppDomainManagerType(__in LPCWSTR wszAppDomainManagerAssembly, + __in LPCWSTR wszAppDomainManagerType, + EInitializeNewDomainFlags dwInitializeDomainFlags) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = CorHost2::SetAppDomainManagerType(wszAppDomainManagerAssembly, + wszAppDomainManagerType, + dwInitializeDomainFlags); + END_ENTRYPOINT_NOTHROW; + return hr; + } + + virtual HRESULT STDMETHODCALLTYPE SetPropertiesForDefaultAppDomain(DWORD nProperties, + __in_ecount(nProperties) LPCWSTR *pwszPropertyNames, + __in_ecount(nProperties) LPCWSTR *pwszPropertyValues) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + hr = CorHost2::SetPropertiesForDefaultAppDomain(nProperties, pwszPropertyNames, pwszPropertyValues); + + END_ENTRYPOINT_NOTHROW; + return hr; + } + + virtual ULONG STDMETHODCALLTYPE AddRef() + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual ULONG STDMETHODCALLTYPE Release() + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual HRESULT STDMETHODCALLTYPE QueryInterface(__in REFIID riid, __out LPVOID *ppvObject) + { + LIMITED_METHOD_CONTRACT; + + if (ppvObject == NULL) + return E_POINTER; + + *ppvObject = NULL; + + if (riid == IID_ICLRDomainManager) + { + *ppvObject = this; + } + else if (riid == IID_IUnknown) + { + *ppvObject = static_cast<IUnknown *>(this); + } + + if (*ppvObject == NULL) + return E_NOINTERFACE; + + AddRef(); + return S_OK; + } +}; + +static CLRDomainManager s_CLRDomainManager; +#endif // FEATURE_APTCA + +BYTE g_CorHostProtectionManagerInstance[sizeof(CorHostProtectionManager)]; + +void InitHostProtectionManager() +{ + WRAPPER_NO_CONTRACT; + new (g_CorHostProtectionManagerInstance) CorHostProtectionManager(); +} + +BOOL g_CLRPolicyRequested = FALSE; + +class CCorCLRControl: public ICLRControl +{ +public: + virtual HRESULT STDMETHODCALLTYPE GetCLRManager(REFIID riid, void **ppObject) + { + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; // no global state updates + } + CONTRACTL_END; + + // Sanity check. + if (ppObject == NULL) + return E_INVALIDARG; + +#ifndef FEATURE_CORECLR + // ErrorReportingManager is allowed, even if runtime is started, so + // make this check first. + // Host must call release on CLRErrorReportingManager after this call + if (riid == IID_ICLRErrorReportingManager) + { + *ppObject = &g_CLRErrorReportingManager; + return S_OK; + } + else +#elif defined(FEATURE_WINDOWSPHONE) + if (riid == IID_ICLRErrorReportingManager2) + { + *ppObject = &g_CLRErrorReportingManager; + return S_OK; + } + else +#endif // !FEATURE_CORECLR || defined(FEATURE_WINDOWSPHONE) + if (g_fEEStarted && !m_fFullAccess) + { + // If runtime has been started, do not allow user to obtain CLR managers. + return HOST_E_INVALIDOPERATION; + } +#ifndef FEATURE_CORECLR + else if (riid == IID_ICLRTaskManager) { + *ppObject = &s_CLRTaskManager; + return S_OK; + } +#endif // !FEATURE_CORECLR + + // CoreCLR supports ICLRPolicyManager since it allows the host + // to specify the policy for AccessViolation. + else if (riid == IID_ICLRPolicyManager) { + *ppObject = &s_PolicyManager; + FastInterlockExchange((LONG*)&g_CLRPolicyRequested, TRUE); + return S_OK; + } +#ifndef FEATURE_CORECLR + else if (riid == IID_ICLRHostProtectionManager) { + *ppObject = GetHostProtectionManager(); + return S_OK; + } + + // Host must call release on CLRDebugManager after this call + else if (riid == IID_ICLRDebugManager) + { + *ppObject = &s_CLRDebugManager; + return S_OK; + } + + else if (riid == IID_ICLROnEventManager) + { + HRESULT hr = s_OnEventManager.Init(); + if (FAILED(hr)) + return hr; + *ppObject = &s_OnEventManager; + return S_OK; + } +#endif // !FEATURE_CORECLR + +#if !defined(FEATURE_CORECLR) || defined(FEATURE_WINDOWSPHONE) + else if ((riid == IID_ICLRGCManager) || (riid == IID_ICLRGCManager2)) + { + *ppObject = &s_GCManager; + return S_OK; + } +#endif // !FEATURE_CORECLR || FEATURE_WINDOWSPHONE + +#ifdef FEATURE_APPDOMAIN_RESOURCE_MONITORING + else if (riid == IID_ICLRAppDomainResourceMonitor) + { + EnableARM(); + *ppObject = &s_Arm; + return S_OK; + } +#endif //FEATURE_APPDOMAIN_RESOURCE_MONITORING +#ifdef FEATURE_APTCA + else if (riid == IID_ICLRDomainManager) + { + *ppObject = &s_CLRDomainManager; + return S_OK; + } +#endif // FEATURE_APTCA + else + return (E_NOINTERFACE); + } + + virtual HRESULT STDMETHODCALLTYPE SetAppDomainManagerType( + LPCWSTR pwzAppDomainManagerAssembly, + LPCWSTR pwzAppDomainManagerType) + { +#ifndef FEATURE_CORECLR + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; // no global state updates + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + hr = CorHost2::SetAppDomainManagerType(pwzAppDomainManagerAssembly, + pwzAppDomainManagerType, + eInitializeNewDomainFlags_None); + END_ENTRYPOINT_NOTHROW; + return hr; +#else // FEATURE_CORECLR + + // CoreCLR does not support this method + return E_NOTIMPL; +#endif // !FEATURE_CORECLR + } + + virtual ULONG STDMETHODCALLTYPE AddRef(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + virtual ULONG STDMETHODCALLTYPE Release(void) + { + LIMITED_METHOD_CONTRACT; + return 1; + } + + BEGIN_INTERFACE HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, + void **ppvObject) + { + LIMITED_METHOD_CONTRACT; + if (riid != IID_ICLRControl && riid != IID_IUnknown) + return (E_NOINTERFACE); + + // Ensure that the out going pointer is not null + if (ppvObject == NULL) + return E_POINTER; + + *ppvObject = this; + return S_OK; + } + + // This is to avoid having ctor. We have static objects, and it is + // difficult to support ctor on certain platform. + void SetAccess(BOOL fFullAccess) + { + LIMITED_METHOD_CONTRACT; + m_fFullAccess = fFullAccess; + } +private: + BOOL m_fFullAccess; +}; + +// Before CLR starts, we give out s_CorCLRControl which has full access to all managers. +// After CLR starts, we give out s_CorCLRControlLimited which allows limited access to managers. +static CCorCLRControl s_CorCLRControl; + +#ifndef FEATURE_CORECLR +static CCorCLRControl s_CorCLRControlLimited; +#endif // FEATURE_CORECLR + +/////////////////////////////////////////////////////////////////////////////// +// ICLRRuntimeHost::GetCLRControl +HRESULT CorHost2::GetCLRControl(ICLRControl** pCLRControl) +{ + LIMITED_METHOD_CONTRACT; + + // Ensure that we have a valid pointer + if (pCLRControl == NULL) + { + return E_POINTER; + } + + HRESULT hr = S_OK; + + STATIC_CONTRACT_ENTRY_POINT; + BEGIN_ENTRYPOINT_NOTHROW; + if (!g_fEEStarted && m_Version >= 2) + { + s_CorCLRControl.SetAccess(TRUE); + *pCLRControl = &s_CorCLRControl; + } + else + { +#ifndef FEATURE_CORECLR + // Even CLR is hosted by v1 hosting interface, we still allow part of CLRControl, like IID_ICLRErrorReportingManager. + s_CorCLRControlLimited.SetAccess(FALSE); + *pCLRControl = &s_CorCLRControlLimited; +#else // FEATURE_CORECLR + // If : + // 1) request comes for interface other than ICLRControl*, OR + // 2) runtime has already started, OR + // 3) version is not 2 + // + // we will return failure and set the out pointer to NULL + *pCLRControl = NULL; + if (g_fEEStarted) + { + // Return HOST_E_INVALIDOPERATION as per MSDN if runtime has already started + hr = HOST_E_INVALIDOPERATION; + } + else + { + hr = E_NOTIMPL; + } +#endif // !FEATURE_CORECLR + } + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#ifndef FEATURE_CORECLR + +// static +HRESULT CorHost2::SetPropertiesForDefaultAppDomain(DWORD nProperties, + __in_ecount(nProperties) LPCWSTR *pwszPropertyNames, + __in_ecount(nProperties) LPCWSTR *pwszPropertyValues) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // Default domain properties can only be set before the CLR has started + if (g_fEEStarted || HasStarted()) + { + return HOST_E_INVALIDOPERATION; + } + + // If the host is specifying properties, they should be there + if (nProperties > 0 && (pwszPropertyNames == NULL || pwszPropertyValues == NULL)) + { + return E_POINTER; + } + + // v4 - since this property is being added late in the cycle to address a specific scenario, we + // reject any attempt to set anything but a single well known property name. This restriction + // can be removed in the future. + for (DWORD iProperty = 0; iProperty < nProperties; ++iProperty) + { + if (pwszPropertyNames[iProperty] == NULL) + { + return E_POINTER; + } + if (pwszPropertyValues[iProperty] == NULL) + { + return E_POINTER; + } + if (wcscmp(PARTIAL_TRUST_VISIBLE_ASSEMBLIES_PROPERTY, pwszPropertyNames[iProperty]) != 0) + { + return HRESULT_FROM_WIN32(ERROR_UNKNOWN_PROPERTY); + } + } + + HRESULT hr = S_OK; + + EX_TRY + { + for (DWORD iProperty = 0; iProperty < nProperties; ++iProperty) + { + SString propertyName(pwszPropertyNames[iProperty]); + s_defaultDomainPropertyNames.Append(propertyName); + + SString propertyValue(pwszPropertyValues[iProperty]); + s_defaultDomainPropertyValues.Append(propertyValue); + } + } + EX_CATCH_HRESULT(hr); + + return hr; +} + +// static +HRESULT CorHost2::SetAppDomainManagerType(LPCWSTR wszAppDomainManagerAssembly, + LPCWSTR wszAppDomainManagerType, + EInitializeNewDomainFlags dwInitializeDomainFlags) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + // The AppDomainManger can only be set by the host before the CLR has started + if (g_fEEStarted || HasStarted()) + { + return HOST_E_INVALIDOPERATION; + } + + // Both the type and assembly must be specified + if (wszAppDomainManagerAssembly == NULL || wszAppDomainManagerType == NULL) + { + return E_INVALIDARG; + } + + // Make sure we understand the incoming flags + const EInitializeNewDomainFlags knownFlags = eInitializeNewDomainFlags_NoSecurityChanges; + if ((dwInitializeDomainFlags & (~knownFlags)) != eInitializeNewDomainFlags_None) + { + return E_INVALIDARG; + } + + // Get a copy of the AppDomainManager assembly + size_t cchAsm = wcslen(wszAppDomainManagerAssembly) + 1; + NewArrayHolder<WCHAR> wszAppDomainManagerAssemblyCopy(new (nothrow) WCHAR[cchAsm]); + if (wszAppDomainManagerAssemblyCopy == NULL) + { + return E_OUTOFMEMORY; + } + wcsncpy_s(wszAppDomainManagerAssemblyCopy, cchAsm, wszAppDomainManagerAssembly, cchAsm - 1); + + // And of the AppDomainManagerType + size_t cchType = wcslen(wszAppDomainManagerType) + 1; + NewArrayHolder<WCHAR> wszAppDomainManagerTypeCopy(new (nothrow) WCHAR[cchType]); + if (wszAppDomainManagerTypeCopy == NULL) + { + return E_OUTOFMEMORY; + } + wcsncpy_s(wszAppDomainManagerTypeCopy, cchType, wszAppDomainManagerType, cchType - 1); + + LPCWSTR wszOldAsmValue = FastInterlockCompareExchangePointer(&s_wszAppDomainManagerAsm, + static_cast<LPCWSTR>(wszAppDomainManagerAssemblyCopy.GetValue()), + NULL); + if (wszOldAsmValue != NULL) + { + // We've tried to setup an AppDomainManager twice ... that's not allowed + return HOST_E_INVALIDOPERATION; + } + + s_wszAppDomainManagerType = wszAppDomainManagerTypeCopy; + s_dwDomainManagerInitFlags = dwInitializeDomainFlags; + + wszAppDomainManagerAssemblyCopy.SuppressRelease(); + wszAppDomainManagerTypeCopy.SuppressRelease(); + return S_OK; +} +#endif // !FEATURE_CORECLR + +LPCWSTR CorHost2::GetAppDomainManagerAsm() +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_CORECLR + return NULL; +#else // FEATURE_CORECLR + _ASSERTE (g_fEEStarted); + return s_wszAppDomainManagerAsm; +#endif // FEATURE_CORECLR +} + +LPCWSTR CorHost2::GetAppDomainManagerType() +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_CORECLR + return NULL; +#else // FEATURE_CORECLR + _ASSERTE (g_fEEStarted); + return s_wszAppDomainManagerType; +#endif // FEATURE_CORECLR +} + +// static +EInitializeNewDomainFlags CorHost2::GetAppDomainManagerInitializeNewDomainFlags() +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_CORECLR + return eInitializeNewDomainFlags_None; +#else // FEAUTRE_CORECLR + _ASSERTE (g_fEEStarted); + return s_dwDomainManagerInitFlags; +#endif // FEATURE_CORECLR +} + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +// We do not implement the Release since our host does not control the lifetime on this object +ULONG CCLRDebugManager::Release() +{ + LIMITED_METHOD_CONTRACT; + return (1); +} + +HRESULT CCLRDebugManager::QueryInterface(REFIID riid, void **ppUnk) +{ + if (!ppUnk) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (ppUnk == NULL) + { + return E_POINTER; + } + + *ppUnk = 0; + + // Deliberately do NOT hand out ICorConfiguration. They must explicitly call + // GetConfiguration to obtain that interface. + if (riid == IID_IUnknown) + { + *ppUnk = (IUnknown *) this; + } + else if (riid == IID_ICLRDebugManager) + { + *ppUnk = (ICLRDebugManager *) this; + } + else + { + hr = E_NOINTERFACE; + } + + return hr; + +} + +/* +* +* Called once to when process start up to initialize the lock for connection name hash table +* +*/ +void CCLRDebugManager::ProcessInit() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + m_lockConnectionNameTable.Init(CrstConnectionNameTable, (CrstFlags) (CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD)); +} + +/* +* Called once to when process shut down to destroy the lock for connection name hash table +* +*/ +void CCLRDebugManager::ProcessCleanup() +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + } + CONTRACTL_END; + + m_lockConnectionNameTable.Destroy(); +} +#endif // FEATURE_INCLUDE_ALL_INTERFACES + +#endif // !DAC + + +#ifdef DACCESS_COMPILE + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + +//--------------------------------------------------------------------------------------- +// Begin an iterating over connections for Debugger +// +// Arguments: +// pHashfind - out: initializes cookie to pass to to future calls to code:CCLRDebugManager.FindNext +// +// Returns: +// NULL if iteration is done. Else a ConnectionNameHashEntry representing the connection. +// +ConnectionNameHashEntry * CCLRDebugManager::FindFirst(HASHFIND * pHashfind) +{ + SUPPORTS_DAC; + if (m_pConnectionNameHash == NULL) + { + return NULL; + } + + ConnectionNameHashEntry * pConnection = dac_cast<PTR_ConnectionNameHashEntry>(m_pConnectionNameHash->FindFirstEntry(pHashfind)); + return pConnection; + } + +//--------------------------------------------------------------------------------------- +// Begin an iterating over connections for Debugger +// +// Arguments: +// pHashfind - in/out: iterator cookie to pass to future calls to code:CCLRDebugManager.FindNext +// +// Returns: +// NULL if iteration is done. Else a ConnectionNameHashEntry representing the connection. +// +ConnectionNameHashEntry * CCLRDebugManager::FindNext(HASHFIND * pHashfind) + { + SUPPORTS_DAC; + ConnectionNameHashEntry * pConnection = dac_cast<PTR_ConnectionNameHashEntry>(m_pConnectionNameHash->FindNextEntry(pHashfind)); + return pConnection; +} + +#endif // FEATURE_INCLUDE_ALL_INTERFACES + +#endif //DACCESS_COMPILE + +#ifndef DACCESS_COMPILE + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES +HRESULT CCLRDebugManager::IsDebuggerAttached(BOOL *pbAttached) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + if (pbAttached == NULL) + return E_INVALIDARG; + + BEGIN_ENTRYPOINT_NOTHROW; + +#ifdef DEBUGGING_SUPPORTED + *pbAttached = (CORDebuggerAttached() != 0); +#else + *pbAttached = FALSE; +#endif + + END_ENTRYPOINT_NOTHROW; + + + return S_OK; +} + +// By default, we permit symbols to be read for full-trust assemblies only +ESymbolReadingSetBy CCLRDebugManager::m_symbolReadingSetBy = eSymbolReadingSetByDefault; +ESymbolReadingPolicy CCLRDebugManager::m_symbolReadingPolicy = eSymbolReadingFullTrustOnly; + +HRESULT CCLRDebugManager::SetSymbolReadingPolicy(ESymbolReadingPolicy policy) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_ENTRY_POINT; + + if( policy > eSymbolReadingFullTrustOnly ) + { + return E_INVALIDARG; + } + + SetSymbolReadingPolicy( policy, eSymbolReadingSetByHost ); + + return S_OK; +} + +void CCLRDebugManager::SetSymbolReadingPolicy( ESymbolReadingPolicy policy, ESymbolReadingSetBy setBy ) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE( policy <= eSymbolReadingFullTrustOnly ); // don't have _COUNT because it's not in convention for mscoree.idl enums + _ASSERTE( setBy < eSymbolReadingSetBy_COUNT ); + + // if the setter meets or exceeds the precendence of the existing setting then override the setting + if( setBy >= m_symbolReadingSetBy ) + { + m_symbolReadingSetBy = setBy; + m_symbolReadingPolicy = policy; + } +} + + +/* +* Call by host to set the name of a connection and begin a connection. +* +*/ +HRESULT CCLRDebugManager::BeginConnection( + CONNID dwConnectionId, + __in_z wchar_t *wzConnectionName) // We should review this in the future. This API is + // public and callable by a host. This SAL annotation + // is the best we can do now. +{ + CONTRACTL + { + GC_TRIGGERS; // I am having problem in putting either GC_TRIGGERS or GC_NOTRIGGER. It is not happy either way when debugger + // call back event needs to enable preemptive GC. + ENTRY_POINT; + NOTHROW; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + ConnectionNameHashEntry *pEntry = NULL; + + // check input parameter + if (dwConnectionId == INVALID_CONNECTION_ID || wzConnectionName == NULL || wzConnectionName[0] == W('\0')) + IfFailGo(E_INVALIDARG); + + if (wcslen(wzConnectionName) >= MAX_CONNECTION_NAME) + IfFailGo(E_INVALIDARG); + + { + CrstHolder ch(&m_lockConnectionNameTable); + + if (m_pConnectionNameHash == NULL) + { + m_pConnectionNameHash = new (nothrow) ConnectionNameTable(50); + IfNullGo(m_pConnectionNameHash); + IfFailGo(m_pConnectionNameHash->NewInit(50, sizeof(ConnectionNameHashEntry), USHRT_MAX)); + } + + // error: Should not have an existing connection id already + if (m_pConnectionNameHash->FindConnection(dwConnectionId)) + IfFailGo(E_INVALIDARG); + + // Our implementation of hashtable cannot throw out of memory exception + pEntry = m_pConnectionNameHash->AddConnection(dwConnectionId, wzConnectionName); + IfNullGo(pEntry); + } + + // send notification to debugger + if (CORDebuggerAttached()) + { + g_pDebugInterface->CreateConnection(dwConnectionId, wzConnectionName); + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +/* +* Call by host to end a connection +*/ +HRESULT CCLRDebugManager::EndConnection(CONNID dwConnectionId) +{ + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + UINT CLRTaskCount = 0; + ICLRTask **ppCLRTaskArray = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + if (dwConnectionId == INVALID_CONNECTION_ID) + IfFailGo(E_INVALIDARG); + + // No connection exist at all + if (m_pConnectionNameHash == NULL) + IfFailGo(E_FAIL); + + { + CrstHolder ch(&m_lockConnectionNameTable); + ConnectionNameHashEntry *pEntry = NULL; + + if ((pEntry = m_pConnectionNameHash->FindConnection(dwConnectionId)) == NULL) + IfFailGo(E_INVALIDARG); + + // Note that the Release on CLRTask chould take a ThreadStoreLock. So we need to finish our + // business with ConnectionNameHash before hand and release our name hash lock + // + CLRTaskCount = pEntry->m_CLRTaskCount; + ppCLRTaskArray = pEntry->m_ppCLRTaskArray; + pEntry->m_ppCLRTaskArray = NULL; + pEntry->m_CLRTaskCount = 0; + m_pConnectionNameHash->DeleteConnection(dwConnectionId); + } + + if (CLRTaskCount != 0) + { + _ASSERTE(ppCLRTaskArray != NULL); + for (UINT i = 0; i < CLRTaskCount; i++) + { + ((Thread *)ppCLRTaskArray[i])->SetConnectionId(INVALID_CONNECTION_ID); + ppCLRTaskArray[i]->Release(); + } + delete [] ppCLRTaskArray; + } + + // send notification to debugger + if (CORDebuggerAttached()) + g_pDebugInterface->DestroyConnection(dwConnectionId); + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +/* +* Call by host to set a set of tasks as a connection. +* +*/ +HRESULT CCLRDebugManager::SetConnectionTasks( + DWORD id, + DWORD dwCount, + ICLRTask **ppCLRTask) +{ + CONTRACTL + { + GC_TRIGGERS; + NOTHROW; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + ICLRTask **ppCLRTaskArrayNew = NULL; + UINT CLRTaskCountPrevious = 0; + ICLRTask **ppCLRTaskArrayPrevious = NULL; + + BEGIN_ENTRYPOINT_NOTHROW; + + DWORD index; + Thread *pThread; + ConnectionNameHashEntry *pEntry = NULL; + + if (id == INVALID_CONNECTION_ID || dwCount == 0 || ppCLRTask == NULL) + IfFailGo(E_INVALIDARG); + + { + CrstHolder ch(&m_lockConnectionNameTable); + + // check the BeginConnectin has been called. + if (m_pConnectionNameHash == NULL) + // No connection exist + IfFailGo(E_INVALIDARG); + + // Host forget to call BeginConnection before calling SetConnectionTask! + if ((pEntry = m_pConnectionNameHash->FindConnection(id)) == NULL) + IfFailGo(E_INVALIDARG); + + for (index = 0; index < dwCount; index++) + { + // Check on input parameter + pThread = (Thread *) ppCLRTask[index]; + if (pThread == NULL) + { + // _ASSERTE(!"Host passed in NULL ICLRTask pointer"); + IfFailGo(E_INVALIDARG); + } + + // Check for Finalizer thread + if (GCHeap::IsGCHeapInitialized() && (pThread == FinalizerThread::GetFinalizerThread())) + { + // _ASSERTE(!"Host should not try to schedule user code on our Finalizer Thread"); + IfFailGo(E_INVALIDARG); + + } + } + + ppCLRTaskArrayNew = new (nothrow) ICLRTask*[dwCount]; + IfNullGo(ppCLRTaskArrayNew); + + CLRTaskCountPrevious = pEntry->m_CLRTaskCount; + ppCLRTaskArrayPrevious = pEntry->m_ppCLRTaskArray; + pEntry->m_ppCLRTaskArray = NULL; + pEntry->m_CLRTaskCount = 0; + + if (CLRTaskCountPrevious != 0) + { + // Clear the old connection set + _ASSERTE(ppCLRTaskArrayPrevious != NULL); + for (UINT i = 0; i < CLRTaskCountPrevious; i++) + ((Thread *)ppCLRTaskArrayPrevious[i])->SetConnectionId(INVALID_CONNECTION_ID); + } + + // now remember the new set + pEntry->m_ppCLRTaskArray = ppCLRTaskArrayNew; + + for (index = 0; index < dwCount; index++) + { + pThread = (Thread *) ppCLRTask[index]; + pThread->SetConnectionId( id ); + pEntry->m_ppCLRTaskArray[index] = ppCLRTask[index]; + } + pEntry->m_CLRTaskCount = dwCount; + + // AddRef and Release on Thread object can call ThreadStoreLock. So we will release our + // lock first of all. + } + + // Does the addref on the new set + for (index = 0; index < dwCount; index++) + ppCLRTaskArrayNew[index]->AddRef(); + + // Does the release on the old set + if (CLRTaskCountPrevious != 0) + { + _ASSERTE(ppCLRTaskArrayPrevious != NULL); + for (UINT i = 0; i < CLRTaskCountPrevious; i++) + ppCLRTaskArrayPrevious[i]->Release(); + delete ppCLRTaskArrayPrevious; + } + + // send notification to debugger + if (CORDebuggerAttached()) + { + g_pDebugInterface->ChangeConnection(id); + } + +ErrExit: + END_ENTRYPOINT_NOTHROW; + return hr; +} + +HRESULT CCLRDebugManager::SetDacl(PACL pacl) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + hr = E_NOTIMPL; + + END_ENTRYPOINT_NOTHROW; + return hr; +} // SetDACL + + +HRESULT CCLRDebugManager::GetDacl(PACL *pacl) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr; + + BEGIN_ENTRYPOINT_NOTHROW; + + hr = E_NOTIMPL; + + END_ENTRYPOINT_NOTHROW; + return hr; +} // SetDACL + +#endif // FEATURE_INCLUDE_ALL_INTERFACES + +#if defined(FEATURE_INCLUDE_ALL_INTERFACES) || defined(FEATURE_WINDOWSPHONE) + +HRESULT CCLRErrorReportingManager::QueryInterface(REFIID riid, void** ppUnk) +{ + if (!ppUnk) + return E_POINTER; + + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + if (ppUnk == NULL) + { + return E_POINTER; + } + + *ppUnk = 0; + + // Deliberately do NOT hand out ICorConfiguration. They must explicitly call + // GetConfiguration to obtain that interface. + if (riid == IID_IUnknown) + { + *ppUnk = (IUnknown *) this; + } + else if (riid == IID_ICLRErrorReportingManager) + { + *ppUnk = (ICLRErrorReportingManager *) this; + } +#ifdef FEATURE_WINDOWSPHONE + else if (riid == IID_ICLRErrorReportingManager2) + { + *ppUnk = (ICLRErrorReportingManager2 *) this; + } +#endif // FEATURE_WINDOWSPHONE + else + { + hr = E_NOINTERFACE; + } + + return hr; + +} // HRESULT CCLRErrorReportingManager::QueryInterface() + +ULONG CCLRErrorReportingManager::AddRef() +{ + LIMITED_METHOD_CONTRACT; + return 1; +} // HRESULT CCLRErrorReportingManager::AddRef() + +ULONG CCLRErrorReportingManager::Release() +{ + LIMITED_METHOD_CONTRACT; + return 1; +} // HRESULT CCLRErrorReportingManager::Release() + +// Get Watson bucket parameters for "current" exception (on calling thread). +HRESULT CCLRErrorReportingManager::GetBucketParametersForCurrentException( + BucketParameters *pParams) +{ + CONTRACTL + { + WRAPPER(THROWS); + WRAPPER(GC_TRIGGERS); + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + BEGIN_ENTRYPOINT_NOTHROW; + + // To avoid confusion, clear the buckets. + memset(pParams, 0, sizeof(BucketParameters)); + +#ifndef FEATURE_PAL + // Defer to Watson helper. + hr = ::GetBucketParametersForCurrentException(pParams); + #else + // Watson doesn't exist on non-windows platforms + hr = E_NOTIMPL; +#endif // !FEATURE_PAL + + END_ENTRYPOINT_NOTHROW; + + return hr; + +} // HRESULT CCLRErrorReportingManager::GetBucketParametersForCurrentException() + +// +// The BeginCustomDump function configures the custom dump support +// +// Parameters - +// dwFlavor - The flavor of the dump +// dwNumItems - The number of items in the CustomDumpItem array. +// Should always be zero today, since no custom items are defined +// items - Array of CustomDumpItem structs specifying items to be added to the dump. +// Should always be NULL today, since no custom items are defined. +// dwReserved - reserved for future use. Must be zero today +// +HRESULT CCLRErrorReportingManager::BeginCustomDump( ECustomDumpFlavor dwFlavor, + DWORD dwNumItems, + CustomDumpItem items[], + DWORD dwReserved) +{ + STATIC_CONTRACT_ENTRY_POINT; + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + if (dwNumItems != 0 || items != NULL || dwReserved != 0) + { + IfFailGo(E_INVALIDARG); + } + if (g_ECustomDumpFlavor != DUMP_FLAVOR_Default) + { + // BeginCustomDump is called without matching EndCustomDump + IfFailGo(E_INVALIDARG); + } + g_ECustomDumpFlavor = dwFlavor; + +ErrExit: + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +// +// EndCustomDump clears the custom dump configuration +// +HRESULT CCLRErrorReportingManager::EndCustomDump() +{ + STATIC_CONTRACT_ENTRY_POINT; + // NOT IMPLEMENTED YET + BEGIN_ENTRYPOINT_NOTHROW; + g_ECustomDumpFlavor = DUMP_FLAVOR_Default; + END_ENTRYPOINT_NOTHROW; + + return S_OK; +} + +#ifdef FEATURE_WINDOWSPHONE +HRESULT CopyStringWorker(_Out_ WCHAR** pTarget, WCHAR const* pSource) +{ + LIMITED_METHOD_CONTRACT; + + if (pTarget == NULL || pSource == NULL) + return E_INVALIDARG; + + if (*pTarget) + delete[] (*pTarget); + + // allocate space for the data plus one wchar for NULL + size_t sourceLen = wcslen(pSource); + *pTarget = new (nothrow) WCHAR[sourceLen + 1]; + + if (!(*pTarget)) + return E_OUTOFMEMORY; + + errno_t result = wcsncpy_s(*pTarget, sourceLen + 1, pSource, sourceLen); + _ASSERTE(result == 0); + + if (result != 0) + { + delete[] (*pTarget); + *pTarget = NULL; + + return E_FAIL; + } + + return S_OK; +} + +CCLRErrorReportingManager::BucketParamsCache::BucketParamsCache(DWORD maxNumParams) : m_pParams(NULL), m_cMaxParams(maxNumParams) +{ + LIMITED_METHOD_CONTRACT; +} + +CCLRErrorReportingManager::BucketParamsCache::~BucketParamsCache() +{ + LIMITED_METHOD_CONTRACT; + + if (m_pParams) + { + for (DWORD i = 0; i < m_cMaxParams; ++i) + if (m_pParams[i]) delete[] m_pParams[i]; + } +} + +WCHAR const* CCLRErrorReportingManager::BucketParamsCache::GetAt(BucketParameterIndex index) +{ + LIMITED_METHOD_CONTRACT; + + if (index >= InvalidBucketParamIndex) + { + _ASSERTE(!"bad bucket parameter index"); + return NULL; + } + + if (!m_pParams) + return NULL; + + return m_pParams[index]; +} + +HRESULT CCLRErrorReportingManager::BucketParamsCache::SetAt(BucketParameterIndex index, WCHAR const* val) +{ + LIMITED_METHOD_CONTRACT; + + if (index >= InvalidBucketParamIndex) + { + _ASSERTE(!"bad bucket parameter index"); + return E_INVALIDARG; + } + + if (!val) + return E_INVALIDARG; + + if (!m_pParams) + { + m_pParams = new (nothrow) WCHAR*[m_cMaxParams]; + if (!m_pParams) + return E_OUTOFMEMORY; + + for (DWORD i = 0; i < m_cMaxParams; ++i) + m_pParams[i] = NULL; + } + + return CopyStringWorker(&m_pParams[index], val); +} + +HRESULT CCLRErrorReportingManager::CopyToDataCache(_Out_ WCHAR** pTarget, WCHAR const* pSource) +{ + LIMITED_METHOD_CONTRACT; + + return CopyStringWorker(pTarget, pSource); +} + +HRESULT CCLRErrorReportingManager::SetApplicationData(ApplicationDataKey key, WCHAR const* pValue) +{ + STATIC_CONTRACT_ENTRY_POINT; + + BEGIN_ENTRYPOINT_NOTHROW; + + if(g_fEEStarted) + return HOST_E_INVALIDOPERATION; + + if (pValue == NULL || wcslen(pValue) > MAX_LONGPATH) + return E_INVALIDARG; + + HRESULT hr = S_OK; + + switch (key) + { + case ApplicationID: + hr = CopyToDataCache(&m_pApplicationId, pValue); + break; + + case InstanceID: + hr = CopyToDataCache(&m_pInstanceId, pValue); + break; + + default: + hr = E_INVALIDARG; + } + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +HRESULT CCLRErrorReportingManager::SetBucketParametersForUnhandledException(BucketParameters const* pBucketParams, DWORD* pCountParams) +{ + STATIC_CONTRACT_ENTRY_POINT; + + BEGIN_ENTRYPOINT_NOTHROW; + + if(g_fEEStarted) + return HOST_E_INVALIDOPERATION; + + if (pBucketParams == NULL || pCountParams == NULL || pBucketParams->fInited != TRUE) + return E_INVALIDARG; + + *pCountParams = 0; + + if (!m_pBucketParamsCache) + { + m_pBucketParamsCache = new (nothrow) BucketParamsCache(InvalidBucketParamIndex); + if (!m_pBucketParamsCache) + return E_OUTOFMEMORY; + } + + HRESULT hr = S_OK; + bool hasOverride = false; + + for (DWORD i = 0; i < InvalidBucketParamIndex; ++i) + { + if (pBucketParams->pszParams[i][0] != W('\0')) + { + hasOverride = true; + hr = m_pBucketParamsCache->SetAt(static_cast<BucketParameterIndex>(i), pBucketParams->pszParams[i]); + if (SUCCEEDED(hr)) + *pCountParams += 1; + else + break; + } + } + + if (!hasOverride) + return E_INVALIDARG; + + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +WCHAR const* CCLRErrorReportingManager::GetApplicationData(ApplicationDataKey key) +{ + LIMITED_METHOD_CONTRACT; + + WCHAR* pValue = NULL; + + switch (key) + { + case ApplicationID: + pValue = m_pApplicationId; + break; + + case InstanceID: + pValue = m_pInstanceId; + break; + + default: + _ASSERTE(!"invalid key specified"); + } + + return pValue; +} + +WCHAR const* CCLRErrorReportingManager::GetBucketParamOverride(BucketParameterIndex bucketParamId) +{ + LIMITED_METHOD_CONTRACT; + + if (!m_pBucketParamsCache) + return NULL; + + return m_pBucketParamsCache->GetAt(bucketParamId); +} + +#endif // FEATURE_WINDOWSPHONE + +CCLRErrorReportingManager::CCLRErrorReportingManager() +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_WINDOWSPHONE + m_pApplicationId = NULL; + m_pInstanceId = NULL; + m_pBucketParamsCache = NULL; +#endif +} + +CCLRErrorReportingManager::~CCLRErrorReportingManager() +{ + LIMITED_METHOD_CONTRACT; +#ifdef FEATURE_WINDOWSPHONE + if (m_pApplicationId) + delete[] m_pApplicationId; + + if (m_pInstanceId) + delete[] m_pInstanceId; + + if (m_pBucketParamsCache) + delete m_pBucketParamsCache; +#endif +} + +#endif // defined(FEATURE_INCLUDE_ALL_INTERFACES) || defined(FEATURE_WINDOWSPHONE) + +#ifdef FEATURE_IPCMAN + +CrstStatic CCLRSecurityAttributeManager::m_hostSAMutex; +PACL CCLRSecurityAttributeManager::m_pACL; + +SECURITY_ATTRIBUTES CCLRSecurityAttributeManager::m_hostSA; +SECURITY_DESCRIPTOR CCLRSecurityAttributeManager::m_hostSD; + +/* +* constructor +* +*/ +void CCLRSecurityAttributeManager::ProcessInit() +{ + CONTRACTL + { + THROWS; + GC_NOTRIGGER; + } + CONTRACTL_END; + + m_hostSAMutex.Init(CrstReDacl, CRST_UNSAFE_ANYMODE); + m_pACL = NULL; +} + +/* +* destructor +* +*/ +void CCLRSecurityAttributeManager::ProcessCleanUp() +{ + CONTRACTL + { + GC_NOTRIGGER; + NOTHROW; + } + CONTRACTL_END; + + m_hostSAMutex.Destroy(); + if (m_pACL) + CoTaskMemFree(m_pACL); +} + +// Set private block and events to the new ACL. +HRESULT CCLRSecurityAttributeManager::SetDACL(PACL pacl) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + DWORD dwError; + PACL pNewACL = NULL; + HANDLE hProc = NULL; + DWORD pid = 0; + + // @todo: How can we make sure that debugger attach will not attempt to happen during this time??? + // + CrstHolder ch(&m_hostSAMutex); + + // make sure our host pass our a valid ACL + if (!IsValidAcl(pacl)) + { + dwError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwError); + goto ErrExit; + } + + // Cannnot set DACL while debugger is attached. Because the events are already all hooked up + // between LS and RS. + if (CORDebuggerAttached()) + return CORDBG_E_DEBUGGER_ALREADY_ATTACHED; + + // make a copy of the new ACL + pNewACL = (PACL) CoTaskMemAlloc(pacl->AclSize); + if (FAILED( CopyACL(pacl, pNewACL))) + goto ErrExit; + + _ASSERTE (SECURITY_DESCRIPTOR_MIN_LENGTH == sizeof(SECURITY_DESCRIPTOR)); + + if (!InitializeSecurityDescriptor(&m_hostSD, SECURITY_DESCRIPTOR_REVISION)) + { + hr = HRESULT_FROM_GetLastError(); + goto ErrExit; + } + + if (!SetSecurityDescriptorDacl(&m_hostSD, TRUE, pNewACL, FALSE)) + { + hr = HRESULT_FROM_GetLastError(); + goto ErrExit; + } + + // Now cache the pNewACL to m_pACL and delete m_pACL. + if (m_pACL) + CoTaskMemFree(m_pACL); + + m_pACL = pNewACL; + pNewACL = NULL; + + m_hostSA.nLength = sizeof(SECURITY_ATTRIBUTES); + m_hostSA.lpSecurityDescriptor = &m_hostSD; + m_hostSA.bInheritHandle = FALSE; + + // first of all, try to reDacl on the process token + pid = GetCurrentProcessId(); + hProc = OpenProcess(WRITE_DAC, FALSE, pid); + if (hProc == NULL) + { + hr = HRESULT_FROM_GetLastError(); + goto ErrExit; + } + if (SetKernelObjectSecurity(hProc, DACL_SECURITY_INFORMATION, &m_hostSD) == 0) + { + // failed! + hr = HRESULT_FROM_GetLastError(); + goto ErrExit; + } + + + // now reset all of the kernel object token's DACL. + // This will reDACL the global shared section + if (FAILED(g_pIPCManagerInterface->ReDaclLegacyPrivateBlock(&m_hostSD))) + goto ErrExit; + + // This will reDacl on debugger events. + if (g_pDebugInterface) + { + g_pDebugInterface->ReDaclEvents(&m_hostSD); + } + +ErrExit: + if (pNewACL) + CoTaskMemFree(pNewACL); + if (hProc != NULL) + CloseHandle(hProc); + + return hr; +} + +// cLen - specify the size of input buffer ppacl. If cLen is zero or ppacl is null, +// pcLenTotal will return the total size of required pacl buffer. +// pacl - caller allocated space. We will fill acl in this buffer. +// pcLenTotal - the total size of ACL. +// +HRESULT CCLRSecurityAttributeManager::GetDACL(PACL *ppacl) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + PACL pNewACL = NULL; + PACL pDefaultACL = NULL; + SECURITY_ATTRIBUTES *pSA = NULL; + + // output parameter cannot be NULL + if (ppacl == NULL) + return E_INVALIDARG; + + *ppacl = NULL; + + CrstHolder ch(&m_hostSAMutex); + + // we want to return the ACL of our default policy + if (m_pACL == NULL) + { + hr = g_pIPCManagerInterface->CreateWinNTDescriptor(GetCurrentProcessId(), &pSA, eDescriptor_Private); + if (FAILED(hr)) + { + goto ErrExit; + } + EX_TRY + { + BOOL bDaclPresent; + BOOL bDaclDefault; + + LeaveRuntimeHolder holder((size_t)(::GetSecurityDescriptorDacl)); + ::GetSecurityDescriptorDacl(pSA->lpSecurityDescriptor, &bDaclPresent, &pDefaultACL, &bDaclDefault); + } + EX_CATCH + { + hr = GET_EXCEPTION()->GetHR(); + } + EX_END_CATCH(SwallowAllExceptions); + if (FAILED(hr) || pDefaultACL == NULL || pDefaultACL->AclSize == 0) + { + goto ErrExit; + } + } + else + { + pDefaultACL = m_pACL; + } + + pNewACL = (PACL) CoTaskMemAlloc(pDefaultACL->AclSize); + if (pNewACL == NULL) + { + hr = E_OUTOFMEMORY; + goto ErrExit; + } + + // make a copy of ACL + hr = CCLRSecurityAttributeManager::CopyACL(pDefaultACL, pNewACL); + if (SUCCEEDED(hr)) + *ppacl = pNewACL; + +ErrExit: + if (FAILED(hr)) + { + if (pNewACL) + { + CoTaskMemFree(pNewACL); + } + } + if (pSA != NULL) + { + g_pIPCManagerInterface->DestroySecurityAttributes(pSA); + } + return hr; +} + + +// This API will duplicate a copy of pAclOrigingal and pass it out on ppAclNew +HRESULT CCLRSecurityAttributeManager::CopyACL(PACL pAclOriginal, PACL pNewACL) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + } + CONTRACTL_END; + + HRESULT hr = NO_ERROR; + DWORD dwError = GetLastError(); + int i; + ACE_HEADER *pDACLAce; + + _ASSERTE(pNewACL && pAclOriginal); + + // initialize the target ACL buffer + if (!InitializeAcl(pNewACL, pAclOriginal->AclSize, ACL_REVISION)) + { + dwError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwError); + goto ErrExit; + } + + // loop through each existing ace and copy it over + for (i = 0; i < pAclOriginal->AceCount; i++) + { + if (!GetAce(pAclOriginal, i, (LPVOID *) &pDACLAce)) + { + dwError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwError); + goto ErrExit; + } + + if (!AddAce(pNewACL, ACL_REVISION, i, pDACLAce, pDACLAce->AceSize)) + { + dwError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwError); + goto ErrExit; + } + } + + // make sure everything went well with the new ACL + if (!IsValidAcl(pNewACL)) + { + dwError = GetLastError(); + hr = HRESULT_FROM_WIN32(dwError); + goto ErrExit; + } + +ErrExit: + return hr; +} + + +HRESULT CCLRSecurityAttributeManager::GetHostSecurityAttributes(SECURITY_ATTRIBUTES **ppSA) +{ + WRAPPER_NO_CONTRACT; + + if(!ppSA) + return E_POINTER; + + HRESULT hr = S_OK; + + *ppSA = NULL; + + // host has specified ACL + if (m_pACL != NULL) + *ppSA = &(m_hostSA); + + else + hr = g_pIPCManagerInterface->CreateWinNTDescriptor(GetCurrentProcessId(), ppSA, eDescriptor_Private); + + return hr; +} + +void CCLRSecurityAttributeManager::DestroyHostSecurityAttributes(SECURITY_ATTRIBUTES *pSA) +{ + WRAPPER_NO_CONTRACT; + + // no pSA to cleanup + if (pSA == NULL) + return; + + // it is our current host SA. + if (&(m_hostSA) == pSA) + return; + + g_pIPCManagerInterface->DestroySecurityAttributes(pSA); +} +#endif // FEATURE_IPCMAN + +void GetProcessMemoryLoad(LPMEMORYSTATUSEX pMSEX) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + } + CONTRACTL_END; + + pMSEX->dwLength = sizeof(MEMORYSTATUSEX); + BOOL fRet = GlobalMemoryStatusEx(pMSEX); + _ASSERTE (fRet); + +#ifdef FEATURE_INCLUDE_ALL_INTERFACES + // CoreCLR cannot be memory hosted + if (CLRMemoryHosted()) + { + BEGIN_SO_TOLERANT_CODE_CALLING_HOST(GetThread()); + DWORD memoryLoad; + SIZE_T availableBytes; + HRESULT hr = CorHost2::GetHostMemoryManager()->GetMemoryLoad(&memoryLoad, &availableBytes); + if (hr == S_OK) { + pMSEX->dwMemoryLoad = memoryLoad; + pMSEX->ullAvailPhys = availableBytes; + } + END_SO_TOLERANT_CODE_CALLING_HOST; + } +#endif // FEATURE_INCLUDE_ALL_INTERFACES + + // If the machine has more RAM than virtual address limit, let us cap it. + // Our GC can never use more than virtual address limit. + if (pMSEX->ullAvailPhys > pMSEX->ullTotalVirtual) + { + pMSEX->ullAvailPhys = pMSEX->ullAvailVirtual; + } +} + +// This is the instance that exposes interfaces out to all the other DLLs of the CLR +// so they can use our services for TLS, synchronization, memory allocation, etc. +static BYTE g_CEEInstance[sizeof(CExecutionEngine)]; +static Volatile<IExecutionEngine*> g_pCEE = NULL; + +PTLS_CALLBACK_FUNCTION CExecutionEngine::Callbacks[MAX_PREDEFINED_TLS_SLOT]; + +extern "C" IExecutionEngine * __stdcall IEE() +{ + LIMITED_METHOD_CONTRACT; + + // Unfortunately,we can't probe here. The probing system requires the + // use of TLS, and in order to initialize TLS we need to call IEE. + + //BEGIN_ENTRYPOINT_VOIDRET; + + + // The following code does NOT contain a race condition. The following code is BY DESIGN. + // The issue is that we can have two separate threads inside this if statement, both of which are + // initializing the g_CEEInstance variable (and subsequently updating g_pCEE). This works fine, + // and will not cause an inconsistent state due to the fact that CExecutionEngine has no + // local variables. If multiple threads make it inside this if statement, it will copy the same + // bytes over g_CEEInstance and there will not be a time when there is an inconsistent state. + if ( !g_pCEE ) + { + // Create a local copy on the stack and then copy it over to the static instance. + // This avoids race conditions caused by multiple initializations of vtable in the constructor + CExecutionEngine local; + memcpy(&g_CEEInstance, (void*)&local, sizeof(CExecutionEngine)); + + g_pCEE = (IExecutionEngine*)(CExecutionEngine*)&g_CEEInstance; + } + //END_ENTRYPOINT_VOIDRET; + + return g_pCEE; +} + + +HRESULT STDMETHODCALLTYPE CExecutionEngine::QueryInterface(REFIID id, void **pInterface) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + if (!pInterface) + return E_POINTER; + + *pInterface = NULL; + + //CANNOTTHROWCOMPLUSEXCEPTION(); + if (id == IID_IExecutionEngine) + *pInterface = (IExecutionEngine *)this; + else if (id == IID_IEEMemoryManager) + *pInterface = (IEEMemoryManager *)this; + else if (id == IID_IUnknown) + *pInterface = (IUnknown *)(IExecutionEngine *)this; + else + return E_NOINTERFACE; + + AddRef(); + return S_OK; +} // HRESULT STDMETHODCALLTYPE CExecutionEngine::QueryInterface() + + +ULONG STDMETHODCALLTYPE CExecutionEngine::AddRef() +{ + LIMITED_METHOD_CONTRACT; + return 1; +} + +ULONG STDMETHODCALLTYPE CExecutionEngine::Release() +{ + LIMITED_METHOD_CONTRACT; + return 1; +} + +struct ClrTlsInfo +{ + void* data[MAX_PREDEFINED_TLS_SLOT]; + // When hosted, we may not be able to delete memory in DLL_THREAD_DETACH. + // We will chain this into a side list, and free these on Finalizer thread. + ClrTlsInfo *next; +}; + +#define DataToClrTlsInfo(a) (a)?(ClrTlsInfo*)((BYTE*)a - offsetof(ClrTlsInfo, data)):NULL + +#if !defined(FEATURE_CORECLR) +#define HAS_FLS_SUPPORT 1 +#endif + +#ifdef HAS_FLS_SUPPORT + +static BOOL fHasFlsSupport = FALSE; + +typedef DWORD (*Func_FlsAlloc)(PFLS_CALLBACK_FUNCTION lpCallback); +typedef BOOL (*Func_FlsFree)(DWORD dwFlsIndex); +typedef BOOL (*Func_FlsSetValue)(DWORD dwFlsIndex,PVOID lpFlsData); +typedef PVOID (*Func_FlsGetValue)(DWORD dwFlsIndex); + +static DWORD FlsIndex = FLS_OUT_OF_INDEXES; +static Func_FlsAlloc pFlsAlloc; +static Func_FlsSetValue pFlsSetValue; +static Func_FlsFree pFlsFree; +static Func_FlsGetValue pFlsGetValue; +static Volatile<BOOL> fFlsSetupDone = FALSE; + +VOID WINAPI FlsCallback( + PVOID lpFlsData +) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE (pFlsGetValue); + if (pFlsGetValue(FlsIndex) != lpFlsData) + { + // The current running fiber is being destroyed. We can not destroy the memory yet, + // because our DllMain function may still need the memory. + CExecutionEngine::ThreadDetaching((void **)lpFlsData); + } + else + { + // The thread is being wound down. + // In hosting scenarios the host will have already called ICLRTask::ExitTask, which + // ends up calling CExecutionEngine::SwitchOut, which will have reset the TLS at TlsIndex. + // + // Unfortunately different OSes have different ordering of destroying FLS data and sending + // the DLL_THREAD_DETACH notification (pre-Vista FlsCallback is called after DllMain, while + // in Vista and up, FlsCallback is called before DllMain). Additionally, starting with + // Vista SP1 and Win2k8, the OS will set the FLS slot to 0 after the call to FlsCallback, + // effectively removing our last reference to this data. Since in EEDllMain we need to be + // able to access the FLS data, we save lpFlsData in the TLS slot at TlsIndex, if needed. + if (CExecutionEngine::GetTlsData() == NULL) + { + CExecutionEngine::SetTlsData((void **)lpFlsData); + } + } +} + +#endif // HAS_FLS_SUPPORT + + +#ifdef FEATURE_IMPLICIT_TLS +void** CExecutionEngine::GetTlsData() +{ + LIMITED_METHOD_CONTRACT; + + return gCurrentThreadInfo.m_EETlsData; +} + +BOOL CExecutionEngine::SetTlsData (void** ppTlsInfo) +{ + LIMITED_METHOD_CONTRACT; + + gCurrentThreadInfo.m_EETlsData = ppTlsInfo; + return TRUE; +} +#else +void** CExecutionEngine::GetTlsData() +{ + LIMITED_METHOD_CONTRACT; + + if (TlsIndex == TLS_OUT_OF_INDEXES) + return NULL; + + void **ppTlsData = (void **)UnsafeTlsGetValue(TlsIndex); + return ppTlsData; +} +BOOL CExecutionEngine::SetTlsData (void** ppTlsInfo) +{ + LIMITED_METHOD_CONTRACT; + + if (TlsIndex == TLS_OUT_OF_INDEXES) + return FALSE; + + return UnsafeTlsSetValue(TlsIndex, ppTlsInfo); +} + +#endif // FEATURE_IMPLICIT_TLS + +static VolatilePtr<ClrTlsInfo> g_pDetachedTlsInfo; + +BOOL CExecutionEngine::HasDetachedTlsInfo() +{ + LIMITED_METHOD_CONTRACT; + + return g_pDetachedTlsInfo.Load() != NULL; +} + +void CExecutionEngine::CleanupDetachedTlsInfo() +{ + WRAPPER_NO_CONTRACT; + + if (g_pDetachedTlsInfo.Load() == NULL) + { + return; + } + ClrTlsInfo *head = FastInterlockExchangePointer(g_pDetachedTlsInfo.GetPointer(), NULL); + + while (head) + { + ClrTlsInfo *node = head; + head = head->next; + DeleteTLS(node->data); + } +} + +void CExecutionEngine::DetachTlsInfo(void **pTlsData) +{ + LIMITED_METHOD_CONTRACT; + + if (pTlsData == NULL) + { + return; + } + + if (CExecutionEngine::GetTlsData() == pTlsData) + { + CExecutionEngine::SetTlsData(0); + } + +#ifdef HAS_FLS_SUPPORT + if (fHasFlsSupport && pFlsGetValue(FlsIndex) == pTlsData) + { + pFlsSetValue(FlsIndex, NULL); + } +#endif + + ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); + // PREFIX_ASSUME needs TLS. If we use it here, we may do memory allocation. +#if defined(_PREFAST_) || defined(_PREFIX_) + if (pTlsInfo == NULL) __UNREACHABLE(); +#else + _ASSERTE(pTlsInfo != NULL); +#endif // _PREFAST_ || _PREFIX_ + + if (pTlsInfo->data[TlsIdx_StressLog]) + { +#ifdef STRESS_LOG + CantAllocHolder caHolder; + StressLog::ThreadDetach ((ThreadStressLog *)pTlsInfo->data[TlsIdx_StressLog]); + pTlsInfo->data[TlsIdx_StressLog] = NULL; +#else + _ASSERTE (!"Shouldn't have stress log!"); +#endif + } + + while (TRUE) + { + ClrTlsInfo *head = g_pDetachedTlsInfo.Load(); + pTlsInfo->next = head; + if (FastInterlockCompareExchangePointer(g_pDetachedTlsInfo.GetPointer(), pTlsInfo, head) == head) + { + return; + } + } +} + +//--------------------------------------------------------------------------------------- +// +// Returns the current logical thread's data block (ClrTlsInfo::data). +// +// Arguments: +// slot - Index of the slot that is about to be requested +// force - If the data block does not exist yet, create it as a side-effect +// +// Return Value: +// NULL, if the data block did not exist yet for the current thread and force was FALSE. +// A pointer to the data block, otherwise. +// +// Notes: +// If the underlying OS does not support fiber mode, the data block is stored in TLS. +// If the underlying OS does support fiber mode, it is primarily stored in FLS, +// and cached in TLS so that we can use our generated optimized TLS accessors. +// +// TLS support for the other DLLs of the CLR operates quite differently in hosted +// and unhosted scenarios. + +void **CExecutionEngine::CheckThreadState(DWORD slot, BOOL force) +{ + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + STATIC_CONTRACT_SO_TOLERANT; + + // !!! This function is called during Thread::SwitchIn and SwitchOut + // !!! It is extremely important that while executing this function, we will not + // !!! cause fiber switch. This means we can not allocate memory, lock, etc... + + //<TODO> @TODO: Decide on an exception strategy for all the DLLs of the CLR, and then + // enable all the exceptions out of this method.</TODO> + + // Treat as a runtime assertion, since the invariant spans many DLLs. + _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); +// if (slot >= MAX_PREDEFINED_TLS_SLOT) +// COMPlusThrow(kArgumentOutOfRangeException); + +#ifdef HAS_FLS_SUPPORT + if (!fFlsSetupDone) + { + // Contract depends on Fls support. Don't use contract here. + HMODULE hmod = GetModuleHandleA(WINDOWS_KERNEL32_DLLNAME_A); + if (hmod) + { + pFlsSetValue = (Func_FlsSetValue) GetProcAddress(hmod, "FlsSetValue"); + pFlsGetValue = (Func_FlsGetValue) GetProcAddress(hmod, "FlsGetValue"); + pFlsAlloc = (Func_FlsAlloc) GetProcAddress(hmod, "FlsAlloc"); + pFlsFree = (Func_FlsFree) GetProcAddress(hmod, "FlsFree"); + + if (pFlsSetValue && pFlsGetValue && pFlsAlloc && pFlsFree ) + { + fHasFlsSupport = TRUE; + } + else + { + // Since we didn't find them all, we shouldn't have found any + _ASSERTE( pFlsSetValue == NULL && pFlsGetValue == NULL && pFlsAlloc == NULL && pFlsFree == NULL); + } + fFlsSetupDone = TRUE; + } + } + + if (fHasFlsSupport && FlsIndex == FLS_OUT_OF_INDEXES) + { + // PREFIX_ASSUME needs TLS. If we use it here, we will loop forever +#if defined(_PREFAST_) || defined(_PREFIX_) + if (pFlsAlloc == NULL) __UNREACHABLE(); +#else + _ASSERTE(pFlsAlloc != NULL); +#endif // _PREFAST_ || _PREFIX_ + + DWORD tryFlsIndex = pFlsAlloc(FlsCallback); + if (tryFlsIndex != FLS_OUT_OF_INDEXES) + { + if (FastInterlockCompareExchange((LONG*)&FlsIndex, tryFlsIndex, FLS_OUT_OF_INDEXES) != FLS_OUT_OF_INDEXES) + { + pFlsFree(tryFlsIndex); + } + } + if (FlsIndex == FLS_OUT_OF_INDEXES) + { + COMPlusThrowOM(); + } + } +#endif // HAS_FLS_SUPPORT + +#ifndef FEATURE_IMPLICIT_TLS + // Ensure we have a TLS Index + if (TlsIndex == TLS_OUT_OF_INDEXES) + { + DWORD tryTlsIndex = UnsafeTlsAlloc(); + if (tryTlsIndex != TLS_OUT_OF_INDEXES) + { + if (FastInterlockCompareExchange((LONG*)&TlsIndex, tryTlsIndex, TLS_OUT_OF_INDEXES) != (LONG)TLS_OUT_OF_INDEXES) + { + UnsafeTlsFree(tryTlsIndex); + } + } + if (TlsIndex == TLS_OUT_OF_INDEXES) + { + COMPlusThrowOM(); + } + } +#endif // FEATURE_IMPLICIT_TLS + + void** pTlsData = CExecutionEngine::GetTlsData(); + BOOL fInTls = (pTlsData != NULL); + +#ifdef HAS_FLS_SUPPORT + if (fHasFlsSupport) + { + if (pTlsData == NULL) + { + pTlsData = (void **)pFlsGetValue(FlsIndex); + } + } +#endif + + ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); + if (pTlsInfo == 0 && force) + { +#undef HeapAlloc +#undef GetProcessHeap + // !!! Contract uses our TLS support. Contract may be used before our host support is set up. + // !!! To better support contract, we call into OS for memory allocation. + pTlsInfo = (ClrTlsInfo*) ::HeapAlloc(GetProcessHeap(),0,sizeof(ClrTlsInfo)); +#define GetProcessHeap() Dont_Use_GetProcessHeap() +#define HeapAlloc(hHeap, dwFlags, dwBytes) Dont_Use_HeapAlloc(hHeap, dwFlags, dwBytes) + if (pTlsInfo == NULL) + { + goto LError; + } + memset (pTlsInfo, 0, sizeof(ClrTlsInfo)); +#ifdef HAS_FLS_SUPPORT + if (fHasFlsSupport && !pFlsSetValue(FlsIndex, pTlsInfo)) + { + goto LError; + } +#endif + // We save the last intolerant marker on stack in this slot. + // -1 is the larget unsigned number, and therefore our marker is always smaller than it. + pTlsInfo->data[TlsIdx_SOIntolerantTransitionHandler] = (void*)(-1); + } + + if (!fInTls && pTlsInfo) + { +#ifdef HAS_FLS_SUPPORT + // If we have a thread object or are on a non-fiber thread, we are safe for fiber switching. + if (!fHasFlsSupport || + GetThread() || + ((g_fEEStarted || g_fEEInit) && !CLRTaskHosted()) || + (((size_t)pTlsInfo->data[TlsIdx_ThreadType]) & (ThreadType_GC | ThreadType_Gate | ThreadType_Timer | ThreadType_DbgHelper))) + { +#ifdef _DEBUG + Thread *pThread = GetThread(); + if (pThread) + { + pThread->AddFiberInfo(Thread::ThreadTrackInfo_Lifetime); + } +#endif + if (!CExecutionEngine::SetTlsData(pTlsInfo->data) && !fHasFlsSupport) + { + goto LError; + } + } +#else + if (!CExecutionEngine::SetTlsData(pTlsInfo->data)) + { + goto LError; + } +#endif + } + + return pTlsInfo?pTlsInfo->data:NULL; + +LError: + if (pTlsInfo) + { +#undef HeapFree +#undef GetProcessHeap + ::HeapFree(GetProcessHeap(), 0, pTlsInfo); +#define GetProcessHeap() Dont_Use_GetProcessHeap() +#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) + } + // If this is for the stack probe, and we failed to allocate memory for it, we won't + // put in a guard page. + if (slot == TlsIdx_ClrDebugState || slot == TlsIdx_StackProbe) + return NULL; + + ThrowOutOfMemory(); +} + + +void **CExecutionEngine::CheckThreadStateNoCreate(DWORD slot +#ifdef _DEBUG + , BOOL fForDestruction +#endif // _DEBUG + ) +{ + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_SO_TOLERANT; + + // !!! This function is called during Thread::SwitchIn and SwitchOut + // !!! It is extremely important that while executing this function, we will not + // !!! cause fiber switch. This means we can not allocate memory, lock, etc... + + + // Treat as a runtime assertion, since the invariant spans many DLLs. + _ASSERTE(slot < MAX_PREDEFINED_TLS_SLOT); + + void **pTlsData = CExecutionEngine::GetTlsData(); + +#ifdef HAS_FLS_SUPPORT + if (fHasFlsSupport) + { + if (pTlsData == NULL) + { + pTlsData = (void **)pFlsGetValue(FlsIndex); + } + } +#endif + + ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); + + return pTlsInfo?pTlsInfo->data:NULL; +} + +// Note: Sampling profilers also use this function to initialize TLS for a unmanaged +// sampling thread so that initialization can be done in advance to avoid deadlocks. +// See ProfToEEInterfaceImpl::InitializeCurrentThread for more details. +void CExecutionEngine::SetupTLSForThread(Thread *pThread) +{ + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_SO_TOLERANT; + STATIC_CONTRACT_MODE_ANY; + +#ifdef _DEBUG + if (pThread) + pThread->AddFiberInfo(Thread::ThreadTrackInfo_Lifetime); +#endif +#ifdef STRESS_LOG + if (StressLog::StressLogOn(~0u, 0)) + { + StressLog::CreateThreadStressLog(); + } +#endif + void **pTlsData; + pTlsData = CheckThreadState(0); + + PREFIX_ASSUME(pTlsData != NULL); + +#ifdef ENABLE_CONTRACTS + // Profilers need the side effect of GetClrDebugState() to perform initialization + // in advance to avoid deadlocks. Refer to ProfToEEInterfaceImpl::InitializeCurrentThread + ClrDebugState *pDebugState = ::GetClrDebugState(); + + if (pThread) + pThread->m_pClrDebugState = pDebugState; +#endif +} + +void CExecutionEngine::SwitchIn() +{ + // No real contracts here. This function is called by Thread::SwitchIn. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_ENTRY_POINT; + + // @TODO - doesn't look like we can probe here.... + +#ifdef HAS_FLS_SUPPORT + if (fHasFlsSupport) + { + void **pTlsData = (void **)pFlsGetValue(FlsIndex); + + BOOL fResult = CExecutionEngine::SetTlsData(pTlsData); + if (fResult) + { +#ifdef STRESS_LOG + // We are in task transition period. We can not call into host to create stress log. + if (ClrTlsGetValue(TlsIdx_StressLog) != NULL) + { + STRESS_LOG1(LF_SYNC, LL_INFO100, ThreadStressLog::TaskSwitchMsg(), ::GetCurrentThreadId()); + } +#endif + } + // It is OK for UnsafeTlsSetValue to fail here, since we can always go back to Fls to get value. + } +#endif +} + +void CExecutionEngine::SwitchOut() +{ + // No real contracts here. This function is called by Thread::SwitchOut + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_ENTRY_POINT; + +#ifdef HAS_FLS_SUPPORT + // @TODO - doesn't look like we can probe here. + if (fHasFlsSupport && pFlsGetValue != NULL && (void **)pFlsGetValue(FlsIndex) != NULL) + { + // Clear out TLS unless we're in the process of ThreadDetach + // We establish that we're in ThreadDetach because fHasFlsSupport will + // be TRUE, but the FLS will not exist. + CExecutionEngine::SetTlsData(NULL); + } +#endif // HAS_FLS_SUPPORT +} + +static void ThreadDetachingHelper(PTLS_CALLBACK_FUNCTION callback, void* pData) +{ + // Do not use contract. We are freeing TLS blocks. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + + callback(pData); + } + +// Called here from a thread detach or from destruction of a Thread object. In +// the detach case, we get our info from TLS. In the destruct case, it comes from +// the object we are destructing. +void CExecutionEngine::ThreadDetaching(void ** pTlsData) +{ + // Can not cause memory allocation during thread detach, so no real contracts. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + + // This function may be called twice: + // 1. When a physical thread dies, our DLL_THREAD_DETACH calls this function with pTlsData = NULL + // 2. When a fiber is destroyed, or OS calls FlsCallback after DLL_THREAD_DETACH process. + // We will null the FLS and TLS entry if it matches the deleted one. + + if (pTlsData) + { + DeleteTLS (pTlsData); + } +} + +void CExecutionEngine::DeleteTLS(void ** pTlsData) +{ + // Can not cause memory allocation during thread detach, so no real contracts. + STATIC_CONTRACT_NOTHROW; + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_MODE_ANY; + + if (CExecutionEngine::GetTlsData() == NULL) + { + // We have not allocated TlsData yet. + return; + } + + PREFIX_ASSUME(pTlsData != NULL); + + ClrTlsInfo *pTlsInfo = DataToClrTlsInfo(pTlsData); + BOOL fNeed; + do + { + fNeed = FALSE; + for (int i=0; i<MAX_PREDEFINED_TLS_SLOT; i++) + { + if (i == TlsIdx_ClrDebugState || + i == TlsIdx_StressLog) + { + // StressLog and DebugState may be needed during callback. + continue; + } + // If we have some data and a callback, issue it. + if (Callbacks[i] != 0 && pTlsInfo->data[i] != 0) + { + void* pData = pTlsInfo->data[i]; + pTlsInfo->data[i] = 0; + ThreadDetachingHelper(Callbacks[i], pData); + fNeed = TRUE; + } + } + } while (fNeed); + + if (pTlsInfo->data[TlsIdx_StressLog] != 0) + { +#ifdef STRESS_LOG + StressLog::ThreadDetach((ThreadStressLog *)pTlsInfo->data[TlsIdx_StressLog]); +#else + _ASSERTE (!"should not have StressLog"); +#endif + } + + if (Callbacks[TlsIdx_ClrDebugState] != 0 && pTlsInfo->data[TlsIdx_ClrDebugState] != 0) + { + void* pData = pTlsInfo->data[TlsIdx_ClrDebugState]; + pTlsInfo->data[TlsIdx_ClrDebugState] = 0; + ThreadDetachingHelper(Callbacks[TlsIdx_ClrDebugState], pData); + } + +#ifdef _DEBUG + Thread *pThread = GetThread(); + if (pThread) + { + pThread->AddFiberInfo(Thread::ThreadTrackInfo_Lifetime); + } +#endif + + // NULL TLS and FLS entry so that we don't double free. + // We may get two callback here on thread death + // 1. From EEDllMain + // 2. From OS callback on FLS destruction + if (CExecutionEngine::GetTlsData() == pTlsData) + { + CExecutionEngine::SetTlsData(0); + } + +#ifdef HAS_FLS_SUPPORT + if (fHasFlsSupport && pFlsGetValue(FlsIndex) == pTlsData) + { + pFlsSetValue(FlsIndex, NULL); + } +#endif + +#undef HeapFree +#undef GetProcessHeap + ::HeapFree (GetProcessHeap(),0,pTlsInfo); +#define HeapFree(hHeap, dwFlags, lpMem) Dont_Use_HeapFree(hHeap, dwFlags, lpMem) +#define GetProcessHeap() Dont_Use_GetProcessHeap() + +} + +#ifdef ENABLE_CONTRACTS_IMPL +// Fls callback to deallocate ClrDebugState when our FLS block goes away. +void FreeClrDebugState(LPVOID pTlsData); +#endif + +VOID STDMETHODCALLTYPE CExecutionEngine::TLS_AssociateCallback(DWORD slot, PTLS_CALLBACK_FUNCTION callback) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + CheckThreadState(slot); + + // They can toggle between a callback and no callback. But anything else looks like + // confusion on their part. + // + // (TlsIdx_ClrDebugState associates its callback from utilcode.lib - which can be replicated. But + // all the callbacks are equally good.) + _ASSERTE(slot == TlsIdx_ClrDebugState || Callbacks[slot] == 0 || Callbacks[slot] == callback || callback == 0); + if (slot == TlsIdx_ClrDebugState) + { +#ifdef ENABLE_CONTRACTS_IMPL + // ClrDebugState is shared among many dlls. Some dll, like perfcounter.dll, may be unloaded. + // We force the callback function to be in mscorwks.dll. + Callbacks[slot] = FreeClrDebugState; +#else + _ASSERTE (!"should not get here"); +#endif + } + else + Callbacks[slot] = callback; +} + +LPVOID* STDMETHODCALLTYPE CExecutionEngine::TLS_GetDataBlock() +{ + STATIC_CONTRACT_GC_NOTRIGGER; + STATIC_CONTRACT_THROWS; + STATIC_CONTRACT_MODE_ANY; + STATIC_CONTRACT_CANNOT_TAKE_LOCK; + STATIC_CONTRACT_SO_TOLERANT; + + return CExecutionEngine::GetTlsData(); +} + +LPVOID STDMETHODCALLTYPE CExecutionEngine::TLS_GetValue(DWORD slot) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EETlsGetValue(slot); +} + +BOOL STDMETHODCALLTYPE CExecutionEngine::TLS_CheckValue(DWORD slot, LPVOID * pValue) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EETlsCheckValue(slot, pValue); +} + +VOID STDMETHODCALLTYPE CExecutionEngine::TLS_SetValue(DWORD slot, LPVOID pData) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + EETlsSetValue(slot,pData); +} + + +VOID STDMETHODCALLTYPE CExecutionEngine::TLS_ThreadDetaching() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + CExecutionEngine::ThreadDetaching(NULL); +} + + +CRITSEC_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateLock(LPCSTR szTag, LPCSTR level, CrstFlags flags) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + CRITSEC_COOKIE cookie = NULL; + BEGIN_ENTRYPOINT_VOIDRET; + cookie = ::EECreateCriticalSection(*(CrstType*)&level, flags); + END_ENTRYPOINT_VOIDRET; + return cookie; +} + +void STDMETHODCALLTYPE CExecutionEngine::DestroyLock(CRITSEC_COOKIE cookie) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + ::EEDeleteCriticalSection(cookie); +} + +void STDMETHODCALLTYPE CExecutionEngine::AcquireLock(CRITSEC_COOKIE cookie) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + BEGIN_SO_INTOLERANT_CODE(GetThread()); + ::EEEnterCriticalSection(cookie); + END_SO_INTOLERANT_CODE; +} + +void STDMETHODCALLTYPE CExecutionEngine::ReleaseLock(CRITSEC_COOKIE cookie) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + BEGIN_SO_INTOLERANT_CODE(GetThread()); + ::EELeaveCriticalSection(cookie); + END_SO_INTOLERANT_CODE; +} + +// Locking routines supplied by the EE to the other DLLs of the CLR. In a _DEBUG +// build of the EE, we poison the Crst as a poor man's attempt to do some argument +// validation. +#define POISON_BITS 3 + +static inline EVENT_COOKIE CLREventToCookie(CLREvent * pEvent) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE((((uintptr_t) pEvent) & POISON_BITS) == 0); +#ifdef _DEBUG + pEvent = (CLREvent *) (((uintptr_t) pEvent) | POISON_BITS); +#endif + return (EVENT_COOKIE) pEvent; +} + +static inline CLREvent *CookieToCLREvent(EVENT_COOKIE cookie) +{ + LIMITED_METHOD_CONTRACT; + + _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS); +#ifdef _DEBUG + if (cookie) + { + cookie = (EVENT_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS); + } +#endif + return (CLREvent *) cookie; +} + + +EVENT_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateAutoEvent(BOOL bInitialState) +{ + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + EVENT_COOKIE event = NULL; + BEGIN_ENTRYPOINT_THROWS; + NewHolder<CLREvent> pEvent(new CLREvent()); + pEvent->CreateAutoEvent(bInitialState); + event = CLREventToCookie(pEvent); + pEvent.SuppressRelease(); + END_ENTRYPOINT_THROWS; + + return event; +} + +EVENT_COOKIE STDMETHODCALLTYPE CExecutionEngine::CreateManualEvent(BOOL bInitialState) +{ + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + ENTRY_POINT; + } + CONTRACTL_END; + + EVENT_COOKIE event = NULL; + BEGIN_ENTRYPOINT_THROWS; + + NewHolder<CLREvent> pEvent(new CLREvent()); + pEvent->CreateManualEvent(bInitialState); + event = CLREventToCookie(pEvent); + pEvent.SuppressRelease(); + + END_ENTRYPOINT_THROWS; + + return event; +} + +void STDMETHODCALLTYPE CExecutionEngine::CloseEvent(EVENT_COOKIE event) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + if (event) { + CLREvent *pEvent = CookieToCLREvent(event); + pEvent->CloseEvent(); + delete pEvent; + } +} + +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrSetEvent(EVENT_COOKIE event) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + if (event) { + CLREvent *pEvent = CookieToCLREvent(event); + return pEvent->Set(); + } + return FALSE; +} + +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrResetEvent(EVENT_COOKIE event) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + if (event) { + CLREvent *pEvent = CookieToCLREvent(event); + return pEvent->Reset(); + } + return FALSE; +} + +DWORD STDMETHODCALLTYPE CExecutionEngine::WaitForEvent(EVENT_COOKIE event, + DWORD dwMilliseconds, + BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + if (event) { + CLREvent *pEvent = CookieToCLREvent(event); + return pEvent->Wait(dwMilliseconds,bAlertable); + } + + if (GetThread() && bAlertable) + ThrowHR(E_INVALIDARG); + return WAIT_FAILED; +} + +DWORD STDMETHODCALLTYPE CExecutionEngine::WaitForSingleObject(HANDLE handle, + DWORD dwMilliseconds) +{ + STATIC_CONTRACT_WRAPPER; + STATIC_CONTRACT_SO_TOLERANT; + LeaveRuntimeHolder holder((size_t)(::WaitForSingleObject)); + return ::WaitForSingleObject(handle,dwMilliseconds); +} + +static inline SEMAPHORE_COOKIE CLRSemaphoreToCookie(CLRSemaphore * pSemaphore) +{ + LIMITED_METHOD_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + _ASSERTE((((uintptr_t) pSemaphore) & POISON_BITS) == 0); +#ifdef _DEBUG + pSemaphore = (CLRSemaphore *) (((uintptr_t) pSemaphore) | POISON_BITS); +#endif + return (SEMAPHORE_COOKIE) pSemaphore; +} + +static inline CLRSemaphore *CookieToCLRSemaphore(SEMAPHORE_COOKIE cookie) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS); +#ifdef _DEBUG + if (cookie) + { + cookie = (SEMAPHORE_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS); + } +#endif + return (CLRSemaphore *) cookie; +} + + +SEMAPHORE_COOKIE STDMETHODCALLTYPE CExecutionEngine::ClrCreateSemaphore(DWORD dwInitial, + DWORD dwMax) +{ + CONTRACTL + { + THROWS; + MODE_ANY; + GC_NOTRIGGER; + SO_TOLERANT; + } + CONTRACTL_END; + + NewHolder<CLRSemaphore> pSemaphore(new CLRSemaphore()); + pSemaphore->Create(dwInitial, dwMax); + SEMAPHORE_COOKIE ret = CLRSemaphoreToCookie(pSemaphore);; + pSemaphore.SuppressRelease(); + return ret; +} + +void STDMETHODCALLTYPE CExecutionEngine::ClrCloseSemaphore(SEMAPHORE_COOKIE semaphore) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + CLRSemaphore *pSemaphore = CookieToCLRSemaphore(semaphore); + pSemaphore->Close(); + delete pSemaphore; +} + +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrReleaseSemaphore(SEMAPHORE_COOKIE semaphore, + LONG lReleaseCount, + LONG *lpPreviousCount) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + CLRSemaphore *pSemaphore = CookieToCLRSemaphore(semaphore); + return pSemaphore->Release(lReleaseCount,lpPreviousCount); +} + +DWORD STDMETHODCALLTYPE CExecutionEngine::ClrWaitForSemaphore(SEMAPHORE_COOKIE semaphore, + DWORD dwMilliseconds, + BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + CLRSemaphore *pSemaphore = CookieToCLRSemaphore(semaphore); + return pSemaphore->Wait(dwMilliseconds,bAlertable); +} + +static inline MUTEX_COOKIE CLRMutexToCookie(CLRMutex * pMutex) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE((((uintptr_t) pMutex) & POISON_BITS) == 0); +#ifdef _DEBUG + pMutex = (CLRMutex *) (((uintptr_t) pMutex) | POISON_BITS); +#endif + return (MUTEX_COOKIE) pMutex; +} + +static inline CLRMutex *CookieToCLRMutex(MUTEX_COOKIE cookie) +{ + LIMITED_METHOD_CONTRACT; + _ASSERTE((((uintptr_t) cookie) & POISON_BITS) == POISON_BITS); +#ifdef _DEBUG + if (cookie) + { + cookie = (MUTEX_COOKIE) (((uintptr_t) cookie) & ~POISON_BITS); + } +#endif + return (CLRMutex *) cookie; +} + + +MUTEX_COOKIE STDMETHODCALLTYPE CExecutionEngine::ClrCreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, + BOOL bInitialOwner, + LPCTSTR lpName) +{ + CONTRACTL + { + NOTHROW; + MODE_ANY; + GC_NOTRIGGER; + SO_TOLERANT; // we catch any erros and free the allocated memory + } + CONTRACTL_END; + + + MUTEX_COOKIE mutex = 0; + CLRMutex *pMutex = new (nothrow) CLRMutex(); + if (pMutex) + { + EX_TRY + { + pMutex->Create(lpMutexAttributes, bInitialOwner, lpName); + mutex = CLRMutexToCookie(pMutex); + } + EX_CATCH + { + delete pMutex; + } + EX_END_CATCH(SwallowAllExceptions); + } + return mutex; +} + +void STDMETHODCALLTYPE CExecutionEngine::ClrCloseMutex(MUTEX_COOKIE mutex) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + CLRMutex *pMutex = CookieToCLRMutex(mutex); + pMutex->Close(); + delete pMutex; +} + +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrReleaseMutex(MUTEX_COOKIE mutex) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_TOLERANT; + MODE_ANY; + } + CONTRACTL_END; + CLRMutex *pMutex = CookieToCLRMutex(mutex); + return pMutex->Release(); +} + +DWORD STDMETHODCALLTYPE CExecutionEngine::ClrWaitForMutex(MUTEX_COOKIE mutex, + DWORD dwMilliseconds, + BOOL bAlertable) +{ + CONTRACTL + { + NOTHROW; + GC_NOTRIGGER; + SO_INTOLERANT; + MODE_ANY; + } + CONTRACTL_END; + CLRMutex *pMutex = CookieToCLRMutex(mutex); + return pMutex->Wait(dwMilliseconds,bAlertable); +} + +#undef ClrSleepEx +DWORD STDMETHODCALLTYPE CExecutionEngine::ClrSleepEx(DWORD dwMilliseconds, BOOL bAlertable) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + return EESleepEx(dwMilliseconds,bAlertable); +} +#define ClrSleepEx EESleepEx + +#undef ClrAllocationDisallowed +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrAllocationDisallowed() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEAllocationDisallowed(); +} +#define ClrAllocationDisallowed EEAllocationDisallowed + +#undef ClrVirtualAlloc +LPVOID STDMETHODCALLTYPE CExecutionEngine::ClrVirtualAlloc(LPVOID lpAddress, + SIZE_T dwSize, + DWORD flAllocationType, + DWORD flProtect) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEVirtualAlloc(lpAddress, dwSize, flAllocationType, flProtect); +} +#define ClrVirtualAlloc EEVirtualAlloc + +#undef ClrVirtualFree +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrVirtualFree(LPVOID lpAddress, + SIZE_T dwSize, + DWORD dwFreeType) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEVirtualFree(lpAddress, dwSize, dwFreeType); +} +#define ClrVirtualFree EEVirtualFree + +#undef ClrVirtualQuery +SIZE_T STDMETHODCALLTYPE CExecutionEngine::ClrVirtualQuery(LPCVOID lpAddress, + PMEMORY_BASIC_INFORMATION lpBuffer, + SIZE_T dwLength) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEVirtualQuery(lpAddress, lpBuffer, dwLength); +} +#define ClrVirtualQuery EEVirtualQuery + +#if defined(_DEBUG) && defined(FEATURE_CORECLR) && !defined(FEATURE_PAL) +static VolatilePtr<BYTE> s_pStartOfUEFSection = NULL; +static VolatilePtr<BYTE> s_pEndOfUEFSectionBoundary = NULL; +static Volatile<DWORD> s_dwProtection = 0; +#endif // _DEBUG && FEATURE_CORECLR && !FEATURE_PAL + +#undef ClrVirtualProtect + +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrVirtualProtect(LPVOID lpAddress, + SIZE_T dwSize, + DWORD flNewProtect, + PDWORD lpflOldProtect) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + // Get the UEF installation details - we will use these to validate + // that the calls to ClrVirtualProtect are not going to affect the UEF. + // + // The OS UEF invocation mechanism was updated. When a UEF is setup,the OS captures + // the following details about it: + // 1) Protection of the pages in which the UEF lives + // 2) The size of the region in which the UEF lives + // 3) The region's Allocation Base + // + // The OS verifies details surrounding the UEF before invocation. For security reasons + // the page protection cannot change between SetUnhandledExceptionFilter and invocation. + // + // Prior to this change, the UEF lived in a common section of code_Seg, along with + // JIT_PatchedCode. Thus, their pages have the same protection, they live + // in the same region (and thus, its size is the same). + // + // In EEStartupHelper, when we setup the UEF and then invoke InitJitHelpers1 and InitJitHelpers2, + // they perform some optimizations that result in the memory page protection being changed. When + // the UEF is to be invoked, the OS does the check on the UEF's cached details against the current + // memory pages. This check used to fail when on 64bit retail builds when JIT_PatchedCode was + // aligned after the UEF with a different memory page protection (post the optimizations by InitJitHelpers). + // Thus, the UEF was never invoked. + // + // To circumvent this, we put the UEF in its own section in the code segment so that any modifications + // to memory pages will not affect the UEF details that the OS cached. This is done in Excep.cpp + // using the "#pragma code_seg" directives. + // + // Below, we double check that: + // + // 1) the address being protected does not lie in the region of of the UEF. + // 2) the section after UEF is not having the same memory protection as UEF section. + // + // We assert if either of the two conditions above are true. + +#if defined(_DEBUG) && defined(FEATURE_CORECLR) && !defined(FEATURE_PAL) + // We do this check in debug/checked builds only + + // Do we have the UEF details? + if (s_pEndOfUEFSectionBoundary.Load() == NULL) + { + // Get reference to MSCORWKS image in memory... + PEDecoder pe(g_pMSCorEE); + + // Find the UEF section from the image + IMAGE_SECTION_HEADER* pUEFSection = pe.FindSection(CLR_UEF_SECTION_NAME); + _ASSERTE(pUEFSection != NULL); + if (pUEFSection) + { + // We got our section - get the start of the section + BYTE* pStartOfUEFSection = static_cast<BYTE*>(pe.GetBase())+pUEFSection->VirtualAddress; + s_pStartOfUEFSection = pStartOfUEFSection; + + // Now we need the protection attributes for the memory region in which the + // UEF section is... + MEMORY_BASIC_INFORMATION uefInfo; + if (ClrVirtualQuery(pStartOfUEFSection, &uefInfo, sizeof(uefInfo)) != 0) + { + // Calculate how many pages does the UEF section take to get to the start of the + // next section. We dont calculate this as + // + // pStartOfUEFSection + uefInfo.RegionSize + // + // because the section following UEF will also be included in the region size + // if it has the same protection as the UEF section. + DWORD dwUEFSectionPageCount = ((pUEFSection->Misc.VirtualSize + OS_PAGE_SIZE - 1)/OS_PAGE_SIZE); + + BYTE* pAddressOfFollowingSection = pStartOfUEFSection + (OS_PAGE_SIZE * dwUEFSectionPageCount); + + // Ensure that the section following us is having different memory protection + MEMORY_BASIC_INFORMATION nextSectionInfo; + _ASSERTE(ClrVirtualQuery(pAddressOfFollowingSection, &nextSectionInfo, sizeof(nextSectionInfo)) != 0); + _ASSERTE(nextSectionInfo.Protect != uefInfo.Protect); + + // save the memory protection details + s_dwProtection = uefInfo.Protect; + + // Get the end of the UEF section + BYTE* pEndOfUEFSectionBoundary = pAddressOfFollowingSection - 1; + + // Set the end of UEF section boundary + FastInterlockExchangePointer(s_pEndOfUEFSectionBoundary.GetPointer(), pEndOfUEFSectionBoundary); + } + else + { + _ASSERTE(!"Unable to get UEF Details!"); + } + } + } + + if (s_pEndOfUEFSectionBoundary.Load() != NULL) + { + // Is the protection being changed? + if (flNewProtect != s_dwProtection) + { + // Is the target address NOT affecting the UEF ? Possible cases: + // 1) Starts and ends before the UEF start + // 2) Starts after the UEF start + + void* pEndOfRangeAddr = static_cast<BYTE*>(lpAddress)+dwSize-1; + + _ASSERTE_MSG(((pEndOfRangeAddr < s_pStartOfUEFSection.Load()) || (lpAddress > s_pEndOfUEFSectionBoundary.Load())), + "Do not virtual protect the section in which UEF lives!"); + } + } +#endif // _DEBUG && FEATURE_CORECLR && !FEATURE_PAL + + return EEVirtualProtect(lpAddress, dwSize, flNewProtect, lpflOldProtect); +} +#define ClrVirtualProtect EEVirtualProtect + +#undef ClrGetProcessHeap +HANDLE STDMETHODCALLTYPE CExecutionEngine::ClrGetProcessHeap() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEGetProcessHeap(); +} +#define ClrGetProcessHeap EEGetProcessHeap + +#undef ClrGetProcessExecutableHeap +HANDLE STDMETHODCALLTYPE CExecutionEngine::ClrGetProcessExecutableHeap() +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEGetProcessExecutableHeap(); +} +#define ClrGetProcessExecutableHeap EEGetProcessExecutableHeap + + +#undef ClrHeapCreate +HANDLE STDMETHODCALLTYPE CExecutionEngine::ClrHeapCreate(DWORD flOptions, + SIZE_T dwInitialSize, + SIZE_T dwMaximumSize) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEHeapCreate(flOptions, dwInitialSize, dwMaximumSize); +} +#define ClrHeapCreate EEHeapCreate + +#undef ClrHeapDestroy +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrHeapDestroy(HANDLE hHeap) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEHeapDestroy(hHeap); +} +#define ClrHeapDestroy EEHeapDestroy + +#undef ClrHeapAlloc +LPVOID STDMETHODCALLTYPE CExecutionEngine::ClrHeapAlloc(HANDLE hHeap, + DWORD dwFlags, + SIZE_T dwBytes) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + // We need to guarentee a very small stack consumption in allocating. And we can't allow + // an SO to happen while calling into the host. This will force a hard SO which is OK because + // we shouldn't ever get this close inside the EE in SO-intolerant code, so this should + // only fail if we call directly in from outside the EE, such as the JIT. + MINIMAL_STACK_PROBE_CHECK_THREAD(GetThread()); + + return EEHeapAlloc(hHeap, dwFlags, dwBytes); +} +#define ClrHeapAlloc EEHeapAlloc + +#undef ClrHeapFree +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrHeapFree(HANDLE hHeap, + DWORD dwFlags, + LPVOID lpMem) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEHeapFree(hHeap, dwFlags, lpMem); +} +#define ClrHeapFree EEHeapFree + +#undef ClrHeapValidate +BOOL STDMETHODCALLTYPE CExecutionEngine::ClrHeapValidate(HANDLE hHeap, + DWORD dwFlags, + LPCVOID lpMem) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + return EEHeapValidate(hHeap, dwFlags, lpMem); +} +#define ClrHeapValidate EEHeapValidate + +//------------------------------------------------------------------------------ +// Helper function to get an exception object from outside the exception. In +// the CLR, it may be from the Thread object. Non-CLR users have no thread object, +// and it will do nothing. + +void CExecutionEngine::GetLastThrownObjectExceptionFromThread(void **ppvException) +{ + WRAPPER_NO_CONTRACT; + STATIC_CONTRACT_SO_TOLERANT; + + // Cast to our real type. + Exception **ppException = reinterpret_cast<Exception**>(ppvException); + + // Try to get a better message. + GetLastThrownObjectExceptionFromThread_Internal(ppException); + +} // HRESULT CExecutionEngine::GetLastThrownObjectExceptionFromThread() + + +#ifdef FEATURE_VERSIONING +LocaleID RuntimeGetFileSystemLocale() +{ + return PEImage::GetFileSystemLocale(); +}; +#endif + +#ifdef FEATURE_CORECLR +HRESULT CorHost2::DllGetActivationFactory(DWORD appDomainID, LPCWSTR wszTypeName, IActivationFactory ** factory) +{ +#ifdef FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION + // WinRT activation currently supported in default domain only + if (appDomainID != DefaultADID) + return HOST_E_INVALIDOPERATION; + + HRESULT hr = S_OK; + + Thread *pThread = GetThread(); + if (pThread == NULL) + { + pThread = SetupThreadNoThrow(&hr); + if (pThread == NULL) + { + return hr; + } + } + + if(SystemDomain::GetCurrentDomain()->GetId().m_dwId != DefaultADID) + { + return HOST_E_INVALIDOPERATION; + } + + return DllGetActivationFactoryImpl(NULL, wszTypeName, NULL, factory); +#else + return E_NOTIMPL; +#endif +} +#endif + + +#ifdef FEATURE_COMINTEROP_WINRT_MANAGED_ACTIVATION + +HRESULT STDMETHODCALLTYPE DllGetActivationFactoryImpl(LPCWSTR wszAssemblyName, + LPCWSTR wszTypeName, + LPCWSTR wszCodeBase, + IActivationFactory ** factory) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + + AppDomain* pDomain = SystemDomain::System()->DefaultDomain(); + _ASSERTE(pDomain); +#ifndef FEATURE_CORECLR // coreclr uses winrt binder which does not allow redirects + { + BaseDomain::LockHolder lh(pDomain); + if (!pDomain->HasLoadContextHostBinder()) + { + // don't allow redirects + SystemDomain::InitializeDefaultDomain(FALSE); + } + } +#endif + + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + GCX_COOP(); + + bool bIsPrimitive; + TypeHandle typeHandle = WinRTTypeNameConverter::GetManagedTypeFromWinRTTypeName(wszTypeName, &bIsPrimitive); + if (!bIsPrimitive && !typeHandle.IsNull() && !typeHandle.IsTypeDesc() && typeHandle.AsMethodTable()->IsExportedToWinRT()) + { + struct _gc { + OBJECTREF type; + } gc; + memset(&gc, 0, sizeof(gc)); + +#if defined(FEATURE_MULTICOREJIT) && defined(FEATURE_APPX_BINDER) + // For Appx, multicore JIT is only needed when root assembly does not have NI image + // When it has NI image, we can't generate profile, and do not need to playback profile + if (AppX::IsAppXProcess() && ! typeHandle.IsZapped()) + { + GCX_PREEMP(); + + pDomain->GetMulticoreJitManager().AutoStartProfileAppx(pDomain); + } +#endif + + IActivationFactory* activationFactory; + GCPROTECT_BEGIN(gc); + + gc.type = typeHandle.GetManagedClassObject(); + + MethodDescCallSite mdcs(METHOD__WINDOWSRUNTIMEMARSHAL__GET_ACTIVATION_FACTORY_FOR_TYPE); + ARG_SLOT args[1] = { + ObjToArgSlot(gc.type) + }; + activationFactory = (IActivationFactory*)mdcs.Call_RetLPVOID(args); + + *factory = activationFactory; + + GCPROTECT_END(); + } + else + { + hr = COR_E_TYPELOAD; + } + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#endif // !FEATURE_COMINTEROP_MANAGED_ACTIVATION + + +#ifdef FEATURE_COMINTEROP_WINRT_DESKTOP_HOST + +HRESULT STDMETHODCALLTYPE GetClassActivatorForApplicationImpl(HSTRING appPath, IWinRTClassActivator** ppActivator) +{ + CONTRACTL + { + DISABLED(NOTHROW); + GC_TRIGGERS; + MODE_ANY; + ENTRY_POINT; + } + CONTRACTL_END; + + HRESULT hr = S_OK; + + BEGIN_ENTRYPOINT_NOTHROW; + BEGIN_EXTERNAL_ENTRYPOINT(&hr); + { + if (GetAppDomain()->GetWinRtBinder()->SetLocalWinMDPath(appPath)) + { + GCX_COOP(); + + struct + { + STRINGREF appbase; + } gc; + ZeroMemory(&gc, sizeof(gc)); + GCPROTECT_BEGIN(gc); + + UINT32 appPathLength = 0; + PCWSTR wszAppPath = WindowsGetStringRawBuffer(appPath, &appPathLength); + + gc.appbase = StringObject::NewString(wszAppPath, appPathLength); + + MethodDescCallSite getClassActivator(METHOD__WINDOWSRUNTIMEMARSHAL__GET_CLASS_ACTIVATOR_FOR_APPLICATION); + + ARG_SLOT args[] = + { + ObjToArgSlot(gc.appbase) + }; + + IWinRTClassActivator* pActivator = reinterpret_cast<IWinRTClassActivator *>(getClassActivator.Call_RetLPVOID(args)); + *ppActivator = pActivator; + + GCPROTECT_END(); + } + else + { + hr = CO_E_BAD_PATH; + } + } + END_EXTERNAL_ENTRYPOINT; + END_ENTRYPOINT_NOTHROW; + + return hr; +} + +#endif // FEATURE_COMINTEROP_WINRT_DESKTOP_HOST + + +#endif // !DACCESS_COMPILE |