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.

2333 lines
61 KiB

  1. /*++
  2. Copyright (c) 1997 Microsoft Corporation
  3. Module Name:
  4. migisol.c
  5. Abstract:
  6. Implements an EXE that is used to run migration DLLs in a
  7. separate address space (sandboxing).
  8. Author:
  9. Jim Schmidt (jimschm) 04-Aug-1997
  10. Revision History:
  11. jimschm 19-Mar-2001 Removed DVD check because it is now
  12. in a migration dll
  13. jimschm 02-Jun-1999 Added DVD checking support to avoid
  14. setup crash on a Win9x blue screen
  15. jimschm 18-Mar-1999 Added cleanup for cases where text mode
  16. fails and the user returns to Win9x.
  17. jimschm 23-Sep-1998 Converted to new IPC mechanism
  18. --*/
  19. #include "pch.h"
  20. #include "master.h"
  21. #include "plugin.h"
  22. #include "migui.h"
  23. #include "ntui.h"
  24. #include "unattend.h"
  25. BOOL g_ReportPhase = FALSE;
  26. BOOL g_MigrationPhase = FALSE;
  27. TCHAR g_DllName[MAX_TCHAR_PATH] = "";
  28. P_INITIALIZE_NT InitializeNT;
  29. P_MIGRATE_USER_NT MigrateUserNT;
  30. P_MIGRATE_SYSTEM_NT MigrateSystemNT;
  31. P_QUERY_VERSION QueryVersion;
  32. P_INITIALIZE_9X Initialize9x;
  33. P_MIGRATE_USER_9X MigrateUser9x;
  34. P_MIGRATE_SYSTEM_9X MigrateSystem9x;
  35. BOOL
  36. WriteDiskSectors(
  37. IN TCHAR Drive,
  38. IN UINT StartSector,
  39. IN UINT SectorCount,
  40. IN UINT SectorSize,
  41. IN LPBYTE Buffer
  42. );
  43. BOOL
  44. WINAPI
  45. MigUtil_Entry (
  46. HINSTANCE hinstDLL,
  47. DWORD fdwReason,
  48. PVOID lpvReserved
  49. );
  50. BOOL
  51. IsNEC98(
  52. VOID
  53. );
  54. #define NO_GUI_ERROR 0
  55. //
  56. // Local functions
  57. //
  58. BOOL
  59. PackExeNames(
  60. PGROWBUFFER GrowBuf,
  61. PCSTR p
  62. );
  63. BOOL
  64. PackDword(
  65. PGROWBUFFER GrowBuf,
  66. DWORD dw
  67. );
  68. BOOL
  69. PackQuadWord(
  70. PGROWBUFFER GrowBuf,
  71. LONGLONG qw
  72. );
  73. BOOL
  74. PackIntArray(
  75. PGROWBUFFER GrowBuf,
  76. PINT Array
  77. );
  78. BOOL
  79. PackString (
  80. PGROWBUFFER GrowBuf,
  81. PCSTR String
  82. );
  83. BOOL
  84. PackBinary (
  85. PGROWBUFFER GrowBuf,
  86. PBYTE Data,
  87. DWORD DataSize
  88. );
  89. HINF
  90. pGetInfHandleFromFileNameW (
  91. PCWSTR UnattendFile
  92. );
  93. VOID
  94. ProcessCommands (
  95. VOID
  96. );
  97. BOOL
  98. pParseCommandLine (
  99. VOID
  100. );
  101. VOID
  102. DoInitializeNT (
  103. PCWSTR Args
  104. );
  105. VOID
  106. DoInitialize9x (
  107. PCSTR Args
  108. );
  109. VOID
  110. DoMigrateUserNT (
  111. PCWSTR Args
  112. );
  113. VOID
  114. DoQueryVersion (
  115. PCSTR Args
  116. );
  117. VOID
  118. DoMigrateUser9x (
  119. PCSTR Args
  120. );
  121. VOID
  122. DoMigrateSystemNT (
  123. PCWSTR Args
  124. );
  125. VOID
  126. DoMigrateSystem9x (
  127. PCSTR Args
  128. );
  129. HWND
  130. pFindParentWindow (
  131. IN PCTSTR WindowTitle,
  132. IN DWORD ProcessId
  133. );
  134. static HINSTANCE g_hLibrary;
  135. HANDLE g_hHeap;
  136. HINSTANCE g_hInst;
  137. #ifdef DEBUG
  138. #define DBG_MIGISOL "MigIsol"
  139. #endif
  140. INT
  141. WINAPI
  142. WinMain (
  143. HINSTANCE hInstance,
  144. HINSTANCE hPrevInstance,
  145. PSTR AnsiCmdLine,
  146. INT CmdShow
  147. )
  148. /*++
  149. Routine Description:
  150. The entry point to migisol.exe. The entire body of code is wrapped
  151. in a try/except block to catch all problems with any migration DLLs.
  152. Arguments:
  153. hInstance - The instance handle of this EXE
  154. hPrevInstance - The previous instance handle of this EXE if it is
  155. running, or NULL if no other instances exist.
  156. AnsiCmdLine - The command line (ANSI version)
  157. CmdShow - The ShowWindow command passed by the shell
  158. Return Value:
  159. Returns -1 if an error occurred, or 0 if the exe completed. The exe
  160. will automatically terminate with 0 if the migration DLL throws an
  161. exception.
  162. --*/
  163. {
  164. TCHAR OurDir[MAX_TCHAR_PATH];
  165. PTSTR p;
  166. __try {
  167. g_hInst = hInstance;
  168. g_hHeap = GetProcessHeap();
  169. *OurDir = 0;
  170. GetModuleFileName (NULL, OurDir, ARRAYSIZE(OurDir));
  171. p = _tcsrchr (OurDir, TEXT('\\'));
  172. if (p) {
  173. *p = 0;
  174. if (!_tcschr (OurDir, TEXT('\\'))) {
  175. p[0] = TEXT('\\');
  176. p[1] = 0;
  177. }
  178. SetCurrentDirectory (OurDir);
  179. //
  180. // Force a specific setupapi.dll to be loaded
  181. //
  182. StringCopy (AppendWack (OurDir), TEXT("setupapi.dll"));
  183. LoadLibraryEx (
  184. OurDir,
  185. NULL,
  186. LOAD_WITH_ALTERED_SEARCH_PATH
  187. );
  188. }
  189. // Initialize utility library
  190. if (!MigUtil_Entry (g_hInst, DLL_PROCESS_ATTACH, NULL)) {
  191. FreeLibrary (g_hLibrary);
  192. return -1;
  193. }
  194. DEBUGMSG ((DBG_MIGISOL, "migisol.exe started"));
  195. if (!pParseCommandLine()) {
  196. FreeLibrary (g_hLibrary);
  197. return -1;
  198. }
  199. DEBUGMSG ((DBG_MIGISOL, "CmdLine parsed"));
  200. if (!OpenIpc (FALSE, NULL, NULL, NULL)) {
  201. DEBUGMSG ((DBG_MIGISOL, "OpenIpc failed"));
  202. FreeLibrary (g_hLibrary);
  203. return -1;
  204. }
  205. __try {
  206. DEBUGMSG ((DBG_MIGISOL, "Processing commands"));
  207. ProcessCommands();
  208. }
  209. __except (TRUE) {
  210. LOG ((LOG_ERROR, "Upgrade Pack process is terminating because of an exception in WinMain"));
  211. }
  212. CloseIpc();
  213. FreeLibrary (g_hLibrary);
  214. DEBUGMSG ((DBG_MIGISOL, "migisol.exe terminating"));
  215. if (!MigUtil_Entry (g_hInst, DLL_PROCESS_DETACH, NULL)) {
  216. return -1;
  217. }
  218. }
  219. __except (TRUE) {
  220. }
  221. return 0;
  222. }
  223. #define WINNT32_SECTOR_SIZE 512
  224. #define WINNT32_FAT_BOOT_SECTOR_COUNT 1
  225. #define WINNT32_FAT_BOOT_SIZE (WINNT32_SECTOR_SIZE * WINNT32_FAT_BOOT_SECTOR_COUNT)
  226. #define FILE_ATTRIBUTE_RHS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM)
  227. BOOL
  228. pWriteFATBootSector (
  229. IN PCTSTR BootDataFile,
  230. IN TCHAR BootDriveLetter
  231. )
  232. {
  233. HANDLE BootDataHandle;
  234. BYTE Data[WINNT32_FAT_BOOT_SIZE];
  235. DWORD BytesRead;
  236. BOOL Success = FALSE;
  237. if (GetFileAttributes (BootDataFile) == INVALID_ATTRIBUTES) {
  238. DEBUGMSG ((DBG_ERROR, "Can't find %s", BootDataFile));
  239. return FALSE;
  240. }
  241. BootDataHandle = CreateFile (
  242. BootDataFile,
  243. GENERIC_READ,
  244. FILE_SHARE_READ,
  245. NULL,
  246. OPEN_EXISTING,
  247. FILE_FLAG_SEQUENTIAL_SCAN,
  248. NULL
  249. );
  250. if (BootDataHandle == INVALID_HANDLE_VALUE) {
  251. DEBUGMSG ((DBG_ERROR, "Can't open %s", BootDataFile));
  252. return FALSE;
  253. }
  254. Success = ReadFile (BootDataHandle, Data, WINNT32_FAT_BOOT_SIZE, &BytesRead, NULL) &&
  255. (BytesRead == WINNT32_FAT_BOOT_SIZE);
  256. CloseHandle (BootDataHandle);
  257. if (Success) {
  258. //
  259. // write the boot sector with this data; don't save NT boot sector
  260. //
  261. Success = WriteDiskSectors (
  262. BootDriveLetter,
  263. 0,
  264. WINNT32_FAT_BOOT_SECTOR_COUNT,
  265. WINNT32_SECTOR_SIZE,
  266. Data
  267. );
  268. DEBUGMSG_IF ((
  269. !Success,
  270. DBG_ERROR,
  271. "WriteDiskSectors failed for %c!",
  272. BootDriveLetter
  273. ));
  274. }
  275. ELSE_DEBUGMSG ((DBG_ERROR, "Unexpected boot sector size %u in %s", BytesRead, BootDataFile));
  276. return Success;
  277. }
  278. VOID
  279. pCleanUpUndoDirectory (
  280. CHAR BootDrive
  281. )
  282. /*++
  283. Routine Description:
  284. This function delete recursively all files and directories
  285. include and in %windir%\undo directory.
  286. Arguments:
  287. none
  288. Return Value:
  289. none
  290. --*/
  291. {
  292. TCHAR PathBuffer[MAX_PATH];
  293. TCHAR Answer[MAX_PATH];
  294. TCHAR NullPath[] = {0};
  295. DEBUGMSG((DBG_MIGISOL, "Cleanup routine of undo directory"));
  296. if(!BootDrive){
  297. if (!GetWindowsDirectory (PathBuffer, ARRAYSIZE(PathBuffer))) {
  298. DEBUGMSG((DBG_MIGISOL, "GetWindowsDirectory failed"));
  299. return;
  300. }
  301. BootDrive = PathBuffer[0];
  302. }
  303. wsprintf(PathBuffer, TEXT("%c:\\$win_nt$.~bt\\winnt.sif"), BootDrive);
  304. GetPrivateProfileString(
  305. S_WIN9XUPGUSEROPTIONS,
  306. S_PATH_FOR_BACKUP,
  307. NullPath,
  308. Answer,
  309. ARRAYSIZE(Answer),
  310. PathBuffer);
  311. if(!Answer[0]) {
  312. DEBUGMSG ((DBG_MIGISOL, "Failed to retrieve directory path"));
  313. return;
  314. }
  315. wsprintf(PathBuffer, TEXT("%c:\\$win_nt$.~bt\\dataloss"), BootDrive);
  316. if (DoesFileExist (PathBuffer)) {
  317. LOG ((
  318. LOG_INFORMATION,
  319. "Data loss was detected because of a failure to restore one or more files. "
  320. "The data can be recovered from backup files in %s.",
  321. Answer
  322. ));
  323. return;
  324. }
  325. SetFileAttributes(Answer, FILE_ATTRIBUTE_NORMAL);
  326. RemoveCompleteDirectory (Answer);
  327. DEBUGMSG ((DBG_MIGISOL, "Cleaned %s directory", Answer));
  328. }
  329. VOID
  330. pCleanUpAfterTextModeFailure (
  331. VOID
  332. )
  333. {
  334. TCHAR squiggleBtDir[] = TEXT("?:\\$win_nt$.~bt");
  335. TCHAR squiggleLsDir[] = TEXT("?:\\$win_nt$.~ls");
  336. TCHAR squiggleBuDir[] = TEXT("?:\\$win_nt$.~bu"); // for NEC98
  337. TCHAR drvLtr[] = TEXT("?:\\$DRVLTR$.~_~");
  338. TCHAR setupLdr[] = TEXT("?:\\$LDR$");
  339. TCHAR txtSetupSif[] = TEXT("?:\\TXTSETUP.SIF");
  340. PCTSTR bootSectDat;
  341. TCHAR setupTempDir[MAX_PATH];
  342. TCHAR bootIni[] = TEXT("?:\\boot.ini");
  343. TCHAR ntLdr[] = TEXT("?:\\NTLDR");
  344. TCHAR ntDetectCom[] = TEXT("?:\\NTDETECT.COM");
  345. TCHAR bootFontBin[] = TEXT("?:\\BOOTFONT.BIN");
  346. TCHAR bootSectDos[] = TEXT("?:\\BootSect.dos");
  347. TCHAR renamedFile1[] = TEXT("?:\\boot~tmp.$$1");
  348. TCHAR renamedFile2[] = TEXT("?:\\boot~tmp.$$2");
  349. TCHAR renamedFile3[] = TEXT("?:\\boot~tmp.$$3");
  350. BOOL noLdr = FALSE;
  351. BOOL noNtDetect = FALSE;
  352. BOOL noBootFontBin = FALSE;
  353. DWORD Drives;
  354. TCHAR DriveLetter;
  355. DWORD Bit;
  356. TCHAR Root[] = TEXT("?:\\");
  357. TCHAR Scratch[MAX_PATH];
  358. PCTSTR bootSectBak;
  359. PCTSTR bootIniBak;
  360. PCTSTR ntldrBak;
  361. PCTSTR bootFontBak;
  362. PCTSTR ntdetectBak;
  363. TCHAR WinDir[MAX_PATH];
  364. DWORD Type;
  365. DWORD Attribs;
  366. FILE_ENUM e;
  367. HANDLE WinInit;
  368. CHAR AnsiBuffer[MAX_PATH + 10];
  369. DWORD Dummy;
  370. PTSTR Write;
  371. BOOL prepareBootIni = FALSE;
  372. CHAR SystemDirPath[MAX_PATH];
  373. TCHAR bootDriveLetter;
  374. PCTSTR bootSectorFile;
  375. BOOL bootLoaderWritten;
  376. HKEY key;
  377. BOOL dontTouchBootCode = FALSE;
  378. if (ISNT()) {
  379. return;
  380. }
  381. DEBUGMSG ((DBG_MIGISOL, "Entering cleanup routine"));
  382. SuppressAllLogPopups (TRUE);
  383. //
  384. // Reinitialize system restore
  385. //
  386. key = OpenRegKeyStr (TEXT("HKLM\\SYSTEM\\CurrentControlSet\\Services\\VxD\\VxDMon"));
  387. if (key) {
  388. RegSetValueEx (key, TEXT("FirstRun"), 0, REG_SZ, (PCBYTE) "Y", 2);
  389. CloseRegKey (key);
  390. }
  391. //
  392. // Prepare windir and temp dir paths, get the bitmask of drive letters
  393. //
  394. // We need to know the system drive to be repaired, since win98 on NEC98
  395. // can boot up from any partition that can be installed.
  396. //
  397. GetSystemDirectory (SystemDirPath, MAX_PATH);
  398. if (!GetWindowsDirectory (setupTempDir, sizeof (setupTempDir) / sizeof (setupTempDir[0]))) {
  399. DEBUGMSG ((DBG_ERROR, "Can't get Windows dir"));
  400. return;
  401. } else {
  402. StringCopy (WinDir, setupTempDir);
  403. }
  404. StringCopy (AppendWack (setupTempDir), TEXT("setup"));
  405. Drives = GetLogicalDrives();
  406. bootDriveLetter = IsNEC98() ? SystemDirPath[0] : TEXT('C');
  407. if (WinDir[0] != bootDriveLetter) {
  408. dontTouchBootCode = TRUE;
  409. }
  410. //
  411. // Create paths
  412. //
  413. bootIniBak = JoinPaths (setupTempDir, S_BOOTINI_BACKUP);
  414. ntldrBak = JoinPaths (setupTempDir, S_NTLDR_BACKUP);
  415. ntdetectBak = JoinPaths (setupTempDir, S_NTDETECT_BACKUP);
  416. bootSectBak = JoinPaths (setupTempDir, S_BOOTSECT_BACKUP);
  417. bootFontBak = JoinPaths (setupTempDir, S_BOOTFONT_BACKUP);
  418. //
  419. // Deltree $win_nt$.~bt and $win_nt$.~ls
  420. //
  421. for (Bit = 1, DriveLetter = TEXT('A') ; Bit ; Bit <<= 1, DriveLetter++) {
  422. if (!(Drives & Bit)) {
  423. continue;
  424. }
  425. Root[0] = DriveLetter;
  426. Type = GetDriveType (Root);
  427. if (Type == DRIVE_FIXED || Type == DRIVE_UNKNOWN) {
  428. //
  429. // Clean this drive
  430. //
  431. squiggleBtDir[0] = DriveLetter;
  432. squiggleLsDir[0] = DriveLetter;
  433. RemoveCompleteDirectory (squiggleBtDir);
  434. RemoveCompleteDirectory (squiggleLsDir);
  435. //
  436. // On NEC98, there may be another temp directry to be clean up.
  437. //
  438. if (IsNEC98()) {
  439. squiggleBuDir[0] = DriveLetter;
  440. RemoveCompleteDirectory (squiggleBuDir);
  441. }
  442. }
  443. }
  444. DEBUGMSG ((DBG_MIGISOL, "Cleaned squiggle dirs"));
  445. //
  446. // Repair boot.ini (do not necessarily restore it to its original form though)
  447. // and clean the root of the drive.
  448. //
  449. for (Bit = 1, DriveLetter = TEXT('A') ; Bit ; Bit <<= 1, DriveLetter++) {
  450. if (!(Drives & Bit)) {
  451. continue;
  452. }
  453. //
  454. // On NEC98, there may be multiple boot files in each partition.
  455. // So we will just take care the system that booted up.
  456. //
  457. if (IsNEC98() && (DriveLetter != SystemDirPath[0])) {
  458. continue;
  459. }
  460. Root[0] = DriveLetter;
  461. Type = GetDriveType (Root);
  462. if (Type == DRIVE_FIXED || Type == DRIVE_UNKNOWN) {
  463. //
  464. // Remove setup from boot.ini if it is on this drive,
  465. // and clean root of the drive.
  466. //
  467. squiggleBtDir[0] = DriveLetter;
  468. squiggleLsDir[0] = DriveLetter;
  469. bootIni[0] = DriveLetter;
  470. drvLtr[0] = DriveLetter;
  471. setupLdr[0] = DriveLetter;
  472. ntLdr[0] = DriveLetter;
  473. ntDetectCom[0] = DriveLetter;
  474. bootFontBin[0] = DriveLetter;
  475. txtSetupSif[0] = DriveLetter;
  476. bootSectDos[0] = DriveLetter;
  477. renamedFile1[0] = DriveLetter;
  478. renamedFile2[0] = DriveLetter;
  479. renamedFile3[0] = DriveLetter;
  480. SetFileAttributes (drvLtr, FILE_ATTRIBUTE_NORMAL);
  481. DeleteFile (drvLtr);
  482. SetFileAttributes (setupLdr, FILE_ATTRIBUTE_NORMAL);
  483. DeleteFile (setupLdr);
  484. SetFileAttributes (txtSetupSif, FILE_ATTRIBUTE_NORMAL);
  485. DeleteFile (txtSetupSif);
  486. //
  487. // If this is the boot drive, and if we have a bootsect.bak and
  488. // boot.bak in the setup temp directory, then that means Win9x had
  489. // an initial boot.ini, and we must restore it. Otherwise there
  490. // was no boot.ini.
  491. //
  492. if (!dontTouchBootCode && DriveLetter == bootDriveLetter) {
  493. DEBUGMSG ((DBG_MIGISOL, "Processing boot drive %c", bootDriveLetter));
  494. //
  495. // test for existence of bootini.bak/bootsect.bak (we don't
  496. // care about the attributes).
  497. //
  498. Attribs = GetFileAttributes (bootIniBak);
  499. DEBUGMSG ((DBG_MIGISOL, "Attributes of %s: 0x%08X", bootIniBak, Attribs));
  500. if (Attribs != INVALID_ATTRIBUTES) {
  501. DEBUGMSG ((DBG_MIGISOL, "Attributes of %s: 0x%08X", bootSectBak, Attribs));
  502. Attribs = GetFileAttributes (bootSectBak);
  503. }
  504. //
  505. // if pair exists, then get attributes of real boot.ini file
  506. //
  507. if (Attribs != INVALID_ATTRIBUTES) {
  508. Attribs = GetFileAttributes (bootIni);
  509. if (Attribs == INVALID_ATTRIBUTES) {
  510. Attribs = FILE_ATTRIBUTE_RHS;
  511. }
  512. //
  513. // Restore ntdetect.com, ntldr, boot sector, and original
  514. // boot.ini.
  515. //
  516. DEBUGMSG ((DBG_MIGISOL, "Restoring dual-boot environment"));
  517. if (pWriteFATBootSector (bootSectBak, bootDriveLetter)) {
  518. SetFileAttributes (bootIni, FILE_ATTRIBUTE_NORMAL);
  519. CopyFile (bootIniBak, bootIni, FALSE); // ignore failure
  520. SetFileAttributes (bootIni, Attribs);
  521. //
  522. // Restore ntldr and ntdetect.com [as a pair]
  523. //
  524. if (DoesFileExist (ntldrBak) && DoesFileExist (ntdetectBak)) {
  525. //
  526. // wipe away collisions with our temp file names,
  527. // then move current working loader to temp files
  528. //
  529. if (DoesFileExist (ntLdr)) {
  530. SetFileAttributes (renamedFile1, FILE_ATTRIBUTE_NORMAL);
  531. DeleteFile (renamedFile1);
  532. MoveFile (ntLdr, renamedFile1);
  533. noLdr = FALSE;
  534. } else {
  535. noLdr = TRUE;
  536. }
  537. if (DoesFileExist (ntDetectCom)) {
  538. SetFileAttributes (renamedFile2, FILE_ATTRIBUTE_NORMAL);
  539. DeleteFile (renamedFile2);
  540. MoveFile (ntDetectCom, renamedFile2);
  541. noNtDetect = FALSE;
  542. } else {
  543. noNtDetect = TRUE;
  544. }
  545. if (DoesFileExist (bootFontBin)) {
  546. SetFileAttributes (renamedFile3, FILE_ATTRIBUTE_NORMAL);
  547. DeleteFile (renamedFile3);
  548. MoveFile (bootFontBin, renamedFile3);
  549. noBootFontBin = FALSE;
  550. } else {
  551. noBootFontBin = TRUE;
  552. }
  553. //
  554. // now attempt to copy backup files to loader location
  555. //
  556. bootLoaderWritten = FALSE;
  557. if (CopyFile (ntldrBak, ntLdr, FALSE)) {
  558. bootLoaderWritten = CopyFile (ntdetectBak, ntDetectCom, FALSE);
  559. DEBUGMSG_IF ((!bootLoaderWritten, DBG_ERROR, "Can't copy %s to %s", ntdetectBak, ntDetectCom));
  560. if (bootLoaderWritten && DoesFileExist (bootFontBak)) {
  561. bootLoaderWritten = CopyFile (bootFontBak, bootFontBin, FALSE);
  562. DEBUGMSG_IF ((!bootLoaderWritten, DBG_ERROR, "Can't copy %s to %s", bootFontBak, bootFontBin));
  563. }
  564. }
  565. ELSE_DEBUGMSG ((DBG_ERROR, "Can't copy %s to %s", ntldrBak, ntLdr));
  566. if (bootLoaderWritten) {
  567. //
  568. // success -- remove temp files
  569. //
  570. SetFileAttributes (renamedFile1, FILE_ATTRIBUTE_NORMAL);
  571. DeleteFile (renamedFile1);
  572. SetFileAttributes (renamedFile2, FILE_ATTRIBUTE_NORMAL);
  573. DeleteFile (renamedFile2);
  574. SetFileAttributes (renamedFile3, FILE_ATTRIBUTE_NORMAL);
  575. DeleteFile (renamedFile3);
  576. } else {
  577. //
  578. // fail -- restore temp files. If restoration
  579. // fails, then generate a working boot.ini.
  580. //
  581. SetFileAttributes (ntLdr, FILE_ATTRIBUTE_NORMAL);
  582. DeleteFile (ntLdr);
  583. SetFileAttributes (ntDetectCom, FILE_ATTRIBUTE_NORMAL);
  584. DeleteFile (ntDetectCom);
  585. SetFileAttributes (bootFontBin, FILE_ATTRIBUTE_NORMAL);
  586. DeleteFile (bootFontBin);
  587. if (!noLdr) {
  588. if (!MoveFile (renamedFile1, ntLdr)) {
  589. prepareBootIni = TRUE;
  590. DEBUGMSG ((DBG_ERROR, "Can't restore %s to %s", renamedFile1, ntLdr));
  591. }
  592. }
  593. if (!noNtDetect) {
  594. if (!MoveFile (renamedFile2, ntDetectCom)) {
  595. prepareBootIni = TRUE;
  596. DEBUGMSG ((DBG_ERROR, "Can't restore %s to %s", renamedFile2, ntDetectCom));
  597. }
  598. }
  599. if (!noBootFontBin) {
  600. if (!MoveFile (renamedFile3, bootFontBin)) {
  601. prepareBootIni = TRUE;
  602. DEBUGMSG ((DBG_ERROR, "Can't restore %s to %s", renamedFile3, bootFontBin));
  603. }
  604. }
  605. }
  606. }
  607. } else {
  608. LOG ((LOG_WARNING, "Unable to restore dual-boot loader"));
  609. }
  610. } else {
  611. //
  612. // Remove the NT boot code. Delete ntdetect.com,
  613. // bootfont.bin and ntldr. If any part of this code fails,
  614. // make a boot.ini that will work. (ntdetect.com won't
  615. // be needed.)
  616. //
  617. SetFileAttributes (ntDetectCom, FILE_ATTRIBUTE_NORMAL);
  618. DeleteFile (ntDetectCom);
  619. Attribs = GetFileAttributes (bootIni);
  620. if (Attribs != INVALID_ATTRIBUTES) {
  621. SetFileAttributes (bootIni, FILE_ATTRIBUTE_NORMAL);
  622. prepareBootIni = TRUE;
  623. //
  624. // SystemDrive is not only C: on NEC98. Also, boot.ini
  625. // should be always sitting on system drive but boot
  626. // drive during setup, if these are separated.
  627. // So we must take care the boot files on system drive.
  628. //
  629. if (GetFileAttributes (bootSectBak) != INVALID_ATTRIBUTES) {
  630. bootSectorFile = bootSectBak;
  631. } else {
  632. bootSectorFile = bootSectDos;
  633. }
  634. if (pWriteFATBootSector (bootSectorFile, bootDriveLetter)) {
  635. DEBUGMSG ((DBG_MIGISOL, "Successfully restored FAT boot sector"));
  636. //
  637. // restored original boot sector, NT boot files not required any longer
  638. //
  639. DeleteFile (bootIni);
  640. SetFileAttributes (ntLdr, FILE_ATTRIBUTE_NORMAL);
  641. DeleteFile (ntLdr);
  642. SetFileAttributes (bootSectDos, FILE_ATTRIBUTE_NORMAL);
  643. DeleteFile (bootSectDos);
  644. SetFileAttributes (ntDetectCom, FILE_ATTRIBUTE_NORMAL);
  645. DeleteFile (ntDetectCom);
  646. SetFileAttributes (bootFontBin, FILE_ATTRIBUTE_NORMAL);
  647. DeleteFile (bootFontBin);
  648. prepareBootIni = FALSE;
  649. } else {
  650. //
  651. // make sure this boot file is not accidentally
  652. // deleted by the end-user
  653. //
  654. SetFileAttributes (ntLdr, FILE_ATTRIBUTE_RHS);
  655. DEBUGMSG ((DBG_ERROR, "Cannot restore FAT boot sector from %s", bootSectDos));
  656. }
  657. }
  658. ELSE_DEBUGMSG ((DBG_MIGISOL, "Skipping removal of boot.ini because it is not present"));
  659. }
  660. //
  661. // If we have any failure, this code here will make a boot
  662. // sector & loader that at least boots Win9x.
  663. //
  664. if (prepareBootIni) {
  665. bootSectDat = JoinPaths (squiggleBtDir, TEXT("BOOTSECT.DAT"));
  666. WritePrivateProfileString (TEXT("Boot Loader"), TEXT("Default"), Root, bootIni);
  667. WritePrivateProfileString (TEXT("Operating Systems"), bootSectDat, NULL, bootIni);
  668. GetPrivateProfileString (TEXT("Operating Systems"), Root, TEXT(""), Scratch, MAX_PATH, bootIni);
  669. if (!Scratch[0]) {
  670. //
  671. // This should never ever occur, but for unknown cases we have this
  672. //
  673. WritePrivateProfileString (TEXT("Operating Systems"), Root, TEXT("Microsoft Windows"), bootIni);
  674. }
  675. WritePrivateProfileString (NULL, NULL, NULL, bootIni);
  676. SetFileAttributes (bootIni, Attribs);
  677. prepareBootIni = FALSE;
  678. FreePathString (bootSectDat);
  679. }
  680. }
  681. }
  682. }
  683. //
  684. // Remove setup's temp dir as best we can. This leaves some junk around,
  685. // but we will fix that on the next reboot.
  686. //
  687. RemoveCompleteDirectory (setupTempDir);
  688. //
  689. // put all remaining files in wininit.ini\[rename] they will be
  690. // automatically removed at next reboot
  691. //
  692. StringCopy (Scratch, WinDir);
  693. StringCopy (AppendWack (Scratch), TEXT("wininit.ini"));
  694. //
  695. // append "manually" since using WritePrivateProfileString will just
  696. // overwrite previous setting
  697. //
  698. if (EnumFirstFile (&e, setupTempDir, NULL)) {
  699. WinInit = CreateFile (
  700. Scratch,
  701. GENERIC_WRITE,
  702. 0,
  703. NULL,
  704. OPEN_ALWAYS,
  705. FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN,
  706. NULL
  707. );
  708. if (WinInit != INVALID_HANDLE_VALUE) {
  709. StringCopyA (AnsiBuffer, "\r\n[rename]");
  710. if (WriteFile (WinInit, AnsiBuffer, _mbslen (AnsiBuffer), &Dummy, NULL)) {
  711. StringCopyA (AnsiBuffer, "\r\nNUL=");
  712. Write = GetEndOfString (AnsiBuffer);
  713. do {
  714. #ifdef UNICODE
  715. KnownSizeUnicodeToDbcs (Write, e.FullPath);
  716. #else
  717. StringCopyA (Write, e.FullPath);
  718. #endif
  719. if (!WriteFile (WinInit, AnsiBuffer, _mbslen (AnsiBuffer), &Dummy, NULL)) {
  720. break;
  721. }
  722. } while (EnumNextFile (&e));
  723. }
  724. CloseHandle (WinInit);
  725. }
  726. ELSE_DEBUGMSG ((DBG_MIGISOL, "Cannot create wininit.ini"));
  727. }
  728. ELSE_DEBUGMSG ((DBG_MIGISOL, "No files found in temp dir"));
  729. FreePathString (bootIniBak);
  730. FreePathString (ntldrBak);
  731. FreePathString (ntdetectBak);
  732. FreePathString (bootSectBak);
  733. FreePathString (bootFontBak);
  734. DEBUGMSG ((DBG_MIGISOL, "Leaving cleanup routine"));
  735. }
  736. BOOL
  737. pParseCommandLine (
  738. VOID
  739. )
  740. /*++
  741. Routine Description:
  742. Prepares the global variables g_hLibrary, g_ReportPhase, g_MigrationPhase,
  743. g_DllName and the migration DLL entry points (Initialize9x, etc.)
  744. Arguments:
  745. none
  746. Return Value:
  747. TRUE if the module was successfully loaded, or FALSE if a parsing
  748. error or load error occurred.
  749. --*/
  750. {
  751. PCTSTR CmdLine;
  752. PCTSTR *argv;
  753. INT argc;
  754. INT i;
  755. PCTSTR p;
  756. TCHAR drive;
  757. CmdLine = GetCommandLine();
  758. argv = CommandLineToArgv (CmdLine, &argc);
  759. if (!argv) {
  760. DEBUGMSG ((DBG_MIGISOL, "Parse error"));
  761. return FALSE;
  762. }
  763. //
  764. // Parse command line
  765. //
  766. for (i = 1 ; i < argc ; i++) {
  767. if (argv[i][0] == TEXT('-') || argv[i][0] == TEXT('/')) {
  768. p = _tcsinc (argv[i]);
  769. switch (_totlower (_tcsnextc (p))) {
  770. case 'r':
  771. // Report-phase
  772. g_ReportPhase = TRUE;
  773. break;
  774. case 'm':
  775. // Migration-phase
  776. g_MigrationPhase = TRUE;
  777. break;
  778. case 'b':
  779. drive = '\0';
  780. p = _tcsinc(p);
  781. if(p && ':' == _tcsnextc(p)){
  782. p = _tcsinc(p);
  783. if(p){
  784. drive = (TCHAR)_tcsnextc(p);
  785. }
  786. }
  787. pCleanUpUndoDirectory(drive);
  788. case 'c':
  789. // Restore Win9x
  790. pCleanUpAfterTextModeFailure();
  791. return FALSE;
  792. }
  793. }
  794. else if (!g_DllName[0]) {
  795. StringCopy (g_DllName, argv[i]);
  796. } else {
  797. DEBUGMSG ((DBG_MIGISOL, "Broken arg: %s", argv[i]));
  798. return FALSE;
  799. }
  800. }
  801. //
  802. // Verify expected options exist
  803. //
  804. // One must be FALSE while the other must be TRUE
  805. if (g_ReportPhase == g_MigrationPhase) {
  806. DEBUGMSG ((DBG_MIGISOL, "Too many args"));
  807. return FALSE;
  808. }
  809. if (!g_DllName[0]) {
  810. DEBUGMSG ((DBG_MIGISOL, "No operation"));
  811. return FALSE;
  812. }
  813. //
  814. // Load migration DLL
  815. //
  816. g_hLibrary = LoadLibraryEx (
  817. g_DllName,
  818. NULL,
  819. LOAD_WITH_ALTERED_SEARCH_PATH
  820. );
  821. // If it fails, assume the DLL does not want to be loaded
  822. if (!g_hLibrary) {
  823. LOG ((LOG_ERROR, "Cannot load %s, rc=%u", g_DllName, GetLastError()));
  824. return FALSE;
  825. }
  826. // Get proc addresses for NT-side functions
  827. InitializeNT = (P_INITIALIZE_NT) GetProcAddress (g_hLibrary, PLUGIN_INITIALIZE_NT);
  828. MigrateUserNT = (P_MIGRATE_USER_NT) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_USER_NT);
  829. MigrateSystemNT = (P_MIGRATE_SYSTEM_NT) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_SYSTEM_NT);
  830. // Get proc addresses for 9x-side functions
  831. QueryVersion = (P_QUERY_VERSION) GetProcAddress (g_hLibrary, PLUGIN_QUERY_VERSION);
  832. Initialize9x = (P_INITIALIZE_9X) GetProcAddress (g_hLibrary, PLUGIN_INITIALIZE_9X);
  833. MigrateUser9x = (P_MIGRATE_USER_9X) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_USER_9X);
  834. MigrateSystem9x = (P_MIGRATE_SYSTEM_9X) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_SYSTEM_9X);
  835. // If any function does not exist, ignore the out-of-spec DLL
  836. if (!QueryVersion || !Initialize9x || !MigrateUser9x || !MigrateSystem9x ||
  837. !InitializeNT || !MigrateUserNT || !MigrateSystemNT
  838. ) {
  839. LOG ((LOG_ERROR, "Cannot load %s, one or more functions missing", g_DllName));
  840. return FALSE;
  841. }
  842. return TRUE;
  843. }
  844. VOID
  845. ProcessCommands (
  846. VOID
  847. )
  848. /*++
  849. Routine Description:
  850. ProcessCommands waits on the IPC pipe for a command message. When
  851. a command message is received, it is dispatched to the processing
  852. function. If a terminate command is received, the EXE terminates.
  853. If no command is received in one second, the EXE terminates. Therefore,
  854. Setup must always be ready to feed the EXE commands.
  855. Arguments:
  856. none
  857. Return Value:
  858. none
  859. --*/
  860. {
  861. DWORD Command;
  862. PBYTE Data;
  863. DWORD DataSize;
  864. DEBUGMSG ((DBG_MIGISOL, "Starting to process %s", g_DllName));
  865. do {
  866. // We wait for an interval: w95upgnt.dll or w95upg.dll should be ready
  867. // to feed us continuously.
  868. //
  869. // Receive command, don't care about size, OK to fail.
  870. //
  871. if (!GetIpcCommand (
  872. IPC_GET_COMMAND_WIN9X,
  873. &Command,
  874. &Data,
  875. &DataSize
  876. )) {
  877. DEBUGMSG ((DBG_WARNING, "MIGISOL: No command recieved"));
  878. break;
  879. }
  880. DEBUGMSG ((DBG_NAUSEA, "MigIsol - Command recieved: %u", Command));
  881. switch (Command) {
  882. case IPC_QUERY:
  883. if (g_MigrationPhase) {
  884. } else {
  885. DoQueryVersion ((PCSTR) Data);
  886. }
  887. break;
  888. case IPC_INITIALIZE:
  889. if (g_MigrationPhase) {
  890. DoInitializeNT ((PCWSTR) Data);
  891. } else {
  892. DoInitialize9x ((PCSTR) Data);
  893. }
  894. break;
  895. case IPC_MIGRATEUSER:
  896. if (g_MigrationPhase) {
  897. DoMigrateUserNT ((PCWSTR) Data);
  898. } else {
  899. DoMigrateUser9x ((PCSTR) Data);
  900. }
  901. break;
  902. case IPC_MIGRATESYSTEM:
  903. if (g_MigrationPhase) {
  904. DoMigrateSystemNT ((PCWSTR) Data);
  905. } else {
  906. DoMigrateSystem9x ((PCSTR) Data);
  907. }
  908. break;
  909. case IPC_TERMINATE:
  910. DEBUGMSG ((DBG_MIGISOL, "Processing of %s is complete", g_DllName));
  911. return;
  912. default:
  913. DEBUGMSG ((DBG_MIGISOL, "ProcessCommands: Unrecognized command -- terminating"));
  914. return;
  915. }
  916. if (Data) {
  917. MemFree (g_hHeap, 0, Data);
  918. Data = NULL;
  919. }
  920. } while (Command != IPC_TERMINATE);
  921. }
  922. VOID
  923. DoInitializeNT (
  924. PCWSTR Args
  925. )
  926. /*++
  927. Routine Description:
  928. Calls migration DLL's InitializeNT function. This function unpacks
  929. the arguments passed by Setup, calls the migration DLL and returns
  930. the status code back to Setup.
  931. Arguments:
  932. Args - A pointer to the argument buffer sent by Setup. This buffer
  933. is received with the initialize command.
  934. Return Value:
  935. none
  936. --*/
  937. {
  938. PCWSTR WorkingDir = NULL;
  939. PCWSTR SourceDirs = NULL;
  940. PCWSTR EndOfSourceDirs;
  941. PDWORD ReservedSize;
  942. PVOID Reserved;
  943. DWORD rc = RPC_S_CALL_FAILED;
  944. DWORD TechnicalLogId = 0;
  945. DWORD GuiLogId = 0;
  946. //
  947. // Set pointers of IN parameters
  948. //
  949. WorkingDir = Args;
  950. SourceDirs = wcschr (Args, 0) + 1;
  951. EndOfSourceDirs = SourceDirs;
  952. while (*EndOfSourceDirs) {
  953. EndOfSourceDirs = wcschr (EndOfSourceDirs, 0);
  954. EndOfSourceDirs++;
  955. }
  956. ReservedSize = (PDWORD) (EndOfSourceDirs + 1);
  957. if (*ReservedSize) {
  958. Reserved = (PVOID) (ReservedSize + 1);
  959. } else {
  960. Reserved = NULL;
  961. }
  962. //
  963. // Set CWD
  964. //
  965. SetCurrentDirectoryW(WorkingDir);
  966. //
  967. // Call migration DLL function
  968. //
  969. __try {
  970. rc = InitializeNT (WorkingDir, SourceDirs, Reserved);
  971. }
  972. __except (TRUE) {
  973. // Send log message
  974. DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in InitializeNT", g_DllName));
  975. rc = ERROR_NOACCESS;
  976. TechnicalLogId = MSG_EXCEPTION_MIGRATE_INIT_NT;
  977. }
  978. //
  979. // No OUT parameters to send
  980. //
  981. SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0);
  982. }
  983. HINF
  984. pGetInfHandleFromFileNameW (
  985. PCWSTR UnattendFile
  986. )
  987. /*++
  988. Routine Description:
  989. pGetInfHandleFromFileName uses the Setup APIs to open the specified
  990. UnattendFile.
  991. Arguments:
  992. UnattendFile - A pointer to the UNICODE file name specifying the unattend
  993. file. This string is converted to ANSI and the ANSI version
  994. of SetupOpenInfFile is called.
  995. Return Value:
  996. The INF handle, or NULL (*not* INVALID_HANDLE_VALUE) if the file could not
  997. be opened.
  998. --*/
  999. {
  1000. CHAR AnsiUnattendFile[MAX_MBCHAR_PATH];
  1001. HINF UnattendHandle;
  1002. KnownSizeWtoA (AnsiUnattendFile, UnattendFile);
  1003. UnattendHandle = SetupOpenInfFileA (AnsiUnattendFile, NULL, INF_STYLE_OLDNT|INF_STYLE_WIN4, NULL);
  1004. if (UnattendHandle == INVALID_HANDLE_VALUE) {
  1005. DEBUGMSG ((DBG_ERROR, "pGetInfHandleFromFileNameW: Could not open %s", UnattendFile));
  1006. UnattendHandle = NULL;
  1007. }
  1008. return UnattendHandle;
  1009. }
  1010. VOID
  1011. DoMigrateUserNT (
  1012. PCWSTR Args
  1013. )
  1014. /*++
  1015. Routine Description:
  1016. Calls migration DLL's MigrateUserNT function. This function unpacks
  1017. the arguments passed by Setup, calls the migration DLL and returns
  1018. the status code back to Setup.
  1019. Arguments:
  1020. Args - A pointer to the argument buffer sent by Setup. This buffer
  1021. is received with the IPC_MIGRATEUSER command.
  1022. Return Value:
  1023. none
  1024. --*/
  1025. {
  1026. PCWSTR UnattendFile;
  1027. PCWSTR UserRegKey;
  1028. PCWSTR UserName;
  1029. PCWSTR UserDomain;
  1030. PCWSTR FixedUserName;
  1031. PCWSTR UserProfileDir;
  1032. WCHAR OrgProfileDir[MAX_WCHAR_PATH];
  1033. HINF UnattendHandle = NULL;
  1034. HKEY UserRegHandle = NULL;
  1035. DWORD rc;
  1036. PVOID Reserved;
  1037. PDWORD ReservedBytesPtr;
  1038. DWORD TechnicalLogId = 0;
  1039. DWORD GuiLogId = 0;
  1040. __try {
  1041. //
  1042. // Preserve USERPROFILE environment variable
  1043. //
  1044. GetEnvironmentVariableW (S_USERPROFILEW, OrgProfileDir, MAX_WCHAR_PATH);
  1045. //
  1046. // Set pointers of IN parameters
  1047. //
  1048. UnattendFile = Args;
  1049. UserRegKey = wcschr (UnattendFile, 0) + 1;
  1050. UserName = wcschr (UserRegKey, 0) + 1;
  1051. UserDomain = wcschr (UserName, 0) + 1;
  1052. FixedUserName = wcschr (UserDomain, 0) + 1;
  1053. UserProfileDir = wcschr (FixedUserName, 0) + 1;
  1054. ReservedBytesPtr = (PDWORD) (wcschr (UserProfileDir, 0) + 1);
  1055. if (*ReservedBytesPtr) {
  1056. Reserved = (PVOID) (ReservedBytesPtr + 1);
  1057. } else {
  1058. Reserved = NULL;
  1059. }
  1060. //
  1061. // Set USERPROFILE
  1062. //
  1063. if (UserProfileDir[0]) {
  1064. WCHAR DebugDir[MAX_WCHAR_PATH];
  1065. SetEnvironmentVariableW (S_USERPROFILEW, UserProfileDir);
  1066. DEBUGMSG ((DBG_MIGISOL, "USERPROFILE set to %ls", UserProfileDir));
  1067. GetEnvironmentVariableW (S_USERPROFILEW, DebugDir, MAX_WCHAR_PATH);
  1068. DEBUGMSG ((DBG_MIGISOL, "USERPROFILE set to %ls", DebugDir));
  1069. }
  1070. //
  1071. // Get UnattendHandle and UserRegHandle
  1072. //
  1073. UnattendHandle = pGetInfHandleFromFileNameW (UnattendFile);
  1074. UserRegHandle = OpenRegKeyStrW (UserRegKey);
  1075. if (!UnattendHandle || !UserRegHandle) {
  1076. // Send log message and failure code
  1077. rc = ERROR_OPEN_FAILED;
  1078. } else {
  1079. //
  1080. // Call migration DLL function
  1081. //
  1082. __try {
  1083. rc = MigrateUserNT (
  1084. UnattendHandle,
  1085. UserRegHandle,
  1086. UserName[0] ? UserName : NULL,
  1087. Reserved
  1088. );
  1089. }
  1090. __except (TRUE) {
  1091. // Send log message and failure code
  1092. DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in MigrateUserNT", g_DllName));
  1093. rc = ERROR_NOACCESS;
  1094. TechnicalLogId = MSG_EXCEPTION_MIGRATE_USER_NT;
  1095. }
  1096. }
  1097. //
  1098. // No OUT parameters to send
  1099. //
  1100. if (UserRegHandle) {
  1101. CloseRegKey (UserRegHandle);
  1102. UserRegHandle = NULL;
  1103. }
  1104. SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0);
  1105. }
  1106. __finally {
  1107. //
  1108. // Clean up
  1109. //
  1110. SetEnvironmentVariableW (S_USERPROFILEW, OrgProfileDir);
  1111. if (UserRegHandle) {
  1112. CloseRegKey (UserRegHandle);
  1113. }
  1114. if (UnattendHandle != INVALID_HANDLE_VALUE) {
  1115. SetupCloseInfFile (UnattendHandle);
  1116. }
  1117. }
  1118. }
  1119. VOID
  1120. DoMigrateSystemNT (
  1121. PCWSTR Args
  1122. )
  1123. /*++
  1124. Routine Description:
  1125. Calls migration DLL's MigrateSystemNT function. This function unpacks
  1126. the arguments passed by Setup, calls the migration DLL and returns
  1127. the status code back to Setup.
  1128. Arguments:
  1129. Args - A pointer to the argument buffer sent by Setup. This buffer
  1130. is received with the IPC_MIGRATESYSTEM command.
  1131. Return Value:
  1132. none
  1133. --*/
  1134. {
  1135. PCWSTR UnattendFile;
  1136. HINF UnattendHandle = NULL;
  1137. DWORD rc;
  1138. PVOID Reserved;
  1139. PDWORD ReservedBytesPtr;
  1140. DWORD TechnicalLogId = 0;
  1141. DWORD GuiLogId = 0;
  1142. __try {
  1143. //
  1144. // Set pointers of IN parameters
  1145. //
  1146. UnattendFile = Args;
  1147. ReservedBytesPtr = (PDWORD) (wcschr (UnattendFile, 0) + 1);
  1148. if (*ReservedBytesPtr) {
  1149. Reserved = (PVOID) (ReservedBytesPtr + 1);
  1150. } else {
  1151. Reserved = NULL;
  1152. }
  1153. //
  1154. // Get UnattendHandle and UserRegHandle
  1155. //
  1156. UnattendHandle = pGetInfHandleFromFileNameW (UnattendFile);
  1157. if (!UnattendHandle) {
  1158. rc = ERROR_OPEN_FAILED;
  1159. } else {
  1160. //
  1161. // Call migration DLL function
  1162. //
  1163. __try {
  1164. rc = MigrateSystemNT (UnattendHandle, Reserved);
  1165. }
  1166. __except (TRUE) {
  1167. DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in MigrateSystemNT", g_DllName));
  1168. rc = ERROR_NOACCESS;
  1169. TechnicalLogId = MSG_EXCEPTION_MIGRATE_SYSTEM_NT;
  1170. }
  1171. }
  1172. //
  1173. // No OUT parameters to send
  1174. //
  1175. SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0);
  1176. }
  1177. __finally {
  1178. if (UnattendHandle != INVALID_HANDLE_VALUE) {
  1179. SetupCloseInfFile (UnattendHandle);
  1180. }
  1181. }
  1182. }
  1183. VOID
  1184. DoQueryVersion (
  1185. PCSTR Args
  1186. )
  1187. /*++
  1188. Routine Description:
  1189. Calls migration DLL's QueryVersion function. This function unpacks
  1190. the arguments passed by Setup, calls the migration DLL and returns
  1191. the status code back to Setup.
  1192. Arguments:
  1193. Args - A pointer to the argument buffer sent by Setup. This buffer
  1194. is received with the IPC_QUERY command.
  1195. Return Value:
  1196. none
  1197. --*/
  1198. {
  1199. DWORD rc = RPC_S_CALL_FAILED;
  1200. GROWBUFFER GrowBuf = GROWBUF_INIT;
  1201. PSTR ProductId = NULL;
  1202. UINT DllVersion = 0;
  1203. PDWORD CodePageArray = NULL;
  1204. PCSTR ExeNames = NULL;
  1205. PCSTR WorkingDir;
  1206. PVENDORINFO VendorInfo = NULL;
  1207. DWORD TechnicalLogId = 0;
  1208. DWORD GuiLogId = 0;
  1209. DEBUGMSG ((DBG_MIGISOL, "Entering DoQueryVersion"));
  1210. __try {
  1211. //
  1212. // Set pointers of IN parameters
  1213. //
  1214. WorkingDir = (PSTR)Args; // CWD for this process
  1215. //
  1216. // Change CWD
  1217. //
  1218. SetCurrentDirectory(WorkingDir);
  1219. //
  1220. // Call migration DLL function
  1221. //
  1222. __try {
  1223. DEBUGMSG ((DBG_MIGISOL, "QueryVersion: WorkingDir=%s", WorkingDir));
  1224. rc = QueryVersion (
  1225. &ProductId,
  1226. &DllVersion,
  1227. &CodePageArray,
  1228. &ExeNames,
  1229. &VendorInfo
  1230. );
  1231. DEBUGMSG ((DBG_MIGISOL, "QueryVersion rc=%u", rc));
  1232. DEBUGMSG ((DBG_MIGISOL, "VendorInfo=0x%X", VendorInfo));
  1233. }
  1234. __except (TRUE) {
  1235. DEBUGMSG ((
  1236. DBG_MIGISOL,
  1237. "%s threw an exception in QueryVersion",
  1238. g_DllName
  1239. ));
  1240. TechnicalLogId = MSG_MIGDLL_QUERYVERSION_EXCEPTION_LOG;
  1241. rc = ERROR_NOACCESS;
  1242. }
  1243. //
  1244. // Unless we know failure occurred, return out parameters
  1245. //
  1246. if (rc == ERROR_SUCCESS) {
  1247. //
  1248. // Pack product id string
  1249. //
  1250. if (!PackString (&GrowBuf, ProductId)) {
  1251. DEBUGMSG ((DBG_MIGISOL, "QueryVersion PackProductId failed"));
  1252. rc = GetLastError();
  1253. __leave;
  1254. }
  1255. //
  1256. // Pack DLL version
  1257. //
  1258. if (!PackDword(&GrowBuf, DllVersion)) {
  1259. rc = GetLastError();
  1260. DEBUGMSG ((DBG_MIGISOL, "QueryVersion DllVersion failed"));
  1261. __leave;
  1262. }
  1263. //
  1264. // Pack CP array
  1265. //
  1266. if (!PackIntArray(&GrowBuf, CodePageArray)) {
  1267. rc = GetLastError();
  1268. DEBUGMSG ((DBG_MIGISOL, "QueryVersion PackIntArray failed"));
  1269. __leave;
  1270. }
  1271. //
  1272. // Pack Exe Names
  1273. //
  1274. if (!PackExeNames(&GrowBuf, ExeNames)) {
  1275. rc = GetLastError();
  1276. DEBUGMSG ((DBG_MIGISOL, "QueryVersion PackExeNames failed"));
  1277. __leave;
  1278. }
  1279. //
  1280. // Pack PVENDORINFO
  1281. //
  1282. if (!PackDword(&GrowBuf, (DWORD) VendorInfo)) {
  1283. rc = GetLastError();
  1284. DEBUGMSG ((DBG_MIGISOL, "QueryVersion VendorInfo failed"));
  1285. __leave;
  1286. }
  1287. if (VendorInfo) {
  1288. if (!PackBinary (&GrowBuf, (PBYTE) VendorInfo, sizeof (VENDORINFO))) {
  1289. rc = GetLastError();
  1290. DEBUGMSG ((DBG_MIGISOL, "QueryVersion VendorInfo failed"));
  1291. __leave;
  1292. }
  1293. }
  1294. }
  1295. //
  1296. // Send the packed parameters
  1297. //
  1298. if (!SendIpcCommandResults (
  1299. rc,
  1300. TechnicalLogId,
  1301. GuiLogId,
  1302. GrowBuf.End ? GrowBuf.Buf : NULL,
  1303. GrowBuf.End
  1304. )) {
  1305. DEBUGMSG ((
  1306. DBG_ERROR,
  1307. "DoQueryVersion failed to send command response"
  1308. ));
  1309. LOG ((LOG_ERROR, "Upgrade Pack process could not send reply data"));
  1310. }
  1311. }
  1312. __finally {
  1313. FreeGrowBuffer(&GrowBuf);
  1314. }
  1315. DEBUGMSG ((DBG_MIGISOL, "Leaving DoQueryVersion, rc=%u", rc));
  1316. }
  1317. VOID
  1318. DoInitialize9x (
  1319. PCSTR Args
  1320. )
  1321. /*++
  1322. Routine Description:
  1323. Calls migration DLL's Initialize9x function. This function unpacks
  1324. the arguments passed by Setup, calls the migration DLL and returns
  1325. the status code back to Setup.
  1326. Arguments:
  1327. Args - A pointer to the argument buffer sent by Setup. This buffer
  1328. is received with the IPC_INITIALIZE command.
  1329. Return Value:
  1330. none
  1331. --*/
  1332. {
  1333. DWORD rc = RPC_S_CALL_FAILED;
  1334. PSTR WorkingDir = NULL;
  1335. PSTR SourceDirs = NULL;
  1336. PVOID Reserved;
  1337. DWORD ReservedSize;
  1338. PCSTR p;
  1339. DWORD TechnicalLogId = 0;
  1340. DWORD GuiLogId = 0;
  1341. GROWBUFFER GrowBuf = GROWBUF_INIT;
  1342. DEBUGMSG ((DBG_MIGISOL, "Entering DoInitialize9x"));
  1343. __try {
  1344. //
  1345. // Set pointers of IN parameters
  1346. //
  1347. WorkingDir = (PSTR)Args; // CWD for this process
  1348. SourceDirs = GetEndOfStringA (WorkingDir) + 1; // arg for Initialize9x
  1349. p = SourceDirs;
  1350. while (*p) {
  1351. p = GetEndOfStringA (p);
  1352. p++;
  1353. }
  1354. p++;
  1355. ReservedSize = *((PDWORD) p);
  1356. p = (PCSTR) ((PBYTE) p + sizeof (DWORD));
  1357. if (ReservedSize) {
  1358. Reserved = (PVOID) p;
  1359. p = (PCSTR) ((PBYTE) p + ReservedSize);
  1360. } else {
  1361. Reserved = NULL;
  1362. }
  1363. //
  1364. // Change CWD
  1365. //
  1366. SetCurrentDirectory(WorkingDir);
  1367. //
  1368. // Call migration DLL function
  1369. //
  1370. __try {
  1371. rc = Initialize9x (
  1372. WorkingDir,
  1373. SourceDirs,
  1374. Reserved
  1375. );
  1376. }
  1377. __except (TRUE) {
  1378. //
  1379. // Send log message
  1380. //
  1381. DEBUGMSG ((DBG_MIGISOL,
  1382. "%s threw an exception in Initialize9x",
  1383. g_DllName));
  1384. TechnicalLogId = MSG_MIGDLL_INITIALIZE9X_EXCEPTION_LOG;
  1385. rc = ERROR_NOACCESS;
  1386. }
  1387. //
  1388. // Send reserved
  1389. //
  1390. if (rc == ERROR_SUCCESS) {
  1391. //
  1392. // Pack reserved parameter
  1393. //
  1394. // Set ReservedSize to zero for now because the Reserved arg is an IN only
  1395. ReservedSize = 0;
  1396. if (!PackBinary (&GrowBuf, (PBYTE) Reserved, ReservedSize)) {
  1397. rc = GetLastError();
  1398. DEBUGMSG ((DBG_MIGISOL, "Initialize9x reserved failed"));
  1399. __leave;
  1400. }
  1401. }
  1402. //
  1403. // Send the packed parameters
  1404. //
  1405. if (!SendIpcCommandResults (
  1406. rc,
  1407. TechnicalLogId,
  1408. GuiLogId,
  1409. GrowBuf.End ? GrowBuf.Buf : NULL,
  1410. GrowBuf.End
  1411. )) {
  1412. DEBUGMSG ((
  1413. DBG_ERROR,
  1414. "DoInitializeNT failed to send command response"
  1415. ));
  1416. LOG ((LOG_ERROR, "Upgrade Pack process could not send reply data"));
  1417. }
  1418. }
  1419. __finally {
  1420. FreeGrowBuffer (&GrowBuf);
  1421. }
  1422. DEBUGMSG ((DBG_MIGISOL, "Leaving DoInitialize9x, rc=%u", rc));
  1423. }
  1424. VOID
  1425. DoMigrateUser9x (
  1426. IN PCSTR Args
  1427. )
  1428. /*++
  1429. Routine Description:
  1430. Calls migration DLL's MigrateUser9x function. This function unpacks
  1431. the arguments passed by Setup, calls the migration DLL and returns
  1432. the status code back to Setup.
  1433. Arguments:
  1434. Args - A pointer to the argument buffer sent by Setup. This buffer
  1435. is received with the IPC_MIGRATEUSER command.
  1436. Return Value:
  1437. none
  1438. --*/
  1439. {
  1440. PCSTR ParentWndTitle = NULL;
  1441. HWND ParentWnd;
  1442. PCSTR UnattendFile = NULL;
  1443. PCSTR UserRegKey = NULL;
  1444. PCSTR UserName = NULL;
  1445. HKEY UserRegHandle = NULL;
  1446. DWORD rc = RPC_S_CALL_FAILED;
  1447. DWORD ProcessId;
  1448. DWORD GuiLogId = 0;
  1449. DWORD TechnicalLogId = 0;
  1450. DEBUGMSG ((DBG_MIGISOL, "Entering DoMigrateUser9x"));
  1451. __try {
  1452. //
  1453. // Set pointers of IN parameters
  1454. //
  1455. ParentWndTitle = Args;
  1456. UnattendFile = GetEndOfStringA (ParentWndTitle) + 1;
  1457. ProcessId = *((PDWORD) UnattendFile);
  1458. UnattendFile = (PCSTR) ((PBYTE) UnattendFile + sizeof (DWORD));
  1459. UserRegKey = GetEndOfStringA (UnattendFile) + 1;
  1460. UserName = GetEndOfStringA (UserRegKey) + 1;
  1461. //
  1462. // Get UserRegHandle
  1463. //
  1464. UserRegHandle = OpenRegKeyStr(UserRegKey);
  1465. if (!UserRegHandle) {
  1466. rc = ERROR_OPEN_FAILED;
  1467. } else {
  1468. ParentWnd = pFindParentWindow (ParentWndTitle, ProcessId);
  1469. //
  1470. // Call migration DLL function
  1471. //
  1472. __try {
  1473. rc = MigrateUser9x(
  1474. ParentWnd,
  1475. UnattendFile,
  1476. UserRegHandle,
  1477. *UserName ? UserName : NULL,
  1478. NULL
  1479. );
  1480. }
  1481. __except (TRUE) {
  1482. //
  1483. // Send log message
  1484. //
  1485. DEBUGMSG ((
  1486. DBG_MIGISOL,
  1487. "%s threw an exception in MigrateUser9x",
  1488. g_DllName
  1489. ));
  1490. TechnicalLogId = MSG_MIGDLL_MIGRATEUSER9X_EXCEPTION_LOG;
  1491. rc = ERROR_NOACCESS;
  1492. }
  1493. }
  1494. //
  1495. // No need to return out parameters
  1496. //
  1497. if (UserRegHandle) {
  1498. CloseRegKey (UserRegHandle);
  1499. UserRegHandle = NULL;
  1500. }
  1501. SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0);
  1502. }
  1503. __finally {
  1504. //
  1505. // Free resources
  1506. //
  1507. if (UserRegHandle) {
  1508. CloseRegKey (UserRegHandle);
  1509. }
  1510. }
  1511. DEBUGMSG ((DBG_MIGISOL, "Leaving MigrateUser9x , rc=%u", rc));
  1512. }
  1513. VOID
  1514. DoMigrateSystem9x(
  1515. IN PCSTR Args
  1516. )
  1517. /*++
  1518. Routine Description:
  1519. Calls migration DLL's MigrateSystem9x function. This function unpacks
  1520. the arguments passed by Setup, calls the migration DLL and returns
  1521. the status code back to Setup.
  1522. Arguments:
  1523. Args - A pointer to the argument buffer sent by Setup. This buffer
  1524. is received with the IPC_MIGRATESYSTEM command.
  1525. Return Value:
  1526. none
  1527. --*/
  1528. {
  1529. PCSTR ParentWndTitle = NULL;
  1530. DWORD ProcessId;
  1531. PCSTR UnattendFile = NULL;
  1532. HWND ParentWnd = NULL;
  1533. DWORD rc = RPC_S_CALL_FAILED;
  1534. DWORD TechnicalLogId = 0;
  1535. DWORD GuiLogId = 0;
  1536. DEBUGMSG ((DBG_MIGISOL, "Entering DoMigrateSystem9x"));
  1537. //
  1538. // Set pointers of IN parameters
  1539. //
  1540. ParentWndTitle = Args;
  1541. UnattendFile = GetEndOfStringA (ParentWndTitle) + 1;
  1542. ProcessId = *((PDWORD) UnattendFile);
  1543. UnattendFile = (PCSTR) ((PBYTE) UnattendFile + sizeof (DWORD));
  1544. //
  1545. // Get ParentWnd
  1546. //
  1547. ParentWnd = pFindParentWindow (ParentWndTitle, ProcessId);
  1548. //
  1549. // Call migration DLL function
  1550. //
  1551. __try {
  1552. rc = MigrateSystem9x(
  1553. ParentWnd,
  1554. UnattendFile,
  1555. NULL
  1556. );
  1557. }
  1558. __except (TRUE) {
  1559. //
  1560. // Send log message
  1561. //
  1562. DEBUGMSG ((
  1563. DBG_MIGISOL,
  1564. "%s threw an exception in MigrateSystem9x",
  1565. g_DllName
  1566. ));
  1567. TechnicalLogId = MSG_MIGDLL_MIGRATESYSTEM9X_EXCEPTION_LOG;
  1568. rc = ERROR_NOACCESS;
  1569. }
  1570. //
  1571. // No need to return out parameters
  1572. //
  1573. SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0);
  1574. DEBUGMSG ((DBG_MIGISOL, "Leaving DoMigrateSystem9x, rc=%u", rc));
  1575. }
  1576. //
  1577. // Function packs a DWORD into a GrowBuffer.
  1578. BOOL
  1579. PackDword(
  1580. PGROWBUFFER GrowBuf,
  1581. DWORD dw
  1582. )
  1583. {
  1584. PVOID p;
  1585. p = GrowBuffer (GrowBuf, sizeof(DWORD));
  1586. if (!p) {
  1587. return FALSE;
  1588. }
  1589. CopyMemory (p, (PVOID)(&dw), sizeof(dw));
  1590. return TRUE;
  1591. }
  1592. //
  1593. // Function packs a LONGLONG into a GrowBuffer
  1594. BOOL
  1595. PackQuadWord(
  1596. PGROWBUFFER GrowBuf,
  1597. LONGLONG qw)
  1598. {
  1599. return (
  1600. PackDword(GrowBuf, (DWORD)qw) &&
  1601. PackDword(GrowBuf, (DWORD)(qw >> 32)));
  1602. }
  1603. //
  1604. // Function packs 1) a NULL ptr, or 2) array of int terminated by -1, into a
  1605. // GrowBuffer.
  1606. //
  1607. BOOL
  1608. PackIntArray(
  1609. PGROWBUFFER GrowBuf,
  1610. PINT Array
  1611. )
  1612. {
  1613. DWORD Count;
  1614. PDWORD ArrayPos;
  1615. if (!Array) {
  1616. if (!GrowBufAppendDword (GrowBuf, 0)) {
  1617. return FALSE;
  1618. }
  1619. } else {
  1620. __try {
  1621. Count = 1;
  1622. for (ArrayPos = Array ; (*ArrayPos) != -1 ; ArrayPos++) {
  1623. Count++;
  1624. }
  1625. }
  1626. __except (TRUE) {
  1627. LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid code page array", g_DllName));
  1628. SetLastError (ERROR_NOACCESS);
  1629. return FALSE;
  1630. }
  1631. if (!GrowBufAppendDword (GrowBuf, Count)) {
  1632. return FALSE;
  1633. }
  1634. for (ArrayPos = Array ; Count ; ArrayPos++, Count--) {
  1635. if (!GrowBufAppendDword (GrowBuf, (DWORD) (UINT) (*ArrayPos))) {
  1636. return FALSE;
  1637. }
  1638. }
  1639. }
  1640. return TRUE;
  1641. }
  1642. //
  1643. // Function packs 1) a NULL pointer, or 2) a multi-sz, into a GrowBuffer.
  1644. //
  1645. BOOL
  1646. PackExeNames(
  1647. PGROWBUFFER GrowBuf,
  1648. PCSTR ExeNames
  1649. )
  1650. {
  1651. PCSTR p;
  1652. if (ExeNames) {
  1653. __try {
  1654. for (p = ExeNames ; *p ; p = GetEndOfStringA (p) + 1) {
  1655. }
  1656. }
  1657. __except (TRUE) {
  1658. LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid file list", g_DllName));
  1659. SetLastError (ERROR_NOACCESS);
  1660. return FALSE;
  1661. }
  1662. // Append non-empty strings
  1663. for (p = ExeNames ; *p ; p = GetEndOfStringA (p) + 1) {
  1664. if (!MultiSzAppendA (GrowBuf, p)) {
  1665. return FALSE;
  1666. }
  1667. }
  1668. }
  1669. // Append a final empty string
  1670. if (!MultiSzAppendA(GrowBuf, "")) {
  1671. return FALSE;
  1672. }
  1673. return TRUE;
  1674. }
  1675. BOOL
  1676. PackString (
  1677. PGROWBUFFER GrowBuf,
  1678. PCSTR String
  1679. )
  1680. {
  1681. __try {
  1682. if (!MultiSzAppendA (GrowBuf, String)) {
  1683. return FALSE;
  1684. }
  1685. }
  1686. __except (TRUE) {
  1687. DEBUGMSG ((
  1688. DBG_ERROR,
  1689. "%s provided an invalid ProductID string (%xh)",
  1690. g_DllName,
  1691. String
  1692. ));
  1693. LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid product ID", g_DllName));
  1694. SetLastError (ERROR_NOACCESS);
  1695. return FALSE;
  1696. }
  1697. return TRUE;
  1698. }
  1699. BOOL
  1700. PackBinary (
  1701. PGROWBUFFER GrowBuf,
  1702. PBYTE Data,
  1703. DWORD DataSize
  1704. )
  1705. {
  1706. PBYTE Buf;
  1707. if (!PackDword (GrowBuf, DataSize)) {
  1708. return FALSE;
  1709. }
  1710. if (!DataSize) {
  1711. return TRUE;
  1712. }
  1713. Buf = GrowBuffer (GrowBuf, DataSize);
  1714. if (!Buf) {
  1715. return FALSE;
  1716. }
  1717. __try {
  1718. CopyMemory (Buf, Data, DataSize);
  1719. }
  1720. __except (TRUE) {
  1721. DEBUGMSG ((
  1722. DBG_ERROR,
  1723. "%s provided an invalid binary parameter (%xh)",
  1724. g_DllName,
  1725. Data
  1726. ));
  1727. LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid binary parameter", g_DllName));
  1728. SetLastError (ERROR_NOACCESS);
  1729. return FALSE;
  1730. }
  1731. return TRUE;
  1732. }
  1733. typedef struct {
  1734. PCTSTR WindowTitle;
  1735. DWORD ProcessId;
  1736. HWND Match;
  1737. } FINDWINDOW_STRUCT, *PFINDWINDOW_STRUCT;
  1738. BOOL
  1739. CALLBACK
  1740. pEnumWndProc (
  1741. HWND hwnd,
  1742. LPARAM lParam
  1743. )
  1744. /*++
  1745. Routine Description:
  1746. A callback that is called for every top level window on the system.
  1747. It is used with pFindParentWindow to locate a specific window.
  1748. Arguments:
  1749. hwnd - Specifies the handle of the current enumerated window
  1750. lParam - Specifies a pointer to a FINDWINDOW_STRUCT variable that
  1751. holds WindowTitle and ProcessId, and receives the
  1752. handle if a match is found.
  1753. Return Value:
  1754. The handle to the matching window, or NULL if no window has the
  1755. specified title and process ID.
  1756. --*/
  1757. {
  1758. TCHAR Title[MAX_TCHAR_PATH];
  1759. DWORD ProcessId;
  1760. PFINDWINDOW_STRUCT p;
  1761. p = (PFINDWINDOW_STRUCT) lParam;
  1762. GetWindowText (hwnd, Title, MAX_TCHAR_PATH);
  1763. GetWindowThreadProcessId (hwnd, &ProcessId);
  1764. DEBUGMSG ((DBG_MIGISOL, "Testing window: %s, ID=%x against %s, %x",
  1765. Title, ProcessId, p->WindowTitle, p->ProcessId));
  1766. if (!StringCompare (Title, p->WindowTitle) &&
  1767. ProcessId == p->ProcessId) {
  1768. p->Match = hwnd;
  1769. LogReInit (&hwnd, NULL);
  1770. DEBUGMSG ((DBG_MIGISOL, "Window found: %s, ID=%u", Title, ProcessId));
  1771. return FALSE;
  1772. }
  1773. return TRUE;
  1774. }
  1775. HWND
  1776. pFindParentWindow (
  1777. IN PCTSTR WindowTitle,
  1778. IN DWORD ProcessId
  1779. )
  1780. /*++
  1781. Routine Description:
  1782. Locates the wizard window by enumerating all top-level windows.
  1783. The first one to match the supplied title and process ID is used.
  1784. Arguments:
  1785. WindowTitle - Specifies the name of the window to find.
  1786. ProcessId - Specifies the ID of the process who owns the window. If
  1787. zero is specified, NULL is returned.
  1788. Return Value:
  1789. The handle to the matching window, or NULL if no window has the
  1790. specified title and process ID.
  1791. --*/
  1792. {
  1793. FINDWINDOW_STRUCT FindWndStruct;
  1794. // If no process ID, we cannot have a match
  1795. if (!ProcessId) {
  1796. return NULL;
  1797. }
  1798. ZeroMemory (&FindWndStruct, sizeof (FindWndStruct));
  1799. FindWndStruct.WindowTitle = WindowTitle;
  1800. FindWndStruct.ProcessId = ProcessId;
  1801. EnumWindows (pEnumWndProc, (LPARAM) &FindWndStruct);
  1802. return FindWndStruct.Match;
  1803. }
  1804. //
  1805. // Check platform that I'm runnig on. Copyed from winnt32[au].dll.
  1806. // TRUE - NEC98
  1807. // FALSE - others(includes x86)
  1808. //
  1809. BOOL
  1810. IsNEC98(
  1811. VOID
  1812. )
  1813. {
  1814. static BOOL Checked = FALSE;
  1815. static BOOL Is98;
  1816. if(!Checked) {
  1817. Is98 = ((GetKeyboardType(0) == 7) && ((GetKeyboardType(1) & 0xff00) == 0x0d00));
  1818. Checked = TRUE;
  1819. }
  1820. return(Is98);
  1821. }