Leaked source code of windows server 2003
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.

650 lines
20 KiB

  1. /* cmdkeyb.c - Keyboard layout support routines
  2. *
  3. *
  4. * Modification History:
  5. *
  6. * YST 14-Jan_1993 Created
  7. *
  8. * 08-Sept-1998, williamh, add third-party KDF support.
  9. */
  10. #include "cmd.h"
  11. #include <winconp.h>
  12. #include <cmdsvc.h>
  13. #include <softpc.h>
  14. #include <mvdm.h>
  15. #include <ctype.h>
  16. #include <string.H>
  17. #include "cmdkeyb.h"
  18. #include <winnls.h>
  19. #include "host_def.h"
  20. CHAR szPrev[5] = "US";
  21. INT iPrevCP = 437;
  22. CHAR szPrevKbdID[8] = "";
  23. extern BOOL bPifFastPaste;
  24. /************************************************************************\
  25. *
  26. * FUNCTION: VOID cmdGetKbdLayout( VOID )
  27. *
  28. * Input Client (DX) = 0 - Keyb.com not installed
  29. * 1 - Keyb.com installed
  30. * Client (DS:SI) = pointer where exe name has to be placed
  31. * Client (DS:CX) = pointer where command options are placed
  32. *
  33. * Output
  34. * Success (DX = 1 )
  35. * Client (DS:SI) = Keyb.com execuatable string
  36. * Client (DS:CX) = command options
  37. *
  38. * Failure (DX = 0)
  39. *
  40. * COMMENTS: This function check KEYBOARD ID for Win session
  41. * and if ID != US then return lines with
  42. * filename and options to COMMAND.COM
  43. *
  44. * If bPifFastPaste is FALSE, then we always run kb16
  45. * for all keyboard ID including US, to give us a more
  46. * bios compatible Int 9 handler. 10-Jun-1993 Jonle
  47. *
  48. *
  49. * HISTORY: 01/05/93 YSt Created.
  50. *
  51. \************************************************************************/
  52. VOID cmdGetKbdLayout( VOID )
  53. {
  54. INT iSize;
  55. CHAR szKeybCode[12];
  56. CHAR szDir[MAX_PATH+15];
  57. CHAR szBuf[5];
  58. CHAR szNewKbdID[8];
  59. CHAR szAutoLine[MAX_PATH+40];
  60. CHAR szKDF[MAX_PATH];
  61. PCHAR pVDMKeyb;
  62. INT iKeyb;
  63. HKEY hKey;
  64. HKEY hKeyLayout;
  65. DWORD dwType;
  66. DWORD retCode;
  67. INT iNewCP;
  68. DWORD cbData;
  69. WORD KeybID;
  70. OFSTRUCT ofstr;
  71. LANGID LcId = GetSystemDefaultLangID();
  72. int keytype;
  73. // Get information about 16 bit KEYB.COM from VDM
  74. iKeyb = getDX();
  75. // The whole logic here is to decide:
  76. // (1). if we have to run kb16.com at all.
  77. // (2). if we have to run kb16.com, what parameters we should pass along,
  78. // such as keyboard id, language id, code page id and kdf file name.
  79. // We do not load kb16.com at all if one of the following
  80. // conditions is met:
  81. // (1). We can not find the console keyboard layout id.
  82. // (2). The console keyvoard layout id is US and kb16.com is not loaded
  83. // and fast paste is disabled.
  84. // (3). We can not get the dos keyboard id/dos key code.
  85. // (4). The new (language id, keyboard id, code page id) is the same
  86. // as the one we loaded previously.
  87. // (5). we can not find kb16.com.
  88. // (6). we can not find the kdf file that supports the
  89. // (language id, keyboard id, code page id) combination.
  90. //
  91. // If everything goes as planned, we end up with a command
  92. // contains kb16.com fully qualified name and a command line contains
  93. // appropriate parameters to kb16.com
  94. //
  95. if (LcId == MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT)) {
  96. // JAPAN build. Language id is always "JP" and code page is either 932
  97. // or 437 depends on keyboard type.
  98. iNewCP = 437;
  99. if (7 == GetKeyboardType(0))
  100. {
  101. keytype = GetKeyboardType(1);
  102. if (keytype == 1 || keytype == 2 || keytype == 3 || (keytype & 0xff00) == 0x1200)
  103. iNewCP = 932;
  104. }
  105. szBuf[0] = 'J';
  106. szBuf[1] = 'P';
  107. szBuf[2] = '\0';
  108. // no keyboard id available.
  109. szNewKbdID[0] = '\0';
  110. }
  111. else {
  112. //
  113. // check point #1: see if we can get the console keyboard layout id
  114. //
  115. if (!GetConsoleKeyboardLayoutName(szKeybCode))
  116. goto NoInstallkb16;
  117. //
  118. // check point #2: see if the layout is US and kb16.com is loaded and
  119. // fast paste is disabled.
  120. // If kb16.com is loaded, we need to run it again
  121. // so that it will load the correct layout.
  122. // If fast paste is disable, we load kb16.com which
  123. // definitely will slow down keys delivery.
  124. //
  125. if( bPifFastPaste && !strcmp(szKeybCode, US_CODE) && !iKeyb)
  126. goto NoInstallkb16;
  127. //
  128. // check point #3: see if we can get the language id and keyboard id(if any)
  129. //
  130. // OPEN THE KEY.
  131. sprintf(szAutoLine, "%s%s", KBDLAYOUT_PATH, DOSCODES_PATH);
  132. if (ERROR_SUCCESS != RegOpenKeyEx (HKEY_LOCAL_MACHINE, // Key handle at root level.
  133. szAutoLine, // Path name of child key.
  134. 0, // Reserved.
  135. KEY_EXECUTE, // Requesting read access.
  136. &hKey)) // Address of key to be returned.
  137. goto NoInstallkb16;
  138. cbData = sizeof(szBuf);
  139. // Query for line from REGISTER file
  140. retCode = RegQueryValueEx(hKey, szKeybCode, NULL, &dwType, szBuf, &cbData);
  141. RegCloseKey(hKey);
  142. if (ERROR_SUCCESS != retCode || REG_SZ != dwType || !cbData)
  143. goto NoInstallkb16;
  144. //
  145. // szBuf now contains language id('SP' for spanish, for example).
  146. //
  147. // look for keyboard id number. For Daytona, Turkish and Italian both
  148. // have one key code and two layouts.
  149. szNewKbdID[0] = '\0';
  150. cbData = sizeof(szNewKbdID);
  151. sprintf(szAutoLine, "%s%s", KBDLAYOUT_PATH, DOSIDS_PATH);
  152. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  153. szAutoLine,
  154. 0,
  155. KEY_EXECUTE,
  156. &hKey
  157. ) == ERROR_SUCCESS)
  158. {
  159. retCode = RegQueryValueEx(hKey, szKeybCode, NULL, &dwType, szNewKbdID, &cbData);
  160. if (ERROR_SUCCESS != retCode || REG_SZ != dwType || !cbData)
  161. szNewKbdID[0] = '\0';
  162. RegCloseKey(hKey);
  163. }
  164. iNewCP = GetConsoleCP();
  165. }
  166. //
  167. // check point #4: see if there are any changes in ids
  168. //
  169. // see if there are changes in language id, keyboard id and code page id.
  170. if(bPifFastPaste && iNewCP == iPrevCP &&
  171. !_stricmp(szBuf, szPrev) &&
  172. !_stricmp(szNewKbdID, szPrevKbdID))
  173. {
  174. goto NoInstallkb16;
  175. }
  176. //
  177. // Check point #5: see if kb16.com can be found.
  178. //
  179. // kb16.com should be found in GetSystemDirectory()\system32 subdirectory.
  180. //
  181. // convert the system directory to short name
  182. cbData = GetShortPathName(pszSystem32Path, szDir, MAX_PATH);
  183. if (!cbData || cbData >= MAX_PATH)
  184. goto NoInstallkb16;
  185. sprintf(szAutoLine, "%s%s",
  186. szDir, // System directory
  187. KEYB_COM // keyb.com
  188. );
  189. // if the fully qualified path name to kb16.com is too long
  190. // we must fail because Dos can not swallow a long path name.
  191. if (strlen(szAutoLine) > 128)
  192. goto NoInstallkb16;
  193. dwType = GetFileAttributes(szAutoLine);
  194. if (dwType == 0xFFFFFFFF || (dwType & FILE_ATTRIBUTE_DIRECTORY) != 0)
  195. {
  196. goto NoInstallkb16;
  197. }
  198. //
  199. // Check point #6: see if we can find kdf file that support the
  200. // (language id, keyboard id, code page id) combination
  201. //
  202. //
  203. // first, convert keyboard id from string to binary if we have one.
  204. //
  205. KeybID = (szNewKbdID[0]) ? (WORD)strtoul(szNewKbdID, NULL, 10) : 0;
  206. cbData = sizeof(szKDF) / sizeof(CHAR);
  207. // locate the kdf file.
  208. if (!LocateKDF(szBuf, KeybID, (WORD)iNewCP, szKDF, &cbData))
  209. {
  210. goto NoInstallkb16;
  211. }
  212. // convert the kdf name to short name
  213. cbData = GetShortPathName(szKDF, szKDF, sizeof(szKDF)/ sizeof(CHAR));
  214. if (!cbData || cbData >= sizeof(szKDF) / sizeof(CHAR))
  215. {
  216. goto NoInstallkb16;
  217. }
  218. //
  219. // everything is checked and in place. Now compose the command
  220. // line to execute kb16.com
  221. // first, the command
  222. pVDMKeyb = (PCHAR) GetVDMAddr((USHORT) getDS(), (USHORT) getSI());
  223. strcpy(pVDMKeyb, szAutoLine);
  224. // then the parameters
  225. // The format is: XX,YYY, <kdf file>, where XXX is the language id
  226. // and YYY is the code page id.
  227. pVDMKeyb = (PCHAR) GetVDMAddr((USHORT) getDS(), (USHORT) getCX());
  228. // The first byte is resevered for the length of the string.
  229. sprintf(szAutoLine, " %s,%d,%s",
  230. szBuf, // keyboard code
  231. iNewCP, // new code page
  232. szKDF // keyboard.sys
  233. );
  234. // if we have a keyboard id, pass it also
  235. if (szNewKbdID[0])
  236. {
  237. strcat(szAutoLine, " /id:");
  238. strcat(szAutoLine, szNewKbdID);
  239. }
  240. // standard parameter line has the format:
  241. // <length><line text><\0xd>, <length> is the length of <line text>
  242. //
  243. iSize = strlen(szAutoLine);
  244. szAutoLine[iSize] = 0xd;
  245. // Move the line to 16bits, including the terminated cr char
  246. RtlMoveMemory(pVDMKeyb + 1, szAutoLine, iSize + 1);
  247. *pVDMKeyb = (CHAR)iSize;
  248. // Save new layout ID and code page for next call
  249. strcpy(szPrev, szBuf);
  250. strcpy(szPrevKbdID, szNewKbdID);
  251. iPrevCP = iNewCP;
  252. setDX(1);
  253. return;
  254. NoInstallkb16:
  255. setDX(0);
  256. cmdInitConsole(); // make sure conoutput is on
  257. return;
  258. }
  259. //
  260. // This function locates the appropriate keyboard definition file from
  261. // the given language, keyboard and code page id. It searches the registry
  262. // for third-party installed KDF files first and then falls back to
  263. // the system default, %systemroot%\system32\keyboard.sys.
  264. //
  265. // INPUT:
  266. // LanguageID -- the language id
  267. // KeyboardID -- the optional keyboard id, 0 means do not care
  268. // CodePageID -- the code page id
  269. // Buffer -- the buffer to receive fully qualified kdf file name
  270. // BufferSize -- the size of Buffer in bytes
  271. //
  272. // OUTPUT:
  273. // TRUE -- Buffer is filled with the kdf fully qualified file name and
  274. // *BufferSize is set with the size of the file name, not including
  275. // the null terminated char. If no kdf file can be found,
  276. // the Buffer is terminated with NULL and *BufferSize if set to 0.
  277. // FALSE -- error. GetLastError() should return the error code
  278. // If the error occurs because the provided buffer is too small
  279. // *BufferSize will set to the required size(excluding null
  280. // terminated char) and error code will be set to
  281. // ERROR_INSUFFICIENT_BUFFER
  282. //
  283. BOOL
  284. LocateKDF(
  285. CHAR* LanguageID,
  286. WORD KeyboardID,
  287. WORD CodePageID,
  288. LPSTR Buffer,
  289. DWORD* BufferSize
  290. )
  291. {
  292. HKEY hKeyWow;
  293. BOOL Result;
  294. DWORD dw, Type;
  295. DWORD Attributes;
  296. DWORD ErrorCode;
  297. CHAR* KDFName;
  298. CHAR* LocalBuffer;
  299. CHAR* FinalKDFName;
  300. CHAR FullName[MAX_PATH*2];
  301. // validate buffer parameter first
  302. if (!CodePageID || !LanguageID || !BufferSize || (*BufferSize && !Buffer))
  303. {
  304. SetLastError(ERROR_INVALID_PARAMETER);
  305. return FALSE;
  306. }
  307. // Open the registry to see if we have alternative kdf files avaialble.
  308. // We seach the file from atlternative file list in the registry first.
  309. // The first KDF in the list has the highest rank and the last one has
  310. // the lowest. The search starts from highest rank and then goes
  311. // on toward the lower ones. As soon as a KDF file is found, the search
  312. // stops. If no appropriate KDF can be found in the alternative list,
  313. // the default kdf, keyboard.sys, will be used.
  314. //
  315. // FinalKDFName serves as an indicator. If it is NULL,
  316. // we do not find any file that satisfies the request.
  317. FinalKDFName = NULL;
  318. LocalBuffer = NULL;
  319. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  320. REG_STR_WOW,
  321. 0,
  322. KEY_EXECUTE,
  323. &hKeyWow
  324. ) == ERROR_SUCCESS)
  325. {
  326. // first probe for size
  327. dw = 0;
  328. RegQueryValueEx(hKeyWow, REG_STR_ALTKDF_FILES, NULL, &Type, NULL, &dw);
  329. if (dw && (REG_MULTI_SZ == Type))
  330. {
  331. // we have something in the registry. Allocate a buffer to reteive
  332. // it. We want the value to be double null terminated,
  333. // so we add one more char in case it is a REG_SZ.
  334. // The returned size from RegQueryValueEx includes the
  335. // null terminated char(and the double null chars for
  336. // REG_MULTI_SZ. By adding one more char, we are in
  337. // good shape.
  338. ASSERT(!LocalBuffer);
  339. LocalBuffer = malloc((dw + 1)* sizeof(CHAR));
  340. if (LocalBuffer)
  341. {
  342. LocalBuffer[0] = '\0';
  343. if (RegQueryValueEx(hKeyWow, REG_STR_ALTKDF_FILES, NULL, &Type,
  344. LocalBuffer, &dw) == ERROR_SUCCESS && dw)
  345. {
  346. KDFName = LocalBuffer;
  347. while ('\0' != *KDFName)
  348. {
  349. // See if we can find the file first.
  350. Attributes = GetFileAttributesA(KDFName);
  351. if (0xFFFFFFFF == Attributes)
  352. {
  353. // file not found, do a search
  354. if (SearchPathA(NULL, // no path
  355. KDFName,
  356. NULL, // no extension
  357. sizeof(FullName) / sizeof(CHAR),
  358. FullName,
  359. NULL
  360. ))
  361. {
  362. FinalKDFName = FullName;
  363. }
  364. }
  365. else
  366. {
  367. FinalKDFName = KDFName;
  368. }
  369. if (MatchKDF(LanguageID, KeyboardID, CodePageID, FinalKDFName))
  370. break;
  371. KDFName += strlen(KDFName) + 1;
  372. FinalKDFName = NULL;
  373. }
  374. }
  375. }
  376. else
  377. {
  378. // not enough memory
  379. RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL,
  380. RMB_ICON_BANG | RMB_ABORT);
  381. TerminateVDM();
  382. }
  383. }
  384. if (!FinalKDFName)
  385. {
  386. // either no alternative kdf files are specified in the registry
  387. // or none of them contains the required specification,
  388. // use the default kdf file
  389. FullName[0] = '\0';
  390. GetSystemDirectory(FullName, sizeof(FullName) / sizeof(CHAR));
  391. if (!_stricmp(LanguageID, "JP") &&
  392. 7 == GetKeyboardType(0)
  393. ) {
  394. // For Japanese language ID, different keyboard types have different
  395. // default kdf.
  396. int Keytype;
  397. Keytype = GetKeyboardType(1);
  398. if (Keytype == 1)
  399. strcat(FullName, KDF_AX);
  400. else if (Keytype == 2)
  401. strcat(FullName, KDF_106);
  402. else if (Keytype == 3)
  403. strcat(FullName, KDF_IBM5576_02_03);
  404. else if ((Keytype & 0xFF00) == 0x1200)
  405. strcat(FullName, KDF_TOSHIBA_J3100);
  406. else
  407. strcat(FullName, KEYBOARD_SYS);
  408. }
  409. else
  410. strcat(FullName, KEYBOARD_SYS);
  411. FinalKDFName = FullName;
  412. }
  413. RegCloseKey(hKeyWow);
  414. }
  415. if (FinalKDFName)
  416. {
  417. dw = strlen(FinalKDFName);
  418. if (dw && dw < *BufferSize)
  419. {
  420. strcpy(Buffer, FinalKDFName);
  421. *BufferSize = dw;
  422. Result = TRUE;
  423. }
  424. else
  425. {
  426. *BufferSize = dw;
  427. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  428. Result = FALSE;
  429. }
  430. }
  431. else
  432. {
  433. Result = FALSE;
  434. *BufferSize = 0;
  435. SetLastError(ERROR_FILE_NOT_FOUND);
  436. }
  437. //
  438. // finally, free the buffer we allocated
  439. //
  440. if (LocalBuffer)
  441. free(LocalBuffer);
  442. return Result;
  443. }
  444. //
  445. // This function determines if the given kdf supports the given
  446. // (language id, keyboard id, code page id) combination
  447. //
  448. // INPUT:
  449. // LanguageID -- the language.
  450. // KeyboardID -- optional keyboard id. 0 if do not care
  451. // CodePageID -- code page id
  452. // KDFPath -- fully qualified kdf file
  453. // OUTPUT:
  454. // TRUE -- The kdf contains the given combination
  455. // FALSE -- either the kdf does not contain the combination or
  456. // can not determine.
  457. //
  458. BOOL
  459. MatchKDF(
  460. CHAR* LanguageID,
  461. WORD KeyboardID,
  462. WORD CodePageID,
  463. LPCSTR KDFPath
  464. )
  465. {
  466. HANDLE hKDF;
  467. KDF_HEADER Header;
  468. KDF_LANGID_ENTRY LangIdEntry;
  469. DWORD BytesRead, BufferSize;
  470. WORD Index;
  471. DWORD LangIdEntryOffset;
  472. PKDF_CODEPAGEID_OFFSET pCodePageIdOffset;
  473. BOOL Matched;
  474. if (!KDFPath || !LanguageID || !CodePageID)
  475. {
  476. SetLastError(ERROR_INVALID_PARAMETER);
  477. return FALSE;
  478. }
  479. Matched = FALSE;
  480. LangIdEntryOffset = 0;
  481. // open the kdf file.
  482. hKDF = CreateFile(KDFPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
  483. NULL, OPEN_EXISTING, 0, NULL);
  484. if (INVALID_HANDLE_VALUE != hKDF &&
  485. ReadFile(hKDF, &Header, sizeof(Header),&BytesRead, NULL) &&
  486. BytesRead == sizeof(Header) && Header.TotalLangIDs &&
  487. Header.TotalKeybIDs &&
  488. !strncmp(Header.Signature, KDF_SIGNATURE, sizeof(Header.Signature))
  489. )
  490. {
  491. // The file header is loaded, the signature checked and sanity check
  492. // on language and keyboard id counts is also done.
  493. // We are now ready to verfiy if the given language id, keyboard id
  494. // and code page id is supported in this file.
  495. // A KDF has two sets of offset table. One is based on language ID
  496. // while the other one is based on keyboard id. Since a language ID
  497. // may contain multiple keyboard id, the keyboard id set is always
  498. // encompass the language id table.
  499. // If the caller gives us a keyboard id, we use the id as the
  500. // key for search and verify language id when we found the keyboard
  501. // id. If no keyboard id is provided, we use the language id as the
  502. // key.
  503. if (KeyboardID)
  504. {
  505. // move the file pointer to the keyboard id offset array
  506. BufferSize = sizeof(KDF_LANGID_OFFSET) * Header.TotalLangIDs;
  507. BufferSize = SetFilePointer(hKDF, BufferSize, NULL, FILE_CURRENT);
  508. if (0xFFFFFFFF != BufferSize)
  509. {
  510. PKDF_KEYBOARDID_OFFSET pKeybIdOffset;
  511. BufferSize = sizeof(KDF_KEYBOARDID_OFFSET) * Header.TotalKeybIDs;
  512. pKeybIdOffset = (PKDF_KEYBOARDID_OFFSET)malloc(BufferSize);
  513. if (!pKeybIdOffset)
  514. {
  515. // not enough memory
  516. RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL,
  517. RMB_ICON_BANG | RMB_ABORT);
  518. TerminateVDM();
  519. }
  520. if (ReadFile(hKDF, pKeybIdOffset, BufferSize, &BytesRead, NULL) &&
  521. BytesRead == BufferSize)
  522. {
  523. // loop though each KDF_KEYBOARDID_OFFSET to see
  524. // if the keyboard id matches.
  525. for (Index = 0; Index < Header.TotalKeybIDs; Index++)
  526. {
  527. if (pKeybIdOffset[Index].ID == KeyboardID)
  528. {
  529. // got it. Remeber the file offset to
  530. // the KDF_LANGID_ENTRY
  531. LangIdEntryOffset = pKeybIdOffset[Index].DataOffset;
  532. break;
  533. }
  534. }
  535. }
  536. free(pKeybIdOffset);
  537. }
  538. }
  539. else
  540. {
  541. PKDF_LANGID_OFFSET pLangIdOffset;
  542. BufferSize = sizeof(KDF_LANGID_OFFSET) * Header.TotalLangIDs;
  543. pLangIdOffset = (PKDF_LANGID_OFFSET)malloc(BufferSize);
  544. if (!pLangIdOffset)
  545. {
  546. // not enough memory
  547. RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL,
  548. RMB_ICON_BANG | RMB_ABORT);
  549. TerminateVDM();
  550. }
  551. if (ReadFile(hKDF, pLangIdOffset, BufferSize, &BytesRead, NULL) &&
  552. BytesRead == BufferSize)
  553. {
  554. // loop through each KDF_LANGID_OFFSET to see if
  555. // language id matches
  556. for (Index = 0; Index < Header.TotalLangIDs; Index++)
  557. {
  558. if (IS_LANGID_EQUAL(pLangIdOffset[Index].ID, LanguageID))
  559. {
  560. LangIdEntryOffset = pLangIdOffset[Index].DataOffset;
  561. break;
  562. }
  563. }
  564. }
  565. free(pLangIdOffset);
  566. }
  567. if (LangIdEntryOffset)
  568. {
  569. BufferSize = SetFilePointer(hKDF, LangIdEntryOffset, NULL, FILE_BEGIN);
  570. if (0xFFFFFFFF != BufferSize &&
  571. ReadFile(hKDF, &LangIdEntry, sizeof(LangIdEntry), &BytesRead, NULL) &&
  572. BytesRead == sizeof(LangIdEntry))
  573. {
  574. // sanity checks
  575. if (IS_LANGID_EQUAL(LangIdEntry.ID, LanguageID) &&
  576. LangIdEntry.TotalCodePageIDs)
  577. {
  578. // the KDF_LANGID_ENTRY looks fine. Now retrieve
  579. // its code page offset table and search the given
  580. // code page id
  581. BufferSize = LangIdEntry.TotalCodePageIDs * sizeof(KDF_CODEPAGEID_OFFSET);
  582. pCodePageIdOffset = (PKDF_CODEPAGEID_OFFSET)malloc(BufferSize);
  583. if (!pCodePageIdOffset)
  584. {
  585. // not enough memory
  586. RcMessageBox(EG_MALLOC_FAILURE, NULL, NULL,
  587. RMB_ICON_BANG | RMB_ABORT);
  588. TerminateVDM();
  589. }
  590. if (ReadFile(hKDF, pCodePageIdOffset, BufferSize, &BytesRead, NULL) &&
  591. BytesRead == BufferSize)
  592. {
  593. for (Index = 0; Index < LangIdEntry.TotalCodePageIDs; Index++)
  594. {
  595. if (CodePageID == pCodePageIdOffset[Index].ID)
  596. {
  597. Matched = TRUE;
  598. break;
  599. }
  600. }
  601. }
  602. free(pCodePageIdOffset);
  603. }
  604. }
  605. }
  606. CloseHandle(hKDF);
  607. }
  608. return Matched;
  609. }