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