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// RegMeta.cpp
6//
7
8//
9// Implementation for meta data public interface methods.
10//
11//*****************************************************************************
12#include "stdafx.h"
13#include "regmeta.h"
14#include "metadata.h"
15#include "corerror.h"
16#include "mdutil.h"
17#include "rwutil.h"
18#include "mdlog.h"
19#include "importhelper.h"
20#include "filtermanager.h"
21#include "mdperf.h"
22#include "switches.h"
23#include "posterror.h"
24#include "stgio.h"
25#include "sstring.h"
26
27#include "mdinternalrw.h"
28
29
30#include <metamodelrw.h>
31
32#define DEFINE_CUSTOM_NODUPCHECK 1
33#define DEFINE_CUSTOM_DUPCHECK 2
34#define SET_CUSTOM 3
35
36#if defined(_DEBUG) && defined(_TRACE_REMAPS)
37#define LOGGING
38#endif
39#include <log.h>
40
41#ifdef _MSC_VER
42#pragma warning(disable: 4102)
43#endif
44
45RegMeta::RegMeta() :
46 m_pStgdb(0),
47 m_pStgdbFreeList(NULL),
48 m_pUnk(0),
49 m_pFilterManager(NULL),
50#ifdef FEATURE_METADATA_INTERNAL_APIS
51 m_pInternalImport(NULL),
52#endif
53 m_pSemReadWrite(NULL),
54 m_fOwnSem(false),
55 m_bRemap(false),
56 m_bSaveOptimized(false),
57 m_hasOptimizedRefToDef(false),
58 m_pHandler(0),
59 m_fIsTypeDefDirty(false),
60 m_fIsMemberDefDirty(false),
61 m_fStartedEE(false),
62 m_pAppDomain(NULL),
63 m_OpenFlags(0),
64 m_cRef(0),
65 m_pFreeThreadedMarshaler(NULL),
66 m_bCached(false),
67 m_trLanguageType(0),
68 m_SetAPICaller(EXTERNAL_CALLER),
69 m_ModuleType(ValidatorModuleTypeInvalid),
70 m_bKeepKnownCa(false),
71 m_pCorProfileData(NULL),
72 m_ReorderingOptions(NoReordering)
73#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
74 , m_safeToDeleteStgdb(true)
75#endif
76{
77 memset(&m_OptionValue, 0, sizeof(OptionValue));
78
79#ifdef _DEBUG
80 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_RegMetaBreak))
81 {
82 _ASSERTE(!"RegMeta()");
83 }
84 if (CLRConfig::GetConfigValue(CLRConfig::INTERNAL_MD_KeepKnownCA))
85 m_bKeepKnownCa = true;
86#endif // _DEBUG
87
88} // RegMeta::RegMeta()
89
90RegMeta::~RegMeta()
91{
92 BEGIN_CLEANUP_ENTRYPOINT;
93
94 _ASSERTE(!m_bCached);
95
96 HRESULT hr = S_OK;
97
98 LOCKWRITENORET();
99
100#ifdef FEATURE_METADATA_INTERNAL_APIS
101 // This should have worked if we've cached the public interface in the past
102 _ASSERTE(SUCCEEDED(hr) || (m_pInternalImport == NULL) || (m_pInternalImport->GetCachedPublicInterface(false) == NULL));
103#endif //FEATURE_METADATA_INTERNAL_APIS
104
105 if (SUCCEEDED(hr))
106 {
107#ifdef FEATURE_METADATA_INTERNAL_APIS
108 if (m_pInternalImport != NULL)
109 {
110 // RegMeta is going away. Make sure we clear up the pointer from MDInternalRW to this RegMeta.
111 if (FAILED(m_pInternalImport->SetCachedPublicInterface(NULL)))
112 { // Do nothing on error
113 }
114 m_pInternalImport = NULL;
115 m_fOwnSem = false;
116 }
117#endif //FEATURE_METADATA_INTERNAL_APIS
118
119 UNLOCKWRITE();
120 }
121
122 if (m_pFreeThreadedMarshaler)
123 {
124 m_pFreeThreadedMarshaler->Release();
125 m_pFreeThreadedMarshaler = NULL;
126 }
127
128 if (m_pSemReadWrite && m_fOwnSem)
129 delete m_pSemReadWrite;
130
131 // If this RegMeta is a wrapper on an external StgDB, release it.
132 if (IsOfExternalStgDB(m_OpenFlags))
133 {
134 _ASSERTE(m_pUnk != NULL); // Owning IUnknown for external StgDB.
135 if (m_pUnk)
136 m_pUnk->Release();
137 m_pUnk = 0;
138 }
139 else
140 { // Not a wrapper, so free our StgDB.
141 _ASSERTE(m_pUnk == NULL);
142 // It's possible m_pStdbg is NULL in OOM scenarios
143 if (m_pStgdb != NULL)
144 delete m_pStgdb;
145 m_pStgdb = 0;
146 }
147
148 // Delete the old copies of Stgdb list. This is the list track all of the
149 // old snapshuts with ReOpenWithMemory call.
150 CLiteWeightStgdbRW *pCur;
151 while (m_pStgdbFreeList)
152 {
153 pCur = m_pStgdbFreeList;
154 m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb;
155 delete pCur;
156 }
157
158 // If This RegMeta spun up the runtime (probably to process security
159 // attributes), shut it down now.
160 if (m_fStartedEE)
161 {
162 m_pAppDomain->Release();
163 }
164
165 if (m_pFilterManager != NULL)
166 delete m_pFilterManager;
167
168
169 if (m_OptionValue.m_RuntimeVersion != NULL)
170 delete[] m_OptionValue.m_RuntimeVersion;
171
172 END_CLEANUP_ENTRYPOINT;
173
174} // RegMeta::~RegMeta()
175
176HRESULT RegMeta::SetOption(OptionValue *pOptionValue)
177{
178 _ASSERTE(pOptionValue);
179 char* pszRuntimeVersion = NULL;
180
181 if (pOptionValue->m_RuntimeVersion != NULL)
182 {
183 SIZE_T dwBufferSize = strlen(pOptionValue->m_RuntimeVersion) + 1; // +1 for null
184 pszRuntimeVersion = new (nothrow) char[dwBufferSize];
185 if (pszRuntimeVersion == NULL)
186 {
187 return E_OUTOFMEMORY;
188 }
189 strcpy_s(pszRuntimeVersion, dwBufferSize, pOptionValue->m_RuntimeVersion);
190 }
191
192 memcpy(&m_OptionValue, pOptionValue, sizeof(OptionValue));
193 m_OptionValue.m_RuntimeVersion = pszRuntimeVersion;
194
195 return S_OK;
196}// SetOption
197
198
199//*****************************************************************************
200// Initialize with an existing stgdb.
201//*****************************************************************************
202__checkReturn
203HRESULT
204RegMeta::InitWithStgdb(
205 IUnknown *pUnk, // The IUnknown that owns the life time for the existing stgdb
206 CLiteWeightStgdbRW *pStgdb) // existing light weight stgdb
207{
208 // RegMeta created this way will not create a read/write lock semaphore.
209 HRESULT hr = S_OK;
210
211 _ASSERTE(m_pStgdb == NULL);
212 m_tdModule = COR_GLOBAL_PARENT_TOKEN;
213 m_pStgdb = pStgdb;
214
215 m_OpenFlags = ofExternalStgDB;
216
217 // remember the owner of the light weight stgdb
218 // AddRef it to ensure the lifetime
219 //
220 m_pUnk = pUnk;
221 m_pUnk->AddRef();
222 IfFailGo(m_pStgdb->m_MiniMd.GetOption(&m_OptionValue));
223ErrExit:
224 return hr;
225} // RegMeta::InitWithStgdb
226
227#ifdef FEATURE_METADATA_EMIT
228
229//*****************************************************************************
230// call stgdb InitNew
231//*****************************************************************************
232__checkReturn
233HRESULT
234RegMeta::CreateNewMD()
235{
236 HRESULT hr = NOERROR;
237
238 m_OpenFlags = ofWrite;
239
240 // Allocate our m_pStgdb.
241 _ASSERTE(m_pStgdb == NULL);
242 IfNullGo(m_pStgdb = new (nothrow) CLiteWeightStgdbRW);
243
244 // Initialize the new, empty database.
245
246 // First tell the new database what sort of metadata to create
247 m_pStgdb->m_MiniMd.m_OptionValue.m_MetadataVersion = m_OptionValue.m_MetadataVersion;
248 m_pStgdb->m_MiniMd.m_OptionValue.m_InitialSize = m_OptionValue.m_InitialSize;
249 IfFailGo(m_pStgdb->InitNew());
250
251 // Set up the Module record.
252 ULONG iRecord;
253 ModuleRec *pModule;
254 GUID mvid;
255 IfFailGo(m_pStgdb->m_MiniMd.AddModuleRecord(&pModule, &iRecord));
256 IfFailGo(CoCreateGuid(&mvid));
257 IfFailGo(m_pStgdb->m_MiniMd.PutGuid(TBL_Module, ModuleRec::COL_Mvid, pModule, mvid));
258
259 // Add the dummy module typedef which we are using to parent global items.
260 TypeDefRec *pRecord;
261 IfFailGo(m_pStgdb->m_MiniMd.AddTypeDefRecord(&pRecord, &iRecord));
262 m_tdModule = TokenFromRid(iRecord, mdtTypeDef);
263 IfFailGo(m_pStgdb->m_MiniMd.PutStringW(TBL_TypeDef, TypeDefRec::COL_Name, pRecord, COR_WMODULE_CLASS));
264 IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue));
265
266 if (IsThreadSafetyOn())
267 {
268 m_pSemReadWrite = new (nothrow) UTSemReadWrite();
269 IfNullGo(m_pSemReadWrite);
270 IfFailGo(m_pSemReadWrite->Init());
271 m_fOwnSem = true;
272
273 INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
274 }
275
276ErrExit:
277 return hr;
278} // RegMeta::CreateNewMD
279
280#endif //FEATURE_METADATA_EMIT
281
282//*****************************************************************************
283// call stgdb OpenForRead
284//*****************************************************************************
285HRESULT RegMeta::OpenExistingMD(
286 LPCWSTR szDatabase, // Name of database.
287 void *pData, // Data to open on top of, 0 default.
288 ULONG cbData, // How big is the data.
289 ULONG dwOpenFlags) // Flags for the open.
290{
291 HRESULT hr = NOERROR;
292 void *pbData = pData; // Pointer to original or copied data.
293
294
295
296 m_OpenFlags = dwOpenFlags;
297
298 if (!IsOfReOpen(dwOpenFlags))
299 {
300 // Allocate our m_pStgdb, if we should.
301 _ASSERTE(m_pStgdb == NULL);
302 IfNullGo( m_pStgdb = new (nothrow) CLiteWeightStgdbRW );
303 }
304
305 IfFailGo( m_pStgdb->OpenForRead(
306 szDatabase,
307 pbData,
308 cbData,
309 m_OpenFlags) );
310
311 if (m_pStgdb->m_MiniMd.m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0 &&
312 m_pStgdb->m_MiniMd.m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0)
313 m_OptionValue.m_MetadataVersion = MDVersion1;
314
315 else
316 m_OptionValue.m_MetadataVersion = MDVersion2;
317
318
319
320 IfFailGo( m_pStgdb->m_MiniMd.SetOption(&m_OptionValue) );
321
322 if (IsThreadSafetyOn())
323 {
324 m_pSemReadWrite = new (nothrow) UTSemReadWrite();
325 IfNullGo(m_pSemReadWrite);
326 IfFailGo(m_pSemReadWrite->Init());
327 m_fOwnSem = true;
328
329 INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
330 }
331
332 if (!IsOfReOpen(dwOpenFlags))
333 {
334 // There must always be a Global Module class and its the first entry in
335 // the TypeDef table.
336 m_tdModule = TokenFromRid(1, mdtTypeDef);
337 }
338
339ErrExit:
340
341 return hr;
342} //RegMeta::OpenExistingMD
343
344#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
345HRESULT RegMeta::OpenExistingMD(
346 IMDCustomDataSource* pDataSource, // Name of database.
347 ULONG dwOpenFlags) // Flags to control open.
348{
349 HRESULT hr = NOERROR;
350
351 m_OpenFlags = dwOpenFlags;
352
353 if (!IsOfReOpen(dwOpenFlags))
354 {
355 // Allocate our m_pStgdb, if we should.
356 _ASSERTE(m_pStgdb == NULL);
357 IfNullGo(m_pStgdb = new (nothrow)CLiteWeightStgdbRW);
358 }
359
360 IfFailGo(m_pStgdb->OpenForRead(
361 pDataSource,
362 m_OpenFlags));
363
364 if (m_pStgdb->m_MiniMd.m_Schema.m_major == METAMODEL_MAJOR_VER_V1_0 &&
365 m_pStgdb->m_MiniMd.m_Schema.m_minor == METAMODEL_MINOR_VER_V1_0)
366 m_OptionValue.m_MetadataVersion = MDVersion1;
367
368 else
369 m_OptionValue.m_MetadataVersion = MDVersion2;
370
371
372
373 IfFailGo(m_pStgdb->m_MiniMd.SetOption(&m_OptionValue));
374
375 if (IsThreadSafetyOn())
376 {
377 m_pSemReadWrite = new (nothrow)UTSemReadWrite();
378 IfNullGo(m_pSemReadWrite);
379 IfFailGo(m_pSemReadWrite->Init());
380 m_fOwnSem = true;
381
382 INDEBUG(m_pStgdb->m_MiniMd.Debug_SetLock(m_pSemReadWrite);)
383 }
384
385 if (!IsOfReOpen(dwOpenFlags))
386 {
387 // There must always be a Global Module class and its the first entry in
388 // the TypeDef table.
389 m_tdModule = TokenFromRid(1, mdtTypeDef);
390 }
391
392ErrExit:
393
394 return hr;
395} //RegMeta::OpenExistingMD
396#endif // FEATURE_METADATA_CUSTOM_DATA_SOURCE
397
398#ifdef FEATURE_METADATA_INTERNAL_APIS
399
400//*****************************************************************************
401// Gets a cached Internal importer, if available.
402//
403// Arguments:
404// fWithLock - if true, takes a reader lock.
405// If false, assumes caller is handling the synchronization.
406//
407// Returns:
408// A cached Internal importer, which gets addreffed. Caller must release!
409// If no importer is set, returns NULL
410//
411// Notes:
412// This function also does not trigger the creation of Internal interface.
413// Set the cached importer via code:RegMeta.SetCachedInternalInterface
414//
415// Implements internal API code:IMetaDataHelper::GetCachedInternalInterface.
416//*****************************************************************************
417IUnknown* RegMeta::GetCachedInternalInterface(BOOL fWithLock)
418{
419 IUnknown *pRet = NULL;
420 HRESULT hr = S_OK;
421
422 if (fWithLock)
423 {
424 LOCKREAD();
425
426 pRet = m_pInternalImport;
427 }
428 else
429 {
430 pRet = m_pInternalImport;
431 }
432 if (pRet) pRet->AddRef();
433
434ErrExit:
435
436 return pRet;
437} //RegMeta::GetCachedInternalInterface
438
439//*****************************************************************************
440// Set the cached Internal interface. This function will return an Error is the
441// current cached internal interface is not empty and trying set a non-empty internal
442// interface. One RegMeta will only associated
443// with one Internal Object. Unless we have bugs somewhere else. It will QI on the
444// IUnknown for the IMDInternalImport. If this failed, error will be returned.
445// Note: Caller should take a write lock
446//
447// This does addref the importer (the public and private importers maintain
448// weak references to each other).
449//
450// Implements internal API code:IMetaDataHelper::SetCachedInternalInterface.
451//*****************************************************************************
452HRESULT RegMeta::SetCachedInternalInterface(IUnknown *pUnk)
453{
454 HRESULT hr = NOERROR;
455 IMDInternalImport *pInternal = NULL;
456
457 if (pUnk)
458 {
459 if (m_pInternalImport)
460 {
461 _ASSERTE(!"Bad state!");
462 }
463 IfFailRet( pUnk->QueryInterface(IID_IMDInternalImport, (void **) &pInternal) );
464
465 // Should be non-null
466 _ASSERTE(pInternal);
467 m_pInternalImport = pInternal;
468
469 // We don't want to add ref the internal interface, so undo the AddRef() from the QI.
470 pInternal->Release();
471 }
472 else
473 {
474 // Internal interface is going away before the public interface. Take ownership on the
475 // reader writer lock.
476 m_fOwnSem = true;
477 m_pInternalImport = NULL;
478 }
479 return hr;
480} // RegMeta::SetCachedInternalInterface
481
482#endif //FEATURE_METADATA_INTERNAL_APIS
483
484//*****************************************************************************
485// IUnknown
486//*****************************************************************************
487
488ULONG RegMeta::AddRef()
489{
490 return InterlockedIncrement(&m_cRef);
491} // ULONG RegMeta::AddRef()
492
493
494HRESULT
495RegMeta::QueryInterface(
496 REFIID riid,
497 void ** ppUnk)
498{
499 HRESULT hr = S_OK;
500 BEGIN_ENTRYPOINT_NOTHROW;
501 int fIsInterfaceRW = false;
502 *ppUnk = 0;
503
504 if (riid == IID_IUnknown)
505 {
506 *ppUnk = (IUnknown *)(IMetaDataImport2 *)this;
507 }
508 else if (riid == IID_IMDCommon)
509 {
510 *ppUnk = (IMDCommon *)this;
511 }
512 else if (riid == IID_IMetaDataImport)
513 {
514 *ppUnk = (IMetaDataImport2 *)this;
515 }
516 else if (riid == IID_IMetaDataImport2)
517 {
518 *ppUnk = (IMetaDataImport2 *)this;
519 }
520 else if (riid == IID_IMetaDataAssemblyImport)
521 {
522 *ppUnk = (IMetaDataAssemblyImport *)this;
523 }
524 else if (riid == IID_IMetaDataTables)
525 {
526 *ppUnk = static_cast<IMetaDataTables *>(this);
527 }
528 else if (riid == IID_IMetaDataTables2)
529 {
530 *ppUnk = static_cast<IMetaDataTables2 *>(this);
531 }
532
533 else if (riid == IID_IMetaDataInfo)
534 {
535 *ppUnk = static_cast<IMetaDataInfo *>(this);
536 }
537
538#ifdef FEATURE_METADATA_EMIT
539 else if (riid == IID_IMetaDataEmit)
540 {
541 *ppUnk = (IMetaDataEmit2 *)this;
542 fIsInterfaceRW = true;
543 }
544 else if (riid == IID_IMetaDataEmit2)
545 {
546 *ppUnk = (IMetaDataEmit2 *)this;
547 fIsInterfaceRW = true;
548 }
549 else if (riid == IID_IMetaDataAssemblyEmit)
550 {
551 *ppUnk = (IMetaDataAssemblyEmit *)this;
552 fIsInterfaceRW = true;
553 }
554#endif //FEATURE_METADATA_EMIT
555
556
557#ifdef FEATURE_METADATA_EMIT_ALL
558 else if (riid == IID_IMetaDataFilter)
559 {
560 *ppUnk = (IMetaDataFilter *)this;
561 }
562#endif //FEATURE_METADATA_EMIT_ALL
563
564#ifdef FEATURE_METADATA_INTERNAL_APIS
565 else if (riid == IID_IMetaDataHelper)
566 {
567 *ppUnk = (IMetaDataHelper *)this;
568 }
569 else if (riid == IID_IMDInternalEmit)
570 {
571 *ppUnk = static_cast<IMDInternalEmit *>(this);
572 }
573 else if (riid == IID_IGetIMDInternalImport)
574 {
575 *ppUnk = static_cast<IGetIMDInternalImport *>(this);
576 }
577#endif //FEATURE_METADATA_INTERNAL_APIS
578
579#if defined(FEATURE_METADATA_EMIT) && defined(FEATURE_METADATA_INTERNAL_APIS)
580 else if (riid == IID_IMetaDataEmitHelper)
581 {
582 *ppUnk = (IMetaDataEmitHelper *)this;
583 fIsInterfaceRW = true;
584 }
585#endif //FEATURE_METADATA_EMIT && FEATURE_METADATA_INTERNAL_APIS
586
587#ifdef FEATURE_METADATA_IN_VM
588#ifdef FEATURE_COMINTEROP
589 else if (riid == IID_IMarshal)
590 {
591 // We will only repond to this interface if scope is opened for ReadOnly
592 if (IsOfReadOnly(m_OpenFlags))
593 {
594 if (m_pFreeThreadedMarshaler == NULL)
595 {
596 // Guard ourselves against first time QI on IMarshal from two different threads..
597 LOCKWRITE();
598 if (m_pFreeThreadedMarshaler == NULL)
599 {
600 // First time! Create the FreeThreadedMarshaler
601 IfFailGo(CoCreateFreeThreadedMarshaler((IUnknown *)(IMetaDataEmit2 *)this, &m_pFreeThreadedMarshaler));
602 }
603 }
604
605 _ASSERTE(m_pFreeThreadedMarshaler != NULL);
606
607 IfFailGo(m_pFreeThreadedMarshaler->QueryInterface(riid, ppUnk));
608
609 // AddRef has happened in the QueryInterface and thus should just return
610 goto ErrExit;
611 }
612 else
613 {
614 IfFailGo(E_NOINTERFACE);
615 }
616 }
617#endif // FEATURE_COMINTEROP
618#ifdef FEATURE_PREJIT
619 else if (riid == IID_IMetaDataCorProfileData)
620 {
621 *ppUnk = (IMetaDataCorProfileData *)this;
622 }
623 else if (riid == IID_IMDInternalMetadataReorderingOptions)
624 {
625 *ppUnk = (IMDInternalMetadataReorderingOptions *)this;
626 }
627#endif //FEATURE_PREJIT
628#endif //FEATURE_METADATA_IN_VM
629 else
630 {
631 IfFailGo(E_NOINTERFACE);
632 }
633
634 if (fIsInterfaceRW && IsOfReadOnly(m_OpenFlags))
635 {
636 // They are asking for a read/write interface and this scope was
637 // opened as Read-Only
638
639 *ppUnk = NULL;
640 IfFailGo(CLDB_E_INCOMPATIBLE);
641 }
642
643 if (fIsInterfaceRW)
644 {
645 LOCKWRITENORET();
646
647 if (SUCCEEDED(hr))
648 {
649 hr = m_pStgdb->m_MiniMd.ConvertToRW();
650 }
651
652 if (FAILED(hr))
653 {
654 *ppUnk = NULL;
655 goto ErrExit;
656 }
657 }
658
659 AddRef();
660ErrExit:
661
662 END_ENTRYPOINT_NOTHROW;
663
664 return hr;
665} // RegMeta::QueryInterface
666
667
668//---------------------------------------------------------------------------------------
669//
670// Returns the memory region of the mapped file and type of its mapping. The choice of the file mapping type
671// for each scope is CLR implementation specific and user cannot explicitly set it.
672//
673// The memory is valid only as long as the underlying MetaData scope is opened (there's a reference to
674// a MetaData interface for this scope).
675//
676// Implements public API code:IMetaDataInfo::GetFileMapping.
677//
678// Arguments:
679// ppvData - Fills with pointer to the start of the mapped file.
680// pcbData - Fills with the size of the mapped memory region (for flat-mapping it is the size of the
681// file).
682// pdwMappingType - Fills with type of file mapping (code:CorFileMapping).
683// Current CLR implementation returns always code:fmFlat. The other value(s) are reserved for future
684// usage. See code:StgIO::MapFileToMem#CreateFileMapping_SEC_IMAGE for more details.
685//
686// Return Value:
687// S_OK - All output data are filled.
688// COR_E_NOTSUPPORTED - CLR cannot (or doesn't want to) provide the memory region.
689// This can happen when:
690// - The MetaData scope was opened with flag code:ofWrite or code:ofCopyMemory.
691// Note: code:ofCopyMemory could be supported in future CLR versions. For example if we change
692// code:CLiteWeightStgdbRW::OpenForRead to copy whole file (or add a new flag ofCopyWholeFile).
693// - The MetaData scope was opened without flag code:ofReadOnly.
694// Note: We could support this API without code:ofReadOnly flag in future CLR versions. We just
695// need some test coverage and user scenario for it.
696// - Only MetaData part of the file was opened using code:OpenScopeOnMemory.
697// - The file is not NT PE file (e.g. it is NT OBJ = .obj file produced by managed C++).
698// E_INVALIDARG - NULL was passed as an argument value.
699//
700HRESULT
701RegMeta::GetFileMapping(
702 const void ** ppvData,
703 ULONGLONG * pcbData,
704 DWORD * pdwMappingType)
705{
706 HRESULT hr = S_OK;
707
708 if ((ppvData == NULL) || (pcbData == NULL) || (pdwMappingType == NULL))
709 {
710 return E_INVALIDARG;
711 }
712
713 // Note: Some of the following checks are duplicit (as some combinations are invalid and ensured by CLR
714 // implementation), but it is easier to check them all
715
716 // OpenScope flags have to be (ofRead | ofReadOnly) and not ofCopyMemory
717 // (as code:CLiteWeightStgdbRW::OpenForRead will copy only the MetaData part of the file)
718 if (((m_OpenFlags & ofReadWriteMask) != ofRead) ||
719 ((m_OpenFlags & ofReadOnly) == 0) ||
720 ((m_OpenFlags & ofCopyMemory) != 0))
721 {
722 IfFailGo(COR_E_NOTSUPPORTED);
723 }
724 // The file has to be NT PE file (not CLDB = managed C++ .obj file) and we have to have its full mapping
725 // (see code:CLiteWeightStgdbRW::OpenForRead)
726 if ((m_pStgdb->m_pImage == NULL) ||
727 (m_pStgdb->m_dwImageSize == 0) ||
728 (m_pStgdb->GetFileType() != FILETYPE_NTPE))
729 {
730 IfFailGo(COR_E_NOTSUPPORTED);
731 }
732 if (m_pStgdb->m_pStgIO->GetFlags() != DBPROP_TMODEF_READ)
733 {
734 IfFailGo(COR_E_NOTSUPPORTED);
735 }
736 // The file has to be flat-mapped, or copied to memory (file mapping code:MTYPE_IMAGE is not currently
737 // supported - see code:StgIO::MapFileToMem#CreateFileMapping_SEC_IMAGE)
738 // Note: Only small files (<=64K) are copied to memory - see code:StgIO::MapFileToMem#CopySmallFiles
739 if ((m_pStgdb->m_pStgIO->GetMemoryMappedType() != MTYPE_FLAT) &&
740 (m_pStgdb->m_pStgIO->GetMemoryMappedType() != MTYPE_NOMAPPING))
741 {
742 IfFailGo(COR_E_NOTSUPPORTED);
743 }
744 // All necessary conditions are satisfied
745
746 *ppvData = m_pStgdb->m_pImage;
747 *pcbData = m_pStgdb->m_dwImageSize;
748 // We checked that the file was flat-mapped above
749 *pdwMappingType = fmFlat;
750
751ErrExit:
752 if (FAILED(hr))
753 {
754 *ppvData = NULL;
755 *pcbData = 0;
756 *pdwMappingType = 0;
757 }
758
759 return hr;
760} // RegMeta::GetFileMapping
761
762
763//------------------------------------------------------------------------------
764// Metadata dump
765//
766#ifdef _DEBUG
767
768#define STRING_BUFFER_LEN 1024
769#define ENUM_BUFFER_SIZE 10
770
771int DumpMD_Write(__in __in_z const char *str)
772{
773 OutputDebugStringA(str);
774 return 0; // strlen(str);
775} // int DumpMD_Write()
776
777int DumpMD_WriteLine(__in __in_z const char *str)
778{
779 OutputDebugStringA(str);
780 OutputDebugStringA("\n");
781 return 0; // strlen(str);
782} // int DumpMD_Write()
783
784int DumpMD_VWriteMarker(__in __in_z const char *str, va_list marker)
785{
786 CQuickBytes m_output;
787
788 int count = -1;
789 int i = 1;
790 HRESULT hr;
791
792 while (count < 0)
793 {
794 if (FAILED(hr = m_output.ReSizeNoThrow(STRING_BUFFER_LEN * i)))
795 return 0;
796 va_list markerCopy;
797 va_copy(markerCopy, marker);
798 count = _vsnprintf_s((char *)m_output.Ptr(), STRING_BUFFER_LEN * i, _TRUNCATE, str, markerCopy);
799 va_end(markerCopy);
800 i *= 2;
801 }
802 OutputDebugStringA((LPCSTR)m_output.Ptr());
803 return count;
804} // int DumpMD_VWriteMarker()
805
806int DumpMD_VWrite(__in __in_z const char *str, ...)
807{
808 va_list marker;
809 int count;
810
811 va_start(marker, str);
812 count = DumpMD_VWriteMarker(str, marker);
813 va_end(marker);
814 return count;
815} // int DumpMD_VWrite()
816
817int DumpMD_VWriteLine(__in __in_z const char *str, ...)
818{
819 va_list marker;
820 int count;
821
822 va_start(marker, str);
823 count = DumpMD_VWriteMarker(str, marker);
824 DumpMD_Write("\n");
825 va_end(marker);
826 return count;
827} // int DumpMD_VWriteLine()
828
829
830const char *DumpMD_DumpRawNameOfType(RegMeta *pMD, ULONG iType)
831{
832 if (iType <= iRidMax)
833 {
834 const char *pNameTable;
835 pMD->GetTableInfo(iType, 0,0,0,0, &pNameTable);
836 return pNameTable;
837 }
838 else
839 // Is the field a coded token?
840 if (iType <= iCodedTokenMax)
841 {
842 int iCdTkn = iType - iCodedToken;
843 const char *pNameCdTkn;
844 pMD->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn);
845 return pNameCdTkn;
846 }
847
848 // Fixed type.
849 switch (iType)
850 {
851 case iBYTE:
852 return "BYTE";
853 case iSHORT:
854 return "short";
855 case iUSHORT:
856 return "USHORT";
857 case iLONG:
858 return "long";
859 case iULONG:
860 return "ULONG";
861 case iSTRING:
862 return "string";
863 case iGUID:
864 return "GUID";
865 case iBLOB:
866 return "blob";
867 }
868 // default:
869 static char buf[30];
870 sprintf_s(buf, NumItems(buf), "unknown type 0x%02x", iType);
871 return buf;
872} // const char *DumpMD_DumpRawNameOfType()
873
874void DumpMD_DumpRawCol(RegMeta *pMD, ULONG ixTbl, ULONG ixCol, ULONG rid, bool bStats)
875{
876 ULONG ulType; // Type of a column.
877 ULONG ulVal; // Value of a column.
878 LPCUTF8 pString; // Pointer to a string.
879 const void *pBlob; // Pointer to a blob.
880 ULONG cb; // Size of something.
881
882 pMD->GetColumn(ixTbl, ixCol, rid, &ulVal);
883 pMD->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0);
884
885 if (ulType <= iRidMax)
886 {
887 const char *pNameTable;
888 pMD->GetTableInfo(ulType, 0,0,0,0, &pNameTable);
889 DumpMD_VWrite("%s[%x]", pNameTable, ulVal);
890 }
891 else
892 // Is the field a coded token?
893 if (ulType <= iCodedTokenMax)
894 {
895 int iCdTkn = ulType - iCodedToken;
896 const char *pNameCdTkn;
897 pMD->GetCodedTokenInfo(iCdTkn, 0,0, &pNameCdTkn);
898 DumpMD_VWrite("%s[%08x]", pNameCdTkn, ulVal);
899 }
900 else
901 {
902 // Fixed type.
903 switch (ulType)
904 {
905 case iBYTE:
906 DumpMD_VWrite("%02x", ulVal);
907 break;
908 case iSHORT:
909 case iUSHORT:
910 DumpMD_VWrite("%04x", ulVal);
911 break;
912 case iLONG:
913 case iULONG:
914 DumpMD_VWrite("%08x", ulVal);
915 break;
916 case iSTRING:
917 DumpMD_VWrite("string#%x", ulVal);
918 if (bStats && ulVal)
919 {
920 pMD->GetString(ulVal, &pString);
921 cb = (ULONG) strlen(pString) + 1;
922 DumpMD_VWrite("(%d)", cb);
923 }
924 break;
925 case iGUID:
926 DumpMD_VWrite("guid#%x", ulVal);
927 if (bStats && ulVal)
928 {
929 DumpMD_VWrite("(16)");
930 }
931 break;
932 case iBLOB:
933 DumpMD_VWrite("blob#%x", ulVal);
934 if (bStats && ulVal)
935 {
936 pMD->GetBlob(ulVal, &cb, &pBlob);
937 cb += 1;
938 if (cb > 128)
939 cb += 1;
940 if (cb > 16535)
941 cb += 1;
942 DumpMD_VWrite("(%d)", cb);
943 }
944 break;
945 default:
946 DumpMD_VWrite("unknown type 0x%04x", ulVal);
947 break;
948 }
949 }
950} // void DumpMD_DumpRawCol()
951
952ULONG DumpMD_DumpRawColStats(RegMeta *pMD, ULONG ixTbl, ULONG ixCol, ULONG cRows)
953{
954 ULONG rslt = 0;
955 ULONG ulType; // Type of a column.
956 ULONG ulVal; // Value of a column.
957 LPCUTF8 pString; // Pointer to a string.
958 const void *pBlob; // Pointer to a blob.
959 ULONG cb; // Size of something.
960
961 pMD->GetColumnInfo(ixTbl, ixCol, 0, 0, &ulType, 0);
962
963 if (IsHeapType(ulType))
964 {
965 for (ULONG rid=1; rid<=cRows; ++rid)
966 {
967 pMD->GetColumn(ixTbl, ixCol, rid, &ulVal);
968 // Fixed type.
969 switch (ulType)
970 {
971 case iSTRING:
972 if (ulVal)
973 {
974 pMD->GetString(ulVal, &pString);
975 cb = (ULONG) strlen(pString);
976 rslt += cb + 1;
977 }
978 break;
979 case iGUID:
980 if (ulVal)
981 rslt += 16;
982 break;
983 case iBLOB:
984 if (ulVal)
985 {
986 pMD->GetBlob(ulVal, &cb, &pBlob);
987 rslt += cb + 1;
988 if (cb > 128)
989 rslt += 1;
990 if (cb > 16535)
991 rslt += 1;
992 }
993 break;
994 default:
995 break;
996 }
997 }
998 }
999 return rslt;
1000} // ULONG DumpMD_DumpRawColStats()
1001
1002int DumpMD_DumpHex(
1003 const char *szPrefix, // String prefix for first line.
1004 const void *pvData, // The data to print.
1005 ULONG cbData, // Bytes of data to print.
1006 int bText=1, // If true, also dump text.
1007 ULONG nLine=16) // Bytes per line to print.
1008{
1009 const BYTE *pbData = static_cast<const BYTE*>(pvData);
1010 ULONG i; // Loop control.
1011 ULONG nPrint; // Number to print in an iteration.
1012 ULONG nSpace; // Spacing calculations.
1013 ULONG nPrefix; // Size of the prefix.
1014 ULONG nLines=0; // Number of lines printed.
1015 const char *pPrefix; // For counting spaces in the prefix.
1016
1017 // Round down to 8 characters.
1018 nLine = nLine & ~0x7;
1019
1020 for (nPrefix=0, pPrefix=szPrefix; *pPrefix; ++pPrefix)
1021 {
1022 if (*pPrefix == '\t')
1023 nPrefix = (nPrefix + 8) & ~7;
1024 else
1025 ++nPrefix;
1026 }
1027 //nPrefix = strlen(szPrefix);
1028 do
1029 { // Write the line prefix.
1030 if (szPrefix)
1031 DumpMD_VWrite("%s:", szPrefix);
1032 else
1033 DumpMD_VWrite("%*s:", nPrefix, "");
1034 szPrefix = 0;
1035 ++nLines;
1036
1037 // Calculate spacing.
1038 nPrint = min(cbData, nLine);
1039 nSpace = nLine - nPrint;
1040
1041 // dump in hex.
1042 for(i=0; i<nPrint; i++)
1043 {
1044 if ((i&7) == 0)
1045 DumpMD_Write(" ");
1046 DumpMD_VWrite("%02x ", pbData[i]);
1047 }
1048 if (bText)
1049 {
1050 // Space out to the text spot.
1051 if (nSpace)
1052 DumpMD_VWrite("%*s", nSpace*3+nSpace/8, "");
1053 // Dump in text.
1054 DumpMD_Write(">");
1055 for(i=0; i<nPrint; i++)
1056 DumpMD_VWrite("%c", (isprint(pbData[i])) ? pbData[i] : ' ');
1057 // Space out the text, and finish the line.
1058 DumpMD_VWrite("%*s<", nSpace, "");
1059 }
1060 DumpMD_VWriteLine("");
1061
1062 // Next data to print.
1063 cbData -= nPrint;
1064 pbData += nPrint;
1065 }
1066 while (cbData > 0);
1067
1068 return nLines;
1069} // int DumpMD_DumpHex()
1070
1071void DumpMD_DisplayUserStrings(
1072 RegMeta *pMD) // The scope to dump.
1073{
1074 HCORENUM stringEnum = NULL; // string enumerator.
1075 mdString Strings[ENUM_BUFFER_SIZE]; // String tokens from enumerator.
1076 CQuickArray<WCHAR> rUserString; // Buffer to receive string.
1077 WCHAR *szUserString; // Working pointer into buffer.
1078 ULONG chUserString; // Size of user string.
1079 CQuickArray<char> rcBuf; // Buffer to hold the BLOB version of the string.
1080 char *szBuf; // Working pointer into buffer.
1081 ULONG chBuf; // Saved size of the user string.
1082 ULONG count; // Items returned from enumerator.
1083 ULONG totalCount = 1; // Running count of strings.
1084 bool bUnprint = false; // Is an unprintable character found?
1085 HRESULT hr; // A result.
1086 while (SUCCEEDED(hr = pMD->EnumUserStrings( &stringEnum,
1087 Strings, NumItems(Strings), &count)) &&
1088 count > 0)
1089 {
1090 if (totalCount == 1)
1091 { // If only one, it is the NULL string, so don't print it.
1092 DumpMD_WriteLine("User Strings");
1093 DumpMD_WriteLine("-------------------------------------------------------");
1094 }
1095 for (ULONG i = 0; i < count; i++, totalCount++)
1096 {
1097 do { // Try to get the string into the existing buffer.
1098 hr = pMD->GetUserString( Strings[i], rUserString.Ptr(),(ULONG32)rUserString.MaxSize(), &chUserString);
1099 if (hr == CLDB_S_TRUNCATION)
1100 { // Buffer wasn't big enough, try to enlarge it.
1101 if (FAILED(rUserString.ReSizeNoThrow(chUserString)))
1102 DumpMD_VWriteLine("malloc failed: %#8x.", E_OUTOFMEMORY);
1103 continue;
1104 }
1105 } while (0);
1106 if (FAILED(hr)) DumpMD_VWriteLine("GetUserString failed: %#8x.", hr);
1107
1108 szUserString = rUserString.Ptr();
1109 chBuf = chUserString;
1110
1111 DumpMD_VWrite("%08x : (%2d) L\"", Strings[i], chUserString);
1112 while (chUserString)
1113 {
1114 switch (*szUserString)
1115 {
1116 case 0:
1117 DumpMD_Write("\\0"); break;
1118 case W('\r'):
1119 DumpMD_Write("\\r"); break;
1120 case W('\n'):
1121 DumpMD_Write("\\n"); break;
1122 case W('\t'):
1123 DumpMD_Write("\\t"); break;
1124 default:
1125 if (iswprint(*szUserString))
1126 DumpMD_VWrite("%lc", *szUserString);
1127 else
1128 {
1129 bUnprint = true;
1130 DumpMD_Write(".");
1131 }
1132 break;
1133 }
1134 ++szUserString;
1135 --chUserString;
1136 }
1137 DumpMD_WriteLine("\"");
1138
1139 // Print the user string as a blob if an unprintable character is found.
1140 if (bUnprint)
1141 {
1142 bUnprint = false;
1143 szUserString = rUserString.Ptr();
1144 // REVISIT_TODO: ReSizeNoThrow can fail. Check its return value and add an error path.
1145 rcBuf.ReSizeNoThrow(81); //(chBuf * 5 + 1);
1146 szBuf = rcBuf.Ptr();
1147 ULONG j,k;
1148 DumpMD_WriteLine("\t\tUser string has unprintables, hex format below:");
1149 for (j = 0,k=0; j < chBuf; j++)
1150 {
1151 // See rcBuf.ResSizeNoThrow(81) above
1152 sprintf_s (&szBuf[k*5],81-(k*5), "%04x ", szUserString[j]);
1153 k++;
1154 if((k==16)||(j == (chBuf-1)))
1155 {
1156 szBuf[k*5] = '\0';
1157 DumpMD_VWriteLine("\t\t%s", szBuf);
1158 k=0;
1159 }
1160 }
1161 }
1162 }
1163 }
1164 if (stringEnum)
1165 pMD->CloseEnum(stringEnum);
1166} // void MDInfo::DisplayUserStrings()
1167
1168void DumpMD_DumpRawHeaps(
1169 RegMeta *pMD) // The scope to dump.
1170{
1171 HRESULT hr; // A result.
1172 ULONG ulSize; // Bytes in a heap.
1173 const BYTE *pData; // Pointer to a blob.
1174 ULONG cbData; // Size of a blob.
1175 ULONG oData; // Offset of current blob.
1176 char rcPrefix[30]; // To format line prefix.
1177
1178 pMD->GetBlobHeapSize(&ulSize);
1179 DumpMD_VWriteLine("");
1180 DumpMD_VWriteLine("Blob Heap: %d(%#x) bytes", ulSize,ulSize);
1181 oData = 0;
1182 do
1183 {
1184 pMD->GetBlob(oData, &cbData, (const void**)&pData);
1185 sprintf_s(rcPrefix, NumItems(rcPrefix), "%5x,%-2x", oData, cbData);
1186 DumpMD_DumpHex(rcPrefix, pData, cbData);
1187 hr = pMD->GetNextBlob(oData, &oData);
1188 }
1189 while (hr == S_OK);
1190
1191 pMD->GetStringHeapSize(&ulSize);
1192 DumpMD_VWriteLine("");
1193 DumpMD_VWriteLine("String Heap: %d(%#x) bytes", ulSize,ulSize);
1194 oData = 0;
1195 const char *pString;
1196 do
1197 {
1198 pMD->GetString(oData, &pString);
1199 sprintf_s(rcPrefix, NumItems(rcPrefix), "%08x", oData);
1200 DumpMD_DumpHex(rcPrefix, pString, (ULONG)strlen(pString)+1);
1201 if (*pString != 0)
1202 DumpMD_VWrite("%08x: %s\n", oData, pString);
1203 hr = pMD->GetNextString(oData, &oData);
1204 }
1205 while (hr == S_OK);
1206 DumpMD_VWriteLine("");
1207
1208 DumpMD_DisplayUserStrings(pMD);
1209
1210} // void DumpMD_DumpRawHeaps()
1211
1212
1213void DumpMD_DumpRaw(RegMeta *pMD, int iDump, bool bStats)
1214{
1215 ULONG cTables; // Tables in the database.
1216 ULONG cCols; // Columns in a table.
1217 ULONG cRows; // Rows in a table.
1218 ULONG cbRow; // Bytes in a row of a table.
1219 ULONG iKey; // Key column of a table.
1220 const char *pNameTable; // Name of a table.
1221 ULONG oCol; // Offset of a column.
1222 ULONG cbCol; // Size of a column.
1223 ULONG ulType; // Type of a column.
1224 const char *pNameColumn; // Name of a column.
1225 ULONG ulSize;
1226
1227 pMD->GetNumTables(&cTables);
1228
1229 pMD->GetStringHeapSize(&ulSize);
1230 DumpMD_VWrite("Strings: %d(%#x)", ulSize, ulSize);
1231 pMD->GetBlobHeapSize(&ulSize);
1232 DumpMD_VWrite(", Blobs: %d(%#x)", ulSize, ulSize);
1233 pMD->GetGuidHeapSize(&ulSize);
1234 DumpMD_VWrite(", Guids: %d(%#x)", ulSize, ulSize);
1235 pMD->GetUserStringHeapSize(&ulSize);
1236 DumpMD_VWriteLine(", User strings: %d(%#x)", ulSize, ulSize);
1237
1238 for (ULONG ixTbl = 0; ixTbl < cTables; ++ixTbl)
1239 {
1240 pMD->GetTableInfo(ixTbl, &cbRow, &cRows, &cCols, &iKey, &pNameTable);
1241
1242 if (cRows == 0 && iDump < 3)
1243 continue;
1244
1245 if (iDump >= 2)
1246 DumpMD_VWriteLine("=================================================");
1247 DumpMD_VWriteLine("%2d: %-20s cRecs:%5d(%#x), cbRec:%3d(%#x), cbTable:%6d(%#x)",
1248 ixTbl, pNameTable, cRows, cRows, cbRow, cbRow, cbRow * cRows, cbRow * cRows);
1249
1250 if (iDump < 2)
1251 continue;
1252
1253 // Dump column definitions for the table.
1254 ULONG ixCol;
1255 for (ixCol=0; ixCol<cCols; ++ixCol)
1256 {
1257 pMD->GetColumnInfo(ixTbl, ixCol, &oCol, &cbCol, &ulType, &pNameColumn);
1258
1259 DumpMD_VWrite(" col %2x:%c %-12s oCol:%2x, cbCol:%x, %-7s",
1260 ixCol, ((ixCol==iKey)?'*':' '), pNameColumn, oCol, cbCol, DumpMD_DumpRawNameOfType(pMD, ulType));
1261
1262 if (bStats)
1263 {
1264 ulSize = DumpMD_DumpRawColStats(pMD, ixTbl, ixCol, cRows);
1265 if (ulSize)
1266 DumpMD_VWrite("(%d)", ulSize);
1267 }
1268 DumpMD_VWriteLine("");
1269 }
1270
1271 if (iDump < 3)
1272 continue;
1273
1274 // Dump the rows.
1275 for (ULONG rid = 1; rid <= cRows; ++rid)
1276 {
1277 if (rid == 1)
1278 DumpMD_VWriteLine("-------------------------------------------------");
1279 DumpMD_VWrite(" %3x == ", rid);
1280 for (ixCol=0; ixCol < cCols; ++ixCol)
1281 {
1282 if (ixCol) DumpMD_VWrite(", ");
1283 DumpMD_VWrite("%d:", ixCol);
1284 DumpMD_DumpRawCol(pMD, ixTbl, ixCol, rid, bStats);
1285 }
1286 DumpMD_VWriteLine("");
1287 }
1288 }
1289
1290 DumpMD_DumpRawHeaps(pMD);
1291
1292} // void DumpMD_DumpRaw()
1293
1294
1295int DumpMD_impl(RegMeta *pMD)
1296{
1297 DumpMD_DumpRaw(pMD, 3, false);
1298 return 0;
1299}
1300
1301int DumpMD(UINT_PTR iMD)
1302{
1303 RegMeta *pMD = reinterpret_cast<RegMeta*>(iMD);
1304 return DumpMD_impl(pMD);
1305}
1306
1307#endif //_DEBUG
1308
1309//*****************************************************************************
1310// Using the existing RegMeta and reopen with another chuck of memory. Make sure that all stgdb
1311// is still kept alive.
1312//*****************************************************************************
1313HRESULT RegMeta::ReOpenWithMemory(
1314 LPCVOID pData, // [in] Location of scope data.
1315 ULONG cbData, // [in] Size of the data pointed to by pData.
1316 DWORD dwReOpenFlags) // [in] ReOpen flags
1317{
1318 HRESULT hr = NOERROR;
1319
1320 // Only allow the ofCopyMemory and ofTakeOwnership flags
1321 if (dwReOpenFlags != 0 && ((dwReOpenFlags & (~(ofCopyMemory|ofTakeOwnership))) > 0))
1322 return E_INVALIDARG;
1323
1324 LOCKWRITE();
1325
1326
1327 // put the current m_pStgdb to the free list
1328 m_pStgdb->m_pNextStgdb = m_pStgdbFreeList;
1329 m_pStgdbFreeList = m_pStgdb;
1330 m_pStgdb = new (nothrow) CLiteWeightStgdbRW;
1331 IfNullGo( m_pStgdb );
1332 IfFailGo( OpenExistingMD(0 /* szFileName */, const_cast<void*>(pData), cbData, ofReOpen|dwReOpenFlags /* flags */) );
1333
1334#ifdef FEATURE_METADATA_INTERNAL_APIS
1335 // We've created a new Stgdb, but may still have an Internal Importer hanging around accessing the old Stgdb.
1336 // The free list ensures we don't have a dangling pointer, but the
1337 // If we have a corresponding InternalInterface, need to clear it because it's now using stale data.
1338 // Others will need to update their Internal interface to get the new data.
1339 {
1340 HRESULT hrIgnore = SetCachedInternalInterface(NULL);
1341 (void)hrIgnore; //prevent "unused variable" error from GCC
1342 _ASSERTE(hrIgnore == NOERROR); // clearing the cached interface should always succeed.
1343 }
1344#endif //FEATURE_METADATA_INTERNAL_APIS
1345
1346 // we are done!
1347ErrExit:
1348 if (FAILED(hr))
1349 {
1350 // recover to the old state
1351 if (m_pStgdb)
1352 delete m_pStgdb;
1353 m_pStgdb = m_pStgdbFreeList;
1354 m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb;
1355 }
1356#ifdef FEATURE_METADATA_RELEASE_MEMORY_ON_REOPEN
1357 else
1358 {
1359 if( !(CLRConfig::GetConfigValue(CLRConfig::EXTERNAL_MD_PreserveDebuggerMetadataMemory)) && IsSafeToDeleteStgdb())
1360 {
1361 // now that success is assured, delete the old block of memory
1362 // This isn't normally a safe operation because we would have given out
1363 // internal pointers to the memory. However when this feature is enabled
1364 // we track calls that might have given out internal pointers. If none
1365 // of the APIs were ever called then we can safely delete.
1366 CLiteWeightStgdbRW* pStgdb = m_pStgdbFreeList;
1367 m_pStgdbFreeList = m_pStgdbFreeList->m_pNextStgdb;
1368 delete pStgdb;
1369 }
1370
1371 MarkSafeToDeleteStgdb(); // As of right now, no APIs have given out internal pointers
1372 // to the newly allocated stgdb
1373 }
1374#endif
1375
1376 return hr;
1377} // RegMeta::ReOpenWithMemory
1378
1379
1380//*****************************************************************************
1381// This function returns the requested public interface based on the given
1382// internal import interface.
1383// A common path to call this is updating the matedata for dynamic modules.
1384//*****************************************************************************
1385STDAPI MDReOpenMetaDataWithMemoryEx(
1386 void *pImport, // [IN] Given scope. public interfaces
1387 LPCVOID pData, // [in] Location of scope data.
1388 ULONG cbData, // [in] Size of the data pointed to by pData.
1389 DWORD dwReOpenFlags) // [in] Flags for ReOpen
1390{
1391 HRESULT hr = S_OK;
1392 IUnknown *pUnk = (IUnknown *) pImport;
1393 IMetaDataImport2 *pMDImport = NULL;
1394 RegMeta *pRegMeta = NULL;
1395
1396 _ASSERTE(pImport);
1397
1398 IfFailGo( pUnk->QueryInterface(IID_IMetaDataImport2, (void **) &pMDImport) );
1399 pRegMeta = (RegMeta*) pMDImport;
1400
1401 IfFailGo( pRegMeta->ReOpenWithMemory(pData, cbData, dwReOpenFlags) );
1402
1403ErrExit:
1404 if (pMDImport)
1405 pMDImport->Release();
1406
1407 return hr;
1408} // MDReOpenMetaDataWithMemoryEx
1409
1410STDAPI MDReOpenMetaDataWithMemory(
1411 void *pImport, // [IN] Given scope. public interfaces
1412 LPCVOID pData, // [in] Location of scope data.
1413 ULONG cbData) // [in] Size of the data pointed to by pData.
1414{
1415 return MDReOpenMetaDataWithMemoryEx(pImport, pData, cbData, 0);
1416}
1417
1418// --------------------------------------------------------------------------------------
1419//
1420// Zeros used by public APIs as return value (or pointer to this memory) for invalid input.
1421// It is used by methods:
1422// * code:RegMeta::GetPublicApiCompatibilityZeros, and
1423// * code:RegMeta::GetPublicApiCompatibilityZerosOfSize.
1424//
1425const BYTE
1426RegMeta::s_rgMetaDataPublicApiCompatibilityZeros[64] =
1427{
1428 0, 0, 0, 0, 0, 0, 0, 0,
1429 0, 0, 0, 0, 0, 0, 0, 0,
1430 0, 0, 0, 0, 0, 0, 0, 0,
1431 0, 0, 0, 0, 0, 0, 0, 0,
1432 0, 0, 0, 0, 0, 0, 0, 0,
1433 0, 0, 0, 0, 0, 0, 0, 0,
1434 0, 0, 0, 0, 0, 0, 0, 0,
1435 0, 0, 0, 0, 0, 0, 0, 0,
1436};
1437
1438// --------------------------------------------------------------------------------------
1439//
1440// Returns pointer to zeros of size (cbSize).
1441// Used by public APIs to return compatible values with previous releases.
1442//
1443const BYTE *
1444RegMeta::GetPublicApiCompatibilityZerosOfSize(UINT32 cbSize)
1445{
1446 if (cbSize <= sizeof(s_rgMetaDataPublicApiCompatibilityZeros))
1447 {
1448 return s_rgMetaDataPublicApiCompatibilityZeros;
1449 }
1450 _ASSERTE(!"Dangerous call to this method! Reconsider fixing the caller.");
1451 return NULL;
1452} // RegMeta::GetPublicApiCompatibilityZerosOfSize
1453
1454
1455
1456
1457//
1458// returns the "built for" version of a metadata scope.
1459//
1460HRESULT RegMeta::GetVersionString( // S_OK or error.
1461 LPCSTR *pVer) // [OUT] Put version string here.
1462{
1463 _ASSERTE(pVer != NULL);
1464 HRESULT hr;
1465 LOCKREAD();
1466#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
1467 if (m_pStgdb->m_pvMd != NULL)
1468 {
1469#endif
1470 *pVer = reinterpret_cast<const char*>(reinterpret_cast<const STORAGESIGNATURE*>(m_pStgdb->m_pvMd)->pVersion);
1471#ifdef FEATURE_METADATA_CUSTOM_DATA_SOURCE
1472 }
1473 else
1474 {
1475 //This emptry string matches the fallback behavior we have in other places that query the version string.
1476 //From what I can tell the only caller to this method is the code that tests if we need to apply the WinMD adapter
1477 //and it checks if pVer == "WindowsRuntime". We don't support the debugger custom metadata source scenario with WinMDs (yet?)
1478 //so we intend for that check to return FALSE.
1479 *pVer = "";
1480 }
1481#endif
1482 hr = S_OK;
1483 ErrExit:
1484 return hr;
1485}
1486
1487#ifdef FEATURE_METADATA_INTERNAL_APIS
1488//*****************************************************************************
1489// IGetIMDInternalImport methods
1490//*****************************************************************************
1491HRESULT RegMeta::GetIMDInternalImport(
1492 IMDInternalImport ** ppIMDInternalImport // [OUT] Buffer to receive IMDInternalImport*
1493 )
1494{
1495 HRESULT hr = S_OK;
1496 MDInternalRW *pInternalRW = NULL;
1497 bool isLockedForWrite = false;
1498 IUnknown *pIUnkInternal = NULL;
1499 IUnknown *pThis = (IMetaDataImport2*)this;
1500
1501 pIUnkInternal = this->GetCachedInternalInterface(TRUE);
1502 if (pIUnkInternal)
1503 {
1504 // there is already a cached Internal interface. GetCachedInternalInterface does add ref the
1505 // returned interface
1506 IfFailGo(pIUnkInternal->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport));
1507 goto ErrExit;
1508 }
1509
1510 if (this->IsThreadSafetyOn())
1511 {
1512 _ASSERTE( this->GetReaderWriterLock() );
1513 IfFailGo(this->GetReaderWriterLock()->LockWrite());
1514 isLockedForWrite = true;
1515 }
1516
1517 // check again. Maybe someone else beat us to setting the internal interface while we are waiting
1518 // for the write lock. Don't need to grab the read lock since we already have the write lock.
1519 pIUnkInternal = this->GetCachedInternalInterface(FALSE);
1520 if (pIUnkInternal)
1521 {
1522 // there is already a cached Internal interface. GetCachedInternalInterface does add ref the
1523 // returned interface
1524 IfFailGo(pIUnkInternal->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport));
1525 goto ErrExit;
1526 }
1527
1528 // now create the compressed object
1529 IfNullGo( pInternalRW = new (nothrow) MDInternalRW );
1530 IfFailGo( pInternalRW->InitWithStgdb(pThis, this->GetMiniStgdb() ) );
1531
1532 // make the public object and the internal object point to each other.
1533 _ASSERTE( pInternalRW->GetReaderWriterLock() == NULL &&
1534 (! this->IsThreadSafetyOn() || this->GetReaderWriterLock() != NULL ));
1535 IfFailGo( this->SetCachedInternalInterface(static_cast<IMDInternalImportENC*>(pInternalRW)) );
1536 IfFailGo( pInternalRW->SetCachedPublicInterface(pThis));
1537 IfFailGo( pInternalRW->SetReaderWriterLock(this->GetReaderWriterLock() ));
1538 IfFailGo( pInternalRW->QueryInterface(IID_IMDInternalImport, (void**)ppIMDInternalImport));
1539
1540ErrExit:
1541 if (isLockedForWrite == true)
1542 this->GetReaderWriterLock()->UnlockWrite();
1543 if (pIUnkInternal)
1544 pIUnkInternal->Release();
1545 if (pInternalRW)
1546 pInternalRW->Release();
1547 if (FAILED(hr))
1548 {
1549 if (ppIMDInternalImport)
1550 *ppIMDInternalImport = 0;
1551 }
1552 return hr;
1553}
1554#endif //FEATURE_METADATA_INTERNAL_APIS
1555
1556