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
14DECLARE_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.
31extern 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
39typedef LANGID (WINAPI *PFNGETUSERDEFAULTUILANGUAGE)(void); // kernel32!GetUserDefaultUILanguage
40
41int 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
90static 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
103void 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
126void GetMUIParentLanguageName(SString* pResult)
127{
128 WRAPPER_NO_CONTRACT;
129 int langid = 1033;
130
131 BuildMUIDirectory(langid, pResult);
132}
133#ifndef DACCESS_COMPILE
134HRESULT 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
170BOOL CCompRC::s_bIsMscoree = FALSE;
171
172//*****************************************************************************
173// Do the mapping from an langId to an hinstance node
174//*****************************************************************************
175HRESOURCEDLL 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//*****************************************************************************
200const int MAP_STARTSIZE = 7;
201const int MAP_GROWSIZE = 5;
202
203HRESULT 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//*****************************************************************************
263LPCWSTR CCompRC::m_pDefaultResource = W("mscorrc.debug.dll");
264LPCWSTR CCompRC::m_pFallbackResource= W("mscorrc.dll");
265
266#ifdef FEATURE_PAL
267LPCSTR CCompRC::m_pDefaultResourceDomain = "mscorrc.debug";
268LPCSTR CCompRC::m_pFallbackResourceDomain = "mscorrc";
269#endif // FEATURE_PAL
270
271HRESULT 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
370void CCompRC::SetResourceCultureCallbacks(
371 FPGETTHREADUICULTURENAMES fpGetThreadUICultureNames,
372 FPGETTHREADUICULTUREID fpGetThreadUICultureId)
373{
374 LIMITED_METHOD_CONTRACT;
375
376 m_fpGetThreadUICultureNames = fpGetThreadUICultureNames;
377 m_fpGetThreadUICultureId = fpGetThreadUICultureId;
378}
379
380void 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
393void 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//*****************************************************************************
453LONG CCompRC::m_dwDefaultInitialized = 0;
454CCompRC CCompRC::m_DefaultResourceDll;
455
456CCompRC* 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
480LONG CCompRC::m_dwFallbackInitialized = 0;
481CCompRC CCompRC::m_FallbackResourceDll;
482
483CCompRC* 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
512HRESULT 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 }
664Exit:
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//*****************************************************************************
674HRESULT 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
700HRESULT 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
860HRESULT 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
882HRESULT 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//*****************************************************************************
909HRESULT 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
997HRESULT 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}
1035HRESULT 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
1061DWORD
1062PALAPI
1063CCompRC::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