// 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 #include #include #ifdef EnC_SUPPORTED #define ENC_DELTA_HACK #endif //***************************************************************************** // Ctor. //***************************************************************************** Disp::Disp() : m_cRef(0) { #if defined(LOGGING) // InitializeLogging() calls scattered around the code. // @future: make this make some sense. 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; } // 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) { optionForNewScope.m_MetadataVersion = MDVersion1; } 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(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_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. // @FUTURE: there are some cleanup for the open code!! 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(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 //***************************************************************************** // 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 { UNREACHABLE_MSG("Calling IMetaDataDispenser::GetCORSystemDirectory! This code should not be " "reachable or needs to be reimplemented for CoreCLR!"); return E_NOTIMPL; } // 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(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) //--------------------------------------------------------------------------------------- // // Process detach destruction. // Called from DllMain of clr.dll/RoMetadata.dll/MidlrtMd.dll. // void DeleteMetaData() { LOADEDMODULES::DeleteStatics(); } #endif //FEATURE_METADATA_IN_VM // // 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); }