/*++ Copyright (c) 1996 Microsoft Corporation Module Name: migdlls.c Abstract: The functions in this module are used to support migration DLLs. Author: Jim Schmidt (jimschm) 04-Feb-1997 Revision History: jimschm 23-Sep-1998 Changed to use new IPC mechanism jimschm 22-Apr-1998 Added USERPROFILE environment variable to MigrateUserNT jimschm 08-Jan-1997 Added alive event, giving certain DLLs up to 30 minutes to complete their work. jimschm 08-Dec-1997 Added support for domains (MigrateUserNT's user name param is multi-sz) --*/ #include "pch.h" #include "migmainp.h" #ifndef UNICODE #error UNICODE required #endif HANDLE g_AliveEvent; BOOL pConnectToDll ( VOID ); VOID pDisconnectFromDll ( VOID ); DWORD pRunMigrationDll ( VOID ); DWORD pCallInitializeNt ( IN PCTSTR WorkingDir, IN PCTSTR *SourceDirArray, IN PVOID Reserved, IN DWORD ReservedBytes ); DWORD pCallMigrateUserNt ( IN PCTSTR WorkingDir, IN PCTSTR UnattendFile, IN PCTSTR RootKey, IN PCTSTR Win9xUserName, IN PCTSTR UserDomain, IN PCTSTR FixedUserName, IN PVOID Reserved, IN DWORD ReservedBytes ); DWORD pCallMigrateSystemNt ( IN PCTSTR WorkingDir, IN PCTSTR UnattendFile, IN PVOID Reserved, IN DWORD ReservedBytes ); static VOID pSetCwd ( OUT PTSTR SavedWorkDir, IN PCTSTR NewWorkDir ); static TCHAR g_DllPath[MAX_TCHAR_PATH]; static TCHAR g_WorkingDir[MAX_TCHAR_PATH]; static TCHAR g_DllDesc[MAX_TCHAR_PATH]; static VENDORINFOW g_VendorInfo; static TCHAR g_FixedUser[MAX_USER_NAME]; static TCHAR g_UserOnWin9x[MAX_USER_NAME]; static HINSTANCE g_hLibrary; P_INITIALIZE_NT InitializeNT; P_MIGRATE_USER_NT MigrateUserNT; P_MIGRATE_SYSTEM_NT MigrateSystemNT; VOID pLogDllFailure ( IN HWND Parent, IN UINT MessageId ) /*++ Routine Description: pLogDllFailure prepares arguments for the specified MessageId, and then displays a popup and adds a log entry. This function gives the user information on what to do when the DLL fails. Arguments: Parent - Specifies the parent window handle of the popup, or NULL if no popup is to be displayed. MessageId - Specifies the message ID for the error. Return Value: None. --*/ { PCTSTR FixupPhone; PCTSTR FixupUrl; PCTSTR FixupInstructions; PCTSTR LineBreak = S_EMPTY; PCTSTR ArgArray[1]; // // Generate fixup strings // if (g_VendorInfo.SupportNumber[0]) { ArgArray[0] = g_VendorInfo.SupportNumber; FixupPhone = ParseMessageID (MSG_MIGDLL_SUPPORT_PHONE_FIXUP, ArgArray); LineBreak = TEXT("\n"); } else { FixupPhone = S_EMPTY; } if (g_VendorInfo.SupportUrl[0]) { ArgArray[0] = g_VendorInfo.SupportUrl; FixupUrl = ParseMessageID (MSG_MIGDLL_SUPPORT_URL_FIXUP, ArgArray); LineBreak = TEXT("\n"); } else { FixupUrl = S_EMPTY; } if (g_VendorInfo.InstructionsToUser[0]) { ArgArray[0] = g_VendorInfo.InstructionsToUser; FixupInstructions = ParseMessageID (MSG_MIGDLL_INSTRUCTIONS_FIXUP, ArgArray); LineBreak = TEXT("\n"); } else { FixupInstructions = S_EMPTY; } // // Display popup and log the error // LOG (( LOG_ERROR, (PCSTR) MessageId, g_DllDesc, g_VendorInfo.CompanyName, FixupPhone, FixupUrl, FixupInstructions, LineBreak )); } VOID pSetCwd ( OUT PTSTR SavedWorkDir, IN PCTSTR NewWorkDir ) { GetCurrentDirectory (MAX_TCHAR_PATH, SavedWorkDir); SetCurrentDirectory (NewWorkDir); } BOOL pCreateEnvironment ( PVOID *BlockPtr ) { return CreateEnvironmentBlock (BlockPtr, NULL, FALSE); } VOID pSetEnvironmentBlock ( PVOID Block ) { DEBUGMSG ((DBG_VERBOSE, "Block: %s", Block)); } DWORD ProcessMigrationDLLs ( DWORD Request ) { MEMDB_ENUM e; DWORD rc; DWORD Ticks = 0; if (Request == REQUEST_QUERYTICKS) { if (MemDbEnumItems (&e, MEMDB_CATEGORY_MIGRATION_DLL)) { do { Ticks += TICKS_MIGRATION_DLL; } while (MemDbEnumNextValue (&e)); } return Ticks; } #ifdef PRERELEASE if (g_ConfigOptions.DiffMode) { TakeSnapShot(); } #endif if (MemDbEnumItems (&e, MEMDB_CATEGORY_MIGRATION_DLL)) { do { // // Retrieve DLL location and settings // // Obtain the DLL name and working directory if (!MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_DLL, g_DllPath) ) { LOG ((LOG_ERROR, "DLL path for %s is not listed in memdb; DLL not processed", e.szName)); continue; // not expected } // Obtain the working directory if (!MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_WD, g_WorkingDir) ) { LOG ((LOG_ERROR, "Working Directory for %s is not listed in memdb; DLL not processed", e.szName)); continue; // not expected } // Obtain a description if (!MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_DESC, g_DllDesc )) { StringCopy (g_DllDesc, GetString (MSG_DEFAULT_MIGDLL_DESC)); } ZeroMemory (&g_VendorInfo, sizeof (g_VendorInfo)); MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_COMPANY_NAME, g_VendorInfo.CompanyName ); MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_SUPPORT_PHONE, g_VendorInfo.SupportNumber ); MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_SUPPORT_URL, g_VendorInfo.SupportUrl ); MemDbGetEndpointValueEx ( MEMDB_CATEGORY_MIGRATION_DLL, e.szName, MEMDB_FIELD_SUPPORT_INSTRUCTIONS, g_VendorInfo.InstructionsToUser ); // // Establish connection with migisol.exe // if (!pConnectToDll()) { continue; } // // Tell migisol.exe to load migration DLL and call NT functions // rc = pRunMigrationDll(); // // If not success, return a setup failure // if (rc != ERROR_SUCCESS) { SetLastError (rc); pLogDllFailure (g_ParentWnd, MSG_MIGDLL_ERROR); } // // Disconnect from migisol.exe and kill the potentially // stalled process // pDisconnectFromDll(); TickProgressBarDelta (TICKS_MIGRATION_DLL); } while (MemDbEnumNextValue (&e)); } /* if */ #ifdef PRERELEASE if (g_ConfigOptions.DiffMode) { CHAR szMigdllDifPath[] = "c:\\migdll.dif"; if (ISPC98()) { szMigdllDifPath[0] = (CHAR)g_System32Dir[0]; } GenerateDiffOutputA (szMigdllDifPath, NULL, TRUE); } #endif return ERROR_SUCCESS; } /* ProcessMigrationDLLs */ BOOL pConnectToDll ( VOID ) { BOOL b = TRUE; TCHAR MigIsolPath[MAX_TCHAR_PATH]; g_AliveEvent = CreateEvent (NULL, FALSE, FALSE, TEXT("MigDllAlive")); DEBUGMSG_IF ((!g_AliveEvent, DBG_WHOOPS, "Could not create MigDllAlive event")); if (!g_ConfigOptions.TestDlls) { // // Establish IPC connection // wsprintf (MigIsolPath, TEXT("%s\\%s"), g_System32Dir, S_MIGISOL_EXE); b = OpenIpc (FALSE, MigIsolPath, g_DllPath, g_System32Dir); if (!b) { LOG ((LOG_WARNING, "Can't establish IPC connection for %s, wd=%s", g_DllPath, g_System32Dir)); pLogDllFailure (g_ParentWnd, MSG_CREATE_PROCESS_ERROR); } } else { g_hLibrary = LoadLibrary (g_DllPath); // If it fails, assume the DLL does not want to be loaded if (!g_hLibrary) { LOG ((LOG_ERROR, "Cannot load %s", g_DllPath)); 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); if (!InitializeNT || !MigrateUserNT || !MigrateSystemNT) { b = FALSE; } } return b; } VOID pDisconnectFromDll ( VOID ) { if (g_AliveEvent) { CloseHandle (g_AliveEvent); g_AliveEvent = NULL; } if (!g_ConfigOptions.TestDlls) { CloseIpc(); } else { if (g_hLibrary) { FreeLibrary (g_hLibrary); g_hLibrary = NULL; } } } BOOL pGetUserFromIndex ( DWORD Index ) { USERPOSITION up; DWORD rc; g_UserOnWin9x[0] = 0; if (Index == INDEX_DEFAULT_USER) { StringCopy (g_FixedUser, g_DefaultUserName); } else if (Index == INDEX_LOGON_PROMPT) { StringCopy (g_FixedUser, S_DOT_DEFAULT); } else if (Index == INDEX_ADMINISTRATOR) { StringCopy (g_FixedUser, g_AdministratorStr); } else { rc = Win95RegGetFirstUser (&up, g_FixedUser); if (rc != ERROR_SUCCESS) { LOG ((LOG_ERROR, "Get User From Index: Win95RegGetFirstUser failed")); return FALSE; } for (Index -= INDEX_MAX ; Win95RegHaveUser(&up) && Index > 0 ; Index--) { Win95RegGetNextUser (&up, g_FixedUser); } if (!Win95RegHaveUser(&up)) { return FALSE; } StringCopy (g_UserOnWin9x, g_FixedUser); GetFixedUserName (g_FixedUser); } if (!g_UserOnWin9x[0]) { StringCopy (g_UserOnWin9x, g_FixedUser); } return TRUE; } DWORD pRunMigrationDll ( VOID ) { DWORD rc; BOOL AbortThisDll; BOOL UnloadRegKey; TCHAR UnattendFile[MAX_TCHAR_PATH]; TCHAR RootKey[MAX_REGISTRY_KEY]; TCHAR HiveFile[MAX_TCHAR_PATH]; DWORD Index; BOOL IsLogonPromptAccount; PCTSTR SourceDirArray[2]; PCTSTR I386SourceDir; PCTSTR p; TCHAR Domain[MAX_USER_NAME]; BOOL Env; PVOID Block; HKEY NewHkcu; LONG MapResult; // // Initialize unattend file and root key // wsprintf (UnattendFile, TEXT("%s\\system32\\$winnt$.inf"), g_WinDir); // // Call InitializeNT // if (ISPC98()) { I386SourceDir = JoinPaths (g_SourceDir, TEXT("NEC98")); } else { I386SourceDir = JoinPaths (g_SourceDir, TEXT("I386")); } if (!I386SourceDir) { return GetLastError(); } SourceDirArray[0] = I386SourceDir; SourceDirArray[1] = NULL; rc = pCallInitializeNt (g_WorkingDir, SourceDirArray, NULL, 0); if (rc != ERROR_SUCCESS) { return rc; } FreePathString (I386SourceDir); // // The user loop // // For each user, call DLL's MigrateUser function AbortThisDll = FALSE; Index = 0; while (!AbortThisDll) { if (Index == INDEX_LOGON_PROMPT) { wsprintf (RootKey, TEXT("HKU\\%s"), S_DOT_DEFAULT); IsLogonPromptAccount = TRUE; } else { wsprintf (RootKey, TEXT("HKU\\%s"), S_TEMP_USER_KEY); IsLogonPromptAccount = FALSE; } if (!pGetUserFromIndex (Index)) { break; } Index++; // // If the following lookup fails, it is because the user isn't supposed to // migrate, or migration failed. // if (!IsLogonPromptAccount) { if (-1 == pSetupStringTableLookUpStringEx ( g_HiveTable, g_FixedUser, STRTAB_CASE_INSENSITIVE, HiveFile, sizeof (HiveFile) ) ) { DEBUGMSG (( DBG_VERBOSE, "pRunMigrationDll: pSetupStringTableLookUpStringEx could not find name of hive for user %s", g_FixedUser )); continue; } } // // Load NT user hive // UnloadRegKey = FALSE; Env = FALSE; NewHkcu = NULL; MapResult = 0; if (!AbortThisDll) { // Don't load .default if (!IsLogonPromptAccount) { rc = RegUnLoadKey (HKEY_USERS, S_TEMP_USER_KEY); if (rc != ERROR_SUCCESS) { DumpOpenKeys (); SetLastError (rc); DEBUGMSG_IF ((rc != ERROR_INVALID_PARAMETER, DBG_ERROR, "Error unloading regkey!")); } rc = RegLoadKey (HKEY_USERS, S_TEMP_USER_KEY, HiveFile); if (rc != ERROR_SUCCESS) { SetLastError(rc); LOG (( LOG_ERROR, "Run Migration Dll: RegLoadKey could not load user hive for %s (%s)", g_FixedUser, HiveFile )); AbortThisDll = TRUE; } else { UnloadRegKey = TRUE; } } } if (!AbortThisDll) { NewHkcu = OpenRegKeyStr (RootKey); if (NewHkcu) { MapResult = RegOverridePredefKey (HKEY_CURRENT_USER, NewHkcu); if (MapResult != ERROR_SUCCESS) { LOG ((LOG_ERROR, "Can't override HKCU")); } } Env = pCreateEnvironment (&Block); if (Env) { pSetEnvironmentBlock (&Block); DestroyEnvironmentBlock (&Block); } } // Call loaded DLL's MigrateUser function if (!AbortThisDll) { if (g_DomainUserName) { p = _tcschr (g_DomainUserName, TEXT('\\')); } else { p = NULL; } if (p) { StringCopyAB (Domain, g_DomainUserName, p); } else { Domain[0] = 0; } rc = pCallMigrateUserNt ( g_WorkingDir, UnattendFile, RootKey, IsLogonPromptAccount ? TEXT("") : g_UserOnWin9x, Domain, IsLogonPromptAccount ? TEXT("") : g_FixedUser, NULL, 0 ); if (rc != ERROR_SUCCESS) { AbortThisDll = TRUE; } } // Restore predefined key if (NewHkcu && MapResult == ERROR_SUCCESS) { MapResult = RegOverridePredefKey (HKEY_CURRENT_USER, NULL); if (MapResult != ERROR_SUCCESS) { LOG ((LOG_ERROR, "Can't restore HKCU")); } CloseRegKey (NewHkcu); } // Unload temporary key if (UnloadRegKey) { UnloadRegKey = FALSE; rc = RegUnLoadKey (HKEY_USERS, S_TEMP_USER_KEY); if (rc != ERROR_SUCCESS) { DumpOpenKeys (); SetLastError (rc); DEBUGMSG_IF ((rc != ERROR_INVALID_PARAMETER, DBG_ERROR, "Error unloading regkey (second case)!")); } } } /* while */ // // System processing // Env = pCreateEnvironment (&Block); if (Env) { pSetEnvironmentBlock (&Block); DestroyEnvironmentBlock (&Block); } // Call MigrateSystemNT if (!AbortThisDll) { rc = pCallMigrateSystemNt ( g_WorkingDir, UnattendFile, NULL, 0 ); if (rc != ERROR_SUCCESS) { AbortThisDll = TRUE; } } return rc; } DWORD pFinishHandshake ( IN PCTSTR FunctionName ) { DWORD TechnicalLogId; DWORD GuiLogId; DWORD rc = ERROR_SUCCESS; BOOL b; UINT Count = 40; // about 5 minutes UINT AliveAllowance = 10; // about 30 minutes do { // // No OUT parameters on the NT side, so we don't care // about the return data // b = GetIpcCommandResults ( IPC_GET_RESULTS_NT, NULL, NULL, &rc, &TechnicalLogId, &GuiLogId ); // // If error code is returned, stuff it in setupact.log // if (b && rc != ERROR_SUCCESS) { LOG (( LOG_WARNING, "Migration DLL %s returned %u (0x%08X) in %s", g_DllDesc, rc, rc, FunctionName )); } // // Loop if no data received, but process is alive // if (!b) { if (!IsIpcProcessAlive()) { rc = ERROR_NOACCESS; break; } // continue if command was not sent yet but exe is still OK Count--; if (Count == 0) { if (WaitForSingleObject (g_AliveEvent, 0) == WAIT_OBJECT_0) { DEBUGMSG ((DBG_WARNING, "Alive allowance given to migration DLL")); AliveAllowance--; if (AliveAllowance) { Count = 24; // about 3 minutes } } if (Count == 0) { rc = ERROR_SEM_TIMEOUT; break; } } } } while (!b); if (b) { // // Recognize log messages // if (TechnicalLogId) { // // LOG message with three args: DllDesc, DllPath, User // LOG (( LOG_ERROR, (PCSTR) TechnicalLogId, g_DllDesc, g_DllPath, g_FixedUser )); } if (GuiLogId) { LOG (( LOG_ERROR, (PCSTR) GuiLogId, g_DllDesc, g_DllPath, g_FixedUser )); } } return rc; } DWORD pCallInitializeNt ( IN PCTSTR WorkingDir, IN PCTSTR *SourceDirArray, IN PVOID Reserved, IN DWORD ReservedBytes ) { DWORD rc = ERROR_SUCCESS; GROWBUFFER GrowBuf = GROWBUF_INIT; INT Count; PBYTE BufPtr; PDWORD ReservedBytesPtr; TCHAR SavedCwd [MAX_TCHAR_PATH]; if (!g_ConfigOptions.TestDlls) { __try { MultiSzAppend (&GrowBuf, WorkingDir); // // Prepare multi-sz directory list // for (Count = 0 ; SourceDirArray[Count] ; Count++) { MultiSzAppend (&GrowBuf, SourceDirArray[Count]); } MultiSzAppend (&GrowBuf, S_EMPTY); ReservedBytesPtr = (PDWORD) GrowBuffer (&GrowBuf, sizeof (ReservedBytes)); *ReservedBytesPtr = ReservedBytes; if (ReservedBytes) { BufPtr = GrowBuffer (&GrowBuf, ReservedBytes); CopyMemory (BufPtr, Reserved, ReservedBytes); } if (!SendIpcCommand ( IPC_INITIALIZE, GrowBuf.Buf, GrowBuf.End )) { LOG ((LOG_ERROR, "Call InitializeNT failed to send command")); rc = GetLastError(); __leave; } rc = pFinishHandshake (TEXT("InitializeNT")); if (rc != ERROR_SUCCESS) { LOG (( LOG_ERROR, "Call InitializeNT failed to complete handshake, rc=%u", rc )); } } __finally { FreeGrowBuffer (&GrowBuf); } } else { pSetCwd ( SavedCwd, // old WorkingDir // new ); __try { // // Prepare multi-sz directory list // for (Count = 0 ; SourceDirArray[Count] ; Count++) { MultiSzAppend (&GrowBuf, SourceDirArray[Count]); } MultiSzAppend (&GrowBuf, S_EMPTY); rc = InitializeNT (WorkingDir, (PCTSTR) GrowBuf.Buf, Reserved); FreeGrowBuffer (&GrowBuf); } __finally { SetCurrentDirectory (SavedCwd); } } return rc; } DWORD pCallMigrateUserNt ( IN PCTSTR WorkingDir, IN PCTSTR UnattendFile, IN PCTSTR RootKey, IN PCTSTR Win9xUserName, IN PCTSTR UserDomain, IN PCTSTR FixedUserName, IN PVOID Reserved, IN DWORD ReservedBytes ) { DWORD rc = ERROR_SUCCESS; GROWBUFFER GrowBuf = GROWBUF_INIT; PDWORD ReservedBytesPtr; PVOID BufPtr; TCHAR SavedCwd [MAX_TCHAR_PATH]; TCHAR UserBuf[MAX_USER_NAME * 3]; PTSTR p; TCHAR OrgUserProfilePath[MAX_TCHAR_PATH]; TCHAR UserProfilePath[MAX_TCHAR_PATH]; if (FixedUserName[0]) { GetUserProfilePath (FixedUserName, &p); StackStringCopy (UserProfilePath, p); FreePathString (p); DEBUGMSG ((DBG_VERBOSE, "Profile path for %s is %s", FixedUserName, UserProfilePath)); } else { UserProfilePath[0] = 0; } GetEnvironmentVariable (S_USERPROFILE, OrgUserProfilePath, MAX_TCHAR_PATH); SetEnvironmentVariable (S_USERPROFILE, UserProfilePath); if (!g_ConfigOptions.TestDlls) { __try { MultiSzAppend (&GrowBuf, UnattendFile); MultiSzAppend (&GrowBuf, RootKey); MultiSzAppend (&GrowBuf, Win9xUserName); MultiSzAppend (&GrowBuf, UserDomain); MultiSzAppend (&GrowBuf, FixedUserName); MultiSzAppend (&GrowBuf, UserProfilePath); ReservedBytesPtr = (PDWORD) GrowBuffer (&GrowBuf, sizeof (ReservedBytes)); *ReservedBytesPtr = ReservedBytes; if (ReservedBytes) { BufPtr = GrowBuffer (&GrowBuf, ReservedBytes); CopyMemory (BufPtr, Reserved, ReservedBytes); } if (!SendIpcCommand ( IPC_MIGRATEUSER, GrowBuf.Buf, GrowBuf.End )) { LOG ((LOG_ERROR, "Call MigrateUserNT failed to send command")); rc = GetLastError(); __leave; } rc = pFinishHandshake (TEXT("MigrateUserNT")); if (rc != ERROR_SUCCESS) { LOG (( LOG_ERROR, "Call MigrateUserNT failed to complete handshake, rc=%u", rc )); } } __finally { FreeGrowBuffer (&GrowBuf); } } else { pSetCwd ( SavedCwd, // old WorkingDir // new ); __try { HINF UnattendHandle; HKEY UserRegHandle; UnattendHandle = InfOpenInfFile (UnattendFile); if (UnattendHandle == INVALID_HANDLE_VALUE) { __leave; } UserRegHandle = OpenRegKeyStr (RootKey); if (!UserRegHandle) { rc = GetLastError(); InfCloseInfFile (UnattendHandle); __leave; } // // Transfer user, user domain and fixed name to a buffer // if (Win9xUserName) { StringCopy (UserBuf, Win9xUserName); } else { UserBuf[0] = 0; } p = GetEndOfString (UserBuf) + 1; if (UserDomain) { StringCopy (p, UserDomain); } else { p[0] = 0; } p = GetEndOfString (p) + 1; if (UserDomain) { StringCopy (p, FixedUserName); } else { p[0] = 0; } // // Call the entry point // rc = MigrateUserNT ( UnattendHandle, UserRegHandle, UserBuf[0] ? UserBuf : NULL, Reserved ); CloseRegKey (UserRegHandle); InfCloseInfFile (UnattendHandle); } __finally { SetCurrentDirectory (SavedCwd); } } SetEnvironmentVariable (S_USERPROFILE, OrgUserProfilePath); return rc; } DWORD pCallMigrateSystemNt ( IN PCTSTR WorkingDir, IN PCTSTR UnattendFile, IN PVOID Reserved, IN DWORD ReservedBytes ) { DWORD rc = ERROR_SUCCESS; GROWBUFFER GrowBuf = GROWBUF_INIT; PDWORD ReservedBytesPtr; PVOID BufPtr; TCHAR SavedCwd [MAX_TCHAR_PATH]; if (!g_ConfigOptions.TestDlls) { __try { MultiSzAppend (&GrowBuf, UnattendFile); ReservedBytesPtr = (PDWORD) GrowBuffer (&GrowBuf, sizeof (ReservedBytes)); *ReservedBytesPtr = ReservedBytes; if (ReservedBytes) { BufPtr = GrowBuffer (&GrowBuf, ReservedBytes); CopyMemory (BufPtr, Reserved, ReservedBytes); } if (!SendIpcCommand (IPC_MIGRATESYSTEM, GrowBuf.Buf, GrowBuf.End)) { LOG ((LOG_ERROR, "Call MigrateSystemNT failed to send command")); rc = GetLastError(); __leave; } rc = pFinishHandshake (TEXT("MigrateSystemNT")); if (rc != ERROR_SUCCESS) { LOG (( LOG_ERROR, "Call MigrateSystemNT failed to complete handshake, rc=%u", rc )); } } __finally { FreeGrowBuffer (&GrowBuf); } } else { pSetCwd ( SavedCwd, // old WorkingDir // new ); __try { HINF UnattendHandle; UnattendHandle = InfOpenInfFile (UnattendFile); if (UnattendHandle == INVALID_HANDLE_VALUE) { rc = GetLastError(); __leave; } rc = MigrateSystemNT (UnattendHandle, Reserved); InfCloseInfFile (UnattendHandle); } __finally { SetCurrentDirectory (SavedCwd); } } return rc; }