| 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 | // MDUtil.cpp |
| 6 | // |
| 7 | |
| 8 | // |
| 9 | // contains utility code to MD directory. This is only used for the full version. |
| 10 | // |
| 11 | //***************************************************************************** |
| 12 | #include "stdafx.h" |
| 13 | #include "metadata.h" |
| 14 | #include "mdutil.h" |
| 15 | #include "regmeta.h" |
| 16 | #include "disp.h" |
| 17 | #include "mdcommon.h" |
| 18 | #include "importhelper.h" |
| 19 | #include "sstring.h" |
| 20 | |
| 21 | #include <rwutil.h> |
| 22 | |
| 23 | #if defined(FEATURE_METADATA_IN_VM) |
| 24 | |
| 25 | LOADEDMODULES * LOADEDMODULES::s_pLoadedModules = NULL; |
| 26 | UTSemReadWrite * LOADEDMODULES::m_pSemReadWrite = NULL; |
| 27 | RegMeta * (LOADEDMODULES::m_HashedModules[LOADEDMODULES_HASH_SIZE]) = { NULL }; |
| 28 | |
| 29 | //***************************************************************************** |
| 30 | // Hash a file name. |
| 31 | //***************************************************************************** |
| 32 | ULONG LOADEDMODULES::HashFileName( |
| 33 | LPCWSTR szName) |
| 34 | { |
| 35 | return HashString(szName) % LOADEDMODULES_HASH_SIZE; |
| 36 | } // LOADEDMODULES::HashFileName |
| 37 | |
| 38 | //--------------------------------------------------------------------------------------- |
| 39 | // |
| 40 | // Initialize the static instance and lock. |
| 41 | // |
| 42 | HRESULT |
| 43 | LOADEDMODULES::InitializeStatics() |
| 44 | { |
| 45 | HRESULT hr = S_OK; |
| 46 | |
| 47 | if (VolatileLoad(&s_pLoadedModules) == NULL) |
| 48 | { |
| 49 | // Initialize global read-write lock |
| 50 | { |
| 51 | NewHolder<UTSemReadWrite> pSemReadWrite = new (nothrow) UTSemReadWrite(); |
| 52 | IfNullGo(pSemReadWrite); |
| 53 | IfFailGo(pSemReadWrite->Init()); |
| 54 | |
| 55 | if (InterlockedCompareExchangeT<UTSemReadWrite *>(&m_pSemReadWrite, pSemReadWrite, NULL) == NULL) |
| 56 | { // We won the initialization race |
| 57 | pSemReadWrite.SuppressRelease(); |
| 58 | } |
| 59 | } |
| 60 | |
| 61 | // Initialize the global instance |
| 62 | { |
| 63 | NewHolder<LOADEDMODULES> pLoadedModules = new (nothrow) LOADEDMODULES(); |
| 64 | IfNullGo(pLoadedModules); |
| 65 | |
| 66 | { |
| 67 | LOCKWRITE(); |
| 68 | |
| 69 | if (VolatileLoad(&s_pLoadedModules) == NULL) |
| 70 | { |
| 71 | VolatileStore(&s_pLoadedModules, pLoadedModules.Extract()); |
| 72 | } |
| 73 | } |
| 74 | } |
| 75 | } |
| 76 | |
| 77 | ErrExit: |
| 78 | return hr; |
| 79 | } // LOADEDMODULES::InitializeStatics |
| 80 | |
| 81 | //--------------------------------------------------------------------------------------- |
| 82 | // |
| 83 | // Destroy the static instance and lock. |
| 84 | // |
| 85 | void |
| 86 | LOADEDMODULES::DeleteStatics() |
| 87 | { |
| 88 | HRESULT hr = S_OK; |
| 89 | |
| 90 | if (s_pLoadedModules != NULL) |
| 91 | { |
| 92 | delete s_pLoadedModules; |
| 93 | s_pLoadedModules = NULL; |
| 94 | } |
| 95 | if (m_pSemReadWrite != NULL) |
| 96 | { |
| 97 | delete m_pSemReadWrite; |
| 98 | m_pSemReadWrite = NULL; |
| 99 | } |
| 100 | } // LOADEDMODULES::DeleteStatics |
| 101 | |
| 102 | //***************************************************************************** |
| 103 | // Add a RegMeta pointer to the loaded module list |
| 104 | //***************************************************************************** |
| 105 | HRESULT LOADEDMODULES::AddModuleToLoadedList(RegMeta * pRegMeta) |
| 106 | { |
| 107 | HRESULT hr = NOERROR; |
| 108 | RegMeta ** ppRegMeta; |
| 109 | |
| 110 | IfFailGo(InitializeStatics()); |
| 111 | |
| 112 | { |
| 113 | LOCKWRITE(); |
| 114 | |
| 115 | ppRegMeta = s_pLoadedModules->Append(); |
| 116 | IfNullGo(ppRegMeta); |
| 117 | |
| 118 | // The cache holds a copy of the pointer, but no ref-count. There is no |
| 119 | // point to the ref-count, because it just changes comparisons against 0 |
| 120 | // to comparisons against 1. |
| 121 | *ppRegMeta = pRegMeta; |
| 122 | |
| 123 | // If the module is read-only, hash it. |
| 124 | if (pRegMeta->IsReadOnly()) |
| 125 | { |
| 126 | ULONG ixHash = HashFileName(pRegMeta->GetNameOfDBFile()); |
| 127 | m_HashedModules[ixHash] = pRegMeta; |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | ErrExit: |
| 132 | return hr; |
| 133 | } // LOADEDMODULES::AddModuleToLoadedList |
| 134 | |
| 135 | //***************************************************************************** |
| 136 | // Remove a RegMeta pointer from the loaded module list |
| 137 | //***************************************************************************** |
| 138 | BOOL LOADEDMODULES::RemoveModuleFromLoadedList(RegMeta * pRegMeta) |
| 139 | { |
| 140 | BOOL bRemoved = FALSE; // Was this module removed from the cache? |
| 141 | int iFound = -1; // Index at which it was found. |
| 142 | ULONG cRef; // Ref count of the module. |
| 143 | |
| 144 | // Lock the cache for write, so that no other thread will find what this |
| 145 | // thread is about to delete, and so that no other thread will delete |
| 146 | // what this thread is about to try to find. |
| 147 | HRESULT hr = S_OK; |
| 148 | |
| 149 | IfFailGo(InitializeStatics()); |
| 150 | |
| 151 | { |
| 152 | LOCKWRITE(); |
| 153 | |
| 154 | // Search for this module in list of loaded modules. |
| 155 | int count = s_pLoadedModules->Count(); |
| 156 | for (int index = 0; index < count; index++) |
| 157 | { |
| 158 | if ((*s_pLoadedModules)[index] == pRegMeta) |
| 159 | { // found a match to remove |
| 160 | iFound = index; |
| 161 | break; |
| 162 | } |
| 163 | } |
| 164 | |
| 165 | // If the module is still in the cache, it hasn't been deleted yet. |
| 166 | if (iFound >= 0) |
| 167 | { |
| 168 | // See if there are any external references left. |
| 169 | cRef = pRegMeta->GetRefCount(); |
| 170 | |
| 171 | // If the cRef that we got from the module is zero, it will stay that way, |
| 172 | // because no other thread can discover the module while this thread holds |
| 173 | // the lock. |
| 174 | |
| 175 | // OTOH, if the cRef is not zero, this thread can just return, because the |
| 176 | // other thread will eventually take the ref count to zero, and will then |
| 177 | // come through here to clean up the module. And this thread must not |
| 178 | // delete the module out from under other threads. |
| 179 | |
| 180 | // It is possible that the cRef is zero, yet another thread has a pointer that |
| 181 | // it discovered before this thread took the lock. (And that thread has |
| 182 | // released the ref-counts.) In such a case, this thread can still remove the |
| 183 | // module from the cache, and tell the caller to delete it, because the |
| 184 | // other thread will wait on the lock, then discover that the module |
| 185 | // is not in the cache, and it won't try to delete the module. |
| 186 | |
| 187 | if (cRef != 0) |
| 188 | { // Some other thread snuck in and found the entry in the cache. |
| 189 | return FALSE; |
| 190 | } |
| 191 | |
| 192 | // No other thread owns the object. Remove from cache, and tell caller |
| 193 | // that we're done with it. (Caller will delete.) |
| 194 | s_pLoadedModules->Delete(iFound); |
| 195 | bRemoved = TRUE; |
| 196 | |
| 197 | // If the module is read-only, remove from hash. |
| 198 | if (pRegMeta->IsReadOnly()) |
| 199 | { |
| 200 | // There may have been multiple capitalizations pointing to the same entry. |
| 201 | // Find and remove all of them. |
| 202 | for (ULONG ixHash = 0; ixHash < LOADEDMODULES_HASH_SIZE; ++ixHash) |
| 203 | { |
| 204 | if (m_HashedModules[ixHash] == pRegMeta) |
| 205 | m_HashedModules[ixHash] = NULL; |
| 206 | } |
| 207 | } |
| 208 | } |
| 209 | } |
| 210 | |
| 211 | ErrExit: |
| 212 | return bRemoved; |
| 213 | } // LOADEDMODULES::RemoveModuleFromLoadedList |
| 214 | |
| 215 | |
| 216 | //***************************************************************************** |
| 217 | // Search the cached RegMetas for a given scope. |
| 218 | //***************************************************************************** |
| 219 | HRESULT LOADEDMODULES::FindCachedReadOnlyEntry( |
| 220 | LPCWSTR szName, // Name of the desired file. |
| 221 | DWORD dwOpenFlags, // Flags the new file is opened with. |
| 222 | RegMeta ** ppMeta) // Put found RegMeta here. |
| 223 | { |
| 224 | RegMeta * pRegMeta = 0; |
| 225 | BOOL bWillBeCopyMemory; // Will the opened file be copied to memory? |
| 226 | DWORD dwLowFileSize; // Low bytes of this file's size |
| 227 | DWORD dwLowFileTime; // Low butes of this file's last write time |
| 228 | HRESULT hr; |
| 229 | ULONG ixHash = 0; |
| 230 | |
| 231 | IfFailGo(InitializeStatics()); |
| 232 | |
| 233 | { |
| 234 | LOCKREAD(); |
| 235 | |
| 236 | hr = S_FALSE; // We haven't found a match yet. |
| 237 | |
| 238 | // Avoid confusion. |
| 239 | *ppMeta = NULL; |
| 240 | |
| 241 | bWillBeCopyMemory = IsOfCopyMemory(dwOpenFlags); |
| 242 | |
| 243 | // The cache is locked for read, so the list will not change. |
| 244 | |
| 245 | // Figure out the size and timestamp of this file |
| 246 | WIN32_FILE_ATTRIBUTE_DATA faData; |
| 247 | if (!WszGetFileAttributesEx(szName, GetFileExInfoStandard, &faData)) |
| 248 | return E_FAIL; |
| 249 | dwLowFileSize = faData.nFileSizeLow; |
| 250 | dwLowFileTime = faData.ftLastWriteTime.dwLowDateTime; |
| 251 | |
| 252 | // Check the hash first. |
| 253 | ixHash = HashFileName(szName); |
| 254 | if ((pRegMeta = m_HashedModules[ixHash]) != NULL) |
| 255 | { |
| 256 | _ASSERTE(pRegMeta->IsReadOnly()); |
| 257 | |
| 258 | // Only match if the IsOfCopyMemory() bit is the same in both. This is because |
| 259 | // when ofCopyMemory is set, the file is not locked on disk, and may become stale |
| 260 | // in memory. |
| 261 | // |
| 262 | // Also, only match if the date and size are the same |
| 263 | if (pRegMeta->IsCopyMemory() == bWillBeCopyMemory && |
| 264 | pRegMeta->GetLowFileTimeOfDBFile() == dwLowFileTime && |
| 265 | pRegMeta->GetLowFileSizeOfDBFile() == dwLowFileSize) |
| 266 | { |
| 267 | // If the name matches... |
| 268 | LPCWSTR pszName = pRegMeta->GetNameOfDBFile(); |
| 269 | #ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM |
| 270 | if (wcscmp(szName, pszName) == 0) |
| 271 | #else |
| 272 | if (SString::_wcsicmp(szName, pszName) == 0) |
| 273 | #endif |
| 274 | { |
| 275 | ULONG cRefs; |
| 276 | |
| 277 | // Found it. Add a reference, and return it. |
| 278 | *ppMeta = pRegMeta; |
| 279 | cRefs = pRegMeta->AddRef(); |
| 280 | |
| 281 | LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope found cached RegMeta in hash: %#8x, crefs: %d\n" , pRegMeta, cRefs)); |
| 282 | |
| 283 | return S_OK; |
| 284 | } |
| 285 | } |
| 286 | } |
| 287 | |
| 288 | // Not found in hash; loop through each loaded modules |
| 289 | int count = s_pLoadedModules->Count(); |
| 290 | for (int index = 0; index < count; index++) |
| 291 | { |
| 292 | pRegMeta = (*s_pLoadedModules)[index]; |
| 293 | |
| 294 | // If the module is read-only, and the CopyMemory bit matches, and the date |
| 295 | // and size are the same.... |
| 296 | if (pRegMeta->IsReadOnly() && |
| 297 | pRegMeta->IsCopyMemory() == bWillBeCopyMemory && |
| 298 | pRegMeta->GetLowFileTimeOfDBFile() == dwLowFileTime && |
| 299 | pRegMeta->GetLowFileSizeOfDBFile() == dwLowFileSize) |
| 300 | { |
| 301 | // If the name matches... |
| 302 | LPCWSTR pszName = pRegMeta->GetNameOfDBFile(); |
| 303 | #ifdef FEATURE_CASE_SENSITIVE_FILESYSTEM |
| 304 | if (wcscmp(szName, pszName) == 0) |
| 305 | #else |
| 306 | if (SString::_wcsicmp(szName, pszName) == 0) |
| 307 | #endif |
| 308 | { |
| 309 | ULONG cRefs; |
| 310 | |
| 311 | // Found it. Add a reference, and return it. |
| 312 | *ppMeta = pRegMeta; |
| 313 | cRefs = pRegMeta->AddRef(); |
| 314 | |
| 315 | // Update the hash. |
| 316 | m_HashedModules[ixHash] = pRegMeta; |
| 317 | |
| 318 | LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope found cached RegMeta by search: %#8x, crefs: %d\n" , pRegMeta, cRefs)); |
| 319 | |
| 320 | return S_OK; |
| 321 | } |
| 322 | } |
| 323 | } |
| 324 | } |
| 325 | |
| 326 | ErrExit: |
| 327 | // Didn't find it. |
| 328 | LOG((LF_METADATA, LL_INFO10, "Disp::OpenScope did not find cached RegMeta\n" )); |
| 329 | |
| 330 | _ASSERTE(hr != S_OK); |
| 331 | return hr; |
| 332 | } // LOADEDMODULES::FindCachedReadOnlyEntry |
| 333 | |
| 334 | #ifdef _DEBUG |
| 335 | |
| 336 | //***************************************************************************** |
| 337 | // Search the cached RegMetas for a given scope. |
| 338 | //***************************************************************************** |
| 339 | BOOL LOADEDMODULES::IsEntryInList( |
| 340 | RegMeta * pRegMeta) |
| 341 | { |
| 342 | HRESULT hr = S_OK; |
| 343 | |
| 344 | IfFailGo(InitializeStatics()); |
| 345 | |
| 346 | { |
| 347 | LOCKREAD(); |
| 348 | |
| 349 | // Loop through each loaded modules |
| 350 | int count = s_pLoadedModules->Count(); |
| 351 | for (int index = 0; index < count; index++) |
| 352 | { |
| 353 | if ((*s_pLoadedModules)[index] == pRegMeta) |
| 354 | { |
| 355 | return TRUE; |
| 356 | } |
| 357 | } |
| 358 | } |
| 359 | |
| 360 | ErrExit: |
| 361 | return FALSE; |
| 362 | } // LOADEDMODULES::IsEntryInList |
| 363 | |
| 364 | #endif //_DEBUG |
| 365 | |
| 366 | #endif //FEATURE_METADATA_IN_VM |
| 367 | |
| 368 | #ifdef FEATURE_METADATA_IN_VM |
| 369 | |
| 370 | //***************************************************************************** |
| 371 | // Remove a RegMeta pointer from the loaded module list |
| 372 | //***************************************************************************** |
| 373 | // static |
| 374 | HRESULT |
| 375 | LOADEDMODULES::ResolveTypeRefWithLoadedModules( |
| 376 | mdTypeRef tkTypeRef, // [IN] TypeRef to be resolved. |
| 377 | RegMeta * pTypeRefRegMeta, // [IN] Scope in which the TypeRef is defined. |
| 378 | IMetaModelCommon * pTypeRefScope, // [IN] Scope in which the TypeRef is defined. |
| 379 | REFIID riid, // [IN] iid for the return interface. |
| 380 | IUnknown ** ppIScope, // [OUT] Return interface. |
| 381 | mdTypeDef * ptd) // [OUT] TypeDef corresponding the TypeRef. |
| 382 | { |
| 383 | HRESULT hr = NOERROR; |
| 384 | RegMeta * pRegMeta; |
| 385 | CQuickArray<mdTypeRef> cqaNesters; |
| 386 | CQuickArray<LPCUTF8> cqaNesterNamespaces; |
| 387 | CQuickArray<LPCUTF8> cqaNesterNames; |
| 388 | |
| 389 | IfFailGo(InitializeStatics()); |
| 390 | |
| 391 | { |
| 392 | LOCKREAD(); |
| 393 | |
| 394 | // Get the Nesting hierarchy. |
| 395 | IfFailGo(ImportHelper::GetNesterHierarchy( |
| 396 | pTypeRefScope, |
| 397 | tkTypeRef, |
| 398 | cqaNesters, |
| 399 | cqaNesterNamespaces, |
| 400 | cqaNesterNames)); |
| 401 | |
| 402 | int count = s_pLoadedModules->Count(); |
| 403 | for (int index = 0; index < count; index++) |
| 404 | { |
| 405 | pRegMeta = (*s_pLoadedModules)[index]; |
| 406 | |
| 407 | { |
| 408 | // Do not lock the TypeRef RegMeta (again), as it is already locked for read by the caller. |
| 409 | // The code:UTSemReadWrite will block ReadLock even for thread holding already the read lock if |
| 410 | // some other thread is waiting for WriteLock on the same lock. That would cause dead-lock if we |
| 411 | // try to lock for read again here. |
| 412 | CMDSemReadWrite cSemRegMeta((pRegMeta == pTypeRefRegMeta) ? NULL : pRegMeta->GetReaderWriterLock()); |
| 413 | IfFailGo(cSemRegMeta.LockRead()); |
| 414 | |
| 415 | hr = ImportHelper::FindNestedTypeDef( |
| 416 | pRegMeta->GetMiniMd(), |
| 417 | cqaNesterNamespaces, |
| 418 | cqaNesterNames, |
| 419 | mdTokenNil, |
| 420 | ptd); |
| 421 | } |
| 422 | if (hr == CLDB_E_RECORD_NOTFOUND) |
| 423 | { // Process next MetaData module |
| 424 | continue; |
| 425 | } |
| 426 | IfFailGo(hr); |
| 427 | |
| 428 | // Found a loaded module containing the TypeDef. |
| 429 | IfFailGo(pRegMeta->QueryInterface(riid, (void **)ppIScope)); |
| 430 | break; |
| 431 | } |
| 432 | } |
| 433 | if (FAILED(hr)) |
| 434 | { |
| 435 | // cannot find the match! |
| 436 | hr = E_FAIL; |
| 437 | } |
| 438 | ErrExit: |
| 439 | return hr; |
| 440 | } // LOADEDMODULES::ResolveTypeRefWithLoadedModules |
| 441 | |
| 442 | #endif //FEATURE_METADATA_IN_VM |
| 443 | |
| 444 | #if defined(FEATURE_METADATA_IN_VM) |
| 445 | |
| 446 | //***************************************************************************** |
| 447 | // This is a routine to try to find a class implementation given its fully |
| 448 | // qualified name by using the CORPATH environment variable. CORPATH is a list |
| 449 | // of directories (like PATH). Before checking CORPATH, this checks the current |
| 450 | // directory, then the directory that the exe lives in. The search is |
| 451 | // performed by parsing off one element at a time from the class name, |
| 452 | // appending it to the directory and looking for a subdirectory or image with |
| 453 | // that name. If the subdirectory exists, it drills down into that subdirectory |
| 454 | // and tries the next element of the class name. When it finally bottoms out |
| 455 | // but can't find the image it takes the rest of the fully qualified class name |
| 456 | // and appends them with intervening '.'s trying to find a matching DLL. |
| 457 | // Example: |
| 458 | // |
| 459 | // CORPATH=c:\bin;c:\prog |
| 460 | // classname = namespace.class |
| 461 | // |
| 462 | // checks the following things in order: |
| 463 | // c:\bin\namespace, (if <-exists) c:\bin\namespace\class.dll, |
| 464 | // c:\bin\namespace.dll, c:\bin\namespace.class.dll |
| 465 | // c:\prog\namespace, (if <-exists) c:\prog\namespace\class.dll, |
| 466 | // c:\prog\namespace.dll, c:\prog\namespace.class.dll |
| 467 | //***************************************************************************** |
| 468 | HRESULT CORPATHService::GetClassFromCORPath( |
| 469 | __in __in_z LPWSTR wzClassname, // [IN] fully qualified class name |
| 470 | mdTypeRef tr, // [IN] TypeRef to be resolved. |
| 471 | IMetaModelCommon *pCommon, // [IN] Scope in which the TypeRef is defined. |
| 472 | REFIID riid, // [IN] Interface type to be returned. |
| 473 | IUnknown **ppIScope, // [OUT] Scope in which the TypeRef resolves. |
| 474 | mdTypeDef *ptd) // [OUT] typedef corresponding the typeref |
| 475 | { |
| 476 | PathString rcCorPath; // The CORPATH environment variable. |
| 477 | LPWSTR szCorPath; // Used to parse CORPATH. |
| 478 | int iLen; // Length of the directory. |
| 479 | PathString rcCorDir; // Buffer for the directory. |
| 480 | WCHAR *temp; // Used as a parsing temp. |
| 481 | WCHAR *szSemiCol; |
| 482 | |
| 483 | // Get the CORPATH environment variable. |
| 484 | if (WszGetEnvironmentVariable(W("CORPATH" ), rcCorPath)) |
| 485 | { |
| 486 | NewArrayHolder<WCHAR> szCorPathHolder = rcCorPath.GetCopyOfUnicodeString(); |
| 487 | szCorPath = szCorPathHolder.GetValue(); |
| 488 | // Try each directory in the path. |
| 489 | for(;*szCorPath != W('\0');) |
| 490 | { |
| 491 | // Get the next directory off the path. |
| 492 | if ((szSemiCol = wcschr(szCorPath, W(';')))) |
| 493 | { |
| 494 | temp = szCorPath; |
| 495 | *szSemiCol = W('\0'); |
| 496 | szCorPath = szSemiCol + 1; |
| 497 | } |
| 498 | else |
| 499 | { |
| 500 | temp = szCorPath; |
| 501 | szCorPath += wcslen(temp); |
| 502 | } |
| 503 | |
| 504 | rcCorDir.Set(temp); |
| 505 | |
| 506 | // Check if we can find the class in the directory. |
| 507 | if (CORPATHService::GetClassFromDir(wzClassname, rcCorDir, tr, pCommon, riid, ppIScope, ptd) == S_OK) |
| 508 | return S_OK; |
| 509 | } |
| 510 | } |
| 511 | |
| 512 | //<TODO>These should go before the path search, but it will cause test |
| 513 | // some headaches right now, so we'll give them a little time to transition.</TODO> |
| 514 | |
| 515 | // Try the current directory first. |
| 516 | if ((iLen = WszGetCurrentDirectory( rcCorDir)) > 0 && |
| 517 | CORPATHService::GetClassFromDir(wzClassname, rcCorDir, tr, pCommon, riid, ppIScope, ptd) == S_OK) |
| 518 | { |
| 519 | return S_OK; |
| 520 | } |
| 521 | |
| 522 | // Try the app directory next. |
| 523 | if ((iLen = WszGetModuleFileName(NULL, rcCorDir)) > 0) |
| 524 | { |
| 525 | |
| 526 | if(SUCCEEDED(CopySystemDirectory(rcCorDir, rcCorDir)) && |
| 527 | CORPATHService::GetClassFromDir( |
| 528 | wzClassname, |
| 529 | rcCorDir, |
| 530 | tr, |
| 531 | pCommon, |
| 532 | riid, |
| 533 | ppIScope, |
| 534 | ptd) == S_OK) |
| 535 | { |
| 536 | return (S_OK); |
| 537 | } |
| 538 | } |
| 539 | |
| 540 | // Couldn't find the class. |
| 541 | return S_FALSE; |
| 542 | } // CORPATHService::GetClassFromCORPath |
| 543 | |
| 544 | //***************************************************************************** |
| 545 | // This is used in conjunction with GetClassFromCORPath. See it for details |
| 546 | // of the algorithm. |
| 547 | //***************************************************************************** |
| 548 | HRESULT CORPATHService::GetClassFromDir( |
| 549 | __in __in_z LPWSTR wzClassname, // Fully qualified class name. |
| 550 | __in SString& directory, // Directory to try. at most appended with a '\\' |
| 551 | mdTypeRef tr, // TypeRef to resolve. |
| 552 | IMetaModelCommon *pCommon, // Scope in which the TypeRef is defined. |
| 553 | REFIID riid, |
| 554 | IUnknown **ppIScope, |
| 555 | mdTypeDef *ptd) // [OUT] typedef |
| 556 | { |
| 557 | WCHAR *temp; // Used as a parsing temp. |
| 558 | int iTmp; |
| 559 | bool bContinue; // Flag to check if the for loop should end. |
| 560 | LPWSTR wzSaveClassname = NULL; // Saved offset into the class name string. |
| 561 | |
| 562 | // Process the class name appending each segment of the name to the |
| 563 | // directory until we find a DLL. |
| 564 | PathString dir; |
| 565 | if (!directory.EndsWith(DIRECTORY_SEPARATOR_CHAR_W)) |
| 566 | { |
| 567 | directory.Append(DIRECTORY_SEPARATOR_CHAR_W); |
| 568 | } |
| 569 | |
| 570 | for(;;) |
| 571 | { |
| 572 | bContinue = false; |
| 573 | dir.Set(directory); |
| 574 | |
| 575 | if ((temp = wcschr(wzClassname, NAMESPACE_SEPARATOR_WCHAR)) != NULL) |
| 576 | { |
| 577 | *temp = W('\0'); //terminate with null so that it can be appended |
| 578 | dir.Append(wzClassname); |
| 579 | *temp = NAMESPACE_SEPARATOR_WCHAR; //recover the '.' |
| 580 | |
| 581 | wzClassname = temp+1; |
| 582 | // Check if a directory by this name exists. |
| 583 | DWORD iAttrs = WszGetFileAttributes(dir); |
| 584 | if (iAttrs != 0xffffffff && (iAttrs & FILE_ATTRIBUTE_DIRECTORY)) |
| 585 | { |
| 586 | // Next element in the class spec. |
| 587 | bContinue = true; |
| 588 | wzSaveClassname = wzClassname; |
| 589 | } |
| 590 | } |
| 591 | else |
| 592 | { |
| 593 | dir.Append(wzClassname); |
| 594 | |
| 595 | // Advance past the class name. |
| 596 | iTmp = (int)wcslen(wzClassname); |
| 597 | wzClassname += iTmp; |
| 598 | } |
| 599 | |
| 600 | // Try to load the image. |
| 601 | dir.Append(W(".dll" )); |
| 602 | |
| 603 | // OpenScope given the dll name and make sure that the class is defined in the module. |
| 604 | if ( SUCCEEDED( CORPATHService::FindTypeDef(dir, tr, pCommon, riid, ppIScope, ptd) ) ) |
| 605 | { |
| 606 | return (S_OK); |
| 607 | } |
| 608 | |
| 609 | // If we didn't find the dll, try some more. |
| 610 | while (*wzClassname != W('\0')) |
| 611 | { |
| 612 | // Find the length of the next class name element. |
| 613 | if ((temp = wcschr(wzClassname, NAMESPACE_SEPARATOR_WCHAR)) == NULL) |
| 614 | { |
| 615 | temp = wzClassname + wcslen(wzClassname); |
| 616 | } |
| 617 | |
| 618 | // Tack on ".element.dll" |
| 619 | SString::Iterator iter = dir.End(); |
| 620 | BOOL findperiod = dir.FindBack(iter, NAMESPACE_SEPARATOR_WCHAR); |
| 621 | _ASSERTE(findperiod); |
| 622 | iter++; |
| 623 | dir.Truncate(iter); |
| 624 | |
| 625 | WCHAR save = *temp; |
| 626 | *temp = W('\0'); |
| 627 | dir.Append(wzClassname); //element |
| 628 | *temp = save; |
| 629 | |
| 630 | // Try to load the image. |
| 631 | dir.Append(W(".dll" )); |
| 632 | |
| 633 | // OpenScope given the dll name and make sure that the class is defined in the module. |
| 634 | if ( SUCCEEDED( CORPATHService::FindTypeDef(dir, tr, pCommon, riid, ppIScope, ptd) ) ) |
| 635 | { |
| 636 | return (S_OK); |
| 637 | } |
| 638 | |
| 639 | // Advance to the next class name element. |
| 640 | wzClassname = temp; |
| 641 | if (*wzClassname != '\0') |
| 642 | ++wzClassname; |
| 643 | } |
| 644 | if (bContinue) |
| 645 | { |
| 646 | |
| 647 | wzClassname = wzSaveClassname; |
| 648 | } |
| 649 | else |
| 650 | { |
| 651 | break; |
| 652 | } |
| 653 | } |
| 654 | return S_FALSE; |
| 655 | } // CORPATHService::GetClassFromDir |
| 656 | |
| 657 | //************************************************************* |
| 658 | // |
| 659 | // Open the file with name wzModule and check to see if there is a type |
| 660 | // with namespace/class of wzNamespace/wzType. If so, return the RegMeta |
| 661 | // corresponding to the file and the mdTypeDef of the typedef |
| 662 | // |
| 663 | //************************************************************* |
| 664 | HRESULT CORPATHService::FindTypeDef( |
| 665 | __in __in_z LPCWSTR wzModule, // name of the module that we are going to open |
| 666 | mdTypeRef tr, // TypeRef to resolve. |
| 667 | IMetaModelCommon * pCommon, // Scope in which the TypeRef is defined. |
| 668 | REFIID riid, |
| 669 | IUnknown ** ppIScope, |
| 670 | mdTypeDef * ptd) // [OUT] the type that we resolve to |
| 671 | { |
| 672 | HRESULT hr = NOERROR; |
| 673 | NewHolder<Disp> pDisp; |
| 674 | ReleaseHolder<IMetaDataImport2> pImport = NULL; |
| 675 | CQuickArray<mdTypeRef> cqaNesters; |
| 676 | CQuickArray<LPCUTF8> cqaNesterNamespaces; |
| 677 | CQuickArray<LPCUTF8> cqaNesterNames; |
| 678 | RegMeta * pRegMeta; |
| 679 | |
| 680 | _ASSERTE((ppIScope != NULL) && (ptd != NULL)); |
| 681 | |
| 682 | *ppIScope = NULL; |
| 683 | |
| 684 | pDisp = new (nothrow) Disp; |
| 685 | IfNullGo(pDisp); |
| 686 | |
| 687 | IfFailGo(pDisp->OpenScope(wzModule, 0, IID_IMetaDataImport2, (IUnknown **)&pImport)); |
| 688 | pRegMeta = static_cast<RegMeta *>(pImport.GetValue()); |
| 689 | |
| 690 | // Get the Nesting hierarchy. |
| 691 | IfFailGo(ImportHelper::GetNesterHierarchy(pCommon, tr, cqaNesters, |
| 692 | cqaNesterNamespaces, cqaNesterNames)); |
| 693 | |
| 694 | hr = ImportHelper::FindNestedTypeDef( |
| 695 | pRegMeta->GetMiniMd(), |
| 696 | cqaNesterNamespaces, |
| 697 | cqaNesterNames, |
| 698 | mdTokenNil, |
| 699 | ptd); |
| 700 | if (SUCCEEDED(hr)) |
| 701 | { |
| 702 | *ppIScope = pImport.Extract(); |
| 703 | } |
| 704 | |
| 705 | ErrExit: |
| 706 | return hr; |
| 707 | } // CORPATHService::FindTypeDef |
| 708 | |
| 709 | #endif //FEATURE_METADATA_IN_VM |
| 710 | |
| 711 | //******************************************************************************* |
| 712 | // |
| 713 | // Determine the blob size base of the ELEMENT_TYPE_* associated with the blob. |
| 714 | // This cannot be a table lookup because ELEMENT_TYPE_STRING is an unicode string. |
| 715 | // The size of the blob is determined by calling wcsstr of the string + 1. |
| 716 | // |
| 717 | //******************************************************************************* |
| 718 | ULONG _GetSizeOfConstantBlob( |
| 719 | DWORD dwCPlusTypeFlag, // ELEMENT_TYPE_* |
| 720 | void * pValue, // BLOB value |
| 721 | ULONG cchString) // String length in wide chars, or -1 for auto. |
| 722 | { |
| 723 | ULONG ulSize = 0; |
| 724 | |
| 725 | switch (dwCPlusTypeFlag) |
| 726 | { |
| 727 | case ELEMENT_TYPE_BOOLEAN: |
| 728 | ulSize = sizeof(BYTE); |
| 729 | break; |
| 730 | case ELEMENT_TYPE_I1: |
| 731 | case ELEMENT_TYPE_U1: |
| 732 | ulSize = sizeof(BYTE); |
| 733 | break; |
| 734 | case ELEMENT_TYPE_CHAR: |
| 735 | case ELEMENT_TYPE_I2: |
| 736 | case ELEMENT_TYPE_U2: |
| 737 | ulSize = sizeof(SHORT); |
| 738 | break; |
| 739 | case ELEMENT_TYPE_I4: |
| 740 | case ELEMENT_TYPE_U4: |
| 741 | case ELEMENT_TYPE_R4: |
| 742 | ulSize = sizeof(LONG); |
| 743 | |
| 744 | break; |
| 745 | |
| 746 | case ELEMENT_TYPE_I8: |
| 747 | case ELEMENT_TYPE_U8: |
| 748 | case ELEMENT_TYPE_R8: |
| 749 | ulSize = sizeof(DOUBLE); |
| 750 | break; |
| 751 | |
| 752 | case ELEMENT_TYPE_STRING: |
| 753 | if (pValue == 0) |
| 754 | ulSize = 0; |
| 755 | else |
| 756 | if (cchString != (ULONG) -1) |
| 757 | ulSize = cchString * sizeof(WCHAR); |
| 758 | else |
| 759 | ulSize = (ULONG)(sizeof(WCHAR) * wcslen((LPWSTR)pValue)); |
| 760 | break; |
| 761 | |
| 762 | case ELEMENT_TYPE_CLASS: |
| 763 | // This was originally 'sizeof(IUnknown *)', but that varies across platforms. |
| 764 | // The only legal value is a null pointer, and on 32 bit platforms we've already |
| 765 | // stored 32 bits, so we will use just 32 bits of null. If the type is |
| 766 | // E_T_CLASS, the caller should know that the value is always NULL anyway. |
| 767 | ulSize = sizeof(ULONG); |
| 768 | break; |
| 769 | default: |
| 770 | _ASSERTE(!"Not a valid type to specify default value!" ); |
| 771 | break; |
| 772 | } |
| 773 | return ulSize; |
| 774 | } // _GetSizeOfConstantBlob |
| 775 | |