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 | //***************************************************************************** |
27 | Disp::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 | |
51 | Disp::~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 |
62 | HRESULT |
63 | Disp::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 | |
164 | ErrExit: |
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 | //***************************************************************************** |
184 | static 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 | //***************************************************************************** |
209 | HRESULT 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 |
240 | HRESULT |
241 | Disp::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 | |
329 | ErrExit: |
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 | //***************************************************************************** |
346 | HRESULT 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 | //***************************************************************************** |
375 | HRESULT 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 | |
416 | ErrExit: |
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 | //***************************************************************************** |
434 | HRESULT |
435 | Disp::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 | |
447 | HRESULT 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 | |
462 | HRESULT 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 | //***************************************************************************** |
481 | HRESULT 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 | |
499 | HRESULT 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)); |
517 | ErrExit: |
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 | //***************************************************************************** |
528 | HRESULT 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 | |
569 | ErrExit: |
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 | |
587 | ULONG Disp::AddRef() |
588 | { |
589 | return InterlockedIncrement(&m_cRef); |
590 | } // Disp::AddRef |
591 | |
592 | ULONG Disp::Release() |
593 | { |
594 | ULONG cRef = InterlockedDecrement(&m_cRef); |
595 | if (cRef == 0) |
596 | delete this; |
597 | return cRef; |
598 | } // Disp::Release |
599 | |
600 | HRESULT 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 | //***************************************************************************** |
624 | HRESULT 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 |
645 | HRESULT |
646 | Disp::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 | |
806 | ErrExit: |
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 | //***************************************************************************** |
817 | HRESULT 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 | } |
872 | ErrExit: |
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 | // |
885 | void 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. |
898 | HRESULT InternalCreateMetaDataDispenser(REFIID riid, void ** pMetaDataDispenserOut) |
899 | { |
900 | _ASSERTE(pMetaDataDispenserOut != NULL); |
901 | return Disp::CreateObject(riid, pMetaDataDispenserOut); |
902 | } |
903 | |