summaryrefslogtreecommitdiff
path: root/src/md/compiler/disp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/md/compiler/disp.cpp')
-rw-r--r--src/md/compiler/disp.cpp939
1 files changed, 939 insertions, 0 deletions
diff --git a/src/md/compiler/disp.cpp b/src/md/compiler/disp.cpp
new file mode 100644
index 0000000000..b091729744
--- /dev/null
+++ b/src/md/compiler/disp.cpp
@@ -0,0 +1,939 @@
+// 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.
+//*****************************************************************************
+// Disp.cpp
+//
+
+//
+// Implementation for the meta data dispenser code.
+//
+//*****************************************************************************
+#include "stdafx.h"
+#include "disp.h"
+#include "regmeta.h"
+#include "mdutil.h"
+#include <corerror.h>
+#include <mdlog.h>
+#include <mdcommon.h>
+#ifdef FEATURE_COMINTEROP_TLB_SUPPORT
+#include <imptlb.h>
+#endif
+
+#ifdef EnC_SUPPORTED
+#define ENC_DELTA_HACK
+#endif
+
+//*****************************************************************************
+// Ctor.
+//*****************************************************************************
+Disp::Disp() : m_cRef(0)
+{
+#if defined(LOGGING)
+ // InitializeLogging() calls scattered around the code.
+ // <TODO>@future: make this make some sense.</TODO>
+ InitializeLogging();
+#endif
+
+ m_OptionValue.m_DupCheck = MDDupDefault;
+ m_OptionValue.m_RefToDefCheck = MDRefToDefDefault;
+ m_OptionValue.m_NotifyRemap = MDNotifyDefault;
+ m_OptionValue.m_UpdateMode = MDUpdateFull;
+ m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault;
+ m_OptionValue.m_ThreadSafetyOptions = MDThreadSafetyDefault;
+ m_OptionValue.m_GenerateTCEAdapters = FALSE;
+ m_OptionValue.m_ImportOption = MDImportOptionDefault;
+ m_OptionValue.m_LinkerOption = MDAssembly;
+ m_OptionValue.m_RuntimeVersion = NULL;
+ m_OptionValue.m_MetadataVersion = MDDefaultVersion;
+ m_OptionValue.m_MergeOptions = MergeFlagsNone;
+ m_OptionValue.m_InitialSize = MDInitialSizeDefault;
+ m_OptionValue.m_LocalRefPreservation = MDPreserveLocalRefsNone;
+
+ // Allow Avalon to use the SecurityCriticalAttribute
+ if (CLRConfig::GetConfigValue(CLRConfig::UNSUPPORTED_FORCE_ASSEMREF_DUPCHECK))
+ {
+ m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor)(m_OptionValue.m_DupCheck|MDDupAssemblyRef);
+ }
+
+} // Disp::Disp
+
+Disp::~Disp()
+{
+ if (m_OptionValue.m_RuntimeVersion != NULL)
+ delete [] m_OptionValue.m_RuntimeVersion;
+} // Disp::~Disp
+
+//*****************************************************************************
+// Create a brand new scope. This is based on the CLSID that was used to get
+// the dispenser.
+//*****************************************************************************
+__checkReturn
+HRESULT
+Disp::DefineScope(
+ REFCLSID rclsid, // [in] What version to create.
+ DWORD dwCreateFlags, // [in] Flags on the create.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+#ifdef FEATURE_METADATA_EMIT
+ HRESULT hr = S_OK;
+ PathString szFileName(PathString::Literal, W("file:"));
+ PathString szFileNameSuffix;
+ DWORD len;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pMeta = 0;
+ OptionValue optionForNewScope = m_OptionValue;
+
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::DefineScope(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", rclsid, dwCreateFlags, riid, ppIUnk));
+
+ if (dwCreateFlags)
+ IfFailGo(E_INVALIDARG);
+
+ // Figure out what version of the metadata to emit
+ if (rclsid == CLSID_CLR_v1_MetaData)
+ {
+#ifdef FEATURE_METADATA_STANDALONE_WINRT
+ IfFailGo(E_NOTIMPL);
+#else
+ optionForNewScope.m_MetadataVersion = MDVersion1;
+#endif //!FEATURE_METADATA_STANDALONE_WINRT
+ }
+ else if (rclsid == CLSID_CLR_v2_MetaData)
+ {
+ optionForNewScope.m_MetadataVersion = MDVersion2;
+ }
+ else
+ {
+ // If it is a version we don't understand, then we cannot continue.
+ IfFailGo(CLDB_E_FILE_OLDVER);
+ }
+
+#ifdef ENC_DELTA_HACK
+// Testers need this flag for their tests.
+
+ EX_TRY{
+ len = WszGetEnvironmentVariable(W("COMP_ENC_OPENSCOPE"), szFileNameSuffix);
+ szFileName.Append(szFileNameSuffix);
+ }
+ EX_CATCH_HRESULT(hr);
+
+ if (len > 0)
+ {
+ // _ASSERTE(!"ENC override on DefineScope");
+// m_OptionValue.m_UpdateMode = MDUpdateENC;
+// m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault;
+// hr = OpenScope(szFileName, ofWrite, riid, ppIUnk);
+
+ IMetaDataEmit *pMetaEmit;
+ hr = OpenScope(szFileName, ofWrite, IID_IMetaDataEmit, (IUnknown **)&pMetaEmit);
+ DWORD cb;
+ CQuickBytes pbMetadata;
+ hr = pMetaEmit->GetSaveSize(cssAccurate,&cb);
+ _ASSERTE(SUCCEEDED(hr));
+
+ IfFailGo(pbMetadata.ReSizeNoThrow(cb));
+
+ hr = pMetaEmit->SaveToMemory(pbMetadata.Ptr(),cb);
+ _ASSERTE(SUCCEEDED(hr));
+// hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite|MDUpdateENC|MDUpdateDelta, riid, ppIUnk);
+
+
+ VARIANT encOption;
+ V_VT(&encOption) = VT_UI4;
+ V_UI4(&encOption) = MDUpdateENC;
+ SetOption(MetaDataSetENC, &encOption);
+ V_UI4(&encOption) = MDErrorOutOfOrderDefault;
+ SetOption(MetaDataErrorIfEmitOutOfOrder, &encOption);
+ hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite, riid, ppIUnk);
+ _ASSERTE(SUCCEEDED(hr));
+ BOOL fResult = SUCCEEDED(hr);
+ // print out a message so people know what's happening
+ printf("Defining scope for EnC using %S %s\n",
+ static_cast<LPCWSTR>(szFileNameSuffix), fResult ? "succeeded" : "failed");
+
+ goto ErrExit;
+ }
+#endif // ENC_DELTA_HACK
+
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+
+ IfFailGo(pMeta->SetOption(&optionForNewScope));
+
+ // Create the MiniMd-style scope.
+ IfFailGo(pMeta->CreateNewMD());
+
+ // Get the requested interface.
+ IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk));
+
+ // Add the new RegMeta to the cache.
+ IfFailGo(pMeta->AddToCache());
+
+ LOG((LOGMD, "{%08x} Created new emit scope\n", pMeta));
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta != NULL)
+ delete pMeta;
+ *ppIUnk = NULL;
+ }
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_EMIT
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_EMIT
+} // Disp::DefineScope
+
+
+//*****************************************************************************
+// Deliver scope to caller of OpenScope or OpenScopeOnMemory (this may
+// involve wrapping a WinMD adapter.)
+//*****************************************************************************
+static HRESULT DeliverScope(IMDCommon *pMDCommon, REFIID riid, DWORD dwOpenFlags, IUnknown **ppIUnk)
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+#if !defined(FEATURE_METADATA_STANDALONE_WINRT) && defined(FEATURE_COMINTEROP)
+ IfFailGo((dwOpenFlags & ofNoTransform) ? S_FALSE : CheckIfWinMDAdapterNeeded(pMDCommon));
+ if (hr == S_OK)
+ {
+ IfFailGo(CreateWinMDImport(pMDCommon, riid, (void**)ppIUnk));
+ }
+ else
+#endif
+ {
+ IfFailGo(pMDCommon->QueryInterface(riid, (void**)ppIUnk));
+ }
+
+ ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+//*****************************************************************************
+// Open an existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenScope( // Return code.
+ LPCWSTR szFileName, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope(%S, 0x%08x, 0x%08x, 0x%08x)\n", MDSTR(szFileName), dwOpenFlags, riid, ppIUnk));
+
+ IMDCommon *pMDCommon = NULL;
+
+ // Validate that there is some sort of file name.
+ if ((szFileName == NULL) || (szFileName[0] == 0) || (ppIUnk == NULL))
+ IfFailGo(E_INVALIDARG);
+
+ *ppIUnk = NULL;
+ IfFailGo(OpenRawScope(szFileName, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
+ IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
+ ErrExit:
+ if (pMDCommon)
+ pMDCommon->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+
+//*****************************************************************************
+// Open a raw view of existing scope.
+//*****************************************************************************
+__checkReturn
+HRESULT
+Disp::OpenRawScope(
+ LPCWSTR szFileName, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ _ASSERTE(szFileName != NULL);
+ _ASSERTE(ppIUnk != NULL);
+ RegMeta *pMeta = NULL;
+
+#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES
+ // Don't assert for code:ofTrustedImage (reserved) flag if the feature is supported
+ _ASSERTE(!IsOfReserved(dwOpenFlags & ~ofTrustedImage));
+#else
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+#endif //!FEATURE_METADATA_LOAD_TRUSTED_IMAGES
+
+ {
+ }
+
+ if (IsOfReadOnly(dwOpenFlags) && IsOfReadWrite(dwOpenFlags))
+ { // Invalid combination of flags - ofReadOnly & ofWrite
+ IfFailGo(E_INVALIDARG);
+ }
+ // If open-for-read, and there is already an open-for-read copy, return it.
+ if (IsOfReadOnly(dwOpenFlags))
+ {
+ RegMeta::FindCachedReadOnlyEntry(szFileName, dwOpenFlags, &pMeta);
+ if (pMeta != NULL)
+ {
+ // Return the requested interface.
+ hr = pMeta->QueryInterface(riid, (void **) ppIUnk);
+ if (FAILED(hr))
+ {
+ pMeta = NULL; // Don't delete cached RegMeta!
+ }
+ else
+ {
+ pMeta->Release(); // Give back refcount from QI
+ LOG((LOGMD, "{%08x} Found in cache '%S'\n", pMeta, MDSTR(szFileName)));
+ }
+
+ goto ErrExit;
+ }
+ }
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+
+ IfFailGo(pMeta->SetOption(&m_OptionValue));
+
+ // Always initialize the RegMeta's stgdb.
+ // <TODO>@FUTURE: there are some cleanup for the open code!!</TODO>
+ if (memcmp(szFileName, W("file:"), 10) == 0)
+ {
+ szFileName = &szFileName[5];
+ }
+
+ // Try to open the MiniMd-style scope.
+ IfFailGo(pMeta->OpenExistingMD(szFileName, 0 /* pbData */,0 /* cbData */, dwOpenFlags));
+
+ // Obtain the requested interface.
+ IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk) );
+
+ // Add the new RegMeta to the cache. If this is read-only, any future opens will
+ // find this entry. If, due to another thread concurrently opening the same file,
+ // there is already another copy in the cache, well, then there will be two
+ // read-only copies in the cache. This is considered to be somewhat of a corner
+ // case, and the only harm is temporary memory usage. All requests will be
+ // satisfied by one or the other (depending on search algorithm), and eventually,
+ // the "other" copy will be released.
+ IfFailGo(pMeta->AddToCache());
+
+ LOG((LOGMD, "{%08x} Successfully opened '%S'\n", pMeta, MDSTR(szFileName)));
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(pMeta);
+ }
+#endif // _DEBUG
+
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta != NULL)
+ delete pMeta;
+ *ppIUnk = NULL;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::OpenScope
+
+
+//*****************************************************************************
+// Open an existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenScopeOnMemory( // Return code.
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnMemory(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pData, cbData, dwOpenFlags, riid, ppIUnk));
+
+ IMDCommon *pMDCommon = NULL;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+ if (ppIUnk == NULL)
+ IfFailGo(E_INVALIDARG);
+ *ppIUnk = NULL;
+ IfFailGo(OpenRawScopeOnMemory(pData, cbData, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
+ IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
+ ErrExit:
+ if (pMDCommon)
+ pMDCommon->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+//*****************************************************************************
+// Open a raw voew of existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenRawScopeOnMemory( // Return code.
+ LPCVOID pData, // [in] Location of scope data.
+ ULONG cbData, // [in] Size of the data pointed to by pData.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pMeta = 0;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow) RegMeta();
+ IfNullGo(pMeta);
+ IfFailGo(pMeta->SetOption(&m_OptionValue));
+
+
+ PREFIX_ASSUME(pMeta != NULL);
+ // Always initialize the RegMeta's stgdb.
+ IfFailGo(pMeta->OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, dwOpenFlags));
+
+ LOG((LOGMD, "{%08x} Opened new scope on memory, pData: %08x cbData: %08x\n", pMeta, pData, cbData));
+
+ // Return the requested interface.
+ IfFailGo( pMeta->QueryInterface(riid, (void **) ppIUnk) );
+
+ // Add the new RegMeta to the cache.
+ IfFailGo(pMeta->AddToCache());
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(pMeta);
+ }
+#endif // _DEBUG
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta) delete pMeta;
+ *ppIUnk = 0;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::OpenScopeOnMemory
+
+#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+
+#include <metahost.h>
+// Pointer to the activated CLR interface provided by the shim.
+extern ICLRRuntimeInfo * g_pCLRRuntime;
+
+#endif
+
+//*****************************************************************************
+// Get the directory where the CLR system resides.
+//
+// Implements public API code:IMetaDataDispenserEx::GetCORSystemDirectory.
+//*****************************************************************************
+HRESULT
+Disp::GetCORSystemDirectory(
+ __out_ecount (cchBuffer) LPWSTR szBuffer, // [out] Buffer for the directory name
+ DWORD cchBuffer, // [in] Size of the buffer
+ DWORD *pcchBuffer) // [out] Number of characters returned
+{
+#if defined(FEATURE_METADATA_IN_VM) && !defined(FEATURE_CORECLR) && !defined(CROSSGEN_COMPILE)
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ // This implies a machine-wide CLR install root, which may not exist for some CLR
+ // skus using standalone metadata.
+ *pcchBuffer = cchBuffer;
+ hr = g_pCLRRuntime->GetRuntimeDirectory(szBuffer, pcchBuffer);
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+#else //!FEATURE_METADATA_IN_VM || FEATURE_CORECLR
+
+#ifdef FEATURE_CORECLR
+ UNREACHABLE_MSG("Calling IMetaDataDispenser::GetCORSystemDirectory! This code should not be "
+ "reachable or needs to be reimplemented for CoreCLR!");
+#endif //FEATURE_CORECLR
+
+ return E_NOTIMPL;
+#endif //!FEATURE_METADATA_IN_VM || FEATURE_CORECLR
+} // Disp::GetCORSystemDirectory
+
+HRESULT Disp::FindAssembly( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szGlobalBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
+ LPCWSTR szName, // [OUT] buffer - to hold name
+ ULONG cchName, // [IN] the name buffer's size
+ ULONG *pcName) // [OUT] the number of characters returend in the buffer
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+} // Disp::FindAssembly
+
+HRESULT Disp::FindAssemblyModule( // S_OK or error
+ LPCWSTR szAppBase, // [IN] optional - can be NULL
+ LPCWSTR szPrivateBin, // [IN] optional - can be NULL
+ LPCWSTR szGlobalBin, // [IN] optional - can be NULL
+ LPCWSTR szAssemblyName, // [IN] The assembly name or code base of the assembly
+ LPCWSTR szModuleName, // [IN] required - the name of the module
+ __out_ecount (cchName) LPWSTR szName, // [OUT] buffer - to hold name
+ ULONG cchName, // [IN] the name buffer's size
+ ULONG *pcName) // [OUT] the number of characters returend in the buffer
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+} // Disp::FindAssemblyModule
+
+//*****************************************************************************
+// Open a scope on an ITypeInfo
+//*****************************************************************************
+HRESULT Disp::OpenScopeOnITypeInfo( // Return code.
+ ITypeInfo *pITI, // [in] ITypeInfo to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ BEGIN_ENTRYPOINT_NOTHROW;
+ END_ENTRYPOINT_NOTHROW;
+
+ return E_NOTIMPL;
+} // Disp::OpenScopeOnITypeInfo
+
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+
+//*****************************************************************************
+// IMetaDataDispenserCustom
+//*****************************************************************************
+
+HRESULT Disp::OpenScopeOnCustomDataSource( // S_OK or error
+ IMDCustomDataSource *pCustomSource, // [in] The scope to open.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+ BEGIN_ENTRYPOINT_NOTHROW;
+ LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnCustomDataSource(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pCustomSource, dwOpenFlags, riid, ppIUnk));
+
+ IMDCommon *pMDCommon = NULL;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+ if (ppIUnk == NULL)
+ IfFailGo(E_INVALIDARG);
+ *ppIUnk = NULL;
+ IfFailGo(OpenRawScopeOnCustomDataSource(pCustomSource, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
+ IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
+ErrExit:
+ if (pMDCommon)
+ pMDCommon->Release();
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+}
+
+
+//*****************************************************************************
+// Open a raw view of existing scope.
+//*****************************************************************************
+HRESULT Disp::OpenRawScopeOnCustomDataSource( // Return code.
+ IMDCustomDataSource* pDataSource, // [in] scope data.
+ DWORD dwOpenFlags, // [in] Open mode flags.
+ REFIID riid, // [in] The interface desired.
+ IUnknown **ppIUnk) // [out] Return interface on success.
+{
+ HRESULT hr;
+
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ RegMeta *pMeta = 0;
+
+ _ASSERTE(!IsOfReserved(dwOpenFlags));
+
+ // Create a new coclass for this guy.
+ pMeta = new (nothrow)RegMeta();
+ IfNullGo(pMeta);
+ IfFailGo(pMeta->SetOption(&m_OptionValue));
+
+
+ PREFIX_ASSUME(pMeta != NULL);
+ // Always initialize the RegMeta's stgdb.
+ // TODO
+ IfFailGo(pMeta->OpenExistingMD(pDataSource, dwOpenFlags));
+
+ LOG((LOGMD, "{%08x} Opened new scope on custom data source, pDataSource: %08x\n", pMeta, pDataSource));
+
+ // Return the requested interface.
+ IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk));
+
+ // Add the new RegMeta to the cache.
+ IfFailGo(pMeta->AddToCache());
+
+#if defined(_DEBUG)
+ if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
+ {
+ int DumpMD_impl(RegMeta *pMD);
+ DumpMD_impl(pMeta);
+ }
+#endif // _DEBUG
+
+ErrExit:
+ if (FAILED(hr))
+ {
+ if (pMeta) delete pMeta;
+ *ppIUnk = 0;
+ }
+
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::OpenRawScopeOnCustomDataSource
+
+#endif
+
+//*****************************************************************************
+// IUnknown
+//*****************************************************************************
+
+ULONG Disp::AddRef()
+{
+ return InterlockedIncrement(&m_cRef);
+} // Disp::AddRef
+
+ULONG Disp::Release()
+{
+ ULONG cRef = InterlockedDecrement(&m_cRef);
+ if (cRef == 0)
+ delete this;
+ return cRef;
+} // Disp::Release
+
+HRESULT Disp::QueryInterface(REFIID riid, void **ppUnk)
+{
+ *ppUnk = 0;
+
+ if (riid == IID_IUnknown)
+ *ppUnk = (IUnknown *) (IMetaDataDispenser *) this;
+ else if (riid == IID_IMetaDataDispenser)
+ *ppUnk = (IMetaDataDispenser *) this;
+ else if (riid == IID_IMetaDataDispenserEx)
+ *ppUnk = (IMetaDataDispenserEx *) this;
+#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
+ else if (riid == IID_IMetaDataDispenserCustom)
+ *ppUnk = static_cast<IMetaDataDispenserCustom*>(this);
+#endif
+ else
+ return E_NOINTERFACE;
+ AddRef();
+ return S_OK;
+} // Disp::QueryInterface
+
+
+//*****************************************************************************
+// Called by the class factory template to create a new instance of this object.
+//*****************************************************************************
+HRESULT Disp::CreateObject(REFIID riid, void **ppUnk)
+{
+ HRESULT hr;
+ Disp *pDisp = new (nothrow) Disp();
+
+ if (pDisp == 0)
+ return (E_OUTOFMEMORY);
+
+ hr = pDisp->QueryInterface(riid, ppUnk);
+ if (FAILED(hr))
+ delete pDisp;
+ return hr;
+} // Disp::CreateObject
+
+//*****************************************************************************
+// This routine provides the user a way to set certain properties on the
+// Dispenser.
+//
+// Implements public API code:IMetaDataDispenserEx::SetOption.
+//*****************************************************************************
+__checkReturn
+HRESULT
+Disp::SetOption(
+ REFGUID optionid, // [in] GUID for the option to be set.
+ const VARIANT *pvalue) // [in] Value to which the option is to be set.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::SetOption(0x%08x, 0x%08x)\n", optionid, pvalue));
+
+ if (optionid == MetaDataCheckDuplicatesFor)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataRefToDefCheck)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_RefToDefCheck = (CorRefToDefCheck) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataErrorIfEmitOutOfOrder)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_ErrorIfEmitOutOfOrder = (CorErrorIfEmitOutOfOrder) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataThreadSafetyOptions)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_ThreadSafetyOptions = (CorThreadSafetyOptions) V_UI4(pvalue);
+ }
+// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat.
+#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER)
+ else if (optionid == MetaDataNotificationForTokenMovement)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it back in
+ // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it here for backward-compat.
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_NotifyRemap = (CorNotificationForTokenMovement)V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataSetENC)
+ { // EnC update mode (also aliased as code:MetaDataSetUpdate)
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_UpdateMode = V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataImportOption)
+ { // Allows enumeration of EnC deleted items by Import API
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_ImportOption = (CorImportOptions) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataLinkerOptions)
+ { // Used only by code:RegMeta::UnmarkAll (code:IMetaDataFilter::UnmarkAll)
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_LinkerOption = (CorLinkerOptions) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataMergerOptions)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_MergeOptions = (MergeFlags) V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataGenerateTCEAdapters)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it back in
+ // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it for backward-compat.
+ if (V_VT(pvalue) != VT_BOOL)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_GenerateTCEAdapters = V_BOOL(pvalue);
+ }
+ else if (optionid == MetaDataTypeLibImportNamespace)
+ { // Note: This is not used in CLR sources anymore, keep it here for backward-compat
+ if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL)
+ {
+ _ASSERTE(!"Invalid Variant Type value for namespace.");
+ IfFailGo(E_INVALIDARG);
+ }
+ }
+#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER
+ else if (optionid == MetaDataRuntimeVersion)
+ {
+ if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL)
+ {
+ _ASSERTE(!"Invalid Variant Type value for version.");
+ IfFailGo(E_INVALIDARG);
+ }
+ if (m_OptionValue.m_RuntimeVersion)
+ delete [] m_OptionValue.m_RuntimeVersion;
+
+ if ((V_VT(pvalue) == VT_EMPTY) || (V_VT(pvalue) == VT_NULL) || (*V_BSTR(pvalue) == 0))
+ {
+ m_OptionValue.m_RuntimeVersion = NULL;
+ }
+ else
+ {
+ INT32 len = WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, NULL, 0, NULL, NULL);
+ m_OptionValue.m_RuntimeVersion = new (nothrow) char[len];
+ if (m_OptionValue.m_RuntimeVersion == NULL)
+ IfFailGo(E_INVALIDARG);
+ WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, m_OptionValue.m_RuntimeVersion, len, NULL, NULL);
+ }
+ }
+ else if (optionid == MetaDataInitialSize)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+ m_OptionValue.m_InitialSize = V_UI4(pvalue);
+ }
+ else if (optionid == MetaDataPreserveLocalRefs)
+ {
+ if (V_VT(pvalue) != VT_UI4)
+ {
+ _ASSERTE(!"Invalid Variant Type value!");
+ IfFailGo(E_INVALIDARG);
+ }
+
+ m_OptionValue.m_LocalRefPreservation = (CorLocalRefPreservation) V_UI4(pvalue);
+ }
+ else
+ {
+ _ASSERTE(!"Invalid GUID");
+ IfFailGo(E_INVALIDARG);
+ }
+
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+ return hr;
+} // Disp::SetOption
+
+//*****************************************************************************
+// This routine provides the user a way to set certain properties on the
+// Dispenser.
+//
+// Implements public API code:IMetaDataDispenserEx::GetOption.
+//*****************************************************************************
+HRESULT Disp::GetOption( // Return code.
+ REFGUID optionid, // [in] GUID for the option to be set.
+ VARIANT *pvalue) // [out] Value to which the option is currently set.
+{
+ HRESULT hr = S_OK;
+ BEGIN_ENTRYPOINT_NOTHROW;
+
+ LOG((LF_METADATA, LL_INFO10, "Disp::GetOption(0x%08x, 0x%08x)\n", optionid, pvalue));
+
+ _ASSERTE(pvalue);
+ if (optionid == MetaDataCheckDuplicatesFor)
+ {
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_DupCheck;
+ }
+ else if (optionid == MetaDataRefToDefCheck)
+ {
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_RefToDefCheck;
+ }
+ else if (optionid == MetaDataErrorIfEmitOutOfOrder)
+ {
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_ErrorIfEmitOutOfOrder;
+ }
+// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat.
+#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER)
+ else if (optionid == MetaDataNotificationForTokenMovement)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it here,
+ // so we keep it for backward-compat.
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_NotifyRemap;
+ }
+ else if (optionid == MetaDataSetENC)
+ { // EnC update mode (also aliased as code:MetaDataSetUpdate)
+ V_VT(pvalue) = VT_UI4;
+ V_UI4(pvalue) = m_OptionValue.m_UpdateMode;
+ }
+ else if (optionid == MetaDataLinkerOptions)
+ { // Allows enumeration of EnC deleted items by Import API
+ V_VT(pvalue) = VT_BOOL;
+ V_UI4(pvalue) = m_OptionValue.m_LinkerOption;
+ }
+ else if (optionid == MetaDataGenerateTCEAdapters)
+ { // Note: This is not used in CLR sources anymore, but we store the value and return it here,
+ // so we keep it for backward-compat.
+ V_VT(pvalue) = VT_BOOL;
+ V_BOOL(pvalue) = m_OptionValue.m_GenerateTCEAdapters;
+ }
+#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER
+ else
+ {
+ _ASSERTE(!"Invalid GUID");
+ IfFailGo(E_INVALIDARG);
+ }
+ErrExit:
+ END_ENTRYPOINT_NOTHROW;
+
+ return hr;
+} // Disp::GetOption
+
+#if defined(FEATURE_METADATA_IN_VM) || defined(FEATURE_METADATA_STANDALONE_WINRT)
+
+//---------------------------------------------------------------------------------------
+//
+// Process detach destruction.
+// Called from DllMain of clr.dll/RoMetadata.dll/MidlrtMd.dll.
+//
+void DeleteMetaData()
+{
+ LOADEDMODULES::DeleteStatics();
+}
+
+#endif //FEATURE_METADATA_IN_VM || FEATURE_METADATA_STANDALONE_WINRT
+
+//
+// This is the entrypoint for usages of MetaData that need to start with the dispenser (e.g.
+// mscordbi.dll and profiling API).
+//
+// Notes:
+// This could be merged with the class factory support.
+HRESULT InternalCreateMetaDataDispenser(REFIID riid, void ** pMetaDataDispenserOut)
+{
+ _ASSERTE(pMetaDataDispenserOut != NULL);
+ return Disp::CreateObject(riid, pMetaDataDispenserOut);
+}