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.

855 lines
23 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. keyboard.c
  5. Abstract:
  6. Implements routines to merge keyboard layouts from the upgraded win9x
  7. system and a clean win2k install. The result is that Windows 2000 has
  8. the base keyboard layout support it expects and any additional layouts
  9. (third party IMEs, newer Microsoft IMEs) that may have been present in
  10. the original operating system.
  11. This code was modified from a base originally in Rulehlpr.c
  12. Author:
  13. Marc R. Whitten (marcw) 26-Jan-1999
  14. Revision History:
  15. marcw 26-Apr-1999 Add support for mapping changed keyboard layouts.
  16. --*/
  17. #include "pch.h"
  18. FILTERRETURN
  19. pKeyboardLayoutsFilter (
  20. IN CPDATAOBJECT SrcObject,
  21. IN CPDATAOBJECT DstObject,
  22. IN FILTERTYPE FilterType,
  23. IN PVOID Arg
  24. )
  25. {
  26. FILTERRETURN rState = FILTER_RETURN_CONTINUE;
  27. DATAOBJECT object;
  28. BOOL freeObject = FALSE;
  29. TCHAR layoutFile[MAX_TCHAR_PATH];
  30. PTSTR extension = NULL;
  31. TCHAR key[MEMDB_MAX];
  32. DWORD unused;
  33. __try {
  34. rState = Standard9xSuppressFilter (SrcObject, DstObject, FilterType, Arg);
  35. if (rState != FILTER_RETURN_CONTINUE) {
  36. return rState;
  37. }
  38. //
  39. // Check to make sure we want to enumerate this entry.
  40. //
  41. if (FilterType == FILTER_KEY_ENUM) {
  42. //
  43. // If the Keyboard Layout begins with a '0' It is a locale specific keyboard layout. In these cases, we
  44. // use the NT value.
  45. //
  46. if (*SrcObject->ChildKey == TEXT('0')) {
  47. //
  48. // This is a standard locale keyboard layout. We want this to go to the NT default after migration.
  49. // Skip copying this over from win95.
  50. //
  51. rState = FILTER_RETURN_HANDLED;
  52. __leave;
  53. }
  54. if (*SrcObject->ChildKey != TEXT('E') && *SrcObject->ChildKey != TEXT('e')) {
  55. DEBUGMSG ((DBG_WHOOPS, "Unknown format. Skipping %s.", DEBUGENCODER(SrcObject)));
  56. rState = FILTER_RETURN_HANDLED;
  57. __leave;
  58. }
  59. }
  60. //
  61. // Don't create empty object. This may be suppressed.
  62. //
  63. if (FilterType == FILTER_CREATE_KEY) {
  64. rState = FILTER_RETURN_HANDLED;
  65. __leave;
  66. }
  67. if (FilterType == FILTER_PROCESS_VALUES) {
  68. //
  69. // We need to look at the value of Ime File.
  70. // This will determine what we do with this entry.
  71. //
  72. if (!DuplicateObjectStruct (&object, SrcObject)) {
  73. rState = FILTER_RETURN_FAIL;
  74. }
  75. freeObject = TRUE;
  76. FreeObjectVal (&object);
  77. SetRegistryValueName (&object, TEXT("IME File"));
  78. if (!ReadObject (&object) || object.Type != REG_SZ) {
  79. DEBUGMSG ((
  80. DBG_WARNING,
  81. "No usable IME File data for %s. It will be suppressed.",
  82. DEBUGENCODER(SrcObject)
  83. ));
  84. rState = FILTER_RETURN_HANDLED;
  85. __leave;
  86. }
  87. if (object.Value.Size > (MAX_PATH * sizeof (WCHAR))) {
  88. rState = FILTER_RETURN_HANDLED;
  89. __leave;
  90. }
  91. //
  92. // Suppress this setting unless we are going to leave the file alone
  93. // (or at worst move it around somewhere...)
  94. //
  95. MemDbBuildKey (key, MEMDB_CATEGORY_GOOD_IMES, (PCTSTR) object.Value.Buffer, NULL, NULL);
  96. if (!MemDbGetValue (key, &unused)) {
  97. rState = FILTER_RETURN_HANDLED;
  98. DEBUGMSG ((
  99. DBG_WARNING,
  100. "Ime Layout Entry for %s will be suppressed.",
  101. DEBUGENCODER(SrcObject)
  102. ));
  103. __leave;
  104. }
  105. }
  106. if (FilterType == FILTER_VALUE_COPY) {
  107. //
  108. // We need to massage the layout file if we are bringing this over.
  109. //
  110. if (StringIMatch (SrcObject->ValueName, S_LAYOUT_FILE)) {
  111. //
  112. // Convert layout file.
  113. //
  114. _tcssafecpy (layoutFile, (PTSTR) SrcObject->Value.Buffer, MAX_TCHAR_PATH);
  115. //
  116. // We must map kbdjp.kbd to kbdjpn.dll In all other cases, we simply replace the
  117. // .kbd extension with .dll.
  118. //
  119. if (StringIMatch (layoutFile, S_KBDJPDOTKBD)) {
  120. StringCopy (layoutFile, S_KBDJPNDOTDLL);
  121. }
  122. else if (IsPatternMatch (TEXT("*.KBD"), layoutFile)) {
  123. extension = _tcsrchr (layoutFile, TEXT('.'));
  124. if (extension) {
  125. StringCopy (extension, S_DLL);
  126. }
  127. }
  128. //
  129. // Now, we need to write this object.
  130. //
  131. if (!DuplicateObjectStruct (&object, DstObject)) {
  132. rState = FILTER_RETURN_FAIL;
  133. __leave;
  134. }
  135. freeObject = TRUE;
  136. if (!ReplaceValueWithString (&object, layoutFile)) {
  137. rState = FILTER_RETURN_FAIL;
  138. __leave;
  139. }
  140. SetRegistryType (&object, REG_SZ);
  141. if (!WriteObject (&object)) {
  142. rState = FILTER_RETURN_FAIL;
  143. __leave;
  144. }
  145. rState = FILTER_RETURN_HANDLED;
  146. }
  147. }
  148. }
  149. __finally {
  150. if (freeObject) {
  151. FreeObjectStruct (&object);
  152. }
  153. }
  154. return rState;
  155. }
  156. /*++
  157. Routine Description:
  158. Migrate Keyboard Layouts is responsible for doing a smart merge between the
  159. win9x and windows NT keyboard layout registry entries. The following rules
  160. are
  161. used:
  162. (1) For basic locale keyboard layouts, we always use the NT default e
  163. ntry.
  164. (2) For IME entries, we examine the IME File entry. If the IME file w
  165. as deleted, we will not use it and will skip the entry. Only if we leave
  166. the IME file alone do we bring across the
  167. setting.
  168. Arguments:
  169. None.
  170. Return Value:
  171. --*/
  172. BOOL
  173. RuleHlpr_MigrateKeyboardLayouts (
  174. IN PCTSTR SrcObjectStr,
  175. IN PCTSTR DestObjectStr,
  176. IN PCTSTR User,
  177. IN PVOID Data
  178. )
  179. {
  180. DATAOBJECT source;
  181. DATAOBJECT destination;
  182. BOOL rSuccess = FALSE;
  183. //
  184. // If not local machine, don't process
  185. //
  186. if (User) {
  187. SetLastError (ERROR_SUCCESS);
  188. return FALSE;
  189. }
  190. //
  191. // We need to enumerate all keys in SrcObjectStr. For each key,
  192. // we examine the default Win9x value, which may cause us to change
  193. // the default value, or skip the key altogether.
  194. //
  195. __try {
  196. ZeroMemory (&source, sizeof (DATAOBJECT));
  197. ZeroMemory (&destination, sizeof (DATAOBJECT));
  198. if (!CreateObjectStruct (SrcObjectStr, &source, WIN95OBJECT)) {
  199. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardLayouts: %s is invalid", SrcObjectStr));
  200. __leave;
  201. }
  202. if (!(source.ObjectType & OT_TREE)) {
  203. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardLayouts %s does not specify subkeys -- skipping rule", SrcObjectStr));
  204. rSuccess = TRUE;
  205. __leave;
  206. }
  207. //
  208. // Our filter function will do the real copying, removing any entries that need to be skipped.
  209. //
  210. DuplicateObjectStruct (&destination, &source);
  211. SetPlatformType (&destination, WINNTOBJECT);
  212. rSuccess = CopyObject (&source, &destination, pKeyboardLayoutsFilter, NULL);
  213. //
  214. // If there were no entries, return success
  215. //
  216. if (!rSuccess) {
  217. if (GetLastError() == ERROR_FILE_NOT_FOUND ||
  218. GetLastError() == ERROR_NO_MORE_ITEMS
  219. ) {
  220. rSuccess = TRUE;
  221. }
  222. }
  223. }
  224. __finally {
  225. FreeObjectStruct (&destination);
  226. FreeObjectStruct (&source);
  227. }
  228. return rSuccess;
  229. }
  230. #define S_KEYBOARD_LAYOUT_MAPPINGS TEXT("Keyboard.Layout.Mappings")
  231. PCTSTR
  232. pMapKeyboardLayoutIfNecessary (
  233. IN PCTSTR Layout
  234. )
  235. {
  236. INFSTRUCT is = INITINFSTRUCT_GROWBUFFER;
  237. PTSTR rData = NULL;
  238. PCTSTR p = NULL;
  239. if (InfFindFirstLine (g_UserMigInf, S_KEYBOARD_LAYOUT_MAPPINGS, Layout, &is)) {
  240. //
  241. // This keyboard layout should be mapped.
  242. //
  243. p = InfGetStringField (&is, 1);
  244. MYASSERT (p);
  245. }
  246. if (p) {
  247. rData = MemAlloc (g_hHeap, 0, SizeOfString (p));
  248. StringCopy (rData, p);
  249. }
  250. else {
  251. rData = (PTSTR) Layout;
  252. }
  253. InfCleanUpInfStruct (&is);
  254. return rData;
  255. }
  256. FILTERRETURN
  257. pMigrateKeyboardSubstitutesFilter (
  258. IN CPDATAOBJECT SrcObject,
  259. IN CPDATAOBJECT DstObject,
  260. IN FILTERTYPE Type,
  261. IN PVOID Arg
  262. )
  263. {
  264. DATAOBJECT newObject;
  265. PKEYTOVALUEARG keyToValueArgs = (PKEYTOVALUEARG) Arg;
  266. PCTSTR data;
  267. //
  268. // We want to create the initial key, but not any of the subkeys.
  269. //
  270. if (Type == FILTER_CREATE_KEY) {
  271. if (keyToValueArgs -> EnumeratingSubKeys) {
  272. return FILTER_RETURN_HANDLED;
  273. }
  274. else {
  275. return FILTER_RETURN_CONTINUE;
  276. }
  277. } else if (Type == FILTER_KEY_ENUM) {
  278. if (!keyToValueArgs -> EnumeratingSubKeys) {
  279. keyToValueArgs -> EnumeratingSubKeys = TRUE;
  280. }
  281. return FILTER_RETURN_CONTINUE;
  282. } else if (Type == FILTER_VALUENAME_ENUM && keyToValueArgs -> EnumeratingSubKeys) {
  283. if (!*SrcObject -> ValueName) {
  284. return FILTER_RETURN_CONTINUE;
  285. }
  286. ELSE_DEBUGMSG((DBG_WHOOPS,"Keyboard Substitutes: Unexpected value names."));
  287. return FILTER_RETURN_HANDLED;
  288. }
  289. else if (Type == FILTER_VALUE_COPY && keyToValueArgs -> EnumeratingSubKeys) {
  290. //
  291. // If this is the default value, we have the information we need to create the value for this.
  292. //
  293. if (!*SrcObject -> ValueName) {
  294. //
  295. // Create the object struct for the Nt setting.
  296. //
  297. DuplicateObjectStruct (&newObject, &(keyToValueArgs->Object));
  298. SetRegistryValueName (&newObject, _tcsrchr(SrcObject->KeyPtr->KeyString, TEXT('\\')) + 1);
  299. //
  300. // We need to see if this keyboard layout string needs to be mapped.
  301. //
  302. data = pMapKeyboardLayoutIfNecessary ((PTSTR) SrcObject->Value.Buffer);
  303. if (!data) {
  304. return FILTER_RETURN_FAIL;
  305. }
  306. //
  307. // Write this into the nt registry.
  308. //
  309. ReplaceValueWithString (&newObject, data);
  310. SetRegistryType (&newObject,REG_SZ);
  311. WriteObject (&newObject);
  312. //
  313. // Clean up resources.
  314. //
  315. if (!StringIMatch (data, (PTSTR) SrcObject->Value.Buffer)) {
  316. MemFree (g_hHeap, 0, data);
  317. }
  318. FreeObjectStruct (&newObject);
  319. }
  320. ELSE_DEBUGMSG((DBG_WHOOPS,"Keyboard Substitutes: Unexpected value names.."));
  321. return FILTER_RETURN_HANDLED;
  322. }
  323. return FILTER_RETURN_CONTINUE;
  324. }
  325. BOOL
  326. RuleHlpr_MigrateKeyboardSubstitutes (
  327. IN PCTSTR SrcObjectStr,
  328. IN PCTSTR DestObjectStr,
  329. IN PCTSTR User,
  330. IN PVOID Data
  331. )
  332. {
  333. BOOL rSuccess = TRUE;
  334. FILTERRETURN fr;
  335. DATAOBJECT srcObject;
  336. DATAOBJECT dstObject;
  337. KEYTOVALUEARG args;
  338. //
  339. // We need to enumerate all keys in SrcObjectStr. For each key,
  340. // we will change the subkey to a value.
  341. //
  342. __try {
  343. ZeroMemory (&srcObject, sizeof (DATAOBJECT));
  344. ZeroMemory (&dstObject, sizeof (DATAOBJECT));
  345. if (!CreateObjectStruct (SrcObjectStr, &srcObject, WIN95OBJECT)) {
  346. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardSubstitutes: %s is invalid", SrcObjectStr));
  347. return FALSE;
  348. }
  349. if (!(srcObject.ObjectType & OT_TREE)) {
  350. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardSubstitutes: %s does not specify subkeys -- skipping rule", SrcObjectStr));
  351. return TRUE;
  352. }
  353. DuplicateObjectStruct (&dstObject, &srcObject);
  354. SetPlatformType (&dstObject, WINNTOBJECT);
  355. ZeroMemory(&args,sizeof(KEYTOVALUEARG));
  356. DuplicateObjectStruct(&(args.Object),&dstObject);
  357. fr = CopyObject (&srcObject, &dstObject, pMigrateKeyboardSubstitutesFilter,&args);
  358. FreeObjectStruct(&(args.Object));
  359. DEBUGMSG_IF((fr == FILTER_RETURN_FAIL,DBG_WHOOPS,"MigrateKeyboardSubstitutes: CopyObject returned false."));
  360. }
  361. __finally {
  362. FreeObjectStruct (&dstObject);
  363. FreeObjectStruct (&srcObject);
  364. }
  365. SetLastError(ERROR_SUCCESS);
  366. return rSuccess;
  367. }
  368. BOOL
  369. pGetKeyboardSubstitutes (
  370. IN PCTSTR LocaleID,
  371. OUT PGROWBUFFER Gb
  372. )
  373. {
  374. HINF inf;
  375. INFCONTEXT ic;
  376. DWORD fields;
  377. DWORD index;
  378. DWORD dLocaleID;
  379. PTSTR substLocaleID;
  380. DWORD dSubstLocaleID;
  381. TCHAR mapping[20];
  382. TCHAR key[MEMDB_MAX];
  383. TCHAR strLocaleID[10];
  384. PTSTR final;
  385. BOOL b = FALSE;
  386. inf = SetupOpenInfFile (TEXT("intl.inf"), NULL, INF_STYLE_WIN4, NULL);
  387. if (inf != INVALID_HANDLE_VALUE) {
  388. if (SetupFindFirstLine (inf, TEXT("Locales"), LocaleID, &ic)) {
  389. fields = SetupGetFieldCount (&ic);
  390. for (index = 5; index <= fields; index++) {
  391. if (SetupGetStringField (&ic, index, mapping, 20, NULL)) {
  392. //
  393. // the format is LCID:SubstituteKLID
  394. //
  395. dLocaleID = _tcstoul (mapping, &substLocaleID, 16);
  396. while (_istspace (*substLocaleID)) {
  397. substLocaleID++;
  398. }
  399. if (*substLocaleID != TEXT(':')) {
  400. //
  401. // unknown field format
  402. //
  403. continue;
  404. }
  405. substLocaleID++;
  406. dSubstLocaleID = _tcstoul (substLocaleID, &final, 16);
  407. if (*final) {
  408. //
  409. // unknown field format
  410. //
  411. continue;
  412. }
  413. if (dSubstLocaleID == dLocaleID) {
  414. continue;
  415. }
  416. //
  417. // record this pair
  418. //
  419. wsprintf (strLocaleID, TEXT("%08x"), dLocaleID);
  420. MemDbBuildKey (key, MEMDB_CATEGORY_KEYBOARD_LAYOUTS, strLocaleID, NULL, NULL);
  421. if (MemDbGetValue (key, NULL)) {
  422. MultiSzAppend (Gb, strLocaleID);
  423. MultiSzAppend (Gb, substLocaleID);
  424. b = TRUE;
  425. }
  426. }
  427. }
  428. }
  429. SetupCloseInfFile (inf);
  430. }
  431. return b;
  432. }
  433. #define S_KEYBOARD_LAYOUT_PRELOAD_REG TEXT("HKCU\\Keyboard Layout\\Preload")
  434. BOOL
  435. RuleHlpr_MigrateKeyboardPreloads (
  436. IN PCTSTR SrcObjectStr,
  437. IN PCTSTR DestObjectStr,
  438. IN PCTSTR User,
  439. IN PVOID Data
  440. )
  441. {
  442. DATAOBJECT source;
  443. DATAOBJECT destination;
  444. REGKEY_ENUM eKey;
  445. REGVALUE_ENUM eValue;
  446. PCTSTR data = NULL;
  447. TCHAR sequencerStr[20];
  448. UINT sequencer;
  449. PCTSTR imeFile = NULL;
  450. BOOL keepPreload = FALSE;
  451. TCHAR key[MEMDB_MAX];
  452. BOOL rSuccess = TRUE;
  453. MEMDB_ENUM e;
  454. UINT unused = 0;
  455. HKEY regKey;
  456. PTSTR regStr = NULL;
  457. PTSTR p;
  458. GROWBUFFER gb = GROWBUF_INIT;
  459. MULTISZ_ENUM sze;
  460. PTSTR localeIDStr;
  461. //
  462. // If not User, don't process.
  463. //
  464. if (!User) {
  465. SetLastError (ERROR_SUCCESS);
  466. return FALSE;
  467. }
  468. __try {
  469. ZeroMemory (&source, sizeof (DATAOBJECT));
  470. ZeroMemory (&destination, sizeof (DATAOBJECT));
  471. if (!CreateObjectStruct (SrcObjectStr, &source, WIN95OBJECT)) {
  472. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardPreloads: %s is invalid", SrcObjectStr));
  473. rSuccess = FALSE;
  474. __leave;
  475. }
  476. if (!OpenObject (&source)) {
  477. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardPreloads: Unable to open %s.", SrcObjectStr));
  478. rSuccess = FALSE;
  479. __leave;
  480. }
  481. if (!(source.ObjectType & OT_TREE)) {
  482. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardPreloads %s does not specify subkeys -- skipping rule", SrcObjectStr));
  483. __leave;
  484. }
  485. //
  486. // First, enumerate the win9x preloads and throw them in memdb.
  487. //
  488. if (EnumFirstRegKey95 (&eKey, source.KeyPtr->OpenKey)) {
  489. do {
  490. keepPreload = FALSE;
  491. data = NULL;
  492. imeFile = NULL;
  493. regKey = OpenRegKey95 (eKey.KeyHandle, eKey.SubKeyName);
  494. if (regKey) {
  495. StringCopy (sequencerStr, eKey.SubKeyName);
  496. data = GetRegValueString95 (regKey, TEXT(""));
  497. CloseRegKey95 (regKey);
  498. }
  499. if (data) {
  500. keepPreload = TRUE;
  501. //
  502. // If this is an IME entry, we have to make sure it will be migrated.
  503. //
  504. if (*data == TEXT('E') || *data == TEXT('e')) {
  505. //
  506. // Determine if this IME will be migrated.
  507. //
  508. regStr = JoinPaths (S_KEYBOARD_LAYOUT_REG, data);
  509. regKey = OpenRegKeyStr95 (regStr);
  510. FreePathString (regStr);
  511. if (regKey) {
  512. imeFile = GetRegValueString95 (regKey, TEXT("IME File"));
  513. CloseRegKey95 (regKey);
  514. }
  515. if (imeFile) {
  516. MemDbBuildKey (key, MEMDB_CATEGORY_GOOD_IMES, imeFile, NULL, NULL);
  517. if (!MemDbGetValue (key, &unused)) {
  518. //
  519. // This layout entry will not be migrated. Blast the preload away.
  520. //
  521. keepPreload = FALSE;
  522. }
  523. MemFree (g_hHeap, 0, imeFile);
  524. }
  525. else {
  526. keepPreload = FALSE;
  527. }
  528. }
  529. //
  530. // See if we need to map the 9x keyboard layout to the proper NT layout.
  531. //
  532. data = pMapKeyboardLayoutIfNecessary (data);
  533. if (keepPreload) {
  534. //
  535. // Usable preload. Save this into memdb. We'll use it later to actually write
  536. // the user preload entries.
  537. //
  538. MemDbSetValueEx (MEMDB_CATEGORY_KEYBOARD_LAYOUTS, sequencerStr, data, NULL, 0, NULL);
  539. }
  540. if (data) {
  541. MemFree (g_hHeap, 0, data);
  542. }
  543. }
  544. } while (EnumNextRegKey95 (&eKey));
  545. }
  546. //
  547. // Now we need to look at what the NT default preloads are. We will move those preloads behind any preloads that will be migrated.
  548. //
  549. sequencer = 900;
  550. regKey = OpenRegKeyStr (S_KEYBOARD_LAYOUT_PRELOAD_REG);
  551. if (regKey) {
  552. if (EnumFirstRegValue (&eValue, regKey)) {
  553. do {
  554. data = GetRegValueString (eValue.KeyHandle, eValue.ValueName);
  555. if (data) {
  556. //
  557. // Check to see if we have already added this entry into memdb.
  558. //
  559. MemDbBuildKey (key, MEMDB_CATEGORY_KEYBOARD_LAYOUTS, TEXT("*"), data, NULL);
  560. if (!MemDbGetValueWithPattern (key, NULL)) {
  561. //
  562. // Preload that was *not* on Windows 9x. We need to add this to our list.
  563. //
  564. wsprintf (sequencerStr, TEXT("%d"), sequencer);
  565. MemDbSetValueEx (MEMDB_CATEGORY_KEYBOARD_LAYOUTS, sequencerStr, data, NULL, 1, NULL);
  566. sequencer++;
  567. }
  568. MemFree (g_hHeap, 0, data);
  569. }
  570. } while (EnumNextRegValue (&eValue));
  571. }
  572. CloseRegKey (regKey);
  573. }
  574. //
  575. // Now we have the complete list of preloads to migrate. We only need to enumerate through memdb and create
  576. // entries for all of the data collected.
  577. //
  578. sequencer = 1;
  579. if (MemDbGetValueEx (&e, MEMDB_CATEGORY_KEYBOARD_LAYOUTS, NULL, NULL)) {
  580. do {
  581. localeIDStr = _tcschr (e.szName, TEXT('\\'));
  582. if (localeIDStr) {
  583. localeIDStr = _tcsinc (localeIDStr);
  584. } else {
  585. MYASSERT (FALSE);
  586. }
  587. //
  588. // Create the object to write and fill in the valuename and data.
  589. //
  590. ZeroMemory (&destination, sizeof (DATAOBJECT));
  591. DuplicateObjectStruct (&destination, &source);
  592. SetPlatformType (&destination, WINNTOBJECT);
  593. wsprintf (sequencerStr, TEXT("%d"), sequencer);
  594. sequencer++;
  595. SetRegistryValueName (&destination, sequencerStr);
  596. SetRegistryType (&destination, REG_SZ);
  597. ReplaceValueWithString (&destination, localeIDStr);
  598. //
  599. // Write the object.
  600. //
  601. WriteObject (&destination);
  602. FreeObjectStruct (&destination);
  603. //
  604. // also write the corresponding substitute, if appropriate
  605. //
  606. if (pGetKeyboardSubstitutes (localeIDStr, &gb)) {
  607. StringCopy (key, DestObjectStr);
  608. p = _tcsrchr (key, TEXT('\\'));
  609. if (!p) {
  610. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardPreloads: %s is invalid", DestObjectStr));
  611. continue;
  612. }
  613. StringCopy (p + 1, TEXT("Substitutes"));
  614. if (!CreateObjectStruct (key, &destination, WINNTOBJECT)) {
  615. DEBUGMSG ((DBG_WARNING, "MigrateKeyboardPreloads: CreateObjectStruct failed with %s", key));
  616. continue;
  617. }
  618. if (EnumFirstMultiSz (&sze, (PCTSTR)gb.Buf)) {
  619. SetRegistryValueName (&destination, sze.CurrentString);
  620. SetRegistryType (&destination, REG_SZ);
  621. if (EnumNextMultiSz (&sze)) {
  622. ReplaceValueWithString (&destination, sze.CurrentString);
  623. WriteObject (&destination);
  624. }
  625. }
  626. FreeObjectStruct (&destination);
  627. FreeGrowBuffer (&gb);
  628. }
  629. } while (MemDbEnumNextValue (&e));
  630. }
  631. }
  632. __finally {
  633. FreeObjectStruct (&source);
  634. FreeObjectStruct (&destination);
  635. }
  636. //
  637. // Delete this every time through.
  638. //
  639. MemDbDeleteTree (MEMDB_CATEGORY_KEYBOARD_LAYOUTS);
  640. SetLastError (ERROR_SUCCESS);
  641. return rSuccess;
  642. }