/*++ Copyright (c) 1997 Microsoft Corporation Module Name: migisol.c Abstract: Implements an EXE that is used to run migration DLLs in a separate address space (sandboxing). Author: Jim Schmidt (jimschm) 04-Aug-1997 Revision History: jimschm 19-Mar-2001 Removed DVD check because it is now in a migration dll jimschm 02-Jun-1999 Added DVD checking support to avoid setup crash on a Win9x blue screen jimschm 18-Mar-1999 Added cleanup for cases where text mode fails and the user returns to Win9x. jimschm 23-Sep-1998 Converted to new IPC mechanism --*/ #include "pch.h" #include "plugin.h" #include "migui.h" #include "ntui.h" #include "unattend.h" BOOL g_ReportPhase = FALSE; BOOL g_MigrationPhase = FALSE; TCHAR g_DllName[MAX_TCHAR_PATH] = ""; P_INITIALIZE_NT InitializeNT; P_MIGRATE_USER_NT MigrateUserNT; P_MIGRATE_SYSTEM_NT MigrateSystemNT; P_QUERY_VERSION QueryVersion; P_INITIALIZE_9X Initialize9x; P_MIGRATE_USER_9X MigrateUser9x; P_MIGRATE_SYSTEM_9X MigrateSystem9x; BOOL WINAPI MigUtil_Entry ( HINSTANCE hinstDLL, DWORD fdwReason, PVOID lpvReserved ); BOOL IsNEC98( VOID ); #define NO_GUI_ERROR 0 // // Local functions // BOOL PackExeNames( PGROWBUFFER GrowBuf, PCSTR p ); BOOL PackDword( PGROWBUFFER GrowBuf, DWORD dw ); BOOL PackQuadWord( PGROWBUFFER GrowBuf, LONGLONG qw ); BOOL PackIntArray( PGROWBUFFER GrowBuf, PINT Array ); BOOL PackString ( PGROWBUFFER GrowBuf, PCSTR String ); BOOL PackBinary ( PGROWBUFFER GrowBuf, PBYTE Data, DWORD DataSize ); HINF pGetInfHandleFromFileNameW ( PCWSTR UnattendFile ); VOID ProcessCommands ( VOID ); BOOL pParseCommandLine ( VOID ); VOID DoInitializeNT ( PCWSTR Args ); VOID DoInitialize9x ( PCSTR Args ); VOID DoMigrateUserNT ( PCWSTR Args ); VOID DoQueryVersion ( PCSTR Args ); VOID DoMigrateUser9x ( PCSTR Args ); VOID DoMigrateSystemNT ( PCWSTR Args ); VOID DoMigrateSystem9x ( PCSTR Args ); HWND pFindParentWindow ( IN PCTSTR WindowTitle, IN DWORD ProcessId ); static HINSTANCE g_hLibrary; HANDLE g_hHeap; HINSTANCE g_hInst; #ifdef DEBUG #define DBG_MIGISOL "MigIsol" #endif INT WINAPI WinMain ( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR AnsiCmdLine, INT CmdShow ) /*++ Routine Description: The entry point to migisol.exe. The entire body of code is wrapped in a try/except block to catch all problems with any migration DLLs. Arguments: hInstance - The instance handle of this EXE hPrevInstance - The previous instance handle of this EXE if it is running, or NULL if no other instances exist. AnsiCmdLine - The command line (ANSI version) CmdShow - The ShowWindow command passed by the shell Return Value: Returns -1 if an error occurred, or 0 if the exe completed. The exe will automatically terminate with 0 if the migration DLL throws an exception. --*/ { TCHAR OurDir[MAX_TCHAR_PATH]; PTSTR p; __try { g_hInst = hInstance; g_hHeap = GetProcessHeap(); *OurDir = 0; GetModuleFileName (NULL, OurDir, ARRAYSIZE(OurDir)); p = _tcsrchr (OurDir, TEXT('\\')); if (p) { *p = 0; if (!_tcschr (OurDir, TEXT('\\'))) { p[0] = TEXT('\\'); p[1] = 0; } SetCurrentDirectory (OurDir); // // Force a specific setupapi.dll to be loaded // StringCopy (AppendWack (OurDir), TEXT("setupapi.dll")); LoadLibraryEx ( OurDir, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); } // Initialize utility library if (!MigUtil_Entry (g_hInst, DLL_PROCESS_ATTACH, NULL)) { FreeLibrary (g_hLibrary); return -1; } DEBUGMSG ((DBG_MIGISOL, "migisol.exe started")); if (!pParseCommandLine()) { FreeLibrary (g_hLibrary); return -1; } DEBUGMSG ((DBG_MIGISOL, "CmdLine parsed")); if (!OpenIpc (FALSE, NULL, NULL, NULL)) { DEBUGMSG ((DBG_MIGISOL, "OpenIpc failed")); FreeLibrary (g_hLibrary); return -1; } __try { DEBUGMSG ((DBG_MIGISOL, "Processing commands")); ProcessCommands(); } __except (TRUE) { LOG ((LOG_ERROR, "Upgrade Pack process is terminating because of an exception in WinMain")); } CloseIpc(); FreeLibrary (g_hLibrary); DEBUGMSG ((DBG_MIGISOL, "migisol.exe terminating")); if (!MigUtil_Entry (g_hInst, DLL_PROCESS_DETACH, NULL)) { return -1; } } __except (TRUE) { } return 0; } #define WINNT32_SECTOR_SIZE 512 #define WINNT32_FAT_BOOT_SECTOR_COUNT 1 #define WINNT32_FAT_BOOT_SIZE (WINNT32_SECTOR_SIZE * WINNT32_FAT_BOOT_SECTOR_COUNT) #define FILE_ATTRIBUTE_RHS (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM) BOOL pWriteFATBootSector ( IN PCTSTR BootDataFile, IN TCHAR BootDriveLetter ) { HANDLE BootDataHandle; BYTE Data[WINNT32_FAT_BOOT_SIZE]; DWORD BytesRead; BOOL Success = FALSE; if (GetFileAttributes (BootDataFile) == INVALID_ATTRIBUTES) { DEBUGMSG ((DBG_ERROR, "Can't find %s", BootDataFile)); return FALSE; } BootDataHandle = CreateFile ( BootDataFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (BootDataHandle == INVALID_HANDLE_VALUE) { DEBUGMSG ((DBG_ERROR, "Can't open %s", BootDataFile)); return FALSE; } Success = ReadFile (BootDataHandle, Data, WINNT32_FAT_BOOT_SIZE, &BytesRead, NULL) && (BytesRead == WINNT32_FAT_BOOT_SIZE); CloseHandle (BootDataHandle); if (Success) { // // write the boot sector with this data; don't save NT boot sector // Success = WriteDiskSectors ( BootDriveLetter, 0, WINNT32_FAT_BOOT_SECTOR_COUNT, WINNT32_SECTOR_SIZE, Data ); DEBUGMSG_IF (( !Success, DBG_ERROR, "WriteDiskSectors failed for %c!", BootDriveLetter )); } ELSE_DEBUGMSG ((DBG_ERROR, "Unexpected boot sector size %u in %s", BytesRead, BootDataFile)); return Success; } VOID pCleanUpUndoDirectory ( CHAR BootDrive ) /*++ Routine Description: This function delete recursively all files and directories include and in %windir%\undo directory. Arguments: none Return Value: none --*/ { TCHAR PathBuffer[MAX_PATH]; TCHAR Answer[MAX_PATH]; TCHAR NullPath[] = {0}; DEBUGMSG((DBG_MIGISOL, "Cleanup routine of undo directory")); if(!BootDrive){ if (!GetWindowsDirectory (PathBuffer, ARRAYSIZE(PathBuffer))) { DEBUGMSG((DBG_MIGISOL, "GetWindowsDirectory failed")); return; } BootDrive = PathBuffer[0]; } wsprintf(PathBuffer, TEXT("%c:\\$win_nt$.~bt\\winnt.sif"), BootDrive); GetPrivateProfileString( S_WIN9XUPGUSEROPTIONS, S_PATH_FOR_BACKUP, NullPath, Answer, ARRAYSIZE(Answer), PathBuffer); if(!Answer[0]) { DEBUGMSG ((DBG_MIGISOL, "Failed to retrieve directory path")); return; } wsprintf(PathBuffer, TEXT("%c:\\$win_nt$.~bt\\dataloss"), BootDrive); if (DoesFileExist (PathBuffer)) { LOG (( LOG_INFORMATION, "Data loss was detected because of a failure to restore one or more files. " "The data can be recovered from backup files in %s.", Answer )); return; } SetFileAttributes(Answer, FILE_ATTRIBUTE_NORMAL); RemoveCompleteDirectory (Answer); DEBUGMSG ((DBG_MIGISOL, "Cleaned %s directory", Answer)); } VOID pCleanUpAfterTextModeFailure ( VOID ) { TCHAR squiggleBtDir[] = TEXT("?:\\$win_nt$.~bt"); TCHAR squiggleLsDir[] = TEXT("?:\\$win_nt$.~ls"); TCHAR squiggleBuDir[] = TEXT("?:\\$win_nt$.~bu"); // for NEC98 TCHAR drvLtr[] = TEXT("?:\\$DRVLTR$.~_~"); TCHAR setupLdr[] = TEXT("?:\\$LDR$"); TCHAR txtSetupSif[] = TEXT("?:\\TXTSETUP.SIF"); PCTSTR bootSectDat; TCHAR setupTempDir[MAX_PATH]; TCHAR bootIni[] = TEXT("?:\\boot.ini"); TCHAR ntLdr[] = TEXT("?:\\NTLDR"); TCHAR ntDetectCom[] = TEXT("?:\\NTDETECT.COM"); TCHAR bootFontBin[] = TEXT("?:\\BOOTFONT.BIN"); TCHAR bootSectDos[] = TEXT("?:\\BootSect.dos"); TCHAR renamedFile1[] = TEXT("?:\\boot~tmp.$$1"); TCHAR renamedFile2[] = TEXT("?:\\boot~tmp.$$2"); TCHAR renamedFile3[] = TEXT("?:\\boot~tmp.$$3"); BOOL noLdr = FALSE; BOOL noNtDetect = FALSE; BOOL noBootFontBin = FALSE; DWORD Drives; TCHAR DriveLetter; DWORD Bit; TCHAR Root[] = TEXT("?:\\"); TCHAR Scratch[MAX_PATH]; PCTSTR bootSectBak; PCTSTR bootIniBak; PCTSTR ntldrBak; PCTSTR bootFontBak; PCTSTR ntdetectBak; TCHAR WinDir[MAX_PATH]; DWORD Type; DWORD Attribs; FILE_ENUM e; HANDLE WinInit; CHAR AnsiBuffer[MAX_PATH + 10]; DWORD Dummy; PTSTR Write; BOOL prepareBootIni = FALSE; CHAR SystemDirPath[MAX_PATH]; TCHAR bootDriveLetter; PCTSTR bootSectorFile; BOOL bootLoaderWritten; HKEY key; BOOL dontTouchBootCode = FALSE; if (ISNT()) { return; } DEBUGMSG ((DBG_MIGISOL, "Entering cleanup routine")); SuppressAllLogPopups (TRUE); // // Reinitialize system restore // key = OpenRegKeyStr (TEXT("HKLM\\SYSTEM\\CurrentControlSet\\Services\\VxD\\VxDMon")); if (key) { RegSetValueEx (key, TEXT("FirstRun"), 0, REG_SZ, (PCBYTE) "Y", 2); CloseRegKey (key); } // // Prepare windir and temp dir paths, get the bitmask of drive letters // // We need to know the system drive to be repaired, since win98 on NEC98 // can boot up from any partition that can be installed. // GetSystemDirectory (SystemDirPath, MAX_PATH); if (!GetWindowsDirectory (setupTempDir, sizeof (setupTempDir) / sizeof (setupTempDir[0]))) { DEBUGMSG ((DBG_ERROR, "Can't get Windows dir")); return; } else { StringCopy (WinDir, setupTempDir); } StringCopy (AppendWack (setupTempDir), TEXT("setup")); Drives = GetLogicalDrives(); bootDriveLetter = IsNEC98() ? SystemDirPath[0] : TEXT('C'); if (WinDir[0] != bootDriveLetter) { dontTouchBootCode = TRUE; } // // Create paths // bootIniBak = JoinPaths (setupTempDir, S_BOOTINI_BACKUP); ntldrBak = JoinPaths (setupTempDir, S_NTLDR_BACKUP); ntdetectBak = JoinPaths (setupTempDir, S_NTDETECT_BACKUP); bootSectBak = JoinPaths (setupTempDir, S_BOOTSECT_BACKUP); bootFontBak = JoinPaths (setupTempDir, S_BOOTFONT_BACKUP); // // Deltree $win_nt$.~bt and $win_nt$.~ls // for (Bit = 1, DriveLetter = TEXT('A') ; Bit ; Bit <<= 1, DriveLetter++) { if (!(Drives & Bit)) { continue; } Root[0] = DriveLetter; Type = GetDriveType (Root); if (Type == DRIVE_FIXED || Type == DRIVE_UNKNOWN) { // // Clean this drive // squiggleBtDir[0] = DriveLetter; squiggleLsDir[0] = DriveLetter; RemoveCompleteDirectory (squiggleBtDir); RemoveCompleteDirectory (squiggleLsDir); // // On NEC98, there may be another temp directry to be clean up. // if (IsNEC98()) { squiggleBuDir[0] = DriveLetter; RemoveCompleteDirectory (squiggleBuDir); } } } DEBUGMSG ((DBG_MIGISOL, "Cleaned squiggle dirs")); // // Repair boot.ini (do not necessarily restore it to its original form though) // and clean the root of the drive. // for (Bit = 1, DriveLetter = TEXT('A') ; Bit ; Bit <<= 1, DriveLetter++) { if (!(Drives & Bit)) { continue; } // // On NEC98, there may be multiple boot files in each partition. // So we will just take care the system that booted up. // if (IsNEC98() && (DriveLetter != SystemDirPath[0])) { continue; } Root[0] = DriveLetter; Type = GetDriveType (Root); if (Type == DRIVE_FIXED || Type == DRIVE_UNKNOWN) { // // Remove setup from boot.ini if it is on this drive, // and clean root of the drive. // squiggleBtDir[0] = DriveLetter; squiggleLsDir[0] = DriveLetter; bootIni[0] = DriveLetter; drvLtr[0] = DriveLetter; setupLdr[0] = DriveLetter; ntLdr[0] = DriveLetter; ntDetectCom[0] = DriveLetter; bootFontBin[0] = DriveLetter; txtSetupSif[0] = DriveLetter; bootSectDos[0] = DriveLetter; renamedFile1[0] = DriveLetter; renamedFile2[0] = DriveLetter; renamedFile3[0] = DriveLetter; SetFileAttributes (drvLtr, FILE_ATTRIBUTE_NORMAL); DeleteFile (drvLtr); SetFileAttributes (setupLdr, FILE_ATTRIBUTE_NORMAL); DeleteFile (setupLdr); SetFileAttributes (txtSetupSif, FILE_ATTRIBUTE_NORMAL); DeleteFile (txtSetupSif); // // If this is the boot drive, and if we have a bootsect.bak and // boot.bak in the setup temp directory, then that means Win9x had // an initial boot.ini, and we must restore it. Otherwise there // was no boot.ini. // if (!dontTouchBootCode && DriveLetter == bootDriveLetter) { DEBUGMSG ((DBG_MIGISOL, "Processing boot drive %c", bootDriveLetter)); // // test for existence of bootini.bak/bootsect.bak (we don't // care about the attributes). // Attribs = GetFileAttributes (bootIniBak); DEBUGMSG ((DBG_MIGISOL, "Attributes of %s: 0x%08X", bootIniBak, Attribs)); if (Attribs != INVALID_ATTRIBUTES) { DEBUGMSG ((DBG_MIGISOL, "Attributes of %s: 0x%08X", bootSectBak, Attribs)); Attribs = GetFileAttributes (bootSectBak); } // // if pair exists, then get attributes of real boot.ini file // if (Attribs != INVALID_ATTRIBUTES) { Attribs = GetFileAttributes (bootIni); if (Attribs == INVALID_ATTRIBUTES) { Attribs = FILE_ATTRIBUTE_RHS; } // // Restore ntdetect.com, ntldr, boot sector, and original // boot.ini. // DEBUGMSG ((DBG_MIGISOL, "Restoring dual-boot environment")); if (pWriteFATBootSector (bootSectBak, bootDriveLetter)) { SetFileAttributes (bootIni, FILE_ATTRIBUTE_NORMAL); CopyFile (bootIniBak, bootIni, FALSE); // ignore failure SetFileAttributes (bootIni, Attribs); // // Restore ntldr and ntdetect.com [as a pair] // if (DoesFileExist (ntldrBak) && DoesFileExist (ntdetectBak)) { // // wipe away collisions with our temp file names, // then move current working loader to temp files // if (DoesFileExist (ntLdr)) { SetFileAttributes (renamedFile1, FILE_ATTRIBUTE_NORMAL); DeleteFile (renamedFile1); MoveFile (ntLdr, renamedFile1); noLdr = FALSE; } else { noLdr = TRUE; } if (DoesFileExist (ntDetectCom)) { SetFileAttributes (renamedFile2, FILE_ATTRIBUTE_NORMAL); DeleteFile (renamedFile2); MoveFile (ntDetectCom, renamedFile2); noNtDetect = FALSE; } else { noNtDetect = TRUE; } if (DoesFileExist (bootFontBin)) { SetFileAttributes (renamedFile3, FILE_ATTRIBUTE_NORMAL); DeleteFile (renamedFile3); MoveFile (bootFontBin, renamedFile3); noBootFontBin = FALSE; } else { noBootFontBin = TRUE; } // // now attempt to copy backup files to loader location // bootLoaderWritten = FALSE; if (CopyFile (ntldrBak, ntLdr, FALSE)) { bootLoaderWritten = CopyFile (ntdetectBak, ntDetectCom, FALSE); DEBUGMSG_IF ((!bootLoaderWritten, DBG_ERROR, "Can't copy %s to %s", ntdetectBak, ntDetectCom)); if (bootLoaderWritten && DoesFileExist (bootFontBak)) { bootLoaderWritten = CopyFile (bootFontBak, bootFontBin, FALSE); DEBUGMSG_IF ((!bootLoaderWritten, DBG_ERROR, "Can't copy %s to %s", bootFontBak, bootFontBin)); } } ELSE_DEBUGMSG ((DBG_ERROR, "Can't copy %s to %s", ntldrBak, ntLdr)); if (bootLoaderWritten) { // // success -- remove temp files // SetFileAttributes (renamedFile1, FILE_ATTRIBUTE_NORMAL); DeleteFile (renamedFile1); SetFileAttributes (renamedFile2, FILE_ATTRIBUTE_NORMAL); DeleteFile (renamedFile2); SetFileAttributes (renamedFile3, FILE_ATTRIBUTE_NORMAL); DeleteFile (renamedFile3); } else { // // fail -- restore temp files. If restoration // fails, then generate a working boot.ini. // SetFileAttributes (ntLdr, FILE_ATTRIBUTE_NORMAL); DeleteFile (ntLdr); SetFileAttributes (ntDetectCom, FILE_ATTRIBUTE_NORMAL); DeleteFile (ntDetectCom); SetFileAttributes (bootFontBin, FILE_ATTRIBUTE_NORMAL); DeleteFile (bootFontBin); if (!noLdr) { if (!MoveFile (renamedFile1, ntLdr)) { prepareBootIni = TRUE; DEBUGMSG ((DBG_ERROR, "Can't restore %s to %s", renamedFile1, ntLdr)); } } if (!noNtDetect) { if (!MoveFile (renamedFile2, ntDetectCom)) { prepareBootIni = TRUE; DEBUGMSG ((DBG_ERROR, "Can't restore %s to %s", renamedFile2, ntDetectCom)); } } if (!noBootFontBin) { if (!MoveFile (renamedFile3, bootFontBin)) { prepareBootIni = TRUE; DEBUGMSG ((DBG_ERROR, "Can't restore %s to %s", renamedFile3, bootFontBin)); } } } } } else { LOG ((LOG_WARNING, "Unable to restore dual-boot loader")); } } else { // // Remove the NT boot code. Delete ntdetect.com, // bootfont.bin and ntldr. If any part of this code fails, // make a boot.ini that will work. (ntdetect.com won't // be needed.) // SetFileAttributes (ntDetectCom, FILE_ATTRIBUTE_NORMAL); DeleteFile (ntDetectCom); Attribs = GetFileAttributes (bootIni); if (Attribs != INVALID_ATTRIBUTES) { SetFileAttributes (bootIni, FILE_ATTRIBUTE_NORMAL); prepareBootIni = TRUE; // // SystemDrive is not only C: on NEC98. Also, boot.ini // should be always sitting on system drive but boot // drive during setup, if these are separated. // So we must take care the boot files on system drive. // if (GetFileAttributes (bootSectBak) != INVALID_ATTRIBUTES) { bootSectorFile = bootSectBak; } else { bootSectorFile = bootSectDos; } if (pWriteFATBootSector (bootSectorFile, bootDriveLetter)) { DEBUGMSG ((DBG_MIGISOL, "Successfully restored FAT boot sector")); // // restored original boot sector, NT boot files not required any longer // DeleteFile (bootIni); SetFileAttributes (ntLdr, FILE_ATTRIBUTE_NORMAL); DeleteFile (ntLdr); SetFileAttributes (bootSectDos, FILE_ATTRIBUTE_NORMAL); DeleteFile (bootSectDos); SetFileAttributes (ntDetectCom, FILE_ATTRIBUTE_NORMAL); DeleteFile (ntDetectCom); SetFileAttributes (bootFontBin, FILE_ATTRIBUTE_NORMAL); DeleteFile (bootFontBin); prepareBootIni = FALSE; } else { // // make sure this boot file is not accidentally // deleted by the end-user // SetFileAttributes (ntLdr, FILE_ATTRIBUTE_RHS); DEBUGMSG ((DBG_ERROR, "Cannot restore FAT boot sector from %s", bootSectDos)); } } ELSE_DEBUGMSG ((DBG_MIGISOL, "Skipping removal of boot.ini because it is not present")); } // // If we have any failure, this code here will make a boot // sector & loader that at least boots Win9x. // if (prepareBootIni) { bootSectDat = JoinPaths (squiggleBtDir, TEXT("BOOTSECT.DAT")); WritePrivateProfileString (TEXT("Boot Loader"), TEXT("Default"), Root, bootIni); WritePrivateProfileString (TEXT("Operating Systems"), bootSectDat, NULL, bootIni); GetPrivateProfileString (TEXT("Operating Systems"), Root, TEXT(""), Scratch, MAX_PATH, bootIni); if (!Scratch[0]) { // // This should never ever occur, but for unknown cases we have this // WritePrivateProfileString (TEXT("Operating Systems"), Root, TEXT("Microsoft Windows"), bootIni); } WritePrivateProfileString (NULL, NULL, NULL, bootIni); SetFileAttributes (bootIni, Attribs); prepareBootIni = FALSE; FreePathString (bootSectDat); } } } } // // Remove setup's temp dir as best we can. This leaves some junk around, // but we will fix that on the next reboot. // RemoveCompleteDirectory (setupTempDir); // // put all remaining files in wininit.ini\[rename] they will be // automatically removed at next reboot // StringCopy (Scratch, WinDir); StringCopy (AppendWack (Scratch), TEXT("wininit.ini")); // // append "manually" since using WritePrivateProfileString will just // overwrite previous setting // if (EnumFirstFile (&e, setupTempDir, NULL)) { WinInit = CreateFile ( Scratch, GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL ); if (WinInit != INVALID_HANDLE_VALUE) { StringCopyA (AnsiBuffer, "\r\n[rename]"); if (WriteFile (WinInit, AnsiBuffer, _mbslen (AnsiBuffer), &Dummy, NULL)) { StringCopyA (AnsiBuffer, "\r\nNUL="); Write = GetEndOfString (AnsiBuffer); do { #ifdef UNICODE KnownSizeUnicodeToDbcs (Write, e.FullPath); #else StringCopyA (Write, e.FullPath); #endif if (!WriteFile (WinInit, AnsiBuffer, _mbslen (AnsiBuffer), &Dummy, NULL)) { break; } } while (EnumNextFile (&e)); } CloseHandle (WinInit); } ELSE_DEBUGMSG ((DBG_MIGISOL, "Cannot create wininit.ini")); } ELSE_DEBUGMSG ((DBG_MIGISOL, "No files found in temp dir")); FreePathString (bootIniBak); FreePathString (ntldrBak); FreePathString (ntdetectBak); FreePathString (bootSectBak); FreePathString (bootFontBak); DEBUGMSG ((DBG_MIGISOL, "Leaving cleanup routine")); } BOOL pParseCommandLine ( VOID ) /*++ Routine Description: Prepares the global variables g_hLibrary, g_ReportPhase, g_MigrationPhase, g_DllName and the migration DLL entry points (Initialize9x, etc.) Arguments: none Return Value: TRUE if the module was successfully loaded, or FALSE if a parsing error or load error occurred. --*/ { PCTSTR CmdLine; PCTSTR *argv; INT argc; INT i; PCTSTR p; TCHAR drive; CmdLine = GetCommandLine(); argv = CommandLineToArgv (CmdLine, &argc); if (!argv) { DEBUGMSG ((DBG_MIGISOL, "Parse error")); return FALSE; } // // Parse command line // for (i = 1 ; i < argc ; i++) { if (argv[i][0] == TEXT('-') || argv[i][0] == TEXT('/')) { p = _tcsinc (argv[i]); switch (_totlower (_tcsnextc (p))) { case 'r': // Report-phase g_ReportPhase = TRUE; break; case 'm': // Migration-phase g_MigrationPhase = TRUE; break; case 'b': drive = '\0'; p = _tcsinc(p); if(p && ':' == _tcsnextc(p)){ p = _tcsinc(p); if(p){ drive = (TCHAR)_tcsnextc(p); } } pCleanUpUndoDirectory(drive); case 'c': // Restore Win9x pCleanUpAfterTextModeFailure(); return FALSE; } } else if (!g_DllName[0]) { StringCopy (g_DllName, argv[i]); } else { DEBUGMSG ((DBG_MIGISOL, "Broken arg: %s", argv[i])); return FALSE; } } // // Verify expected options exist // // One must be FALSE while the other must be TRUE if (g_ReportPhase == g_MigrationPhase) { DEBUGMSG ((DBG_MIGISOL, "Too many args")); return FALSE; } if (!g_DllName[0]) { DEBUGMSG ((DBG_MIGISOL, "No operation")); return FALSE; } // // Load migration DLL // g_hLibrary = LoadLibraryEx ( g_DllName, NULL, LOAD_WITH_ALTERED_SEARCH_PATH ); // If it fails, assume the DLL does not want to be loaded if (!g_hLibrary) { LOG ((LOG_ERROR, "Cannot load %s, rc=%u", g_DllName, GetLastError())); return FALSE; } // Get proc addresses for NT-side functions InitializeNT = (P_INITIALIZE_NT) GetProcAddress (g_hLibrary, PLUGIN_INITIALIZE_NT); MigrateUserNT = (P_MIGRATE_USER_NT) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_USER_NT); MigrateSystemNT = (P_MIGRATE_SYSTEM_NT) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_SYSTEM_NT); // Get proc addresses for 9x-side functions QueryVersion = (P_QUERY_VERSION) GetProcAddress (g_hLibrary, PLUGIN_QUERY_VERSION); Initialize9x = (P_INITIALIZE_9X) GetProcAddress (g_hLibrary, PLUGIN_INITIALIZE_9X); MigrateUser9x = (P_MIGRATE_USER_9X) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_USER_9X); MigrateSystem9x = (P_MIGRATE_SYSTEM_9X) GetProcAddress (g_hLibrary, PLUGIN_MIGRATE_SYSTEM_9X); // If any function does not exist, ignore the out-of-spec DLL if (!QueryVersion || !Initialize9x || !MigrateUser9x || !MigrateSystem9x || !InitializeNT || !MigrateUserNT || !MigrateSystemNT ) { LOG ((LOG_ERROR, "Cannot load %s, one or more functions missing", g_DllName)); return FALSE; } return TRUE; } VOID ProcessCommands ( VOID ) /*++ Routine Description: ProcessCommands waits on the IPC pipe for a command message. When a command message is received, it is dispatched to the processing function. If a terminate command is received, the EXE terminates. If no command is received in one second, the EXE terminates. Therefore, Setup must always be ready to feed the EXE commands. Arguments: none Return Value: none --*/ { DWORD Command; PBYTE Data; DWORD DataSize; DEBUGMSG ((DBG_MIGISOL, "Starting to process %s", g_DllName)); do { // We wait for an interval: w95upgnt.dll or w95upg.dll should be ready // to feed us continuously. // // Receive command, don't care about size, OK to fail. // if (!GetIpcCommand ( IPC_GET_COMMAND_WIN9X, &Command, &Data, &DataSize )) { DEBUGMSG ((DBG_WARNING, "MIGISOL: No command recieved")); break; } DEBUGMSG ((DBG_NAUSEA, "MigIsol - Command recieved: %u", Command)); switch (Command) { case IPC_QUERY: if (g_MigrationPhase) { } else { DoQueryVersion ((PCSTR) Data); } break; case IPC_INITIALIZE: if (g_MigrationPhase) { DoInitializeNT ((PCWSTR) Data); } else { DoInitialize9x ((PCSTR) Data); } break; case IPC_MIGRATEUSER: if (g_MigrationPhase) { DoMigrateUserNT ((PCWSTR) Data); } else { DoMigrateUser9x ((PCSTR) Data); } break; case IPC_MIGRATESYSTEM: if (g_MigrationPhase) { DoMigrateSystemNT ((PCWSTR) Data); } else { DoMigrateSystem9x ((PCSTR) Data); } break; case IPC_TERMINATE: DEBUGMSG ((DBG_MIGISOL, "Processing of %s is complete", g_DllName)); return; default: DEBUGMSG ((DBG_MIGISOL, "ProcessCommands: Unrecognized command -- terminating")); return; } if (Data) { MemFree (g_hHeap, 0, Data); Data = NULL; } } while (Command != IPC_TERMINATE); } VOID DoInitializeNT ( PCWSTR Args ) /*++ Routine Description: Calls migration DLL's InitializeNT function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the initialize command. Return Value: none --*/ { PCWSTR WorkingDir = NULL; PCWSTR SourceDirs = NULL; PCWSTR EndOfSourceDirs; PDWORD ReservedSize; PVOID Reserved; DWORD rc = RPC_S_CALL_FAILED; DWORD TechnicalLogId = 0; DWORD GuiLogId = 0; // // Set pointers of IN parameters // WorkingDir = Args; SourceDirs = wcschr (Args, 0) + 1; EndOfSourceDirs = SourceDirs; while (*EndOfSourceDirs) { EndOfSourceDirs = wcschr (EndOfSourceDirs, 0); EndOfSourceDirs++; } ReservedSize = (PDWORD) (EndOfSourceDirs + 1); if (*ReservedSize) { Reserved = (PVOID) (ReservedSize + 1); } else { Reserved = NULL; } // // Set CWD // SetCurrentDirectoryW(WorkingDir); // // Call migration DLL function // __try { rc = InitializeNT (WorkingDir, SourceDirs, Reserved); } __except (TRUE) { // Send log message DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in InitializeNT", g_DllName)); rc = ERROR_NOACCESS; TechnicalLogId = MSG_EXCEPTION_MIGRATE_INIT_NT; } // // No OUT parameters to send // SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0); } HINF pGetInfHandleFromFileNameW ( PCWSTR UnattendFile ) /*++ Routine Description: pGetInfHandleFromFileName uses the Setup APIs to open the specified UnattendFile. Arguments: UnattendFile - A pointer to the UNICODE file name specifying the unattend file. This string is converted to ANSI and the ANSI version of SetupOpenInfFile is called. Return Value: The INF handle, or NULL (*not* INVALID_HANDLE_VALUE) if the file could not be opened. --*/ { CHAR AnsiUnattendFile[MAX_MBCHAR_PATH]; HINF UnattendHandle; KnownSizeWtoA (AnsiUnattendFile, UnattendFile); UnattendHandle = SetupOpenInfFileA (AnsiUnattendFile, NULL, INF_STYLE_OLDNT|INF_STYLE_WIN4, NULL); if (UnattendHandle == INVALID_HANDLE_VALUE) { DEBUGMSG ((DBG_ERROR, "pGetInfHandleFromFileNameW: Could not open %s", UnattendFile)); UnattendHandle = NULL; } return UnattendHandle; } VOID DoMigrateUserNT ( PCWSTR Args ) /*++ Routine Description: Calls migration DLL's MigrateUserNT function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the IPC_MIGRATEUSER command. Return Value: none --*/ { PCWSTR UnattendFile; PCWSTR UserRegKey; PCWSTR UserName; PCWSTR UserDomain; PCWSTR FixedUserName; PCWSTR UserProfileDir; WCHAR OrgProfileDir[MAX_WCHAR_PATH]; HINF UnattendHandle = NULL; HKEY UserRegHandle = NULL; DWORD rc; PVOID Reserved; PDWORD ReservedBytesPtr; DWORD TechnicalLogId = 0; DWORD GuiLogId = 0; __try { // // Preserve USERPROFILE environment variable // GetEnvironmentVariableW (S_USERPROFILEW, OrgProfileDir, MAX_WCHAR_PATH); // // Set pointers of IN parameters // UnattendFile = Args; UserRegKey = wcschr (UnattendFile, 0) + 1; UserName = wcschr (UserRegKey, 0) + 1; UserDomain = wcschr (UserName, 0) + 1; FixedUserName = wcschr (UserDomain, 0) + 1; UserProfileDir = wcschr (FixedUserName, 0) + 1; ReservedBytesPtr = (PDWORD) (wcschr (UserProfileDir, 0) + 1); if (*ReservedBytesPtr) { Reserved = (PVOID) (ReservedBytesPtr + 1); } else { Reserved = NULL; } // // Set USERPROFILE // if (UserProfileDir[0]) { WCHAR DebugDir[MAX_WCHAR_PATH]; SetEnvironmentVariableW (S_USERPROFILEW, UserProfileDir); DEBUGMSG ((DBG_MIGISOL, "USERPROFILE set to %ls", UserProfileDir)); GetEnvironmentVariableW (S_USERPROFILEW, DebugDir, MAX_WCHAR_PATH); DEBUGMSG ((DBG_MIGISOL, "USERPROFILE set to %ls", DebugDir)); } // // Get UnattendHandle and UserRegHandle // UnattendHandle = pGetInfHandleFromFileNameW (UnattendFile); UserRegHandle = OpenRegKeyStrW (UserRegKey); if (!UnattendHandle || !UserRegHandle) { // Send log message and failure code rc = ERROR_OPEN_FAILED; } else { // // Call migration DLL function // __try { rc = MigrateUserNT ( UnattendHandle, UserRegHandle, UserName[0] ? UserName : NULL, Reserved ); } __except (TRUE) { // Send log message and failure code DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in MigrateUserNT", g_DllName)); rc = ERROR_NOACCESS; TechnicalLogId = MSG_EXCEPTION_MIGRATE_USER_NT; } } // // No OUT parameters to send // if (UserRegHandle) { CloseRegKey (UserRegHandle); UserRegHandle = NULL; } SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0); } __finally { // // Clean up // SetEnvironmentVariableW (S_USERPROFILEW, OrgProfileDir); if (UserRegHandle) { CloseRegKey (UserRegHandle); } if (UnattendHandle != INVALID_HANDLE_VALUE) { SetupCloseInfFile (UnattendHandle); } } } VOID DoMigrateSystemNT ( PCWSTR Args ) /*++ Routine Description: Calls migration DLL's MigrateSystemNT function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the IPC_MIGRATESYSTEM command. Return Value: none --*/ { PCWSTR UnattendFile; HINF UnattendHandle = NULL; DWORD rc; PVOID Reserved; PDWORD ReservedBytesPtr; DWORD TechnicalLogId = 0; DWORD GuiLogId = 0; __try { // // Set pointers of IN parameters // UnattendFile = Args; ReservedBytesPtr = (PDWORD) (wcschr (UnattendFile, 0) + 1); if (*ReservedBytesPtr) { Reserved = (PVOID) (ReservedBytesPtr + 1); } else { Reserved = NULL; } // // Get UnattendHandle and UserRegHandle // UnattendHandle = pGetInfHandleFromFileNameW (UnattendFile); if (!UnattendHandle) { rc = ERROR_OPEN_FAILED; } else { // // Call migration DLL function // __try { rc = MigrateSystemNT (UnattendHandle, Reserved); } __except (TRUE) { DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in MigrateSystemNT", g_DllName)); rc = ERROR_NOACCESS; TechnicalLogId = MSG_EXCEPTION_MIGRATE_SYSTEM_NT; } } // // No OUT parameters to send // SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0); } __finally { if (UnattendHandle != INVALID_HANDLE_VALUE) { SetupCloseInfFile (UnattendHandle); } } } VOID DoQueryVersion ( PCSTR Args ) /*++ Routine Description: Calls migration DLL's QueryVersion function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the IPC_QUERY command. Return Value: none --*/ { DWORD rc = RPC_S_CALL_FAILED; GROWBUFFER GrowBuf = GROWBUF_INIT; PSTR ProductId = NULL; UINT DllVersion = 0; PDWORD CodePageArray = NULL; PCSTR ExeNames = NULL; PCSTR WorkingDir; PVENDORINFO VendorInfo = NULL; DWORD TechnicalLogId = 0; DWORD GuiLogId = 0; DEBUGMSG ((DBG_MIGISOL, "Entering DoQueryVersion")); __try { // // Set pointers of IN parameters // WorkingDir = (PSTR)Args; // CWD for this process // // Change CWD // SetCurrentDirectory(WorkingDir); // // Call migration DLL function // __try { DEBUGMSG ((DBG_MIGISOL, "QueryVersion: WorkingDir=%s", WorkingDir)); rc = QueryVersion ( &ProductId, &DllVersion, &CodePageArray, &ExeNames, &VendorInfo ); DEBUGMSG ((DBG_MIGISOL, "QueryVersion rc=%u", rc)); DEBUGMSG ((DBG_MIGISOL, "VendorInfo=0x%X", VendorInfo)); } __except (TRUE) { DEBUGMSG (( DBG_MIGISOL, "%s threw an exception in QueryVersion", g_DllName )); TechnicalLogId = MSG_MIGDLL_QUERYVERSION_EXCEPTION_LOG; rc = ERROR_NOACCESS; } // // Unless we know failure occurred, return out parameters // if (rc == ERROR_SUCCESS) { // // Pack product id string // if (!PackString (&GrowBuf, ProductId)) { DEBUGMSG ((DBG_MIGISOL, "QueryVersion PackProductId failed")); rc = GetLastError(); __leave; } // // Pack DLL version // if (!PackDword(&GrowBuf, DllVersion)) { rc = GetLastError(); DEBUGMSG ((DBG_MIGISOL, "QueryVersion DllVersion failed")); __leave; } // // Pack CP array // if (!PackIntArray(&GrowBuf, CodePageArray)) { rc = GetLastError(); DEBUGMSG ((DBG_MIGISOL, "QueryVersion PackIntArray failed")); __leave; } // // Pack Exe Names // if (!PackExeNames(&GrowBuf, ExeNames)) { rc = GetLastError(); DEBUGMSG ((DBG_MIGISOL, "QueryVersion PackExeNames failed")); __leave; } // // Pack PVENDORINFO // if (!PackDword(&GrowBuf, (DWORD) VendorInfo)) { rc = GetLastError(); DEBUGMSG ((DBG_MIGISOL, "QueryVersion VendorInfo failed")); __leave; } if (VendorInfo) { if (!PackBinary (&GrowBuf, (PBYTE) VendorInfo, sizeof (VENDORINFO))) { rc = GetLastError(); DEBUGMSG ((DBG_MIGISOL, "QueryVersion VendorInfo failed")); __leave; } } } // // Send the packed parameters // if (!SendIpcCommandResults ( rc, TechnicalLogId, GuiLogId, GrowBuf.End ? GrowBuf.Buf : NULL, GrowBuf.End )) { DEBUGMSG (( DBG_ERROR, "DoQueryVersion failed to send command response" )); LOG ((LOG_ERROR, "Upgrade Pack process could not send reply data")); } } __finally { FreeGrowBuffer(&GrowBuf); } DEBUGMSG ((DBG_MIGISOL, "Leaving DoQueryVersion, rc=%u", rc)); } VOID DoInitialize9x ( PCSTR Args ) /*++ Routine Description: Calls migration DLL's Initialize9x function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the IPC_INITIALIZE command. Return Value: none --*/ { DWORD rc = RPC_S_CALL_FAILED; PSTR WorkingDir = NULL; PSTR SourceDirs = NULL; PVOID Reserved; DWORD ReservedSize; PCSTR p; DWORD TechnicalLogId = 0; DWORD GuiLogId = 0; GROWBUFFER GrowBuf = GROWBUF_INIT; DEBUGMSG ((DBG_MIGISOL, "Entering DoInitialize9x")); __try { // // Set pointers of IN parameters // WorkingDir = (PSTR)Args; // CWD for this process SourceDirs = GetEndOfStringA (WorkingDir) + 1; // arg for Initialize9x p = SourceDirs; while (*p) { p = GetEndOfStringA (p); p++; } p++; ReservedSize = *((PDWORD) p); p = (PCSTR) ((PBYTE) p + sizeof (DWORD)); if (ReservedSize) { Reserved = (PVOID) p; p = (PCSTR) ((PBYTE) p + ReservedSize); } else { Reserved = NULL; } // // Change CWD // SetCurrentDirectory(WorkingDir); // // Call migration DLL function // __try { rc = Initialize9x ( WorkingDir, SourceDirs, Reserved ); } __except (TRUE) { // // Send log message // DEBUGMSG ((DBG_MIGISOL, "%s threw an exception in Initialize9x", g_DllName)); TechnicalLogId = MSG_MIGDLL_INITIALIZE9X_EXCEPTION_LOG; rc = ERROR_NOACCESS; } // // Send reserved // if (rc == ERROR_SUCCESS) { // // Pack reserved parameter // // Set ReservedSize to zero for now because the Reserved arg is an IN only ReservedSize = 0; if (!PackBinary (&GrowBuf, (PBYTE) Reserved, ReservedSize)) { rc = GetLastError(); DEBUGMSG ((DBG_MIGISOL, "Initialize9x reserved failed")); __leave; } } // // Send the packed parameters // if (!SendIpcCommandResults ( rc, TechnicalLogId, GuiLogId, GrowBuf.End ? GrowBuf.Buf : NULL, GrowBuf.End )) { DEBUGMSG (( DBG_ERROR, "DoInitializeNT failed to send command response" )); LOG ((LOG_ERROR, "Upgrade Pack process could not send reply data")); } } __finally { FreeGrowBuffer (&GrowBuf); } DEBUGMSG ((DBG_MIGISOL, "Leaving DoInitialize9x, rc=%u", rc)); } VOID DoMigrateUser9x ( IN PCSTR Args ) /*++ Routine Description: Calls migration DLL's MigrateUser9x function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the IPC_MIGRATEUSER command. Return Value: none --*/ { PCSTR ParentWndTitle = NULL; HWND ParentWnd; PCSTR UnattendFile = NULL; PCSTR UserRegKey = NULL; PCSTR UserName = NULL; HKEY UserRegHandle = NULL; DWORD rc = RPC_S_CALL_FAILED; DWORD ProcessId; DWORD GuiLogId = 0; DWORD TechnicalLogId = 0; DEBUGMSG ((DBG_MIGISOL, "Entering DoMigrateUser9x")); __try { // // Set pointers of IN parameters // ParentWndTitle = Args; UnattendFile = GetEndOfStringA (ParentWndTitle) + 1; ProcessId = *((PDWORD) UnattendFile); UnattendFile = (PCSTR) ((PBYTE) UnattendFile + sizeof (DWORD)); UserRegKey = GetEndOfStringA (UnattendFile) + 1; UserName = GetEndOfStringA (UserRegKey) + 1; // // Get UserRegHandle // UserRegHandle = OpenRegKeyStr(UserRegKey); if (!UserRegHandle) { rc = ERROR_OPEN_FAILED; } else { ParentWnd = pFindParentWindow (ParentWndTitle, ProcessId); // // Call migration DLL function // __try { rc = MigrateUser9x( ParentWnd, UnattendFile, UserRegHandle, *UserName ? UserName : NULL, NULL ); } __except (TRUE) { // // Send log message // DEBUGMSG (( DBG_MIGISOL, "%s threw an exception in MigrateUser9x", g_DllName )); TechnicalLogId = MSG_MIGDLL_MIGRATEUSER9X_EXCEPTION_LOG; rc = ERROR_NOACCESS; } } // // No need to return out parameters // if (UserRegHandle) { CloseRegKey (UserRegHandle); UserRegHandle = NULL; } SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0); } __finally { // // Free resources // if (UserRegHandle) { CloseRegKey (UserRegHandle); } } DEBUGMSG ((DBG_MIGISOL, "Leaving MigrateUser9x , rc=%u", rc)); } VOID DoMigrateSystem9x( IN PCSTR Args ) /*++ Routine Description: Calls migration DLL's MigrateSystem9x function. This function unpacks the arguments passed by Setup, calls the migration DLL and returns the status code back to Setup. Arguments: Args - A pointer to the argument buffer sent by Setup. This buffer is received with the IPC_MIGRATESYSTEM command. Return Value: none --*/ { PCSTR ParentWndTitle = NULL; DWORD ProcessId; PCSTR UnattendFile = NULL; HWND ParentWnd = NULL; DWORD rc = RPC_S_CALL_FAILED; DWORD TechnicalLogId = 0; DWORD GuiLogId = 0; DEBUGMSG ((DBG_MIGISOL, "Entering DoMigrateSystem9x")); // // Set pointers of IN parameters // ParentWndTitle = Args; UnattendFile = GetEndOfStringA (ParentWndTitle) + 1; ProcessId = *((PDWORD) UnattendFile); UnattendFile = (PCSTR) ((PBYTE) UnattendFile + sizeof (DWORD)); // // Get ParentWnd // ParentWnd = pFindParentWindow (ParentWndTitle, ProcessId); // // Call migration DLL function // __try { rc = MigrateSystem9x( ParentWnd, UnattendFile, NULL ); } __except (TRUE) { // // Send log message // DEBUGMSG (( DBG_MIGISOL, "%s threw an exception in MigrateSystem9x", g_DllName )); TechnicalLogId = MSG_MIGDLL_MIGRATESYSTEM9X_EXCEPTION_LOG; rc = ERROR_NOACCESS; } // // No need to return out parameters // SendIpcCommandResults (rc, TechnicalLogId, GuiLogId, NULL, 0); DEBUGMSG ((DBG_MIGISOL, "Leaving DoMigrateSystem9x, rc=%u", rc)); } // // Function packs a DWORD into a GrowBuffer. BOOL PackDword( PGROWBUFFER GrowBuf, DWORD dw ) { PVOID p; p = GrowBuffer (GrowBuf, sizeof(DWORD)); if (!p) { return FALSE; } CopyMemory (p, (PVOID)(&dw), sizeof(dw)); return TRUE; } // // Function packs a LONGLONG into a GrowBuffer BOOL PackQuadWord( PGROWBUFFER GrowBuf, LONGLONG qw) { return ( PackDword(GrowBuf, (DWORD)qw) && PackDword(GrowBuf, (DWORD)(qw >> 32))); } // // Function packs 1) a NULL ptr, or 2) array of int terminated by -1, into a // GrowBuffer. // BOOL PackIntArray( PGROWBUFFER GrowBuf, PINT Array ) { DWORD Count; PDWORD ArrayPos; if (!Array) { if (!GrowBufAppendDword (GrowBuf, 0)) { return FALSE; } } else { __try { Count = 1; for (ArrayPos = Array ; (*ArrayPos) != -1 ; ArrayPos++) { Count++; } } __except (TRUE) { LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid code page array", g_DllName)); SetLastError (ERROR_NOACCESS); return FALSE; } if (!GrowBufAppendDword (GrowBuf, Count)) { return FALSE; } for (ArrayPos = Array ; Count ; ArrayPos++, Count--) { if (!GrowBufAppendDword (GrowBuf, (DWORD) (UINT) (*ArrayPos))) { return FALSE; } } } return TRUE; } // // Function packs 1) a NULL pointer, or 2) a multi-sz, into a GrowBuffer. // BOOL PackExeNames( PGROWBUFFER GrowBuf, PCSTR ExeNames ) { PCSTR p; if (ExeNames) { __try { for (p = ExeNames ; *p ; p = GetEndOfStringA (p) + 1) { } } __except (TRUE) { LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid file list", g_DllName)); SetLastError (ERROR_NOACCESS); return FALSE; } // Append non-empty strings for (p = ExeNames ; *p ; p = GetEndOfStringA (p) + 1) { if (!MultiSzAppendA (GrowBuf, p)) { return FALSE; } } } // Append a final empty string if (!MultiSzAppendA(GrowBuf, "")) { return FALSE; } return TRUE; } BOOL PackString ( PGROWBUFFER GrowBuf, PCSTR String ) { __try { if (!MultiSzAppendA (GrowBuf, String)) { return FALSE; } } __except (TRUE) { DEBUGMSG (( DBG_ERROR, "%s provided an invalid ProductID string (%xh)", g_DllName, String )); LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid product ID", g_DllName)); SetLastError (ERROR_NOACCESS); return FALSE; } return TRUE; } BOOL PackBinary ( PGROWBUFFER GrowBuf, PBYTE Data, DWORD DataSize ) { PBYTE Buf; if (!PackDword (GrowBuf, DataSize)) { return FALSE; } if (!DataSize) { return TRUE; } Buf = GrowBuffer (GrowBuf, DataSize); if (!Buf) { return FALSE; } __try { CopyMemory (Buf, Data, DataSize); } __except (TRUE) { DEBUGMSG (( DBG_ERROR, "%s provided an invalid binary parameter (%xh)", g_DllName, Data )); LOG ((LOG_ERROR, "Upgrade Pack %s provided an invalid binary parameter", g_DllName)); SetLastError (ERROR_NOACCESS); return FALSE; } return TRUE; } typedef struct { PCTSTR WindowTitle; DWORD ProcessId; HWND Match; } FINDWINDOW_STRUCT, *PFINDWINDOW_STRUCT; BOOL CALLBACK pEnumWndProc ( HWND hwnd, LPARAM lParam ) /*++ Routine Description: A callback that is called for every top level window on the system. It is used with pFindParentWindow to locate a specific window. Arguments: hwnd - Specifies the handle of the current enumerated window lParam - Specifies a pointer to a FINDWINDOW_STRUCT variable that holds WindowTitle and ProcessId, and receives the handle if a match is found. Return Value: The handle to the matching window, or NULL if no window has the specified title and process ID. --*/ { TCHAR Title[MAX_TCHAR_PATH]; DWORD ProcessId; PFINDWINDOW_STRUCT p; p = (PFINDWINDOW_STRUCT) lParam; GetWindowText (hwnd, Title, MAX_TCHAR_PATH); GetWindowThreadProcessId (hwnd, &ProcessId); DEBUGMSG ((DBG_MIGISOL, "Testing window: %s, ID=%x against %s, %x", Title, ProcessId, p->WindowTitle, p->ProcessId)); if (!StringCompare (Title, p->WindowTitle) && ProcessId == p->ProcessId) { p->Match = hwnd; LogReInit (&hwnd, NULL); DEBUGMSG ((DBG_MIGISOL, "Window found: %s, ID=%u", Title, ProcessId)); return FALSE; } return TRUE; } HWND pFindParentWindow ( IN PCTSTR WindowTitle, IN DWORD ProcessId ) /*++ Routine Description: Locates the wizard window by enumerating all top-level windows. The first one to match the supplied title and process ID is used. Arguments: WindowTitle - Specifies the name of the window to find. ProcessId - Specifies the ID of the process who owns the window. If zero is specified, NULL is returned. Return Value: The handle to the matching window, or NULL if no window has the specified title and process ID. --*/ { FINDWINDOW_STRUCT FindWndStruct; // If no process ID, we cannot have a match if (!ProcessId) { return NULL; } ZeroMemory (&FindWndStruct, sizeof (FindWndStruct)); FindWndStruct.WindowTitle = WindowTitle; FindWndStruct.ProcessId = ProcessId; EnumWindows (pEnumWndProc, (LPARAM) &FindWndStruct); return FindWndStruct.Match; } // // Check platform that I'm runnig on. Copyed from winnt32[au].dll. // TRUE - NEC98 // FALSE - others(includes x86) // BOOL IsNEC98( VOID ) { static BOOL Checked = FALSE; static BOOL Is98; if(!Checked) { Is98 = ((GetKeyboardType(0) == 7) && ((GetKeyboardType(1) & 0xff00) == 0x0d00)); Checked = TRUE; } return(Is98); }