1// Licensed to the .NET Foundation under one or more agreements.
2// The .NET Foundation licenses this file to you under the MIT license.
3// See the LICENSE file in the project root for more information.
4//*****************************************************************************
5// Disp.cpp
6//
7
8//
9// Implementation for the meta data dispenser code.
10//
11//*****************************************************************************
12#include "stdafx.h"
13#include "disp.h"
14#include "regmeta.h"
15#include "mdutil.h"
16#include <corerror.h>
17#include <mdlog.h>
18#include <mdcommon.h>
19
20#ifdef EnC_SUPPORTED
21#define ENC_DELTA_HACK
22#endif
23
24//*****************************************************************************
25// Ctor.
26//*****************************************************************************
27Disp::Disp() : m_cRef(0)
28{
29#if defined(LOGGING)
30 // InitializeLogging() calls scattered around the code.
31 // <TODO>@future: make this make some sense.</TODO>
32 InitializeLogging();
33#endif
34
35 m_OptionValue.m_DupCheck = MDDupDefault;
36 m_OptionValue.m_RefToDefCheck = MDRefToDefDefault;
37 m_OptionValue.m_NotifyRemap = MDNotifyDefault;
38 m_OptionValue.m_UpdateMode = MDUpdateFull;
39 m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault;
40 m_OptionValue.m_ThreadSafetyOptions = MDThreadSafetyDefault;
41 m_OptionValue.m_GenerateTCEAdapters = FALSE;
42 m_OptionValue.m_ImportOption = MDImportOptionDefault;
43 m_OptionValue.m_LinkerOption = MDAssembly;
44 m_OptionValue.m_RuntimeVersion = NULL;
45 m_OptionValue.m_MetadataVersion = MDDefaultVersion;
46 m_OptionValue.m_MergeOptions = MergeFlagsNone;
47 m_OptionValue.m_InitialSize = MDInitialSizeDefault;
48 m_OptionValue.m_LocalRefPreservation = MDPreserveLocalRefsNone;
49} // Disp::Disp
50
51Disp::~Disp()
52{
53 if (m_OptionValue.m_RuntimeVersion != NULL)
54 delete [] m_OptionValue.m_RuntimeVersion;
55} // Disp::~Disp
56
57//*****************************************************************************
58// Create a brand new scope. This is based on the CLSID that was used to get
59// the dispenser.
60//*****************************************************************************
61__checkReturn
62HRESULT
63Disp::DefineScope(
64 REFCLSID rclsid, // [in] What version to create.
65 DWORD dwCreateFlags, // [in] Flags on the create.
66 REFIID riid, // [in] The interface desired.
67 IUnknown **ppIUnk) // [out] Return interface on success.
68{
69#ifdef FEATURE_METADATA_EMIT
70 HRESULT hr = S_OK;
71 PathString szFileName(PathString::Literal, W("file:"));
72 PathString szFileNameSuffix;
73 BEGIN_ENTRYPOINT_NOTHROW;
74
75 RegMeta *pMeta = 0;
76 OptionValue optionForNewScope = m_OptionValue;
77
78
79 LOG((LF_METADATA, LL_INFO10, "Disp::DefineScope(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", rclsid, dwCreateFlags, riid, ppIUnk));
80
81 if (dwCreateFlags)
82 IfFailGo(E_INVALIDARG);
83
84 // Figure out what version of the metadata to emit
85 if (rclsid == CLSID_CLR_v1_MetaData)
86 {
87 optionForNewScope.m_MetadataVersion = MDVersion1;
88 }
89 else if (rclsid == CLSID_CLR_v2_MetaData)
90 {
91 optionForNewScope.m_MetadataVersion = MDVersion2;
92 }
93 else
94 {
95 // If it is a version we don't understand, then we cannot continue.
96 IfFailGo(CLDB_E_FILE_OLDVER);
97 }
98
99#ifdef ENC_DELTA_HACK
100// Testers need this flag for their tests.
101
102 DWORD len;
103 EX_TRY{
104 len = WszGetEnvironmentVariable(W("COMP_ENC_OPENSCOPE"), szFileNameSuffix);
105 szFileName.Append(szFileNameSuffix);
106 }
107 EX_CATCH_HRESULT(hr);
108
109 if (len > 0)
110 {
111 // _ASSERTE(!"ENC override on DefineScope");
112// m_OptionValue.m_UpdateMode = MDUpdateENC;
113// m_OptionValue.m_ErrorIfEmitOutOfOrder = MDErrorOutOfOrderDefault;
114// hr = OpenScope(szFileName, ofWrite, riid, ppIUnk);
115
116 IMetaDataEmit *pMetaEmit;
117 hr = OpenScope(szFileName, ofWrite, IID_IMetaDataEmit, (IUnknown **)&pMetaEmit);
118 DWORD cb;
119 CQuickBytes pbMetadata;
120 hr = pMetaEmit->GetSaveSize(cssAccurate,&cb);
121 _ASSERTE(SUCCEEDED(hr));
122
123 IfFailGo(pbMetadata.ReSizeNoThrow(cb));
124
125 hr = pMetaEmit->SaveToMemory(pbMetadata.Ptr(),cb);
126 _ASSERTE(SUCCEEDED(hr));
127// hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite|MDUpdateENC|MDUpdateDelta, riid, ppIUnk);
128
129
130 VARIANT encOption;
131 V_VT(&encOption) = VT_UI4;
132 V_UI4(&encOption) = MDUpdateENC;
133 SetOption(MetaDataSetENC, &encOption);
134 V_UI4(&encOption) = MDErrorOutOfOrderDefault;
135 SetOption(MetaDataErrorIfEmitOutOfOrder, &encOption);
136 hr = OpenScopeOnMemory( pbMetadata.Ptr(), cb, ofWrite, riid, ppIUnk);
137 _ASSERTE(SUCCEEDED(hr));
138 BOOL fResult = SUCCEEDED(hr);
139 // print out a message so people know what's happening
140 printf("Defining scope for EnC using %S %s\n",
141 static_cast<LPCWSTR>(szFileNameSuffix), fResult ? "succeeded" : "failed");
142
143 goto ErrExit;
144 }
145#endif // ENC_DELTA_HACK
146
147 // Create a new coclass for this guy.
148 pMeta = new (nothrow) RegMeta();
149 IfNullGo(pMeta);
150
151 IfFailGo(pMeta->SetOption(&optionForNewScope));
152
153 // Create the MiniMd-style scope.
154 IfFailGo(pMeta->CreateNewMD());
155
156 // Get the requested interface.
157 IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk));
158
159 // Add the new RegMeta to the cache.
160 IfFailGo(pMeta->AddToCache());
161
162 LOG((LOGMD, "{%08x} Created new emit scope\n", pMeta));
163
164ErrExit:
165 if (FAILED(hr))
166 {
167 if (pMeta != NULL)
168 delete pMeta;
169 *ppIUnk = NULL;
170 }
171 END_ENTRYPOINT_NOTHROW;
172
173 return hr;
174#else //!FEATURE_METADATA_EMIT
175 return E_NOTIMPL;
176#endif //!FEATURE_METADATA_EMIT
177} // Disp::DefineScope
178
179
180//*****************************************************************************
181// Deliver scope to caller of OpenScope or OpenScopeOnMemory (this may
182// involve wrapping a WinMD adapter.)
183//*****************************************************************************
184static HRESULT DeliverScope(IMDCommon *pMDCommon, REFIID riid, DWORD dwOpenFlags, IUnknown **ppIUnk)
185{
186 HRESULT hr;
187 BEGIN_ENTRYPOINT_NOTHROW;
188
189#if defined(FEATURE_COMINTEROP)
190 IfFailGo((dwOpenFlags & ofNoTransform) ? S_FALSE : CheckIfWinMDAdapterNeeded(pMDCommon));
191 if (hr == S_OK)
192 {
193 IfFailGo(CreateWinMDImport(pMDCommon, riid, (void**)ppIUnk));
194 }
195 else
196#endif
197 {
198 IfFailGo(pMDCommon->QueryInterface(riid, (void**)ppIUnk));
199 }
200
201 ErrExit:
202 END_ENTRYPOINT_NOTHROW;
203 return hr;
204}
205
206//*****************************************************************************
207// Open an existing scope.
208//*****************************************************************************
209HRESULT Disp::OpenScope( // Return code.
210 LPCWSTR szFileName, // [in] The scope to open.
211 DWORD dwOpenFlags, // [in] Open mode flags.
212 REFIID riid, // [in] The interface desired.
213 IUnknown **ppIUnk) // [out] Return interface on success.
214{
215 HRESULT hr;
216 BEGIN_ENTRYPOINT_NOTHROW;
217 LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope(%S, 0x%08x, 0x%08x, 0x%08x)\n", MDSTR(szFileName), dwOpenFlags, riid, ppIUnk));
218
219 IMDCommon *pMDCommon = NULL;
220
221 // Validate that there is some sort of file name.
222 if ((szFileName == NULL) || (szFileName[0] == 0) || (ppIUnk == NULL))
223 IfFailGo(E_INVALIDARG);
224
225 *ppIUnk = NULL;
226 IfFailGo(OpenRawScope(szFileName, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
227 IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
228 ErrExit:
229 if (pMDCommon)
230 pMDCommon->Release();
231 END_ENTRYPOINT_NOTHROW;
232 return hr;
233}
234
235
236//*****************************************************************************
237// Open a raw view of existing scope.
238//*****************************************************************************
239__checkReturn
240HRESULT
241Disp::OpenRawScope(
242 LPCWSTR szFileName, // [in] The scope to open.
243 DWORD dwOpenFlags, // [in] Open mode flags.
244 REFIID riid, // [in] The interface desired.
245 IUnknown **ppIUnk) // [out] Return interface on success.
246{
247 HRESULT hr;
248
249 BEGIN_ENTRYPOINT_NOTHROW;
250
251 _ASSERTE(szFileName != NULL);
252 _ASSERTE(ppIUnk != NULL);
253 RegMeta *pMeta = NULL;
254
255#ifdef FEATURE_METADATA_LOAD_TRUSTED_IMAGES
256 // Don't assert for code:ofTrustedImage (reserved) flag if the feature is supported
257 _ASSERTE(!IsOfReserved(dwOpenFlags & ~ofTrustedImage));
258#else
259 _ASSERTE(!IsOfReserved(dwOpenFlags));
260#endif //!FEATURE_METADATA_LOAD_TRUSTED_IMAGES
261
262 {
263 }
264
265 if (IsOfReadOnly(dwOpenFlags) && IsOfReadWrite(dwOpenFlags))
266 { // Invalid combination of flags - ofReadOnly & ofWrite
267 IfFailGo(E_INVALIDARG);
268 }
269 // If open-for-read, and there is already an open-for-read copy, return it.
270 if (IsOfReadOnly(dwOpenFlags))
271 {
272 RegMeta::FindCachedReadOnlyEntry(szFileName, dwOpenFlags, &pMeta);
273 if (pMeta != NULL)
274 {
275 // Return the requested interface.
276 hr = pMeta->QueryInterface(riid, (void **) ppIUnk);
277 if (FAILED(hr))
278 {
279 pMeta = NULL; // Don't delete cached RegMeta!
280 }
281 else
282 {
283 pMeta->Release(); // Give back refcount from QI
284 LOG((LOGMD, "{%08x} Found in cache '%S'\n", pMeta, MDSTR(szFileName)));
285 }
286
287 goto ErrExit;
288 }
289 }
290 // Create a new coclass for this guy.
291 pMeta = new (nothrow) RegMeta();
292 IfNullGo(pMeta);
293
294 IfFailGo(pMeta->SetOption(&m_OptionValue));
295
296 // Always initialize the RegMeta's stgdb.
297 // <TODO>@FUTURE: there are some cleanup for the open code!!</TODO>
298 if (memcmp(szFileName, W("file:"), 10) == 0)
299 {
300 szFileName = &szFileName[5];
301 }
302
303 // Try to open the MiniMd-style scope.
304 IfFailGo(pMeta->OpenExistingMD(szFileName, 0 /* pbData */,0 /* cbData */, dwOpenFlags));
305
306 // Obtain the requested interface.
307 IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk) );
308
309 // Add the new RegMeta to the cache. If this is read-only, any future opens will
310 // find this entry. If, due to another thread concurrently opening the same file,
311 // there is already another copy in the cache, well, then there will be two
312 // read-only copies in the cache. This is considered to be somewhat of a corner
313 // case, and the only harm is temporary memory usage. All requests will be
314 // satisfied by one or the other (depending on search algorithm), and eventually,
315 // the "other" copy will be released.
316 IfFailGo(pMeta->AddToCache());
317
318 LOG((LOGMD, "{%08x} Successfully opened '%S'\n", pMeta, MDSTR(szFileName)));
319
320#if defined(_DEBUG)
321 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
322 {
323 int DumpMD_impl(RegMeta *pMD);
324 DumpMD_impl(pMeta);
325 }
326#endif // _DEBUG
327
328
329ErrExit:
330 if (FAILED(hr))
331 {
332 if (pMeta != NULL)
333 delete pMeta;
334 *ppIUnk = NULL;
335 }
336
337 END_ENTRYPOINT_NOTHROW;
338
339 return hr;
340} // Disp::OpenScope
341
342
343//*****************************************************************************
344// Open an existing scope.
345//*****************************************************************************
346HRESULT Disp::OpenScopeOnMemory( // Return code.
347 LPCVOID pData, // [in] Location of scope data.
348 ULONG cbData, // [in] Size of the data pointed to by pData.
349 DWORD dwOpenFlags, // [in] Open mode flags.
350 REFIID riid, // [in] The interface desired.
351 IUnknown **ppIUnk) // [out] Return interface on success.
352{
353 HRESULT hr;
354 BEGIN_ENTRYPOINT_NOTHROW;
355 LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnMemory(0x%08x, 0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pData, cbData, dwOpenFlags, riid, ppIUnk));
356
357 IMDCommon *pMDCommon = NULL;
358
359 _ASSERTE(!IsOfReserved(dwOpenFlags));
360 if (ppIUnk == NULL)
361 IfFailGo(E_INVALIDARG);
362 *ppIUnk = NULL;
363 IfFailGo(OpenRawScopeOnMemory(pData, cbData, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
364 IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
365 ErrExit:
366 if (pMDCommon)
367 pMDCommon->Release();
368 END_ENTRYPOINT_NOTHROW;
369 return hr;
370}
371
372//*****************************************************************************
373// Open a raw voew of existing scope.
374//*****************************************************************************
375HRESULT Disp::OpenRawScopeOnMemory( // Return code.
376 LPCVOID pData, // [in] Location of scope data.
377 ULONG cbData, // [in] Size of the data pointed to by pData.
378 DWORD dwOpenFlags, // [in] Open mode flags.
379 REFIID riid, // [in] The interface desired.
380 IUnknown **ppIUnk) // [out] Return interface on success.
381{
382 HRESULT hr;
383
384 BEGIN_ENTRYPOINT_NOTHROW;
385
386 RegMeta *pMeta = 0;
387
388 _ASSERTE(!IsOfReserved(dwOpenFlags));
389
390 // Create a new coclass for this guy.
391 pMeta = new (nothrow) RegMeta();
392 IfNullGo(pMeta);
393 IfFailGo(pMeta->SetOption(&m_OptionValue));
394
395
396 PREFIX_ASSUME(pMeta != NULL);
397 // Always initialize the RegMeta's stgdb.
398 IfFailGo(pMeta->OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, dwOpenFlags));
399
400 LOG((LOGMD, "{%08x} Opened new scope on memory, pData: %08x cbData: %08x\n", pMeta, pData, cbData));
401
402 // Return the requested interface.
403 IfFailGo( pMeta->QueryInterface(riid, (void **) ppIUnk) );
404
405 // Add the new RegMeta to the cache.
406 IfFailGo(pMeta->AddToCache());
407
408#if defined(_DEBUG)
409 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
410 {
411 int DumpMD_impl(RegMeta *pMD);
412 DumpMD_impl(pMeta);
413 }
414#endif // _DEBUG
415
416ErrExit:
417 if (FAILED(hr))
418 {
419 if (pMeta) delete pMeta;
420 *ppIUnk = 0;
421 }
422
423 END_ENTRYPOINT_NOTHROW;
424
425 return hr;
426} // Disp::OpenScopeOnMemory
427
428
429//*****************************************************************************
430// Get the directory where the CLR system resides.
431//
432// Implements public API code:IMetaDataDispenserEx::GetCORSystemDirectory.
433//*****************************************************************************
434HRESULT
435Disp::GetCORSystemDirectory(
436 __out_ecount (cchBuffer) LPWSTR szBuffer, // [out] Buffer for the directory name
437 DWORD cchBuffer, // [in] Size of the buffer
438 DWORD *pcchBuffer) // [out] Number of characters returned
439{
440
441 UNREACHABLE_MSG("Calling IMetaDataDispenser::GetCORSystemDirectory! This code should not be "
442 "reachable or needs to be reimplemented for CoreCLR!");
443
444 return E_NOTIMPL;
445} // Disp::GetCORSystemDirectory
446
447HRESULT Disp::FindAssembly( // S_OK or error
448 LPCWSTR szAppBase, // [IN] optional - can be NULL
449 LPCWSTR szPrivateBin, // [IN] optional - can be NULL
450 LPCWSTR szGlobalBin, // [IN] optional - can be NULL
451 LPCWSTR szAssemblyName, // [IN] required - this is the assembly you are requesting
452 LPCWSTR szName, // [OUT] buffer - to hold name
453 ULONG cchName, // [IN] the name buffer's size
454 ULONG *pcName) // [OUT] the number of characters returend in the buffer
455{
456 BEGIN_ENTRYPOINT_NOTHROW;
457 END_ENTRYPOINT_NOTHROW;
458
459 return E_NOTIMPL;
460} // Disp::FindAssembly
461
462HRESULT Disp::FindAssemblyModule( // S_OK or error
463 LPCWSTR szAppBase, // [IN] optional - can be NULL
464 LPCWSTR szPrivateBin, // [IN] optional - can be NULL
465 LPCWSTR szGlobalBin, // [IN] optional - can be NULL
466 LPCWSTR szAssemblyName, // [IN] The assembly name or code base of the assembly
467 LPCWSTR szModuleName, // [IN] required - the name of the module
468 __out_ecount (cchName) LPWSTR szName, // [OUT] buffer - to hold name
469 ULONG cchName, // [IN] the name buffer's size
470 ULONG *pcName) // [OUT] the number of characters returend in the buffer
471{
472 BEGIN_ENTRYPOINT_NOTHROW;
473 END_ENTRYPOINT_NOTHROW;
474
475 return E_NOTIMPL;
476} // Disp::FindAssemblyModule
477
478//*****************************************************************************
479// Open a scope on an ITypeInfo
480//*****************************************************************************
481HRESULT Disp::OpenScopeOnITypeInfo( // Return code.
482 ITypeInfo *pITI, // [in] ITypeInfo to open.
483 DWORD dwOpenFlags, // [in] Open mode flags.
484 REFIID riid, // [in] The interface desired.
485 IUnknown **ppIUnk) // [out] Return interface on success.
486{
487 BEGIN_ENTRYPOINT_NOTHROW;
488 END_ENTRYPOINT_NOTHROW;
489
490 return E_NOTIMPL;
491} // Disp::OpenScopeOnITypeInfo
492
493#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
494
495//*****************************************************************************
496// IMetaDataDispenserCustom
497//*****************************************************************************
498
499HRESULT Disp::OpenScopeOnCustomDataSource( // S_OK or error
500 IMDCustomDataSource *pCustomSource, // [in] The scope to open.
501 DWORD dwOpenFlags, // [in] Open mode flags.
502 REFIID riid, // [in] The interface desired.
503 IUnknown **ppIUnk) // [out] Return interface on success.
504{
505 HRESULT hr;
506 BEGIN_ENTRYPOINT_NOTHROW;
507 LOG((LF_METADATA, LL_INFO10, "Disp::OpenScopeOnCustomDataSource(0x%08x, 0x%08x, 0x%08x, 0x%08x)\n", pCustomSource, dwOpenFlags, riid, ppIUnk));
508
509 IMDCommon *pMDCommon = NULL;
510
511 _ASSERTE(!IsOfReserved(dwOpenFlags));
512 if (ppIUnk == NULL)
513 IfFailGo(E_INVALIDARG);
514 *ppIUnk = NULL;
515 IfFailGo(OpenRawScopeOnCustomDataSource(pCustomSource, dwOpenFlags, IID_IMDCommon, (IUnknown**)&pMDCommon));
516 IfFailGo(DeliverScope(pMDCommon, riid, dwOpenFlags, ppIUnk));
517ErrExit:
518 if (pMDCommon)
519 pMDCommon->Release();
520 END_ENTRYPOINT_NOTHROW;
521 return hr;
522}
523
524
525//*****************************************************************************
526// Open a raw view of existing scope.
527//*****************************************************************************
528HRESULT Disp::OpenRawScopeOnCustomDataSource( // Return code.
529 IMDCustomDataSource* pDataSource, // [in] scope data.
530 DWORD dwOpenFlags, // [in] Open mode flags.
531 REFIID riid, // [in] The interface desired.
532 IUnknown **ppIUnk) // [out] Return interface on success.
533{
534 HRESULT hr;
535
536 BEGIN_ENTRYPOINT_NOTHROW;
537
538 RegMeta *pMeta = 0;
539
540 _ASSERTE(!IsOfReserved(dwOpenFlags));
541
542 // Create a new coclass for this guy.
543 pMeta = new (nothrow)RegMeta();
544 IfNullGo(pMeta);
545 IfFailGo(pMeta->SetOption(&m_OptionValue));
546
547
548 PREFIX_ASSUME(pMeta != NULL);
549 // Always initialize the RegMeta's stgdb.
550 // TODO
551 IfFailGo(pMeta->OpenExistingMD(pDataSource, dwOpenFlags));
552
553 LOG((LOGMD, "{%08x} Opened new scope on custom data source, pDataSource: %08x\n", pMeta, pDataSource));
554
555 // Return the requested interface.
556 IfFailGo(pMeta->QueryInterface(riid, (void **)ppIUnk));
557
558 // Add the new RegMeta to the cache.
559 IfFailGo(pMeta->AddToCache());
560
561#if defined(_DEBUG)
562 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaDump))
563 {
564 int DumpMD_impl(RegMeta *pMD);
565 DumpMD_impl(pMeta);
566 }
567#endif // _DEBUG
568
569ErrExit:
570 if (FAILED(hr))
571 {
572 if (pMeta) delete pMeta;
573 *ppIUnk = 0;
574 }
575
576 END_ENTRYPOINT_NOTHROW;
577
578 return hr;
579} // Disp::OpenRawScopeOnCustomDataSource
580
581#endif
582
583//*****************************************************************************
584// IUnknown
585//*****************************************************************************
586
587ULONG Disp::AddRef()
588{
589 return InterlockedIncrement(&m_cRef);
590} // Disp::AddRef
591
592ULONG Disp::Release()
593{
594 ULONG cRef = InterlockedDecrement(&m_cRef);
595 if (cRef == 0)
596 delete this;
597 return cRef;
598} // Disp::Release
599
600HRESULT Disp::QueryInterface(REFIID riid, void **ppUnk)
601{
602 *ppUnk = 0;
603
604 if (riid == IID_IUnknown)
605 *ppUnk = (IUnknown *) (IMetaDataDispenser *) this;
606 else if (riid == IID_IMetaDataDispenser)
607 *ppUnk = (IMetaDataDispenser *) this;
608 else if (riid == IID_IMetaDataDispenserEx)
609 *ppUnk = (IMetaDataDispenserEx *) this;
610#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
611 else if (riid == IID_IMetaDataDispenserCustom)
612 *ppUnk = static_cast<IMetaDataDispenserCustom*>(this);
613#endif
614 else
615 return E_NOINTERFACE;
616 AddRef();
617 return S_OK;
618} // Disp::QueryInterface
619
620
621//*****************************************************************************
622// Called by the class factory template to create a new instance of this object.
623//*****************************************************************************
624HRESULT Disp::CreateObject(REFIID riid, void **ppUnk)
625{
626 HRESULT hr;
627 Disp *pDisp = new (nothrow) Disp();
628
629 if (pDisp == 0)
630 return (E_OUTOFMEMORY);
631
632 hr = pDisp->QueryInterface(riid, ppUnk);
633 if (FAILED(hr))
634 delete pDisp;
635 return hr;
636} // Disp::CreateObject
637
638//*****************************************************************************
639// This routine provides the user a way to set certain properties on the
640// Dispenser.
641//
642// Implements public API code:IMetaDataDispenserEx::SetOption.
643//*****************************************************************************
644__checkReturn
645HRESULT
646Disp::SetOption(
647 REFGUID optionid, // [in] GUID for the option to be set.
648 const VARIANT *pvalue) // [in] Value to which the option is to be set.
649{
650 HRESULT hr = S_OK;
651 BEGIN_ENTRYPOINT_NOTHROW;
652
653 LOG((LF_METADATA, LL_INFO10, "Disp::SetOption(0x%08x, 0x%08x)\n", optionid, pvalue));
654
655 if (optionid == MetaDataCheckDuplicatesFor)
656 {
657 if (V_VT(pvalue) != VT_UI4)
658 {
659 _ASSERTE(!"Invalid Variant Type value!");
660 IfFailGo(E_INVALIDARG);
661 }
662 m_OptionValue.m_DupCheck = (CorCheckDuplicatesFor) V_UI4(pvalue);
663 }
664 else if (optionid == MetaDataRefToDefCheck)
665 {
666 if (V_VT(pvalue) != VT_UI4)
667 {
668 _ASSERTE(!"Invalid Variant Type value!");
669 IfFailGo(E_INVALIDARG);
670 }
671 m_OptionValue.m_RefToDefCheck = (CorRefToDefCheck) V_UI4(pvalue);
672 }
673 else if (optionid == MetaDataErrorIfEmitOutOfOrder)
674 {
675 if (V_VT(pvalue) != VT_UI4)
676 {
677 _ASSERTE(!"Invalid Variant Type value!");
678 IfFailGo(E_INVALIDARG);
679 }
680 m_OptionValue.m_ErrorIfEmitOutOfOrder = (CorErrorIfEmitOutOfOrder) V_UI4(pvalue);
681 }
682 else if (optionid == MetaDataThreadSafetyOptions)
683 {
684 if (V_VT(pvalue) != VT_UI4)
685 {
686 _ASSERTE(!"Invalid Variant Type value!");
687 IfFailGo(E_INVALIDARG);
688 }
689 m_OptionValue.m_ThreadSafetyOptions = (CorThreadSafetyOptions) V_UI4(pvalue);
690 }
691// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat.
692#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER)
693 else if (optionid == MetaDataNotificationForTokenMovement)
694 { // Note: This is not used in CLR sources anymore, but we store the value and return it back in
695 // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it here for backward-compat.
696 if (V_VT(pvalue) != VT_UI4)
697 {
698 _ASSERTE(!"Invalid Variant Type value!");
699 IfFailGo(E_INVALIDARG);
700 }
701 m_OptionValue.m_NotifyRemap = (CorNotificationForTokenMovement)V_UI4(pvalue);
702 }
703 else if (optionid == MetaDataSetENC)
704 { // EnC update mode (also aliased as code:MetaDataSetUpdate)
705 if (V_VT(pvalue) != VT_UI4)
706 {
707 _ASSERTE(!"Invalid Variant Type value!");
708 IfFailGo(E_INVALIDARG);
709 }
710 m_OptionValue.m_UpdateMode = V_UI4(pvalue);
711 }
712 else if (optionid == MetaDataImportOption)
713 { // Allows enumeration of EnC deleted items by Import API
714 if (V_VT(pvalue) != VT_UI4)
715 {
716 _ASSERTE(!"Invalid Variant Type value!");
717 IfFailGo(E_INVALIDARG);
718 }
719 m_OptionValue.m_ImportOption = (CorImportOptions) V_UI4(pvalue);
720 }
721 else if (optionid == MetaDataLinkerOptions)
722 { // Used only by code:RegMeta::UnmarkAll (code:IMetaDataFilter::UnmarkAll)
723 if (V_VT(pvalue) != VT_UI4)
724 {
725 _ASSERTE(!"Invalid Variant Type value!");
726 IfFailGo(E_INVALIDARG);
727 }
728 m_OptionValue.m_LinkerOption = (CorLinkerOptions) V_UI4(pvalue);
729 }
730 else if (optionid == MetaDataMergerOptions)
731 {
732 if (V_VT(pvalue) != VT_UI4)
733 {
734 _ASSERTE(!"Invalid Variant Type value!");
735 IfFailGo(E_INVALIDARG);
736 }
737 m_OptionValue.m_MergeOptions = (MergeFlags) V_UI4(pvalue);
738 }
739 else if (optionid == MetaDataGenerateTCEAdapters)
740 { // Note: This is not used in CLR sources anymore, but we store the value and return it back in
741 // IMetaDataDispenserEx::GetOption (code:RegMeta::GetOption), so we keep it for backward-compat.
742 if (V_VT(pvalue) != VT_BOOL)
743 {
744 _ASSERTE(!"Invalid Variant Type value!");
745 IfFailGo(E_INVALIDARG);
746 }
747 m_OptionValue.m_GenerateTCEAdapters = V_BOOL(pvalue);
748 }
749 else if (optionid == MetaDataTypeLibImportNamespace)
750 { // Note: This is not used in CLR sources anymore, keep it here for backward-compat
751 if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL)
752 {
753 _ASSERTE(!"Invalid Variant Type value for namespace.");
754 IfFailGo(E_INVALIDARG);
755 }
756 }
757#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER
758 else if (optionid == MetaDataRuntimeVersion)
759 {
760 if (V_VT(pvalue) != VT_BSTR && V_VT(pvalue) != VT_EMPTY && V_VT(pvalue) != VT_NULL)
761 {
762 _ASSERTE(!"Invalid Variant Type value for version.");
763 IfFailGo(E_INVALIDARG);
764 }
765 if (m_OptionValue.m_RuntimeVersion)
766 delete [] m_OptionValue.m_RuntimeVersion;
767
768 if ((V_VT(pvalue) == VT_EMPTY) || (V_VT(pvalue) == VT_NULL) || (*V_BSTR(pvalue) == 0))
769 {
770 m_OptionValue.m_RuntimeVersion = NULL;
771 }
772 else
773 {
774 INT32 len = WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, NULL, 0, NULL, NULL);
775 m_OptionValue.m_RuntimeVersion = new (nothrow) char[len];
776 if (m_OptionValue.m_RuntimeVersion == NULL)
777 IfFailGo(E_INVALIDARG);
778 WszWideCharToMultiByte(CP_UTF8, 0, V_BSTR(pvalue), -1, m_OptionValue.m_RuntimeVersion, len, NULL, NULL);
779 }
780 }
781 else if (optionid == MetaDataInitialSize)
782 {
783 if (V_VT(pvalue) != VT_UI4)
784 {
785 _ASSERTE(!"Invalid Variant Type value!");
786 IfFailGo(E_INVALIDARG);
787 }
788 m_OptionValue.m_InitialSize = V_UI4(pvalue);
789 }
790 else if (optionid == MetaDataPreserveLocalRefs)
791 {
792 if (V_VT(pvalue) != VT_UI4)
793 {
794 _ASSERTE(!"Invalid Variant Type value!");
795 IfFailGo(E_INVALIDARG);
796 }
797
798 m_OptionValue.m_LocalRefPreservation = (CorLocalRefPreservation) V_UI4(pvalue);
799 }
800 else
801 {
802 _ASSERTE(!"Invalid GUID");
803 IfFailGo(E_INVALIDARG);
804 }
805
806ErrExit:
807 END_ENTRYPOINT_NOTHROW;
808 return hr;
809} // Disp::SetOption
810
811//*****************************************************************************
812// This routine provides the user a way to set certain properties on the
813// Dispenser.
814//
815// Implements public API code:IMetaDataDispenserEx::GetOption.
816//*****************************************************************************
817HRESULT Disp::GetOption( // Return code.
818 REFGUID optionid, // [in] GUID for the option to be set.
819 VARIANT *pvalue) // [out] Value to which the option is currently set.
820{
821 HRESULT hr = S_OK;
822 BEGIN_ENTRYPOINT_NOTHROW;
823
824 LOG((LF_METADATA, LL_INFO10, "Disp::GetOption(0x%08x, 0x%08x)\n", optionid, pvalue));
825
826 _ASSERTE(pvalue);
827 if (optionid == MetaDataCheckDuplicatesFor)
828 {
829 V_VT(pvalue) = VT_UI4;
830 V_UI4(pvalue) = m_OptionValue.m_DupCheck;
831 }
832 else if (optionid == MetaDataRefToDefCheck)
833 {
834 V_VT(pvalue) = VT_UI4;
835 V_UI4(pvalue) = m_OptionValue.m_RefToDefCheck;
836 }
837 else if (optionid == MetaDataErrorIfEmitOutOfOrder)
838 {
839 V_VT(pvalue) = VT_UI4;
840 V_UI4(pvalue) = m_OptionValue.m_ErrorIfEmitOutOfOrder;
841 }
842// Note: mscordbi had all these options accessible in 3.5/4.0 RTM, let's keep it this way for AppCompat.
843#if defined(FEATURE_METADATA_EMIT_ALL) || defined(FEATURE_METADATA_EMIT_IN_DEBUGGER)
844 else if (optionid == MetaDataNotificationForTokenMovement)
845 { // Note: This is not used in CLR sources anymore, but we store the value and return it here,
846 // so we keep it for backward-compat.
847 V_VT(pvalue) = VT_UI4;
848 V_UI4(pvalue) = m_OptionValue.m_NotifyRemap;
849 }
850 else if (optionid == MetaDataSetENC)
851 { // EnC update mode (also aliased as code:MetaDataSetUpdate)
852 V_VT(pvalue) = VT_UI4;
853 V_UI4(pvalue) = m_OptionValue.m_UpdateMode;
854 }
855 else if (optionid == MetaDataLinkerOptions)
856 { // Allows enumeration of EnC deleted items by Import API
857 V_VT(pvalue) = VT_BOOL;
858 V_UI4(pvalue) = m_OptionValue.m_LinkerOption;
859 }
860 else if (optionid == MetaDataGenerateTCEAdapters)
861 { // Note: This is not used in CLR sources anymore, but we store the value and return it here,
862 // so we keep it for backward-compat.
863 V_VT(pvalue) = VT_BOOL;
864 V_BOOL(pvalue) = m_OptionValue.m_GenerateTCEAdapters;
865 }
866#endif //FEATURE_METADATA_EMIT_ALL || FEATURE_METADATA_EMIT_IN_DEBUGGER
867 else
868 {
869 _ASSERTE(!"Invalid GUID");
870 IfFailGo(E_INVALIDARG);
871 }
872ErrExit:
873 END_ENTRYPOINT_NOTHROW;
874
875 return hr;
876} // Disp::GetOption
877
878#if defined(FEATURE_METADATA_IN_VM)
879
880//---------------------------------------------------------------------------------------
881//
882// Process detach destruction.
883// Called from DllMain of clr.dll/RoMetadata.dll/MidlrtMd.dll.
884//
885void DeleteMetaData()
886{
887 LOADEDMODULES::DeleteStatics();
888}
889
890#endif //FEATURE_METADATA_IN_VM
891
892//
893// This is the entrypoint for usages of MetaData that need to start with the dispenser (e.g.
894// mscordbi.dll and profiling API).
895//
896// Notes:
897// This could be merged with the class factory support.
898HRESULT InternalCreateMetaDataDispenser(REFIID riid, void ** pMetaDataDispenserOut)
899{
900 _ASSERTE(pMetaDataDispenserOut != NULL);
901 return Disp::CreateObject(riid, pMetaDataDispenserOut);
902}
903