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.

849 lines
20 KiB

  1. #include "precomp.h"
  2. #pragma hdrstop
  3. //
  4. // Global variables defined here
  5. //
  6. //
  7. // TargetNativeLangID : this is native language ID of running system
  8. //
  9. LANGID TargetNativeLangID;
  10. //
  11. // SourceNativeLangID : this is native language ID of new NT you want to install
  12. //
  13. LANGID SourceNativeLangID;
  14. //
  15. // IsLanguageMatched : if source and target language are matched (or compatible)
  16. //
  17. // 1. if SourceNativeLangID == TargetNativeLangID
  18. //
  19. // 2. if SourceNativeLangID's alternative ID == TargetNativeLangID
  20. //
  21. BOOL IsLanguageMatched;
  22. typedef struct _tagLANGINFO {
  23. LANGID LangID;
  24. INT Count;
  25. } LANGINFO,*PLANGINFO;
  26. BOOL
  27. TrustedDefaultUserLocale(
  28. HINF Inf,
  29. LANGID LangID);
  30. BOOL
  31. MySetupapiGetIntField(
  32. IN PINFCONTEXT Context,
  33. IN DWORD FieldIndex,
  34. OUT PINT IntegerValue,
  35. IN int Base
  36. );
  37. BOOL
  38. CALLBACK
  39. EnumLangProc(
  40. HANDLE hModule, // resource-module handle
  41. LPCTSTR lpszType, // pointer to resource type
  42. LPCTSTR lpszName, // pointer to resource name
  43. WORD wIDLanguage, // resource language identifier
  44. LONG_PTR lParam // application-defined parameter
  45. )
  46. /*++
  47. Routine Description:
  48. Callback that counts versions stamps.
  49. Arguments:
  50. Details of version enumerated version stamp. (Ignore.)
  51. Return Value:
  52. Indirectly thru lParam: count, langID
  53. --*/
  54. {
  55. PLANGINFO LangInfo;
  56. LangInfo = (PLANGINFO) lParam;
  57. LangInfo->Count++;
  58. //
  59. // for localized build contains multiple resource,
  60. // it usually contains 0409 as backup lang.
  61. //
  62. // if LangInfo->LangID != 0 means we already assigned an ID to it
  63. //
  64. // so when wIDLanguage == 0x409, we keep the one we got from last time
  65. //
  66. if ((wIDLanguage == 0x409) && (LangInfo->LangID != 0)) {
  67. return TRUE;
  68. }
  69. LangInfo->LangID = wIDLanguage;
  70. return TRUE; // continue enumeration
  71. }
  72. LANGID
  73. GetNTDLLNativeLangID()
  74. /*++
  75. Routine Description:
  76. This function is designed specifically for getting native lang of ntdll.dll
  77. This is not a generic function to get other module's language
  78. the assumption is:
  79. 1. if only one language in resource then return this lang
  80. 2. if two languages in resource then return non-US language
  81. 3. if more than two languages, it's invalid in our case, but returns the last one.
  82. Arguments:
  83. None
  84. Return Value:
  85. Native lang ID in ntdll.dll
  86. --*/
  87. {
  88. LPCTSTR Type = (LPCTSTR) RT_VERSION;
  89. LPCTSTR Name = (LPCTSTR) 1;
  90. LANGINFO LangInfo;
  91. ZeroMemory(&LangInfo,sizeof(LangInfo));
  92. EnumResourceLanguages(
  93. GetModuleHandle(TEXT("ntdll.dll")),
  94. Type,
  95. Name,
  96. EnumLangProc,
  97. (LONG_PTR) &LangInfo
  98. );
  99. if ((LangInfo.Count > 2) || (LangInfo.Count < 1) ) {
  100. //
  101. // put error log here
  102. //
  103. // so far, for NT 3.51, only JPN has two language resources
  104. }
  105. return LangInfo.LangID;
  106. }
  107. BOOL IsHongKongVersion()
  108. /*++
  109. Routine Description:
  110. Try to identify HongKong NT 4.0
  111. It based on:
  112. NTDLL's language is English and build is 1381 and
  113. pImmReleaseContext return TRUE
  114. Arguments:
  115. Return Value:
  116. Language ID of running system
  117. --*/
  118. {
  119. HMODULE hMod;
  120. BOOL bRet=FALSE;
  121. typedef BOOL (*IMMRELEASECONTEXT) (HWND,HANDLE);
  122. IMMRELEASECONTEXT pImmReleaseContext;
  123. LANGID TmpID = GetNTDLLNativeLangID();
  124. if ((OsVersion.dwBuildNumber == 1381) &&
  125. (TmpID == 0x0409)){
  126. hMod = LoadLibrary(TEXT("imm32.dll"));
  127. if (hMod) {
  128. pImmReleaseContext = (IMMRELEASECONTEXT) GetProcAddress(hMod,"ImmReleaseContext");
  129. if (pImmReleaseContext) {
  130. bRet = pImmReleaseContext(NULL,NULL);
  131. }
  132. FreeLibrary(hMod);
  133. }
  134. }
  135. return (bRet);
  136. }
  137. LANGID GetDefaultUserLangID()
  138. {
  139. LONG dwErr;
  140. HKEY hkey;
  141. DWORD dwSize;
  142. CHAR buffer[512];
  143. LANGID langid = 0;
  144. dwErr = RegOpenKeyEx( HKEY_USERS,
  145. TEXT(".DEFAULT\\Control Panel\\International"),
  146. 0,
  147. KEY_READ,
  148. &hkey );
  149. if( dwErr == ERROR_SUCCESS ) {
  150. dwSize = sizeof(buffer);
  151. dwErr = RegQueryValueExA(hkey,
  152. "Locale",
  153. NULL, //reserved
  154. NULL, //type
  155. buffer,
  156. &dwSize );
  157. if(dwErr == ERROR_SUCCESS) {
  158. langid = LANGIDFROMLCID(strtoul(buffer,NULL,16));
  159. }
  160. RegCloseKey(hkey);
  161. }
  162. return langid;
  163. }
  164. LANGID
  165. GetTargetNativeLangID(
  166. HINF Inf)
  167. /*++
  168. Routine Description:
  169. Applies different rules to different platforms
  170. NT
  171. build number <= 1840 : check ntdll's language,
  172. we scaned all 3.51's ntdll on boneyard\intl,
  173. it looks like we can trust them.
  174. build number > 1840 : user MUI language
  175. Win9x
  176. use default user's resource language
  177. Arguments:
  178. Return Value:
  179. Language ID of running system
  180. --*/
  181. {
  182. LONG dwErr;
  183. HKEY hkey;
  184. DWORD dwSize;
  185. CHAR buffer[512];
  186. LANGID rcLang;
  187. LANGID langid = 0;
  188. // Find out if we are running on NT or WIN9X
  189. if( ISNT() ) {
  190. //
  191. // We're on NT, but which version? GetSystemDefaultUILanguage() was broke until 1840...
  192. //
  193. if( OsVersion.dwBuildNumber > 1840 ) {
  194. FARPROC NT5API;
  195. //
  196. // Use the API to find out our locale.
  197. //
  198. if( NT5API = GetProcAddress( GetModuleHandle(TEXT("kernel32.dll")), "GetSystemDefaultUILanguage") ) {
  199. rcLang = (LANGID)NT5API();
  200. //
  201. // need to convert decimal to hex, LANGID to chr.
  202. //
  203. langid = rcLang;
  204. }
  205. } else {
  206. //
  207. // by looking into \\boneyard\intl, almost every ntdll.dll marked correct lang ID
  208. // so get langID from ntdll.dll
  209. //
  210. langid = GetNTDLLNativeLangID();
  211. if (langid == 0x0409) {
  212. if (IsHongKongVersion()) {
  213. langid = 0x0C04;
  214. } else {
  215. //
  216. // if default user's locale is in [TrustedDefaultUserLocale] in intl.inf
  217. //
  218. // then this is a backdoor for some localized build that its ntdll.dll marked
  219. //
  220. // as English but can't be upgrade by US version.
  221. //
  222. LANGID DefaultUserLangID = GetDefaultUserLangID();
  223. if (DefaultUserLangID &&
  224. TrustedDefaultUserLocale(Inf,DefaultUserLangID)) {
  225. langid = DefaultUserLangID;
  226. }
  227. }
  228. }
  229. }
  230. } else {
  231. //
  232. // We're on Win9x.
  233. //
  234. dwErr = RegOpenKeyEx( HKEY_USERS,
  235. TEXT(".Default\\Control Panel\\desktop\\ResourceLocale"),
  236. 0,
  237. KEY_READ,
  238. &hkey );
  239. if (dwErr == ERROR_SUCCESS) {
  240. dwSize = sizeof(buffer);
  241. dwErr = RegQueryValueExA( hkey,
  242. "",
  243. NULL, //reserved
  244. NULL, //type
  245. buffer,
  246. &dwSize );
  247. if(dwErr == ERROR_SUCCESS) {
  248. langid = LANGIDFROMLCID(strtoul(buffer,NULL,16));
  249. }
  250. RegCloseKey(hkey);
  251. }
  252. if ( dwErr != ERROR_SUCCESS ) {
  253. // Check HKLM\System\CurrentControlSet\Control\Nls\Locale
  254. dwErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  255. TEXT("System\\CurrentControlSet\\Control\\Nls\\Locale"),
  256. 0,
  257. KEY_READ,
  258. &hkey );
  259. if (dwErr == ERROR_SUCCESS) {
  260. dwSize = sizeof(buffer);
  261. dwErr = RegQueryValueExA( hkey,
  262. "",
  263. NULL, //reserved
  264. NULL, //type
  265. buffer,
  266. &dwSize );
  267. if (dwErr == ERROR_SUCCESS) {
  268. langid = LANGIDFROMLCID(strtoul(buffer,NULL,16));
  269. }
  270. RegCloseKey(hkey);
  271. }
  272. }
  273. }
  274. return (langid);
  275. }
  276. LANGID
  277. GetSourceNativeLangID(
  278. HINF Inf)
  279. /*++
  280. Routine Description:
  281. [DefaultValues]
  282. Locale = xxxx
  283. every localized build has it's own Locale in intl.inf,
  284. so we use this value to identify source languag
  285. Arguments:
  286. Return Value:
  287. Language ID of source
  288. --*/
  289. {
  290. INFCONTEXT InfContext;
  291. LANGID langid = 0;
  292. INT i=0;
  293. if (SetupapiFindFirstLine( Inf,
  294. TEXT("DefaultValues"),
  295. TEXT("Locale"),
  296. &InfContext )) {
  297. if (MySetupapiGetIntField( &InfContext, 1, &i, 16 )) {
  298. langid = (LANGID)i;
  299. }
  300. }
  301. return langid;
  302. }
  303. DWORD
  304. GetOSMajorID(
  305. HINF Inf)
  306. {
  307. INFCONTEXT InfContext;
  308. DWORD MajorId;
  309. INT i=0;
  310. MajorId = 0;
  311. if (SetupapiFindFirstLine( Inf,
  312. TEXT("OSVersionMajorID"),
  313. NULL,
  314. &InfContext )) {
  315. do {
  316. if (MySetupapiGetIntField( &InfContext, 2, &i, 10 )) {
  317. if (((ULONG)i) != OsVersion.dwPlatformId) {
  318. continue;
  319. }
  320. }
  321. if (MySetupapiGetIntField( &InfContext, 3, &i, 10 )) {
  322. if (((ULONG)i) != OsVersion.dwMajorVersion) {
  323. continue;
  324. }
  325. }
  326. if (MySetupapiGetIntField( &InfContext, 4, &i, 10 )) {
  327. if (((ULONG)i) != OsVersion.dwMinorVersion) {
  328. continue;
  329. }
  330. }
  331. if (MySetupapiGetIntField( &InfContext, 1, &i, 16 )) {
  332. MajorId = (DWORD)i;
  333. break;
  334. }
  335. } while ( SetupapiFindNextLine(&InfContext,&InfContext));
  336. }
  337. return MajorId;
  338. }
  339. DWORD
  340. GetOSMinorID(
  341. HINF Inf)
  342. {
  343. TCHAR Field[128];
  344. INFCONTEXT InfContext;
  345. DWORD MinorId;
  346. INT i = 0;
  347. MinorId = 0;
  348. if (SetupapiFindFirstLine( Inf,
  349. TEXT("OSVVersionMinorID"),
  350. NULL,
  351. &InfContext )) {
  352. do {
  353. if (MySetupapiGetIntField( &InfContext, 2, &i, 10 )) {
  354. if (((ULONG)i) != OsVersion.dwPlatformId) {
  355. continue;
  356. }
  357. }
  358. if (MySetupapiGetIntField( &InfContext, 3, &i, 10 )) {
  359. if (((ULONG)i) != OsVersion.dwMajorVersion) {
  360. continue;
  361. }
  362. }
  363. if (MySetupapiGetIntField( &InfContext, 4, &i, 10 )) {
  364. if (((ULONG)i) != OsVersion.dwMinorVersion) {
  365. continue;
  366. }
  367. }
  368. if (MySetupapiGetIntField( &InfContext, 5, &i, 10 )) {
  369. if (((ULONG)i) != OsVersion.dwBuildNumber) {
  370. continue;
  371. }
  372. }
  373. if (SetupapiGetStringField( &InfContext, 6, Field, (sizeof(Field)/sizeof(TCHAR)), NULL )) {
  374. if (lstrcmpi(Field,OsVersion.szCSDVersion) != 0) {
  375. continue;
  376. }
  377. }
  378. if (MySetupapiGetIntField( &InfContext, 1, &i, 16 )) {
  379. MinorId = (DWORD)i;
  380. break;
  381. }
  382. } while ( SetupapiFindNextLine(&InfContext,&InfContext));
  383. }
  384. return MinorId;
  385. }
  386. BOOL
  387. TrustedDefaultUserLocale(
  388. HINF Inf,
  389. LANGID LangID)
  390. {
  391. TCHAR LangIDStr[9];
  392. LPCTSTR Field;
  393. INFCONTEXT InfContext;
  394. INT i = 0;
  395. wsprintf(LangIDStr,TEXT("0000%04X"),LangID);
  396. if (SetupapiFindFirstLine( Inf,
  397. TEXT("TrustedDefaultUserLocale"),
  398. LangIDStr,
  399. &InfContext )) {
  400. do {
  401. //
  402. // if in excluded field, this is not what we want
  403. //
  404. if (MySetupapiGetIntField( &InfContext, 3, &i, 16 )) {
  405. if (((ULONG)i) & GetOSMinorID(Inf)) {
  406. continue;
  407. }
  408. }
  409. //
  410. // if it is in minor version list, we got what we want
  411. //
  412. if (MySetupapiGetIntField( &InfContext, 2, &i, 16 )) {
  413. if (((ULONG)i) & GetOSMinorID(Inf)) {
  414. return TRUE;
  415. }
  416. }
  417. //
  418. // or if it is in major version list, we also got what we want
  419. //
  420. if (MySetupapiGetIntField( &InfContext, 1, &i, 16 )) {
  421. if (((ULONG)i) & GetOSMajorID(Inf)) {
  422. return TRUE;
  423. }
  424. }
  425. } while ( SetupapiFindNextLine(&InfContext,&InfContext));
  426. }
  427. return FALSE;
  428. }
  429. BOOL
  430. IsInExcludeList(
  431. HINF Inf,
  432. LANGID LangID)
  433. {
  434. TCHAR LangIDStr[9];
  435. LPCTSTR Field;
  436. INFCONTEXT InfContext;
  437. INT i = 0;
  438. wsprintf(LangIDStr,TEXT("0000%04X"),LangID);
  439. if (SetupapiFindFirstLine( Inf,
  440. TEXT("ExcludeSourceLocale"),
  441. LangIDStr,
  442. &InfContext )) {
  443. do {
  444. //
  445. // if in excluded field, this is not what we want
  446. //
  447. if (MySetupapiGetIntField( &InfContext, 3, &i, 16 )) {
  448. if (((ULONG)i) & GetOSMinorID(Inf)) {
  449. continue;
  450. }
  451. }
  452. //
  453. // if it is in minor version list, we got what we want
  454. //
  455. if (MySetupapiGetIntField( &InfContext, 2, &i, 16 )) {
  456. if (((ULONG)i) & GetOSMinorID(Inf)) {
  457. return TRUE;
  458. }
  459. }
  460. //
  461. // or if it is in major version list, we also got what we want
  462. //
  463. if (MySetupapiGetIntField( &InfContext, 1, &i, 16 )) {
  464. if (((ULONG)i) & GetOSMajorID(Inf)) {
  465. return TRUE;
  466. }
  467. }
  468. } while ( SetupapiFindNextLine(&InfContext,&InfContext));
  469. }
  470. return FALSE;
  471. }
  472. BOOL
  473. CheckLanguageVersion(
  474. HINF Inf,
  475. LANGID SourceLangID,
  476. LANGID TargetLangID)
  477. /*++
  478. Routine Description:
  479. Check if the language of source NT is same as target NT or ,at least,
  480. compatibile
  481. Arguments:
  482. Inf handle of intl.inf
  483. Return Value:
  484. TRUE They are same or compatibile
  485. FALSE They are different
  486. --*/
  487. {
  488. TCHAR TargetLangIDStr[9];
  489. LANGID SrcLANGID;
  490. LANGID DstLANGID;
  491. LPCTSTR Field;
  492. INFCONTEXT InfContext;
  493. INT i = 0;
  494. //
  495. // If either one is 0, allow the upgrade. This is Windows 2000 Beta3 behavior.
  496. //
  497. if (SourceLangID == 0 || TargetLangID == 0) {
  498. return TRUE;
  499. }
  500. if (SourceLangID == TargetLangID) {
  501. //
  502. // special case, for Middle East version, NT5 is localized build but NT4 is not
  503. //
  504. // they don't allow NT5 localized build to upgrade NT4, although they are same language
  505. //
  506. // so we need to exclude these
  507. //
  508. return ((IsInExcludeList(Inf, SourceLangID) == FALSE));
  509. }
  510. //
  511. // if Src != Dst, then we need to look up inf file to see
  512. //
  513. // if we can open a backdoor for Target language
  514. //
  515. //
  516. // use TargetLangID as key to find alternative SourceLangID
  517. //
  518. wsprintf(TargetLangIDStr,TEXT("0000%04X"),TargetLangID);
  519. if (SetupapiFindFirstLine( Inf,
  520. TEXT("AlternativeSourceLocale"),
  521. TargetLangIDStr,
  522. &InfContext )) {
  523. do {
  524. //
  525. // Check if we found alternative locale
  526. //
  527. if (MySetupapiGetIntField(&InfContext, 1, &i, 16)) {
  528. LANGID AltSourceLangID = LANGIDFROMLCID(i);
  529. if (SourceLangID != AltSourceLangID) {
  530. continue;
  531. }
  532. }
  533. //
  534. // We are here if we found alternative source lang,
  535. //
  536. // now check the version criteria
  537. //
  538. //
  539. // if in excluded list, this is not what we want
  540. //
  541. if (MySetupapiGetIntField( &InfContext, 4, &i, 16 )) {
  542. if (((ULONG)i) & GetOSMinorID(Inf)) {
  543. continue;
  544. }
  545. }
  546. //
  547. // if it is in minor version list, we got what we want
  548. //
  549. if (MySetupapiGetIntField( &InfContext, 3, &i, 16 )) {
  550. if (((ULONG)i) & GetOSMinorID(Inf)) {
  551. return TRUE;
  552. }
  553. }
  554. //
  555. // or if it is in major version list, we also got what we want
  556. //
  557. if (MySetupapiGetIntField( &InfContext, 2, &i, 16 )) {
  558. if (((ULONG)i) & GetOSMajorID(Inf)) {
  559. return TRUE;
  560. }
  561. }
  562. } while ( SetupapiFindNextMatchLine (&InfContext,TargetLangIDStr,&InfContext));
  563. }
  564. return FALSE;
  565. }
  566. BOOL InitLanguageDetection(LPCTSTR SourcePath,LPCTSTR InfFile)
  567. /*++
  568. Routine Description:
  569. Initialize language detection and put the result in 3 global variables
  570. SourceNativeLangID - LANGID of Source (NT is going to be installed)
  571. TargetNativeLangID - LANGID of Target (OS system which is running)
  572. IsLanguageMatched - If language is not matched, then blocks upgrade
  573. Arguments:
  574. SourcePath directory path of INF file
  575. InfFile INF file name
  576. Return Value:
  577. TRUE init correctly
  578. FALSE init failed
  579. --*/
  580. {
  581. HINF Inf;
  582. TCHAR InfName[MAX_PATH];
  583. FindPathToInstallationFile( InfFile, InfName, MAX_PATH );
  584. Inf = SetupapiOpenInfFile( InfName, NULL, INF_STYLE_WIN4, NULL );
  585. if (Inf == INVALID_HANDLE_VALUE) {
  586. return FALSE;
  587. }
  588. //
  589. // Init Global Variables
  590. //
  591. SourceNativeLangID = GetSourceNativeLangID(Inf);
  592. TargetNativeLangID = GetTargetNativeLangID(Inf);
  593. IsLanguageMatched = CheckLanguageVersion(Inf,SourceNativeLangID,TargetNativeLangID);
  594. SetupapiCloseInfFile(Inf);
  595. return TRUE;
  596. }
  597. BOOL
  598. MySetupapiGetIntField(
  599. IN PINFCONTEXT Context,
  600. IN DWORD FieldIndex,
  601. OUT PINT IntegerValue,
  602. IN int Base
  603. )
  604. /*
  605. Routine to get a field from an INF file and convert to an integer. The reason
  606. we have this and don't use Setupapi!SetupGetIntField is because intl.inf mixes and matches
  607. numeric conventions. It may use Hex values without a 0x notation and this is an attempt
  608. to clean that up without modifying the INF. With this change we also won't link to internal Setupapi routines
  609. like pSetupGetField.
  610. Arguments:
  611. PINFCONTEXT : - Pointer to setupapi INFCONTEXT structure
  612. FieldIndex : - 1-based index for fields, 0 for key itself.
  613. IntegerValue : - Converted Integer value that is returned by the function
  614. Base : - Base used for string to integer conversion.
  615. Return Value:
  616. TRUE - If we could convert the returned string to an integer else,
  617. FALSE
  618. */
  619. {
  620. DWORD Size = 0;
  621. PTSTR Field = NULL;
  622. BOOL Ret = FALSE;
  623. if( IntegerValue == NULL )
  624. return FALSE;
  625. if (Context) {
  626. if( SetupapiGetStringField( Context, FieldIndex, NULL, 0, &Size )){
  627. if (Field = MALLOC( Size * sizeof( TCHAR))){
  628. if( SetupapiGetStringField( Context, FieldIndex, Field, Size, NULL )){
  629. *IntegerValue = _tcstoul( Field, NULL, Base );
  630. Ret = TRUE;
  631. }
  632. }
  633. }
  634. }
  635. if( Field )
  636. FREE( Field );
  637. return Ret;
  638. }