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 | #include "stdafx.h" // Standard header. |
6 | #include <utilcode.h> // Utility helpers. |
7 | #include <corerror.h> |
8 | #include "ndpversion.h" |
9 | |
10 | #include "../dlls/mscorrc/resource.h" |
11 | #ifdef FEATURE_PAL |
12 | #include "resourcestring.h" |
13 | #define NATIVE_STRING_RESOURCE_NAME mscorrc_debug |
14 | DECLARE_NATIVE_STRING_RESOURCE_TABLE(NATIVE_STRING_RESOURCE_NAME); |
15 | #endif |
16 | #include "sstring.h" |
17 | #include "stringarraylist.h" |
18 | |
19 | #include <stdlib.h> |
20 | |
21 | #ifdef USE_FORMATMESSAGE_WRAPPER |
22 | // we implement the wrapper for FormatMessageW. |
23 | // Need access to the original |
24 | #undef WszFormatMessage |
25 | #define WszFormatMessage ::FormatMessageW |
26 | #endif |
27 | |
28 | #define MAX_VERSION_STRING 30 |
29 | |
30 | // External prototypes. |
31 | extern HINSTANCE GetModuleInst(); |
32 | |
33 | #ifndef FEATURE_PAL |
34 | |
35 | //***************************************************************************** |
36 | // Get the MUI ID, on downlevel platforms where MUI is not supported it |
37 | // returns the default system ID. |
38 | |
39 | typedef LANGID (WINAPI *PFNGETUSERDEFAULTUILANGUAGE)(void); // kernel32!GetUserDefaultUILanguage |
40 | |
41 | int GetMUILanguageID(LocaleIDValue* pResult) |
42 | { |
43 | CONTRACTL |
44 | { |
45 | GC_NOTRIGGER; |
46 | NOTHROW; |
47 | #ifdef MODE_PREEMPTIVE |
48 | MODE_PREEMPTIVE; |
49 | #endif |
50 | } |
51 | CONTRACTL_END; |
52 | #if FEATURE_USE_LCID |
53 | int langId=0; |
54 | static PFNGETUSERDEFAULTUILANGUAGE pfnGetUserDefaultUILanguage=NULL; |
55 | |
56 | if( NULL == pfnGetUserDefaultUILanguage ) |
57 | { |
58 | PFNGETUSERDEFAULTUILANGUAGE proc = NULL; |
59 | |
60 | HMODULE hmod = GetModuleHandleA(WINDOWS_KERNEL32_DLLNAME_A); |
61 | |
62 | if( hmod ) |
63 | proc = (PFNGETUSERDEFAULTUILANGUAGE) |
64 | GetProcAddress(hmod, "GetUserDefaultUILanguage" ); |
65 | |
66 | if(proc == NULL) |
67 | proc = (PFNGETUSERDEFAULTUILANGUAGE) -1; |
68 | |
69 | PVOID value = InterlockedExchangeT(&pfnGetUserDefaultUILanguage, |
70 | proc); |
71 | } |
72 | |
73 | // We should never get NULL here, the function is -1 or a valid address. |
74 | _ASSERTE(pfnGetUserDefaultUILanguage != NULL); |
75 | |
76 | |
77 | if( pfnGetUserDefaultUILanguage == (PFNGETUSERDEFAULTUILANGUAGE) -1) |
78 | langId = GetSystemDefaultLangID(); |
79 | else |
80 | langId = pfnGetUserDefaultUILanguage(); |
81 | |
82 | *pResult= langId; |
83 | #else // FEATURE_USE_LCID |
84 | _ASSERTE(sizeof(LocaleID)/sizeof(WCHAR) >=LOCALE_NAME_MAX_LENGTH); |
85 | return ::GetSystemDefaultLocaleName(*pResult, LOCALE_NAME_MAX_LENGTH); |
86 | #endif //FEATURE_USE_LCID |
87 | return 1; |
88 | } |
89 | |
90 | static void BuildMUIDirectory(int langid, __out SString* pResult) |
91 | { |
92 | CONTRACTL |
93 | { |
94 | THROWS; |
95 | GC_NOTRIGGER; |
96 | PRECONDITION(CheckPointer(pResult)); |
97 | } |
98 | CONTRACTL_END; |
99 | |
100 | pResult->Printf(W("MUI\\%04x\\" ), langid); |
101 | } |
102 | |
103 | void GetMUILanguageName(__out SString* pResult) |
104 | { |
105 | CONTRACTL |
106 | { |
107 | THROWS; |
108 | GC_NOTRIGGER; |
109 | PRECONDITION(CheckPointer(pResult)); |
110 | } |
111 | CONTRACTL_END; |
112 | |
113 | LocaleIDValue langid; |
114 | GetMUILanguageID(&langid); |
115 | |
116 | int lcid; |
117 | #ifdef FEATURE_USE_LCID |
118 | lcid=langid; |
119 | #else |
120 | lcid=::LocaleNameToLCID(langid,0); |
121 | #endif |
122 | |
123 | return BuildMUIDirectory(lcid, pResult); |
124 | } |
125 | |
126 | void GetMUIParentLanguageName(SString* pResult) |
127 | { |
128 | WRAPPER_NO_CONTRACT; |
129 | int langid = 1033; |
130 | |
131 | BuildMUIDirectory(langid, pResult); |
132 | } |
133 | #ifndef DACCESS_COMPILE |
134 | HRESULT GetMUILanguageNames(__inout StringArrayList* pCultureNames) |
135 | { |
136 | CONTRACTL |
137 | { |
138 | NOTHROW; |
139 | GC_NOTRIGGER; |
140 | PRECONDITION(CheckPointer(pCultureNames)); |
141 | SO_INTOLERANT; |
142 | } |
143 | CONTRACTL_END; |
144 | |
145 | HRESULT hr=S_OK; |
146 | EX_TRY |
147 | { |
148 | SString result; |
149 | GetMUILanguageName(&result); |
150 | |
151 | if(!result.IsEmpty()) |
152 | { |
153 | pCultureNames->Append(result); |
154 | } |
155 | |
156 | GetMUIParentLanguageName(&result); |
157 | |
158 | _ASSERTE(!result.IsEmpty()); |
159 | pCultureNames->Append(result); |
160 | pCultureNames->Append(SString::Empty()); |
161 | } |
162 | EX_CATCH_HRESULT(hr) |
163 | return hr; |
164 | |
165 | } |
166 | #endif // DACCESS_COMPILE |
167 | |
168 | #endif // !FEATURE_PAL |
169 | |
170 | BOOL CCompRC::s_bIsMscoree = FALSE; |
171 | |
172 | //***************************************************************************** |
173 | // Do the mapping from an langId to an hinstance node |
174 | //***************************************************************************** |
175 | HRESOURCEDLL CCompRC::LookupNode(LocaleID langId, BOOL &fMissing) |
176 | { |
177 | LIMITED_METHOD_CONTRACT; |
178 | |
179 | if (m_pHash == NULL) return NULL; |
180 | |
181 | // Linear search |
182 | int i; |
183 | for(i = 0; i < m_nHashSize; i ++) { |
184 | if (m_pHash[i].IsSet() && m_pHash[i].HasID(langId)) { |
185 | return m_pHash[i].GetLibraryHandle(); |
186 | } |
187 | if (m_pHash[i].IsMissing() && m_pHash[i].HasID(langId)) |
188 | { |
189 | fMissing = TRUE; |
190 | return NULL; |
191 | } |
192 | } |
193 | |
194 | return NULL; |
195 | } |
196 | |
197 | //***************************************************************************** |
198 | // Add a new node to the map and return it. |
199 | //***************************************************************************** |
200 | const int MAP_STARTSIZE = 7; |
201 | const int MAP_GROWSIZE = 5; |
202 | |
203 | HRESULT CCompRC::AddMapNode(LocaleID langId, HRESOURCEDLL hInst, BOOL fMissing) |
204 | { |
205 | CONTRACTL |
206 | { |
207 | GC_NOTRIGGER; |
208 | NOTHROW; |
209 | INJECT_FAULT(return E_OUTOFMEMORY;); |
210 | } |
211 | CONTRACTL_END; |
212 | |
213 | |
214 | if (m_pHash == NULL) { |
215 | m_pHash = new (nothrow)CCulturedHInstance[MAP_STARTSIZE]; |
216 | if (m_pHash==NULL) |
217 | return E_OUTOFMEMORY; |
218 | m_nHashSize = MAP_STARTSIZE; |
219 | } |
220 | |
221 | // For now, place in first open slot |
222 | int i; |
223 | for(i = 0; i < m_nHashSize; i ++) { |
224 | if (!m_pHash[i].IsSet() && !m_pHash[i].IsMissing()) { |
225 | if (fMissing) |
226 | { |
227 | m_pHash[i].SetMissing(langId); |
228 | } |
229 | else |
230 | { |
231 | m_pHash[i].Set(langId,hInst); |
232 | } |
233 | |
234 | return S_OK; |
235 | } |
236 | } |
237 | |
238 | // Out of space, regrow |
239 | CCulturedHInstance * pNewHash = new (nothrow)CCulturedHInstance[m_nHashSize + MAP_GROWSIZE]; |
240 | if (pNewHash) |
241 | { |
242 | memcpy(pNewHash, m_pHash, sizeof(CCulturedHInstance) * m_nHashSize); |
243 | delete [] m_pHash; |
244 | m_pHash = pNewHash; |
245 | if (fMissing) |
246 | { |
247 | m_pHash[m_nHashSize].SetMissing(langId); |
248 | } |
249 | else |
250 | { |
251 | m_pHash[m_nHashSize].Set(langId,hInst); |
252 | } |
253 | m_nHashSize += MAP_GROWSIZE; |
254 | } |
255 | else |
256 | return E_OUTOFMEMORY; |
257 | return S_OK; |
258 | } |
259 | |
260 | //***************************************************************************** |
261 | // Initialize |
262 | //***************************************************************************** |
263 | LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.debug.dll" ); |
264 | LPCWSTR CCompRC::m_pFallbackResource= W("mscorrc.dll" ); |
265 | |
266 | #ifdef FEATURE_PAL |
267 | LPCSTR CCompRC::m_pDefaultResourceDomain = "mscorrc.debug" ; |
268 | LPCSTR CCompRC::m_pFallbackResourceDomain = "mscorrc" ; |
269 | #endif // FEATURE_PAL |
270 | |
271 | HRESULT CCompRC::Init(LPCWSTR pResourceFile, BOOL bUseFallback) |
272 | { |
273 | CONTRACTL |
274 | { |
275 | GC_NOTRIGGER; |
276 | NOTHROW; |
277 | INJECT_FAULT(return E_OUTOFMEMORY;); |
278 | } |
279 | CONTRACTL_END; |
280 | |
281 | // This function is called during Watson process. We need to make sure |
282 | // that this function is restartable. |
283 | // |
284 | // Make sure to NEVER null out the function callbacks in the Init |
285 | // function. They get set for the "Default CCompRC" during EEStartup |
286 | // and we want to make sure we don't wipe them out. |
287 | |
288 | m_bUseFallback = bUseFallback; |
289 | |
290 | if (m_pResourceFile == NULL) |
291 | { |
292 | if(pResourceFile) |
293 | { |
294 | NewArrayHolder<WCHAR> pwszResourceFile(NULL); |
295 | |
296 | DWORD lgth = (DWORD) wcslen(pResourceFile) + 1; |
297 | pwszResourceFile = new(nothrow) WCHAR[lgth]; |
298 | if (pwszResourceFile) |
299 | { |
300 | wcscpy_s(pwszResourceFile, lgth, pResourceFile); |
301 | LPCWSTR pFile = pwszResourceFile.Extract(); |
302 | if (InterlockedCompareExchangeT(&m_pResourceFile, pFile, NULL) != NULL) |
303 | { |
304 | delete [] pFile; |
305 | } |
306 | } |
307 | } |
308 | else |
309 | InterlockedCompareExchangeT(&m_pResourceFile, m_pDefaultResource, NULL); |
310 | } |
311 | |
312 | if (m_pResourceFile == NULL) |
313 | { |
314 | return E_OUTOFMEMORY; |
315 | } |
316 | |
317 | #ifdef FEATURE_PAL |
318 | |
319 | if (m_pResourceFile == m_pDefaultResource) |
320 | { |
321 | m_pResourceDomain = m_pDefaultResourceDomain; |
322 | } |
323 | else if (m_pResourceFile == m_pFallbackResource) |
324 | { |
325 | m_pResourceDomain = m_pFallbackResourceDomain; |
326 | } |
327 | else |
328 | { |
329 | _ASSERTE(!"Unsupported resource file" ); |
330 | } |
331 | |
332 | #ifndef CROSSGEN_COMPILE |
333 | // PAL_BindResources requires that libcoreclr.so has been loaded, |
334 | // and thus can'be be called by crossgen. |
335 | if (!PAL_BindResources(m_pResourceDomain)) |
336 | { |
337 | // The function can fail only due to OOM |
338 | return E_OUTOFMEMORY; |
339 | } |
340 | #endif |
341 | |
342 | #endif // FEATURE_PAL |
343 | |
344 | if (m_csMap == NULL) |
345 | { |
346 | // NOTE: there are times when the debugger's helper thread is asked to do a favor for another thread in the |
347 | // process. Typically, this favor involves putting up a dialog for the user. Putting up a dialog usually ends |
348 | // up involving the CCompRC code since (of course) the strings in the dialog are in a resource file. Thus, the |
349 | // debugger's helper thread will attempt to acquire this CRST. This is okay, since the helper thread only does |
350 | // these favors for other threads when there is no debugger attached. Thus, there are no deadlock hazards with |
351 | // this lock, and its safe for the helper thread to take, so this CRST is marked with CRST_DEBUGGER_THREAD. |
352 | CRITSEC_COOKIE csMap = ClrCreateCriticalSection(CrstCCompRC, |
353 | (CrstFlags)(CRST_UNSAFE_ANYMODE | CRST_DEBUGGER_THREAD | CRST_TAKEN_DURING_SHUTDOWN)); |
354 | |
355 | if (csMap) |
356 | { |
357 | if (InterlockedCompareExchangeT(&m_csMap, csMap, NULL) != NULL) |
358 | { |
359 | ClrDeleteCriticalSection(csMap); |
360 | } |
361 | } |
362 | } |
363 | |
364 | if (m_csMap == NULL) |
365 | return E_OUTOFMEMORY; |
366 | |
367 | return S_OK; |
368 | } |
369 | |
370 | void CCompRC::SetResourceCultureCallbacks( |
371 | FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames, |
372 | FPGETTHREADUICULTUREID fpGetThreadUICultureId) |
373 | { |
374 | LIMITED_METHOD_CONTRACT; |
375 | |
376 | m_fpGetThreadUICultureNames = fpGetThreadUICultureNames; |
377 | m_fpGetThreadUICultureId = fpGetThreadUICultureId; |
378 | } |
379 | |
380 | void CCompRC::GetResourceCultureCallbacks( |
381 | FPGETTHREADUICULTURENAMES* fpGetThreadUICultureNames, |
382 | FPGETTHREADUICULTUREID* fpGetThreadUICultureId) |
383 | { |
384 | LIMITED_METHOD_CONTRACT; |
385 | |
386 | if(fpGetThreadUICultureNames) |
387 | *fpGetThreadUICultureNames=m_fpGetThreadUICultureNames; |
388 | |
389 | if(fpGetThreadUICultureId) |
390 | *fpGetThreadUICultureId=m_fpGetThreadUICultureId; |
391 | } |
392 | |
393 | void CCompRC::Destroy() |
394 | { |
395 | CONTRACTL |
396 | { |
397 | GC_NOTRIGGER; |
398 | NOTHROW; |
399 | #ifdef MODE_PREEMPTIVE |
400 | MODE_PREEMPTIVE; |
401 | #endif |
402 | } |
403 | CONTRACTL_END; |
404 | |
405 | // Free all resource libraries |
406 | |
407 | //***************************************************************************** |
408 | // Free the loaded library if we ever loaded it and only if we are not on |
409 | // Win 95 which has a known bug with DLL unloading (it randomly unloads a |
410 | // dll on shut down, not necessarily the one you asked for). This is done |
411 | // only in debug mode to make coverage runs accurate. |
412 | //***************************************************************************** |
413 | |
414 | #if defined(_DEBUG) |
415 | if (m_Primary.GetLibraryHandle()) { |
416 | ::FreeLibrary(m_Primary.GetLibraryHandle()); |
417 | } |
418 | |
419 | if (m_pHash != NULL) { |
420 | int i; |
421 | for(i = 0; i < m_nHashSize; i ++) { |
422 | if (m_pHash[i].GetLibraryHandle() != NULL) { |
423 | ::FreeLibrary(m_pHash[i].GetLibraryHandle()); |
424 | break; |
425 | } |
426 | } |
427 | } |
428 | #endif |
429 | |
430 | // destroy map structure |
431 | if(m_pResourceFile != m_pDefaultResource) |
432 | delete [] m_pResourceFile; |
433 | m_pResourceFile = NULL; |
434 | |
435 | if(m_csMap) { |
436 | ClrDeleteCriticalSection(m_csMap); |
437 | ZeroMemory(&(m_csMap), sizeof(CRITSEC_COOKIE)); |
438 | } |
439 | |
440 | if(m_pHash != NULL) { |
441 | delete [] m_pHash; |
442 | m_pHash = NULL; |
443 | } |
444 | } |
445 | |
446 | |
447 | //***************************************************************************** |
448 | // Initialization is done lazily, for backwards compatibility "mscorrc.dll" |
449 | // is consider the default location for all strings that use CCompRC. |
450 | // An instance value for CCompRC can be created to load resources from a different |
451 | // resource dll. |
452 | //***************************************************************************** |
453 | LONG CCompRC::m_dwDefaultInitialized = 0; |
454 | CCompRC CCompRC::m_DefaultResourceDll; |
455 | |
456 | CCompRC* CCompRC::GetDefaultResourceDll() |
457 | { |
458 | CONTRACTL |
459 | { |
460 | GC_NOTRIGGER; |
461 | NOTHROW; |
462 | #ifdef MODE_PREEMPTIVE |
463 | MODE_PREEMPTIVE; |
464 | #endif |
465 | } |
466 | CONTRACTL_END; |
467 | |
468 | if (m_dwDefaultInitialized) |
469 | return &m_DefaultResourceDll; |
470 | |
471 | if(FAILED(m_DefaultResourceDll.Init(NULL, TRUE))) |
472 | { |
473 | return NULL; |
474 | } |
475 | m_dwDefaultInitialized = 1; |
476 | |
477 | return &m_DefaultResourceDll; |
478 | } |
479 | |
480 | LONG CCompRC::m_dwFallbackInitialized = 0; |
481 | CCompRC CCompRC::m_FallbackResourceDll; |
482 | |
483 | CCompRC* CCompRC::GetFallbackResourceDll() |
484 | { |
485 | CONTRACTL |
486 | { |
487 | GC_NOTRIGGER; |
488 | NOTHROW; |
489 | #ifdef MODE_PREEMPTIVE |
490 | MODE_PREEMPTIVE; |
491 | #endif |
492 | } |
493 | CONTRACTL_END; |
494 | |
495 | if (m_dwFallbackInitialized) |
496 | return &m_FallbackResourceDll; |
497 | |
498 | if(FAILED(m_FallbackResourceDll.Init(m_pFallbackResource, FALSE))) |
499 | { |
500 | return NULL; |
501 | } |
502 | m_dwFallbackInitialized = 1; |
503 | |
504 | return &m_FallbackResourceDll; |
505 | } |
506 | |
507 | |
508 | |
509 | //***************************************************************************** |
510 | //***************************************************************************** |
511 | |
512 | HRESULT CCompRC::GetLibrary(LocaleID langId, HRESOURCEDLL* phInst) |
513 | { |
514 | CONTRACTL |
515 | { |
516 | GC_NOTRIGGER; |
517 | NOTHROW; |
518 | #ifdef MODE_PREEMPTIVE |
519 | MODE_PREEMPTIVE; |
520 | #endif |
521 | PRECONDITION(phInst != NULL); |
522 | } |
523 | CONTRACTL_END; |
524 | |
525 | HRESULT hr = E_FAIL; |
526 | HRESOURCEDLL hInst = 0; |
527 | #ifndef DACCESS_COMPILE |
528 | HRESOURCEDLL hLibInst = 0; //Holds early library instance |
529 | BOOL fLibAlreadyOpen = FALSE; //Determine if we can close the opened library. |
530 | #endif |
531 | |
532 | // Try to match the primary entry, or else use the primary if we don't care. |
533 | if (m_Primary.IsSet()) |
534 | { |
535 | if (langId == UICULTUREID_DONTCARE || m_Primary.HasID(langId)) |
536 | { |
537 | hInst = m_Primary.GetLibraryHandle(); |
538 | hr = S_OK; |
539 | } |
540 | } |
541 | else if(m_Primary.IsMissing()) |
542 | { |
543 | // If primary is missing then the hash will not have anything either |
544 | hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); |
545 | } |
546 | #ifndef DACCESS_COMPILE |
547 | // If this is the first visit, we must set the primary entry |
548 | else |
549 | { |
550 | // Don't immediately return if LoadLibrary fails so we can indicate the file was missing |
551 | hr = LoadLibrary(&hLibInst); |
552 | // If it's a transient failure, don't cache the failure |
553 | if (FAILED(hr) && Exception::IsTransient(hr)) |
554 | { |
555 | return hr; |
556 | } |
557 | |
558 | CRITSEC_Holder csh (m_csMap); |
559 | // As we expected |
560 | if (!m_Primary.IsSet() && !m_Primary.IsMissing()) |
561 | { |
562 | hInst = hLibInst; |
563 | if (SUCCEEDED(hr)) |
564 | { |
565 | m_Primary.Set(langId,hLibInst); |
566 | } |
567 | else |
568 | { |
569 | m_Primary.SetMissing(langId); |
570 | } |
571 | } |
572 | |
573 | // Someone got into this critical section before us and set the primary already |
574 | else if (m_Primary.HasID(langId)) |
575 | { |
576 | hInst = m_Primary.GetLibraryHandle(); |
577 | fLibAlreadyOpen = TRUE; |
578 | } |
579 | |
580 | // If neither case is true, someone got into this critical section before us and |
581 | // set the primary to other than the language we want... |
582 | else |
583 | { |
584 | fLibAlreadyOpen = TRUE; |
585 | } |
586 | |
587 | IfFailRet(hr); |
588 | |
589 | if (fLibAlreadyOpen) |
590 | { |
591 | FreeLibrary(hLibInst); |
592 | fLibAlreadyOpen = FALSE; |
593 | } |
594 | } |
595 | #endif |
596 | |
597 | // If we enter here, we know that the primary is set to something other than the |
598 | // language we want - multiple languages use the hash table |
599 | if (hInst == NULL && !m_Primary.IsMissing()) |
600 | { |
601 | // See if the resource exists in the hash table |
602 | { |
603 | CRITSEC_Holder csh(m_csMap); |
604 | BOOL fMissing = FALSE; |
605 | hInst = LookupNode(langId, fMissing); |
606 | if (fMissing == TRUE) |
607 | { |
608 | hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); |
609 | goto Exit; |
610 | } |
611 | } |
612 | |
613 | #ifndef DACCESS_COMPILE |
614 | // If we didn't find it, we have to load the library and insert it into the hash |
615 | if (hInst == NULL) |
616 | { |
617 | hr = LoadLibrary(&hLibInst); |
618 | // If it's a transient failure, don't cache the failure |
619 | if (FAILED(hr) && Exception::IsTransient(hr)) |
620 | { |
621 | return hr; |
622 | } |
623 | { |
624 | CRITSEC_Holder csh (m_csMap); |
625 | |
626 | // Double check - someone may have entered this section before us |
627 | BOOL fMissing = FALSE; |
628 | hInst = LookupNode(langId, fMissing); |
629 | if (hInst == NULL && !fMissing) |
630 | { |
631 | if (SUCCEEDED(hr)) |
632 | { |
633 | hInst = hLibInst; |
634 | hr = AddMapNode(langId, hInst); |
635 | } else |
636 | { |
637 | HRESULT hrLoadLibrary = hr; |
638 | hr = AddMapNode(langId, hInst, TRUE /* fMissing */); |
639 | if (SUCCEEDED(hr)) |
640 | { |
641 | hr = hrLoadLibrary; |
642 | } |
643 | } |
644 | } |
645 | else |
646 | { |
647 | fLibAlreadyOpen = TRUE; |
648 | } |
649 | } |
650 | |
651 | if (fLibAlreadyOpen || FAILED(hr)) |
652 | { |
653 | FreeLibrary(hLibInst); |
654 | } |
655 | } |
656 | |
657 | // We found the node, so set hr to be a success. |
658 | else |
659 | { |
660 | hr = S_OK; |
661 | } |
662 | #endif // DACCESS_COMPILE |
663 | } |
664 | Exit: |
665 | *phInst = hInst; |
666 | return hr; |
667 | } |
668 | |
669 | //***************************************************************************** |
670 | // Load the string |
671 | // We load the localized libraries and cache the handle for future use. |
672 | // Mutliple threads may call this, so the cache structure is thread safe. |
673 | //***************************************************************************** |
674 | HRESULT CCompRC::LoadString(ResourceCategory eCategory, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed) |
675 | { |
676 | WRAPPER_NO_CONTRACT; |
677 | LocaleIDValue langIdValue; |
678 | LocaleID langId; |
679 | // Must resolve current thread's langId to a dll. |
680 | if(m_fpGetThreadUICultureId) { |
681 | int ret = (*m_fpGetThreadUICultureId)(&langIdValue); |
682 | |
683 | // Callback can't return 0, since that indicates empty. |
684 | // To indicate empty, callback should return UICULTUREID_DONTCARE |
685 | _ASSERTE(ret != 0); |
686 | |
687 | if (ret == 0) |
688 | return E_UNEXPECTED; |
689 | langId=langIdValue; |
690 | |
691 | } |
692 | else { |
693 | langId = UICULTUREID_DONTCARE; |
694 | } |
695 | |
696 | |
697 | return LoadString(eCategory, langId, iResourceID, szBuffer, iMax, pcwchUsed); |
698 | } |
699 | |
700 | HRESULT CCompRC::LoadString(ResourceCategory eCategory, LocaleID langId, UINT iResourceID, __out_ecount(iMax) LPWSTR szBuffer, int iMax, int *pcwchUsed) |
701 | { |
702 | CONTRACTL |
703 | { |
704 | GC_NOTRIGGER; |
705 | NOTHROW; |
706 | #ifdef MODE_PREEMPTIVE |
707 | MODE_PREEMPTIVE; |
708 | #endif |
709 | } |
710 | CONTRACTL_END; |
711 | |
712 | #ifndef FEATURE_PAL |
713 | HRESULT hr; |
714 | HRESOURCEDLL hInst = 0; //instance of cultured resource dll |
715 | int length; |
716 | |
717 | hr = GetLibrary(langId, &hInst); |
718 | |
719 | if (SUCCEEDED(hr)) |
720 | { |
721 | // Now that we have the proper dll handle, load the string |
722 | _ASSERTE(hInst != NULL); |
723 | |
724 | length = ::WszLoadString(hInst, iResourceID, szBuffer, iMax); |
725 | if(length > 0) |
726 | { |
727 | if(pcwchUsed) |
728 | { |
729 | *pcwchUsed = length; |
730 | } |
731 | return (S_OK); |
732 | } |
733 | if(GetLastError()==ERROR_SUCCESS) |
734 | hr=HRESULT_FROM_WIN32(ERROR_NOT_FOUND); |
735 | else |
736 | hr=HRESULT_FROM_GetLastError(); |
737 | } |
738 | |
739 | |
740 | // Failed to load string |
741 | if ( hr != E_OUTOFMEMORY && ShouldUseFallback()) |
742 | { |
743 | CCompRC* pFallback=CCompRC::GetFallbackResourceDll(); |
744 | if (pFallback) |
745 | { |
746 | //should not fall back to itself |
747 | _ASSERTE(pFallback != this); |
748 | |
749 | // check existence in the fallback Dll |
750 | |
751 | hr = pFallback->LoadString(Optional, langId, iResourceID,szBuffer, iMax, pcwchUsed); |
752 | |
753 | if(SUCCEEDED(hr)) |
754 | return hr; |
755 | } |
756 | switch (eCategory) |
757 | { |
758 | case Optional: |
759 | hr = E_FAIL; |
760 | break; |
761 | case DesktopCLR: |
762 | hr = E_FAIL; |
763 | break; |
764 | case Debugging: |
765 | case Error: |
766 | // get stub message |
767 | { |
768 | |
769 | if (pFallback) |
770 | { |
771 | |
772 | StackSString ssErrorFormat; |
773 | if (eCategory == Error) |
774 | { |
775 | hr=ssErrorFormat.LoadResourceAndReturnHR(pFallback, CCompRC::Required, IDS_EE_LINK_FOR_ERROR_MESSAGES); |
776 | } |
777 | else |
778 | { |
779 | _ASSERTE(eCategory == Debugging); |
780 | hr=ssErrorFormat.LoadResourceAndReturnHR(pFallback, CCompRC::Required, IDS_EE_LINK_FOR_DEBUGGING_MESSAGES); |
781 | } |
782 | |
783 | if (SUCCEEDED(hr)) |
784 | { |
785 | StackSString sFormattedMessage; |
786 | int iErrorCode = HR_FOR_URT_MSG(iResourceID); |
787 | |
788 | hr = S_OK; |
789 | |
790 | DWORD_PTR args[] = {(DWORD_PTR)VER_FILEVERSION_STR_L, iResourceID, iErrorCode}; |
791 | |
792 | length = WszFormatMessage(FORMAT_MESSAGE_FROM_STRING | FORMAT_MESSAGE_ARGUMENT_ARRAY , |
793 | (LPCWSTR)ssErrorFormat, 0, 0, |
794 | szBuffer,iMax,(va_list*)args); |
795 | |
796 | if (length == 0 && GetLastError() == ERROR_INSUFFICIENT_BUFFER) |
797 | { |
798 | // The buffer wasn't big enough for the message. Tell the caller this. |
799 | // |
800 | // Clear the buffer, just in case. |
801 | if (szBuffer && iMax) |
802 | *szBuffer = W('\0'); |
803 | |
804 | length = iMax; |
805 | hr=HRESULT_FROM_GetLastError(); |
806 | } |
807 | |
808 | if(length > 0) |
809 | { |
810 | if(pcwchUsed) |
811 | { |
812 | *pcwchUsed = length; |
813 | } |
814 | return hr; |
815 | } |
816 | |
817 | // Format mesage failed |
818 | hr=HRESULT_FROM_GetLastError(); |
819 | |
820 | } |
821 | } |
822 | else // if (pFallback) |
823 | { |
824 | _ASSERTE(FAILED(hr)); |
825 | } |
826 | } |
827 | // if we got here then we couldn't get the fallback message |
828 | // the fallback message is required so just falling through into "Required" |
829 | |
830 | case Required: |
831 | |
832 | if ( hr != E_OUTOFMEMORY) |
833 | { |
834 | // Shouldn't be any reason for this condition but the case where |
835 | // the resource dll is missing, code used the wrong ID or developer didn't |
836 | // update the resource DLL. |
837 | _ASSERTE(!"Missing mscorrc.dll or mscorrc.debug.dll?" ); |
838 | hr = HRESULT_FROM_GetLastError(); |
839 | } |
840 | break; |
841 | default: |
842 | { |
843 | _ASSERTE(!"Invalid eCategory" ); |
844 | } |
845 | } |
846 | } |
847 | |
848 | // Return an empty string to save the people with a bad error handling |
849 | if (szBuffer && iMax) |
850 | *szBuffer = W('\0'); |
851 | |
852 | return hr; |
853 | #else // !FEATURE_PAL |
854 | return LoadNativeStringResource(NATIVE_STRING_RESOURCE_TABLE(NATIVE_STRING_RESOURCE_NAME), iResourceID, |
855 | szBuffer, iMax, pcwchUsed); |
856 | #endif // !FEATURE_PAL |
857 | } |
858 | |
859 | #ifndef DACCESS_COMPILE |
860 | HRESULT CCompRC::LoadMUILibrary(HRESOURCEDLL * pHInst) |
861 | { |
862 | WRAPPER_NO_CONTRACT; |
863 | _ASSERTE(pHInst != NULL); |
864 | LocaleID langId; |
865 | LocaleIDValue langIdValue; |
866 | // Must resolve current thread's langId to a dll. |
867 | if(m_fpGetThreadUICultureId) { |
868 | int ret = (*m_fpGetThreadUICultureId)(&langIdValue); |
869 | |
870 | // Callback can't return 0, since that indicates empty. |
871 | // To indicate empty, callback should return UICULTUREID_DONTCARE |
872 | _ASSERTE(ret != 0); |
873 | langId=langIdValue; |
874 | } |
875 | else |
876 | langId = UICULTUREID_DONTCARE; |
877 | |
878 | HRESULT hr = GetLibrary(langId, pHInst); |
879 | return hr; |
880 | } |
881 | |
882 | HRESULT CCompRC::LoadResourceFile(HRESOURCEDLL * pHInst, LPCWSTR lpFileName) |
883 | { |
884 | #ifndef FEATURE_PAL |
885 | DWORD dwLoadLibraryFlags; |
886 | if(m_pResourceFile == m_pDefaultResource) |
887 | dwLoadLibraryFlags = LOAD_LIBRARY_AS_DATAFILE; |
888 | else |
889 | dwLoadLibraryFlags = 0; |
890 | |
891 | if ((*pHInst = WszLoadLibraryEx(lpFileName, NULL, dwLoadLibraryFlags)) == NULL) { |
892 | return HRESULT_FROM_GetLastError(); |
893 | } |
894 | #else // !FEATURE_PAL |
895 | PORTABILITY_ASSERT("UNIXTODO: Implement resource loading - use peimagedecoder?" ); |
896 | #endif // !FEATURE_PAL |
897 | return S_OK; |
898 | } |
899 | |
900 | //***************************************************************************** |
901 | // Load the library for this thread's current language |
902 | // Called once per language. |
903 | // Search order is: |
904 | // 1. Dll in localized path (<dir passed>\<lang name (en-US format)>\mscorrc.dll) |
905 | // 2. Dll in localized (parent) path (<dir passed>\<lang name> (en format)\mscorrc.dll) |
906 | // 3. Dll in root path (<dir passed>\mscorrc.dll) |
907 | // 4. Dll in current path (<current dir>\mscorrc.dll) |
908 | //***************************************************************************** |
909 | HRESULT CCompRC::LoadLibraryHelper(HRESOURCEDLL *pHInst, |
910 | SString& rcPath) |
911 | { |
912 | CONTRACTL |
913 | { |
914 | GC_NOTRIGGER; |
915 | NOTHROW; |
916 | #ifdef MODE_PREEMPTIVE |
917 | MODE_PREEMPTIVE; |
918 | #endif |
919 | } |
920 | CONTRACTL_END; |
921 | |
922 | HRESULT hr = E_FAIL; |
923 | |
924 | |
925 | _ASSERTE(m_pResourceFile != NULL); |
926 | |
927 | // must initialize before calling SString::Empty() |
928 | SString::Startup(); |
929 | |
930 | // Try and get both the culture fallback sequence |
931 | |
932 | StringArrayList cultureNames; |
933 | |
934 | if (m_fpGetThreadUICultureNames) |
935 | { |
936 | hr = (*m_fpGetThreadUICultureNames)(&cultureNames); |
937 | } |
938 | else |
939 | { |
940 | EX_TRY |
941 | { |
942 | cultureNames.Append(SString::Empty()); |
943 | } |
944 | EX_CATCH_HRESULT(hr); |
945 | } |
946 | |
947 | if (hr == E_OUTOFMEMORY) |
948 | return hr; |
949 | EX_TRY |
950 | { |
951 | for (DWORD i=0; i< cultureNames.GetCount();i++) |
952 | { |
953 | SString& sLang = cultureNames[i]; |
954 | |
955 | PathString rcPathName(rcPath); |
956 | |
957 | if (!rcPathName.EndsWith(W("\\" ))) |
958 | { |
959 | rcPathName.Append(W("\\" )); |
960 | } |
961 | |
962 | if (!sLang.IsEmpty()) |
963 | { |
964 | rcPathName.Append(sLang); |
965 | rcPathName.Append(W("\\" )); |
966 | rcPathName.Append(m_pResourceFile); |
967 | } |
968 | else |
969 | { |
970 | rcPathName.Append(m_pResourceFile); |
971 | } |
972 | |
973 | // Feedback for debugging to eliminate unecessary loads. |
974 | DEBUG_STMT(DbgWriteEx(W("Loading %s to load strings.\n" ), rcPath.GetUnicode())); |
975 | |
976 | // Load the resource library as a data file, so that the OS doesn't have |
977 | // to allocate it as code. This only works so long as the file contains |
978 | // only strings. |
979 | hr = LoadResourceFile(pHInst, rcPathName); |
980 | if (SUCCEEDED(hr)) |
981 | break; |
982 | |
983 | } |
984 | } |
985 | EX_CATCH_HRESULT(hr); |
986 | |
987 | // Last ditch search effort in current directory |
988 | if (FAILED(hr)) { |
989 | hr = LoadResourceFile(pHInst, m_pResourceFile); |
990 | } |
991 | |
992 | return hr; |
993 | } |
994 | |
995 | // Two-stage approach: |
996 | // First try module directory, then try CORSystemDirectory for default resource |
997 | HRESULT CCompRC::LoadLibraryThrows(HRESOURCEDLL * pHInst) |
998 | { |
999 | CONTRACTL |
1000 | { |
1001 | GC_NOTRIGGER; |
1002 | THROWS; |
1003 | #ifdef MODE_PREEMPTIVE |
1004 | MODE_PREEMPTIVE; |
1005 | #endif |
1006 | } |
1007 | CONTRACTL_END; |
1008 | |
1009 | HRESULT hr = S_OK; |
1010 | |
1011 | _ASSERTE(pHInst != NULL); |
1012 | |
1013 | #ifdef CROSSGEN_COMPILE |
1014 | // The resources are embeded into the .exe itself for crossgen |
1015 | *pHInst = GetModuleInst(); |
1016 | #else |
1017 | PathString rcPath; // Path to resource DLL. |
1018 | |
1019 | // Try first in the same directory as this dll. |
1020 | |
1021 | VALIDATECORECLRCALLBACKS(); |
1022 | |
1023 | |
1024 | hr = g_CoreClrCallbacks.m_pfnGetCORSystemDirectory(rcPath); |
1025 | if (FAILED(hr)) |
1026 | return hr; |
1027 | |
1028 | hr = LoadLibraryHelper(pHInst, rcPath); |
1029 | |
1030 | |
1031 | #endif // CROSSGEN_COMPILE |
1032 | |
1033 | return hr; |
1034 | } |
1035 | HRESULT CCompRC::LoadLibrary(HRESOURCEDLL * pHInst) |
1036 | { |
1037 | CONTRACTL |
1038 | { |
1039 | GC_NOTRIGGER; |
1040 | NOTHROW; |
1041 | #ifdef MODE_PREEMPTIVE |
1042 | MODE_PREEMPTIVE; |
1043 | #endif |
1044 | } |
1045 | CONTRACTL_END; |
1046 | |
1047 | HRESULT hr = S_OK; |
1048 | EX_TRY |
1049 | { |
1050 | hr = LoadLibraryThrows(pHInst); |
1051 | } |
1052 | EX_CATCH_HRESULT(hr); |
1053 | |
1054 | return hr; |
1055 | } |
1056 | #endif // DACCESS_COMPILE |
1057 | |
1058 | |
1059 | |
1060 | #ifdef USE_FORMATMESSAGE_WRAPPER |
1061 | DWORD |
1062 | PALAPI |
1063 | CCompRC::FormatMessage( |
1064 | IN DWORD dwFlags, |
1065 | IN LPCVOID lpSource, |
1066 | IN DWORD dwMessageId, |
1067 | IN DWORD dwLanguageId, |
1068 | OUT LPWSTR lpBuffer, |
1069 | IN DWORD nSize, |
1070 | IN va_list *Arguments) |
1071 | { |
1072 | STATIC_CONTRACT_NOTHROW; |
1073 | StackSString str; |
1074 | if (dwFlags & FORMAT_MESSAGE_FROM_SYSTEM) |
1075 | { |
1076 | dwFlags&=~FORMAT_MESSAGE_FROM_SYSTEM; |
1077 | dwFlags|=FORMAT_MESSAGE_FROM_STRING; |
1078 | str.LoadResourceAndReturnHR(NULL,CCompRC::Error,dwMessageId); |
1079 | lpSource=str.GetUnicode(); |
1080 | } |
1081 | return WszFormatMessage(dwFlags, |
1082 | lpSource, |
1083 | dwMessageId, |
1084 | dwLanguageId, |
1085 | lpBuffer, |
1086 | nSize, |
1087 | Arguments); |
1088 | } |
1089 | #endif // USE_FORMATMESSAGE_WRAPPER |
1090 | |
1091 | |