Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

436 lines
12 KiB

  1. //////////////////////////////////////////////////////////////////////////
  2. //
  3. //
  4. // language.cpp --- Implementation for language related classes.
  5. //
  6. //
  7. /*
  8. This file contains classes related to managing the UI language of HHCTRL.
  9. */
  10. //////////////////////////////////////////////////////////////////////////
  11. //
  12. // Includes
  13. //
  14. // Get the precompiled header file.
  15. #include "header.h"
  16. // Get our header
  17. #include "language.h"
  18. //////////////////////////////////////////////////////////////////////////
  19. //
  20. // Constants
  21. //
  22. const int c_NumBaseLangIds = 10 ; // The min. number of ids which we search.
  23. const int c_NumSysLangIds = 7 ; // The num of ids we check if we are only checking the system ids.
  24. const char c_Suffix[] = "ui" ;
  25. const char c_Ext[] = ".dll" ;
  26. const char c_txtNlsModuleName[] = "kernel32.dll" ;
  27. const char c_txtUserUILanguage[] = "GetUserDefaultUILanguage" ;
  28. const char c_txtSystemUILanguage[] = "GetSystemDefaultUILanguage" ;
  29. const char c_SatelliteSubDir[] = "mui" ;
  30. //////////////////////////////////////////////////////////////////////////
  31. //
  32. // CLanguageEnum
  33. //
  34. /*
  35. This class enumerates the langids to attempt. If you are trying to access
  36. some object using a langid and that object may not be in that particular langid,
  37. you should use this class to get an array of langids to try.
  38. NOTE: You should never use the CLanguageEnum class directly. It is meant to only
  39. be used via the CLanguage class below. The only exception is the CHtmlHelpModule
  40. class which uses it for initialization.
  41. */
  42. //////////////////////////////////////////////////////////////////////////
  43. //
  44. // Construction
  45. //
  46. CLanguageEnum::CLanguageEnum(LANGID langid1, LANGID langid2, bool bSysOnly /*=false*/)
  47. : m_langids(NULL),
  48. m_index(0)
  49. {
  50. int i = 0 ;
  51. LCID lcidUser = GetUserDefaultLCID();
  52. LCID lcidSystem = GetSystemDefaultLCID();
  53. LANGID os = NULL ;
  54. //--- Count number of items.
  55. if (bSysOnly)
  56. {
  57. m_items = c_NumSysLangIds ;
  58. }
  59. else // The following are not included if we are only using the system LCID
  60. {
  61. m_items = c_NumBaseLangIds ;
  62. // This is an optionaly extra langid to search first. Mainly used by toc.cpp and collect.cpp to find random chms.
  63. if (langid1 != NULL)
  64. {
  65. m_items += 3 ;
  66. }
  67. // This is another optional langid. It is usually set to the langid of the UI.
  68. if (langid2 != NULL)
  69. {
  70. m_items += 3 ;
  71. }
  72. }
  73. // This is an optionaly extra langid to search first. Mainly used by toc.cpp and collect.cpp to find random chms.
  74. if (langid1 != NULL)
  75. {
  76. m_items += 3 ;
  77. }
  78. // If NT5 use the UI Language
  79. // The UI Language is included in both the sys count and the non-sys count.
  80. os = _Module.m_Language.GetUserOsUiLanguage() ;
  81. if (os)
  82. {
  83. m_items += 3 ;
  84. }
  85. //--- Allocate memory for the array
  86. m_langids = new LANGID[m_items] ;
  87. if (langid1 != NULL) // User defined langid
  88. {
  89. m_langids[i++] = langid1 ;
  90. m_langids[i++] = MAKELANGID(PRIMARYLANGID(langid1), SUBLANG_DEFAULT) ;
  91. m_langids[i++] = MAKELANGID(PRIMARYLANGID(langid1), SUBLANG_NEUTRAL) ;
  92. }
  93. //--- Fill in the memory.
  94. if (!bSysOnly) // The following are not included if we are only using the system LCID
  95. {
  96. if (langid2 != NULL) // User defined langid
  97. {
  98. m_langids[i++] = langid2 ;
  99. m_langids[i++] = MAKELANGID(PRIMARYLANGID(langid2), SUBLANG_DEFAULT) ;
  100. m_langids[i++] = MAKELANGID(PRIMARYLANGID(langid2), SUBLANG_NEUTRAL) ;
  101. }
  102. // If NT5 use the UI Language
  103. if (os)
  104. {
  105. m_langids[i++] = os;
  106. m_langids[i++] = MAKELANGID(PRIMARYLANGID(os), SUBLANG_DEFAULT) ;
  107. m_langids[i++] = MAKELANGID(PRIMARYLANGID(os), SUBLANG_NEUTRAL) ;
  108. }
  109. m_langids[i++] = LANGIDFROMLCID(lcidUser);
  110. m_langids[i++] = MAKELANGID(PRIMARYLANGID(lcidUser), SUBLANG_DEFAULT);
  111. m_langids[i++] = MAKELANGID(PRIMARYLANGID(lcidUser), SUBLANG_NEUTRAL);
  112. }
  113. else
  114. {
  115. // If NT5 use the UI Language
  116. // This is placed here so that UI Language can be before lcidUser.
  117. if (os)
  118. {
  119. m_langids[i++] = os;
  120. m_langids[i++] = MAKELANGID(PRIMARYLANGID(os), SUBLANG_DEFAULT) ;
  121. m_langids[i++] = MAKELANGID(PRIMARYLANGID(os), SUBLANG_NEUTRAL) ;
  122. }
  123. }
  124. m_langids[i++] = LANGIDFROMLCID(lcidSystem);
  125. m_langids[i++] = MAKELANGID(PRIMARYLANGID(lcidSystem), SUBLANG_DEFAULT);
  126. m_langids[i++] = MAKELANGID(PRIMARYLANGID(lcidSystem), SUBLANG_NEUTRAL);
  127. m_langids[i++] = LANGIDFROMLCID(0x0409); // Try English
  128. m_langids[i++] = MAKELANGID(PRIMARYLANGID(0x0409), SUBLANG_DEFAULT);
  129. m_langids[i++] = MAKELANGID(PRIMARYLANGID(0x0409), SUBLANG_NEUTRAL);
  130. m_langids[i++] = 0x0000; // Try to find anything which was installed.
  131. ASSERT(i == m_items) ;
  132. }
  133. //////////////////////////////////////////////////////////////////////////
  134. //
  135. // Destructor
  136. //
  137. CLanguageEnum::~CLanguageEnum()
  138. {
  139. if (m_langids)
  140. {
  141. delete [] m_langids ;
  142. }
  143. }
  144. //////////////////////////////////////////////////////////////////////////
  145. //
  146. // Access
  147. //
  148. //////////////////////////////////////////////////////////////////////////
  149. //
  150. // start
  151. //
  152. LANGID
  153. CLanguageEnum::start()
  154. {
  155. m_index = 0 ;
  156. return m_langids[0] ;
  157. }
  158. //////////////////////////////////////////////////////////////////////////
  159. //
  160. // next
  161. //
  162. LANGID
  163. CLanguageEnum::next()
  164. {
  165. LANGID nextLangId = c_LANGID_ENUM_EOF ;
  166. if (m_index >= 0 )
  167. {
  168. while (++m_index < m_items)
  169. {
  170. // If not the first item, check if its the same as any LCID already checked, which is
  171. // very possible
  172. bool bAlreadyTried= false;
  173. for (int n = 0 ; n < m_index ; n++)
  174. {
  175. if (m_langids[n] == m_langids[m_index])
  176. {
  177. bAlreadyTried = true;
  178. break; // Already done this one.
  179. }
  180. }
  181. // If we haven't already done it. return it.
  182. if (!bAlreadyTried)
  183. {
  184. nextLangId = m_langids[m_index] ;
  185. break;
  186. }
  187. }
  188. }
  189. return nextLangId ;
  190. }
  191. //////////////////////////////////////////////////////////////////////////
  192. //
  193. // CLanguage
  194. //
  195. //////////////////////////////////////////////////////////////////////////
  196. //
  197. // Construction
  198. //////////////////////////////////////////////////////////////////////////
  199. //
  200. // Simple construction
  201. //
  202. CLanguage::CLanguage()
  203. : m_langid(NULL),
  204. m_hSatellite(NULL),
  205. m_hNlsModule(NULL),
  206. m_fpGetUserDefaultUILanguage(NULL),
  207. m_fpGetSystemDefaultUILanguage(NULL)
  208. {
  209. }
  210. //////////////////////////////////////////////////////////////////////////
  211. //
  212. // Destructor
  213. //
  214. CLanguage::~CLanguage()
  215. {
  216. }
  217. //////////////////////////////////////////////////////////////////////////
  218. //
  219. // Member Access
  220. //
  221. //////////////////////////////////////////////////////////////////////////
  222. //
  223. // GetUiLanguage
  224. //
  225. LANGID
  226. CLanguage::GetUiLanguage()
  227. {
  228. _Init() ;
  229. return m_langid ; // If this is NULL, we should set it here.
  230. }
  231. //////////////////////////////////////////////////////////////////////////
  232. //
  233. // SetUiLanguage
  234. //
  235. LANGID
  236. CLanguage::SetUiLanguage(LANGID langid)
  237. {
  238. if (m_langid)
  239. {
  240. ASSERT(0)
  241. return m_langid; // There can be only one! - Highlander.
  242. }
  243. else
  244. {
  245. m_langid = langid; // More validation?
  246. // Load the satellite dll. Will change the value of m_langid.
  247. _LoadSatellite() ;
  248. return m_langid ;
  249. }
  250. }
  251. //////////////////////////////////////////////////////////////////////////
  252. //
  253. // GetEnumerator
  254. //
  255. CLanguageEnum*
  256. CLanguage::_GetEnumerator(LANGID langidOther)
  257. {
  258. return new CLanguageEnum(langidOther, m_langid) ; // What if m_langid is null?
  259. }
  260. //////////////////////////////////////////////////////////////////////////
  261. //
  262. // _GetSysEnumerator --- Enumerates just the system LCID and English LCID. Used by the satellite DLLs.
  263. //
  264. CLanguageEnum*
  265. CLanguage::_GetSysEnumerator(LANGID landidOther)
  266. {
  267. return new CLanguageEnum(landidOther, NULL, true) ;
  268. }
  269. //////////////////////////////////////////////////////////////////////////
  270. //
  271. // LoadSatellite --- The purpose of this function is to set m_langid.
  272. // As a side affect, it will load a satellite if required.
  273. //
  274. void
  275. CLanguage::_LoadSatellite()
  276. {
  277. ASSERT(m_hSatellite == NULL) ;
  278. // Buffer for building satellite pathname
  279. char pathname[MAX_PATH] ;
  280. // Get Modulename
  281. char modulename[MAX_PATH] ;
  282. char drive[_MAX_DRIVE];
  283. char dir[_MAX_DIR];
  284. char fname[_MAX_FNAME];
  285. char ext[_MAX_EXT];
  286. GetModuleFileName(_Module.GetModuleInstance(), modulename, MAX_PATH) ;
  287. _splitpath(modulename, drive, dir, fname, ext );
  288. // This piece of code queries for the "install language" of the OS. This LCID value
  289. // corresponds to the language of the resources used by the OS shell. For example, on
  290. // an "enabled Hebrew Win98" system the language of the shell is 0x409 (English).
  291. //
  292. // The purpose of this code is to force the language of the satellite DLL to match the
  293. // language of the OS shell. See bug #7860 and #7845 for more information.
  294. //
  295. LANGID langOther = NULL;
  296. HKEY hkey;
  297. if(!g_fSysWinNT && RegOpenKeyEx(HKEY_CURRENT_USER, "Control Panel\\Desktop\\ResourceLocale", 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  298. {
  299. char szLanguage[20];
  300. DWORD cbLanguage = sizeof(szLanguage);
  301. *szLanguage = 0;
  302. if((RegQueryValueEx(hkey, NULL, NULL, NULL, (LPBYTE) szLanguage, &cbLanguage) == ERROR_SUCCESS))
  303. {
  304. langOther = LOWORD(strtol(szLanguage, NULL, 16));
  305. }
  306. }
  307. // Get the langid enumerator for the UI language, system LCID and the English LCID.
  308. // Currently, we don't support changing the UI language to anything but the system language.
  309. // To make it easy to change this support in the future, this function still uses GetSysEnumerator
  310. // To make it try more LCID types change the call below to _GetEnumerator...
  311. CLanguageEnum* pEnum = _GetSysEnumerator(langOther) ;
  312. ASSERT(pEnum) ;
  313. // Get first langid.
  314. LANGID langid = pEnum->start() ;
  315. while (langid != 0)
  316. {
  317. // Attempt to load satellite DLL in this language.
  318. if (PRIMARYLANGID(langid) == 0x0009) // English
  319. {
  320. // We do not need an English satellite DLL, so we are done.
  321. m_langid = langid ;
  322. break ;
  323. }
  324. // Convert the langid to a hex string without leading 0x.
  325. wsprintf(pathname, "%s%s%s\\%04x\\%s%s%s",drive,dir,c_SatelliteSubDir,langid,fname,c_Suffix,c_Ext) ;
  326. if (IsFile(pathname))
  327. {
  328. // Load the satellite.
  329. m_hSatellite = LoadLibrary(pathname) ;
  330. ASSERT(m_hSatellite) ;
  331. m_langid = langid ;
  332. break ;
  333. }
  334. // Get next langid to attempt.
  335. langid = pEnum->next() ;
  336. }
  337. // Cleanup.
  338. if (pEnum)
  339. {
  340. delete pEnum ;
  341. }
  342. ASSERT(m_langid != NULL) ;
  343. // m_hSatellite may be NULL. English has no satellite.
  344. }
  345. //////////////////////////////////////////////////////////////////////////
  346. //
  347. // GetUserOsUiLanguage - Return the user's ui language.
  348. //
  349. /*
  350. If this is a non-NT5 system, return 0x0.
  351. If the user's UI language is the same as the system's ui language, return 0x0.
  352. Otherwise, return GetUserDefaultUiLanguage
  353. */
  354. LANGID
  355. CLanguage::GetUserOsUiLanguage()
  356. {
  357. if (!g_bWinNT5)
  358. {
  359. return NULL ;
  360. }
  361. if (m_fpGetUserDefaultUILanguage == NULL)
  362. {
  363. // Assume that if this is null then the module handle is null.
  364. ASSERT(m_hNlsModule== NULL) ;
  365. // Get a module handle.
  366. m_hNlsModule = LoadLibrary(c_txtNlsModuleName);
  367. if (m_hNlsModule == NULL)
  368. {
  369. // Couldn't get the module handle. Fail!
  370. return NULL ;
  371. }
  372. // Get the function's address
  373. m_fpGetUserDefaultUILanguage = (FntPtr_GetDefaultUILanguage)::GetProcAddress(m_hNlsModule, c_txtUserUILanguage) ;
  374. if (m_fpGetUserDefaultUILanguage == NULL)
  375. {
  376. return NULL ;
  377. }
  378. }
  379. LANGID user = m_fpGetUserDefaultUILanguage() ;
  380. return user ;
  381. }