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.
6 // LoadRC-impl.cpp : Utility to load a localized file (primarily a DLL)
10 #if defined(USE_SSTRING)
12 // This is the normal path: use SafeString, wrappers, etc...
17 typedef SString MyString;
18 typedef SString::CIterator MyStringIterator;
19 #define EndsWithChar(OBJ, CHAR) (OBJ.EndsWith(CHAR))
20 #define AppendChar(OBJ, CHAR) (OBJ.Append(CHAR))
21 #define AppendStr(OBJ, STR) (OBJ.Append(STR))
22 #define TrimLastChar(OBJ) (OBJ.Truncate(OBJ.End() - 1))
23 #define GetChars(OBJ) (OBJ.GetUnicode())
24 #define IsEmptyStr(OBJ) (OBJ.IsEmpty())
25 #define CharLength(OBJ) (OBJ.GetCount())
26 #define StrBeginIter(OBJ) (OBJ.Begin())
27 #define StrEndIter(OBJ) (OBJ.End())
28 #define FindNext(OBJ, ITER, CHAR) (OBJ.Find(ITER, CHAR))
29 #define MakeString(DST, OBJ, BEG, END) (DST.Set(OBJ, BEG, END))
30 #define StrEquals(STR1, STR2) (STR1.Compare(STR2)==0)
31 #define FindLast(OBJ, ITER, CHAR) (OBJ.FindBack(ITER, CHAR))
32 void SkipChars(MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (str.Skip(i, c1) || str.Skip(i, c2)); }
34 #elif defined(USE_WSTRING)
36 // This stuff is used by GacUtil, because it _really_ doesn't want to link with utilcode :-(
40 typedef std::wstring MyString;
41 typedef std::wstring::const_iterator MyStringIterator;
42 #define EndsWithChar(OBJ, CHAR) (*(OBJ.rbegin()) == CHAR)
43 #define AppendChar(OBJ, CHAR) (OBJ.push_back(CHAR))
44 #define AppendStr(OBJ, STR) (OBJ += STR)
45 #define TrimLastChar(OBJ) (OBJ.resize(OBJ.size() - 1))
46 #define GetChars(OBJ) (OBJ.c_str())
47 #define IsEmptyStr(OBJ) (OBJ.empty())
48 #define CharLength(OBJ) (OBJ.size())
49 #define StrBeginIter(OBJ) (OBJ.begin())
50 #define StrEndIter(OBJ) (OBJ.end())
51 #define FindNext(OBJ, ITER, CHAR) (ITER = std::find<std::wstring::const_iterator>(ITER, OBJ.end(), CHAR))
52 #define MakeString(DST, OBJ, BEG, END) (DST = MyString(BEG, END))
53 #define StrEquals(STR1, STR2) (STR1 == STR2)
54 #define ClrGetEnvironmentVariable(var, res) GetEnvVar(L##var, res)
55 bool FindLast(const MyString &str, MyStringIterator &iter, wchar_t c)
57 size_t pos = str.find_last_of(c);
58 iter = (pos == std::wstring::npos) ? str.end() : (str.begin() + pos);
59 return pos != std::wstring::npos;
61 void SkipChars(const MyString &str, MyStringIterator &i, WCHAR c1, WCHAR c2) { while (*i == c1 || *i == c2) i++; }
62 bool GetEnvVar(_In_z_ wchar_t *var, MyString &res)
66 _wdupenv_s(&buffer, &size, var);
70 free(buffer); // Don't forget to free the buffer!
73 void ClrGetModuleFileName(HMODULE hModule, MyString& value)
75 wchar_t driverpath_tmp[_MAX_PATH];
76 GetModuleFileNameW(hModule, driverpath_tmp, _MAX_PATH);
77 value = driverpath_tmp;
82 #error You must define either USE_SSTRING or USE_WSTRING to use this file
86 // This is a helper for loading localized string resource DLL files
87 HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf);
89 // This is a slight variation that can be used for anything else (ildasm.chm, for example)
90 typedef void* (__cdecl *LocalizedFileHandler)(LPCWSTR);
91 void* FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePath);
93 // Helper functions to combine paths
94 static MyString MakePath(const MyString &root, const MyString &file)
97 if (!EndsWithChar(res, W('\\')))
98 AppendChar(res, W('\\'));
102 static MyString MakePath(const MyString &root, const MyString &dir, const MyString &file)
104 return MakePath(MakePath(root, dir), file);
107 // Helper to deal with occasional training back-slashes
108 static bool FileExists(const MyString &file)
110 if (!EndsWithChar(file, W('\\')))
111 return GetFileAttributesW(GetChars(file)) != INVALID_FILE_ATTRIBUTES;
116 return GetFileAttributesW(GetChars(tmp)) != INVALID_FILE_ATTRIBUTES;
120 // Little helper function to get the codepage integer ID from the LocaleInfo
121 static UINT GetCodePage(LANGID LanguageID, DWORD locale)
123 wchar_t CodePageInt[12];
124 GetLocaleInfo(MAKELCID(LanguageID, SORT_DEFAULT), LOCALE_IDEFAULTCODEPAGE, CodePageInt, _countof(CodePageInt));
125 return _wtoi(CodePageInt);
129 #define ENGLISH_LCID MAKELCID(MAKELANGID( LANG_ENGLISH, SUBLANG_ENGLISH_US ), SORT_DEFAULT)
131 // FindLocaleDirectory: Search the provided path for one of the expected code page subdirectories
132 // Returns empty string on failure, or the full path to the c:\my\directory\1033\myrcfile.dll
133 static MyString FindLocaleDirectory(const MyString &path, const MyString &dllName)
135 // We'll be checking for 3 different locales: The user's default locale
136 // The user's primary language locale, and english (in that order)
137 const LCID lcidUser = MAKELCID(GetUserDefaultUILanguage(), SORT_DEFAULT);
138 LCID rglcid[3] = {lcidUser, MAKELCID(MAKELANGID(PRIMARYLANGID(lcidUser), SUBLANG_DEFAULT), SORTIDFROMLCID(lcidUser)), ENGLISH_LCID};
140 for (int i = 0; i < _countof(rglcid); i++)
142 LCID lcid = rglcid[i];
143 // Turn the LCID into a string
144 wchar_t wzNumBuf[12];
145 _itow_s(lcid, wzNumBuf, _countof(wzNumBuf), 10);
146 MyString localePath = MakePath(path, wzNumBuf, dllName);
148 // Check to see if the file exists
149 if (FileExists(localePath))
151 // make sure the console can support a codepage for this language.
152 UINT ConsoleCP = GetConsoleOutputCP();
154 // Dev10 #843375: For a GUI application, GetConsoleOutputCP returns 0
155 // If that's the case, we don't care about capabilities of the console,
156 // since we're not outputting to the console, anyway...
157 if ( ConsoleCP != 0 && lcid != ENGLISH_LCID )
159 LANGID LanguageID = MAKELANGID( lcid, SUBLANGID(lcid) );
160 // we know the console cannot support arabic or hebrew (right to left scripts?)
161 if( PRIMARYLANGID(LanguageID) == LANG_ARABIC || PRIMARYLANGID(LanguageID) == LANG_HEBREW )
164 UINT LangOEMCodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTCODEPAGE);
165 UINT LangANSICodepage = GetCodePage(LanguageID, LOCALE_IDEFAULTANSICODEPAGE);
167 // We can only support it if the console's code page is UTF8, OEM, or ANSI
168 if( ConsoleCP != CP_UTF8 && ConsoleCP != LangOEMCodepage && ConsoleCP != LangANSICodepage )
178 // Attempt to load the resource file from the locale, first.
179 // If that fails, then just try any subdirectory of of the path provided
180 static void *LoadLocalFile(const MyString &path, const MyString &dllName, LocalizedFileHandler lfh)
182 if (IsEmptyStr(path) || IsEmptyStr(dllName))
185 MyString pathTemp = path;
187 // Languages are checked in the following order.
188 // 1) The UI language: this is returned by GetUserDefaultUILanguage.
189 // 2) As step 1, but with SUBLANG_DEFAULT
191 // 4) Any language that can be found!
193 MyString localePath = FindLocaleDirectory(pathTemp, dllName);
195 if (IsEmptyStr(localePath))
197 // None of the default choices exists, so now look for the first version of the dll in the given path.
198 // We don't bother to see if the console supports the dll's language.
199 MyString wildCard = MakePath(pathTemp, W("*.*"));
200 WIN32_FIND_DATAW wfdw;
201 HANDLE hDirs = FindFirstFileW(GetChars(wildCard), &wfdw);
202 if (hDirs == INVALID_HANDLE_VALUE)
206 // We are only interested in directories, since at this level, that should
207 // be the only thing in this directory, i.e, LCID sub dirs
208 if (wfdw.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
210 MyString file(wfdw.cFileName);
212 if (StrEquals(file, W(".")))
215 if (StrEquals(file, W("..")))
218 // Does this dir have the resource dll?
219 MyString fullPath = MakePath(pathTemp, file, dllName);
221 if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES)
223 localePath = fullPath; // Got it - bail out of here
227 } while (FindNextFileW(hDirs, &wfdw));
232 if (IsEmptyStr(localePath))
235 // With CoreCLR we have the resource dll directly in the bin directory so check there now.
238 // Does this dir have the resource dll?
239 MyString fullPath = MakePath(path, dllName);
241 if (GetFileAttributesW(GetChars(fullPath)) != INVALID_FILE_ATTRIBUTES)
243 localePath = fullPath; // Got it - bail out of here
248 // Attempt to load the library
249 // Beware! A dll loaded with LOAD_LIBRARY_AS_DATAFILE won't
250 // let you use LoadIcon and things like that (only general calls like FindResource and LoadResource).
251 return IsEmptyStr(localePath) ? NULL : lfh(GetChars(localePath));
254 // Try to load the resource DLL from [each directory in %PATH%]/<lcid>/
255 static void *LoadSearchPath(const MyString &resourceDllName, LocalizedFileHandler lfh)
259 // Get the PATH variable into a C++ string
262 if (ClrGetEnvironmentVariable("PATH", envPath))
265 MyStringIterator endOfChunk, startOfChunk = StrBeginIter(envPath);
267 for (SkipChars(envPath, startOfChunk, W(' '), W(';'));
268 hmod == NULL && startOfChunk != StrEndIter(envPath);
269 SkipChars(envPath, startOfChunk, W(' '), W(';')))
271 // copy this chunk of the path into our trypath
272 endOfChunk = startOfChunk;
273 FindNext(envPath, endOfChunk, W(';'));
274 MakeString(tryPath, envPath, startOfChunk, endOfChunk);
276 // Don't try invalid locations
277 if (IsEmptyStr(tryPath) || CharLength(tryPath) >= _MAX_PATH)
280 // Try to load the dll
281 hmod = LoadLocalFile(tryPath, resourceDllName, lfh);
282 startOfChunk = endOfChunk;
287 void * __cdecl LibraryLoader(_In_z_ LPCWSTR lpFileName)
289 return (void *)(LoadLibraryExW(lpFileName, NULL, LOAD_LIBRARY_AS_DATAFILE));
292 void *FindLocalizedFile(_In_z_ LPCWSTR wzResourceDllName, LocalizedFileHandler lfh, _In_opt_z_ LPCWSTR modulePathW)
294 // find path of the modulePath
297 ClrGetModuleFileName(GetModuleHandleW(modulePathW), modulePath);
299 // Rip off the application name.
300 MyStringIterator trailingSlashLocation = StrEndIter(modulePath);
301 if (FindLast(modulePath, trailingSlashLocation, W('\\')))
302 MakeString(driverPath, modulePath, StrBeginIter(modulePath), trailingSlashLocation);
304 // If it's not a full path, look in the current directory
307 // return the first of the local directory's copy or the resource DLL on %PATH%
308 void *hmod = LoadLocalFile(driverPath, wzResourceDllName, lfh);
310 hmod = LoadSearchPath(wzResourceDllName, lfh);
314 // load the satellite dll which contains string resources
315 HMODULE LoadLocalizedResourceDLLForSDK(_In_z_ LPCWSTR wzResourceDllName, _In_opt_z_ LPCWSTR modulePath, bool trySelf)
317 HMODULE hmod = (HMODULE)FindLocalizedFile(wzResourceDllName, &LibraryLoader, modulePath);
318 if (hmod == NULL && trySelf)
319 hmod = GetModuleHandleW(NULL);