/*++ File Description: This file contains all the functions required to add a registry entry to force execution of the system clone worker upon reboot. --*/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sysprep.h" #include "msg.h" #include "resource.h" #include #include #include #include #include #include // External functions // extern void uiDialogTopRight(HWND hwndDlg); extern HWND ghwndOemResetDlg; // // Does the user want a new SID? // BOOL NoSidGen = FALSE; BOOL SetupClPresent = TRUE; // // Does the user want confirmation? // BOOL QuietMode = FALSE; // // Do PnP re-enumeration? // BOOL PnP = FALSE; // // Do we shutdown when we're done? // BOOL NoReboot = FALSE; // // Instead of shutting down, do we reboot? // BOOL Reboot = FALSE; // // Clean out the critical devices database? // BOOL Clean = FALSE; // // Force the shutdown instead of trying to poweroff? // BOOL ForceShutdown = FALSE; // // Generating an Image for Factory Preinstallation. // BOOL Factory = FALSE; // // Reseal a machine after running FACTORY.EXE // BOOL Reseal = FALSE; // Per/Pro SKUs defaults to OOBE, Server SKUs always use MiniSetup. // Pro SKU can override OOBE with -mini to use MiniSetup also // via sysprep.inf BOOL bMiniSetup = FALSE; // // Just do an audit boot if this switch is passed in. ( '-audit' ) // BOOL Audit = FALSE; // // Rollback // BOOL bActivated = FALSE; // // Build list of pnpids in [sysprepmassstorage] section in sysprep.inf // BOOL BuildMSD = FALSE; // // Internal Define(s): // #define SYSPREP_LOG _T("SYSPREP.LOG") // Sysprep log file #define SYSPREP_MUTEX _T("SYSPREP-APP-5c9fbbd0-ee0e-11d2-9a21-0000f81edacc") // GUID used to determine if sysprep is currently running #define SYSPREP_LOCK_SLEEP 100 // Number of miliseconds to sleep in LockApplication function #define SYSPREP_LOCK_SLEEP_COUNT 10 // Number of times to sleep during LockApplication function // Path to the sysprep directory. // TCHAR g_szSysprepDir[MAX_PATH] = NULLSTR; // Path to the SYSPREP.EXE. // TCHAR g_szSysprepPath[MAX_PATH] = NULLSTR; // Path to the Sysprep log file. // TCHAR g_szLogFile[MAX_PATH] = NULLSTR; // Path to the Winbom file. // TCHAR g_szWinBOMPath[MAX_PATH] = NULLSTR; // Public functions // BOOL FProcessSwitches(); // Local functions static BOOL RenameWinbom(); static INT CleanupPhantomDevices(); static VOID CleanUpDevices(); #if !defined(_WIN64) static BOOL SaveDiskSignature(); #endif // !defined(_WIN64) // // UI stuff... // HINSTANCE ghInstance; UINT AppTitleStringId = IDS_APPTITLE; HANDLE ghWaitEvent = NULL, ghWaitThread = NULL; BOOL gbScreenSaver = FALSE; void StartWaitThread(); void EndWaitThread(); void DisableScreenSaver(BOOL *pScreenSaver); void EnableScreenSaver(BOOL *pScreenSaver); int MessageBoxFromMessageV( IN DWORD MessageId, IN DWORD CaptionStringId, IN UINT Style, IN va_list *Args ) { TCHAR Caption[512]; TCHAR Buffer[5000]; if(!LoadString(ghInstance,CaptionStringId,Caption,sizeof(Caption)/sizeof(TCHAR))) { Caption[0] = 0; } if( !FormatMessage( FORMAT_MESSAGE_FROM_HMODULE, ghInstance, MessageId, 0, Buffer, sizeof(Buffer) / sizeof(TCHAR), Args ) ) { return GetLastError(); } else { return(MessageBox(NULL,Buffer,Caption,Style)); } } int MessageBoxFromMessage( IN DWORD MessageId, IN DWORD CaptionStringId, IN UINT Style, ... ) { va_list arglist; int i = IDOK; // Default return value of "OK". // If we're in the middle of a Wait thread kill it // EndWaitThread(); if ( !QuietMode ) { va_start(arglist,Style); i = MessageBoxFromMessageV(MessageId,CaptionStringId,Style,&arglist); va_end(arglist); } return(i); } /*++ =============================================================================== Routine Description: This routine will attempt to disjoin a user from a domain, if he is already in a domain Arguments: none Return Value: TRUE - Everything is okay. FALSE - Something bad happened. =============================================================================== --*/ BOOL UnjoinNetworkDomain ( void ) { if (IsDomainMember()) { // He's a member of some domain. Let's try and remove him // from the domain. if (NO_ERROR != NetUnjoinDomain( NULL, NULL, NULL, 0 )) { return FALSE; } } return TRUE; } /*++ =============================================================================== Routine Description: This routine will setup the setup for operation on the "factory floor" The purpose here is to run a process which will facilitate the installation of updated drivers for new devices, and to boot quickly into full GUI mode for application pre-install/config, as well as to customize the system. Arguments: none Return Value: TRUE if no errors, FALSE otherise =============================================================================== --*/ BOOL SetupForFactoryFloor ( void ) { TCHAR szFactory[MAX_PATH] = NULLSTR, szSysprep[MAX_PATH] = NULLSTR, szSystem[MAX_PATH] = NULLSTR; LPTSTR lpFilePart = NULLSTR; // Make sure we have the right privileges // pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE); pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE); // We need the path to sysprep.exe and factory.exe. // if ( !( GetModuleFileName(NULL, szSysprep, AS(szSysprep)) && szSysprep[0] && GetFullPathName(szSysprep, AS(szFactory), szFactory, &lpFilePart) && szFactory[0] && lpFilePart ) ) { return FALSE; } // Replace the sysprep.exe filename with factory.exe. // StringCchCopy ( lpFilePart, AS ( szFactory ) - ( lpFilePart - szFactory ), TEXT( "factory.exe" ) ); // Make sure that sysprep.exe and factory.exe are on the system drive. // if ( ( ExpandEnvironmentStrings(TEXT("%SystemDrive%"), szSystem, AS(szSystem)) ) && ( szSystem[0] ) && ( szSystem[0] != szSysprep[0] ) ) { // Well that sucks, we should try and copy the files over to the %SystemDrive%\sysprep folder. // AddPath(szSystem, TEXT("sysprep")); lpFilePart = szSystem + lstrlen(szSystem); CreateDirectory(szSystem, NULL); // First copy factory locally. // AddPath(szSystem, TEXT("factory.exe")); CopyFile(szFactory, szSystem, FALSE); StringCchCopy ( szFactory, AS ( szFactory ), szSystem ); // Now try to copy sysprep.exe. // *lpFilePart = TEXT('\0'); AddPath(szSystem, TEXT("sysprep.exe")); CopyFile(szSysprep, szSystem, FALSE); //lstrcpy(szSysprep, szSystem); } if (!SetFactoryStartup(szFactory)) return FALSE; // Clear out any previous Factory.exe state settings RegDeleteKey(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Factory\\State"); // Remove any setting before Factory // NukeMruList(); // Rearm // if (!IsIA64() && !bActivated && (ERROR_SUCCESS != ReArm())) { // Display warning that grace period limit has reached and cannot // re-active grace period, and we continue thru. // MessageBoxFromMessage( MSG_REARM_ERROR, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); } return TRUE; } INT_PTR WaitDlgProc ( IN HWND hwndDlg, IN UINT msg, IN WPARAM wParam, IN LPARAM lParam ) { switch (msg) { case WM_INITDIALOG: { // Centers the wait dialog in parent or screen // HWND hwndParent = GetParent(hwndDlg); CenterDialogEx(hwndParent, hwndDlg); // If no parent then make sure this is visible // if (hwndParent == NULL) SetForegroundWindow(hwndDlg); // Play the animation // Animate_Open(GetDlgItem(hwndDlg,IDC_ANIMATE),MAKEINTRESOURCE(IDA_CLOCK_AVI)); Animate_Play(GetDlgItem(hwndDlg,IDC_ANIMATE),0,-1,-1); } break; } return (BOOL) FALSE; } DWORD WaitThread(LPVOID lpVoid) { HWND hwnd; if ( hwnd = CreateDialog(ghInstance, MAKEINTRESOURCE(IDD_WAIT), ghwndOemResetDlg, (DLGPROC) WaitDlgProc) ) { MSG msg; HANDLE hEvent = (HANDLE) lpVoid; ShowWindow(hwnd, SW_SHOWNORMAL); while ( MsgWaitForMultipleObjects(1, &hEvent, FALSE, INFINITE, QS_ALLINPUT) == (WAIT_OBJECT_0 + 1) ) { while ( PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) ) { TranslateMessage(&msg); DispatchMessage(&msg); } } DestroyWindow(hwnd); } else GetLastError(); return 0; } void StartWaitThread() { // Create a dialog to show progress is being made. // DWORD dwThread; // Disable the toplevel Oemreset dialog // if (ghwndOemResetDlg) EnableWindow(ghwndOemResetDlg, FALSE); if ( ghWaitEvent = CreateEvent(NULL, TRUE, FALSE, TEXT("SYSPREP_EVENT_WAIT"))) ghWaitThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) WaitThread, (LPVOID) ghWaitEvent, 0, &dwThread); } void EndWaitThread() { // Kill the Status Dialog. // if ( ghWaitEvent ) SetEvent(ghWaitEvent); // Try and let the thread terminate nicely. // if ( ghWaitThread ) WaitForSingleObject(ghWaitThread, 2000); // Clear the handles // ghWaitEvent = NULL; ghWaitThread = NULL; // Enable the toplevel OemReset dialog // if (ghwndOemResetDlg) EnableWindow(ghwndOemResetDlg, TRUE); } /*++ =============================================================================== Routine Description: This is the error callback handler for SetDefaultOEMApps() =============================================================================== --*/ void ReportSetDefaultOEMAppsError(LPCTSTR pszAppName, LPCTSTR pszIniVar) { MessageBoxFromMessage( MSG_SETDEFAULTS_NOTFOUND, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL, pszAppName, pszIniVar); } /*++ =============================================================================== Routine Description: This routine will perform the tasks necessary to reseal the machine, readying it to be shipped to the end user. Arguments: BOOL fIgnoreFactory - ignores if factory floor was run Return Value: TRUE if no errors, FALSE otherwise =============================================================================== --*/ BOOL ResealMachine ( void ) { // Make sure privileges have been set // pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE); pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE); // Prepare the machine to be hardware independent. // if (!FPrepareMachine()) { MessageBoxFromMessage( MSG_REGISTRY_ERROR, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); return FALSE; } // // Cleanup registry no matter what since factorymode=yes can set this // and winbom.ini can set this and sysprep -factory can set this, or else // PnP will hang on FactoryPreInstallInProgress being set. // CleanupRegistry(); // Clean up the factory mess. // RegDelete(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon", L"AutoAdminLogon"); SHDeleteKey(HKEY_LOCAL_MACHINE, L"Software\\Microsoft\\Factory"); // Rearm // if (!IsIA64() && !bActivated && (ERROR_SUCCESS != ReArm())) { // Display warning that grace period limit has reached and cannot // re-active grace period, and we continue thru. // MessageBoxFromMessage( MSG_REARM_ERROR, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); } #if defined(_WIN64) // // For EFI machines set the boot timeout to 5 seconds so that developers get a chance to see // the boot menu and have the option to boot to the EFI shell, CD, or other menu options, // for development purposes. // ChangeBootTimeout(5); #else ChangeBootTimeout(0); // reset the timeout to 0 secs #endif // !defined(_WIN64) // // First part of reseal. // AdjustFiles(); // // Second part of reseal. // // This is common reseal code used by both Riprep and Sysprep. // These happen whether or not factory floor was run before. // if (!FCommonReseal()) { MessageBoxFromMessage( MSG_COMMON_ERROR, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); return FALSE; } // ISSUE-2000/06/06-DONALDM // We need to handle the network configuration problem for factory more cleanly // We need to define what the network state is when factory first comes up, and // what the network state is for final customer delivery. Simply disjoining from // a domain during reseal is probably not enough... // // if( !UnjoinNetworkDomain()) // { // // We failed to disjoin. Our only option is to // // inform the user and bail. // MessageBoxFromMessage( MSG_DOMAIN_INCOMPATIBILITY, // AppTitleStringId, // MB_OK | MB_ICONSTOP | MB_TASKMODAL ); // return FALSE; // } // // Set default middleware applications. // if (!SetDefaultOEMApps(g_szWinBOMPath)) { // SetDefaultApplications will do its own MessageBoxFromMessage // with more detailed information return FALSE; } // Call functions in published SYSPREP_.C file that we skiped when the // FACTORY option was selected // ISSUE-2000/06/05-DONALDM - We need to really decide about how to handle network settings for // the factory case. I think we don't need this call, becase we should have // already dealt with networking settings when FACTORY.EXE ran. // // RemoveNetworkSettings(NULL); return TRUE; } // Macro for processing command line options. // Setting bVar to 1 (not to 'TRUE') because we need it for mutually exclusive option checks below. // #define CHECK_PARAM(lpCmdLine, lpOption, bVar) if ( LSTRCMPI(lpCmdLine, lpOption) == 0 ) bVar = 1 // // Parse command line parameters // static BOOL ParseCmdLine() { DWORD dwArgs; LPTSTR *lpArgs; BOOL bError = FALSE; BOOL bHelp = FALSE; if ( (dwArgs = GetCommandLineArgs(&lpArgs) ) && lpArgs ) { LPTSTR lpArg; DWORD dwArg; // We want to skip over the first argument (it is the path // to the command being executed. // if ( dwArgs > 1 ) { dwArg = 1; lpArg = *(lpArgs + dwArg); } else lpArg = NULL; // Loop through all the arguments. // while ( lpArg && !bError ) { // Now we check to see if the first char is a dash or forward slash. // if ( *lpArg == _T('-') || *lpArg == _T('/')) { LPTSTR lpOption = CharNext(lpArg); // This is where you add command line options that start with a dash (-). // CHECK_PARAM( lpOption, _T("quiet"), QuietMode); CHECK_PARAM( lpOption, _T("nosidgen"), NoSidGen); CHECK_PARAM( lpOption, _T("pnp"), PnP); CHECK_PARAM( lpOption, _T("noreboot"), NoReboot); CHECK_PARAM( lpOption, _T("reboot"), Reboot); CHECK_PARAM( lpOption, _T("clean"), Clean); CHECK_PARAM( lpOption, _T("forceshutdown"), ForceShutdown); CHECK_PARAM( lpOption, _T("factory"), Factory); CHECK_PARAM( lpOption, _T("reseal"), Reseal); CHECK_PARAM( lpOption, _T("mini"), bMiniSetup); CHECK_PARAM( lpOption, _T("audit"), Audit); CHECK_PARAM( lpOption, _T("activated"), bActivated); CHECK_PARAM( lpOption, _T("bmsd"), BuildMSD); CHECK_PARAM( lpOption, _T("?"), bHelp); } else if ( *lpArg ) { bError = TRUE; } // Setup the pointer to the next argument in the command line. // if ( ++dwArg < dwArgs ) lpArg = *(lpArgs + dwArg); else lpArg = NULL; } // Make sure to free the two buffers allocated by the GetCommandLineArgs() function. // FREE(*lpArgs); FREE(lpArgs); } if (bError || bHelp) { // Set the quiet switch in this case so we display the error. // Note that we return FALSE and exit the application following this. // QuietMode = FALSE; MessageBoxFromMessage( MSG_USAGE, AppTitleStringId, MB_OK | MB_TASKMODAL ); return FALSE; } // // Now look at the switches passed in and make sure that they are consistent. // If they are not, display an error message and quit, unless we're in quiet // mode where we do not display any error messages. // // // Check that the shutdown options are not conflicting with each other. if ( (NoReboot + Reboot + ForceShutdown) > 1 ) { bError = TRUE; } // These top-level options are exclusive: -bmsd, -clean, -audit, -factory, -reseal. // else if ( (BuildMSD + Clean + Audit + Factory + Reseal) > 1 ) { bError = TRUE; } // For Clean or BuildMSD none of the options except -quiet are valid. // else if ( Clean || BuildMSD ) { if ( NoSidGen || PnP || NoReboot || Reboot || ForceShutdown || bMiniSetup || bActivated ) { bError = TRUE; } } else if ( Audit ) { if ( NoSidGen || PnP || bMiniSetup || bActivated ) { bError = TRUE; } } else if ( Factory ) { if ( PnP || bMiniSetup ) { bError = TRUE; } } else if ( Reseal ) { // If -pnp is specified -mini must have been specified unless we're running on server or ia64 (because // later we force bMiniSetup to be true on server and ia64. // if ( PnP && !bMiniSetup && !(IsServerSKU() || IsIA64()) ) { bError = TRUE; } } // If there was some inconsistency in the switches specified put up // an error message. if ( bError ) { // Reset the quiet switch in this case so we display the error. // Note that we return FALSE and exit the application following this. // QuietMode = FALSE; MessageBoxFromMessage( MSG_USAGE_COMBINATIONS, AppTitleStringId, MB_OK | MB_TASKMODAL | MB_ICONERROR); return FALSE; } // Force MiniSetup on IA64 and Servers. // if (IsIA64() || IsServerSKU()) { bMiniSetup = TRUE; } else if ( IsPersonalSKU() ) { if ( bMiniSetup ) { // Can't specify mini-setup for personal sku // MessageBoxFromMessage( MSG_NO_MINISETUP, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); bMiniSetup = FALSE; } if ( PnP ) { // Can't specify -pnp because we're not running mini-setup on personal sku. // MessageBoxFromMessage( MSG_NO_PNP, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); PnP = FALSE; } } // // If we're cleaning up the critical device database, // then we'll be wanting to set some additional flags. // if (Clean || BuildMSD) { QuietMode = TRUE; NoReboot = TRUE; } return !bError; } BOOL IsFactoryPresent( VOID ) /*++ =============================================================================== Routine Description: This routine tests to see if FACTORY.EXE is present on the machine. FACTORY.EXE will be required to run on reboot, so if it's not here, we need to know. Arguments: None. Return Value: TRUE - FACTORY.EXE is present. FALSE - FACTORY.EXE is not present. =============================================================================== --*/ { WCHAR FileName[MAX_PATH]; // Attempt to locate FACTORY.EXE // if (GetModuleFileName(NULL, FileName, MAX_PATH)) { if (PathRemoveFileSpec(FileName)) { OPKAddPathN(FileName, TEXT("FACTORY.EXE"), AS ( FileName )); if (FileExists(FileName)) return TRUE; } } return FALSE; } void PowerOff(BOOL fForceShutdown) { SYSTEM_POWER_CAPABILITIES spc; ULONG uiFlags = EWX_POWEROFF; ZeroMemory(&spc, sizeof(spc)); // Make sure we have privilege to shutdown // pSetupEnablePrivilege(SE_SHUTDOWN_NAME,TRUE); // // Use flag else query system for power capabilities // if (fForceShutdown) uiFlags = EWX_SHUTDOWN; else if (NT_SUCCESS(NtPowerInformation(SystemPowerCapabilities, NULL, 0, &spc, sizeof(spc)))) { // // spc.SystemS1 == sleep 1 // spc.SystemS2 == sleep 2 // spc.SystemS3 == sleep 3 // spc.SystemS4 == hibernate support // spc.SystemS5 == poweroff support // if (spc.SystemS5) { // ACPI capable uiFlags = EWX_POWEROFF; } else { // Non-ACPI uiFlags = EWX_SHUTDOWN; } } ExitWindowsEx(uiFlags|EWX_FORCE, SYSPREP_SHUTDOWN_FLAGS); } int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow ) /*++ =============================================================================== Routine Description: This routine is the main entry point for the program. We do a bit of error checking, then, if all goes well, we update the registry to enable execution of our second half. =============================================================================== --*/ { DWORD dwVal; HKEY hKey; LPTSTR lpFilePart = NULL; INITCOMMONCONTROLSEX icex; LPTSTR lpAppName = NULL; ghInstance = hInstance; SetErrorMode(SEM_FAILCRITICALERRORS); memset(&icex, 0, sizeof(icex)); icex.dwSize = sizeof(icex); icex.dwICC = ICC_PROGRESS_CLASS|ICC_ANIMATE_CLASS; InitCommonControlsEx(&icex); // We need the path to sysprep.exe and where it is located. // GetModuleFileName(NULL, g_szSysprepPath, AS(g_szSysprepPath)); if ( GetFullPathName(g_szSysprepPath, AS(g_szSysprepDir), g_szSysprepDir, &lpFilePart) && g_szSysprepDir[0] && lpFilePart ) { // Chop off the file name. // *lpFilePart = NULLCHR; } // If either of those file, we must quit (can't imagine that every happening). // if ( ( g_szSysprepPath[0] == NULLCHR ) || ( g_szSysprepDir[0] == NULLCHR ) ) { // TODO: Log this failure. // // LogFile(WINBOM_LOGFILE, _T("\n")); return 0; } // Need the full path to the log file. // StringCchCopy ( g_szLogFile, AS ( g_szLogFile ), g_szSysprepDir); AddPath(g_szLogFile, SYSPREP_LOG); // Attempt to aquire a lock on the application // if ( !LockApplication(TRUE) ) { // Let the user know that we are busy // MessageBoxFromMessage( MSG_ALREADY_RUNNING, AppTitleStringId, MB_OK | MB_ICONINFORMATION | MB_SYSTEMMODAL ); return 0; } // // Check to see if we are allowed to run on this build of the OS // if ( !OpklibCheckVersion( VER_PRODUCTBUILD, VER_PRODUCTBUILD_QFE ) ) { MessageBoxFromMessage( MSG_NOT_ALLOWED, AppTitleStringId, MB_OK | MB_ICONERROR | MB_SYSTEMMODAL ); return 0; } // Ensure that the user has privilege/access to run this app. if(!pSetupIsUserAdmin() || !pSetupDoesUserHavePrivilege(SE_SHUTDOWN_NAME) || !pSetupDoesUserHavePrivilege(SE_BACKUP_NAME) || !pSetupDoesUserHavePrivilege(SE_RESTORE_NAME) || !pSetupDoesUserHavePrivilege(SE_SYSTEM_ENVIRONMENT_NAME)) { MessageBoxFromMessage( MSG_NOT_AN_ADMINISTRATOR, AppTitleStringId, MB_OK | MB_ICONSTOP | MB_TASKMODAL ); LockApplication(FALSE); return 0; } // Check the command line if( !ParseCmdLine() ) { LockApplication(FALSE); return 0; } // Determines whether we can run SidGen. If not quit the application // // Make sure setupcl.exe is present in the system32 directory, if we need // to use it. if( !(SetupClPresent = IsSetupClPresent()) && !NoSidGen ) { MessageBoxFromMessage( MSG_NO_SUPPORT, AppTitleStringId, MB_OK | MB_ICONSTOP | MB_TASKMODAL ); LockApplication(FALSE); return 1; } // Put up a dialog to identify ourselves and make sure the user // really wants to do this. if ( IDCANCEL == MessageBoxFromMessage( MSG_IDENTIFY_SYSPREP, AppTitleStringId, MB_OKCANCEL| MB_ICONEXCLAMATION | MB_SYSTEMMODAL ) ) { LockApplication(FALSE); return 0; } // Allocate memory for window // if ( (lpAppName = AllocateString(NULL, IDS_APPNAME)) && *lpAppName ) { ghwndOemResetDlg = FindWindow(NULL, lpAppName); // Free up the allocated memory // FREE(lpAppName); } DisableScreenSaver(&gbScreenSaver); // // Call RenameWinbom() once to initialize it. First time it is called it will check the factory // state registry key for the current winbom.ini that we are using. The second time it gets called it // will actually perform the rename if necessary. Make sure that the first time this gets called // for intialization it is before LocateWinBom() because LocateWinBom() populates the registry with // the winbom it finds. // RenameWinbom(); // Need full path to winbom too. It is not an error if the file is // not found. (It is optional.) // LocateWinBom(g_szWinBOMPath, AS(g_szWinBOMPath), g_szSysprepDir, INI_VAL_WBOM_TYPE_FACTORY, LOCATE_NORMAL); // Process switches // if ( !FProcessSwitches() && !ghwndOemResetDlg) { ShowOemresetDialog(hInstance); } EnableScreenSaver(&gbScreenSaver); // Unlock application and free up memory // LockApplication(FALSE); return 0; } // Factory Preinstall now also prepares the machine // BOOL FDoFactoryPreinstall() { HKEY hKey; DWORD dwVal; if (!IsFactoryPresent()) { MessageBoxFromMessage( MSG_NO_FACTORYEXE, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); return FALSE; } // Setup factory.exe for factory floor // if (!SetupForFactoryFloor()) { MessageBoxFromMessage( MSG_SETUPFACTORYFLOOR_ERROR, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); return FALSE; } // Prepare machine to be hardware independent for factory floor // if (!FPrepareMachine()) { MessageBoxFromMessage( MSG_REGISTRY_ERROR, AppTitleStringId, MB_OK | MB_ICONERROR | MB_TASKMODAL ); return FALSE; } // Set the boot timeout for boot on factory floor if (!ChangeBootTimeout( 1 )) return FALSE; return TRUE; } // Prepares the machine to be hardware independent // BOOL FPrepareMachine() { TCHAR szSysprepInf[MAX_PATH] = TEXT(""); // // Make sure we've got the required privileges to update the registry. // pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE); pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE); // Build path to sysprep.inf from where sysprep.exe is located // if (GetModuleFileName(NULL, szSysprepInf, MAX_PATH)) { PathRemoveFileSpec(szSysprepInf); OPKAddPathN(szSysprepInf, TEXT("sysprep.inf"), AS ( szSysprepInf ) ); } // Disable System Restore // DisableSR(); // Make sure we're not a member of a domain. If we are, then try and // force the unjoin. // if( !UnjoinNetworkDomain()) { // We failed to disjoin. Our only option is to // inform the user and bail. MessageBoxFromMessage( MSG_DOMAIN_INCOMPATIBILITY, AppTitleStringId, MB_OK | MB_ICONSTOP | MB_TASKMODAL ); return FALSE; } #if !defined(_WIN64) // Set the boot disk signature in the registry. The mount manager uses this // to avoid a PNP pop-up after imaging. // if ( !SaveDiskSignature() ) { return FALSE; } #endif // !defined(_WIN64) // Determine if we should set the BigLba support in registry // if ( !SetBigLbaSupport(szSysprepInf) ) { return FALSE; } // Determine if we should remove the tapi settings // if ( !RemoveTapiSettings(szSysprepInf) ) { return FALSE; } // Set OEMDuplicatorString if (!SetOEMDuplicatorString(szSysprepInf)) return FALSE; // If we want to regenerate the SID's on the next boot do it. // if ( NoSidGen ) { // Remember that we didn't generate SIDs // RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_SIDGEN, 0); } else { if ( PrepForSidGen() ) { // Write out registry value so that we know that we've regenerated SIDs. // RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_SIDGEN, 1); // Set this registry key, only UpdateSecurityKeys can remove this key // RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_SIDGENHISTORY, 1); } else { return FALSE; } } // If Mass Storage Devices were installed, clean up the ones not being used. // Note: We only want to CleanUpDevices() if we are resealing. This is the equivalent of // automatically running "sysprep -clean" on reseal if we know that we need to do it. // if ( RegCheck(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE)) { if ( Reseal ) { // Clean the critical device database, since we might have put some // HDC and network drivers in there during factory floor from PopulateDeviceDatabase() CleanUpDevices(); // Remove this key because we just ran CleanUpDevices(). // RegDelete(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE); } } else { BOOL fPopulated = FALSE; // Set up Hardware independence for mass storage controllers. // BuildMassStorageSection(FALSE); if (!PopulateDeviceDatabase(&fPopulated)) return FALSE; // Write out signature value to know that we have built the mass-storage section. // if ( fPopulated && !RegSetDword(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE, 1) ) return FALSE; } // Remove network settings/card last so any errors during Device Database won't loose // networking. // if (!RemoveNetworkSettings(szSysprepInf)) return FALSE; return TRUE; } // Reseal and Factory should behave the same according to the shutdown path. // void DoShutdownTypes() { pSetupEnablePrivilege(SE_SHUTDOWN_NAME,TRUE); if (Reboot) ExitWindowsEx(EWX_REBOOT|EWX_FORCE, SYSPREP_SHUTDOWN_FLAGS); else if (NoReboot) PostQuitMessage(0); else PowerOff(ForceShutdown); // Default } // Process action switches return TRUE if processed // BOOL FProcessSwitches() { // There are currently 4 basic operating modes for SYSPREP: // 1) Factory floor mode. This mode is new for Whistler and will not completly // clone the system, but will prep the system for OEM factory floor installation // 2) Clean mode. In this mode, sysprep will clean up the critical device database // 3) Reseal mode. This is the complement to factory mode which will "complete" the // cloning process after factory floor mode has been used. // 4) "Audit" mode. The system just executes an audit boot. Used to restart the system // at the end of factory.exe processing. // These are just flags for reseal // if (Reseal) { StartWaitThread(); // Ensure that we're running on the right OS. // if( !CheckOSVersion() ) { MessageBoxFromMessage( MSG_OS_INCOMPATIBILITY, AppTitleStringId, MB_OK | MB_ICONSTOP | MB_TASKMODAL ); return TRUE; } // Reseal the machine // if (!ResealMachine()) { MessageBoxFromMessage( MSG_RESEAL_ERROR, AppTitleStringId, MB_OK | MB_ICONSTOP | MB_TASKMODAL ); return TRUE; } // Rename the current winbom so we don't use it again. // RenameWinbom(); // Shutdown or reboot? DoShutdownTypes(); EndWaitThread(); return TRUE; } else if (Factory) { StartWaitThread(); // Set Factory to start on next boot and prepare for imaging // if (!FDoFactoryPreinstall()) return TRUE; // Rename the current winbom so we don't use it again. // RenameWinbom(); // Shutdown or reboot? DoShutdownTypes(); EndWaitThread(); return TRUE; } else if (Clean) { CleanUpDevices(); // Remove this key because we just ran CleanUpDevices(). // RegDelete(HKLM, REGSTR_PATH_SYSPREP, REGSTR_VAL_MASS_STORAGE); return TRUE; } else if (Audit) { // Prepare for pseudo factory but get back to audit. // if ( RegCheck(HKLM, REGSTR_PATH_SYSTEM_SETUP, REGSTR_VALUE_AUDIT) ) { TCHAR szFactoryPath[MAX_PATH] = NULLSTR; // Going into Audit mode requires Factory.exe and winbom.ini // to exist. // if (FGetFactoryPath(szFactoryPath)) { SetFactoryStartup(szFactoryPath); DoShutdownTypes(); } else { LogFile(g_szLogFile, MSG_NO_FACTORYEXE); MessageBoxFromMessage( MSG_NO_FACTORYEXE, IDS_APPTITLE, MB_OK | MB_ICONERROR | MB_TASKMODAL ); } } else { LogFile(g_szLogFile, IDS_ERR_FACTORYMODE); } return TRUE; } else if (BuildMSD) { StartWaitThread(); BuildMassStorageSection(TRUE /* Force build */); EndWaitThread(); return TRUE; } // Return False to show the UI // Reseal = Factory = Clean = Audit = 0; return FALSE; } BOOL LockApplication(BOOL bState) { static HANDLE hMutex; BOOL bReturn = FALSE, bBail = FALSE; DWORD dwSleepCount = 0; // We want to lock the application // if ( bState ) { // Check to see if we can create the mutex and that the mutex did not // already exist // while ( !bReturn && (dwSleepCount < SYSPREP_LOCK_SLEEP_COUNT) && !bBail) { SetLastError(ERROR_SUCCESS); if ( hMutex = CreateMutex(NULL, FALSE, SYSPREP_MUTEX) ) { if ( GetLastError() == ERROR_ALREADY_EXISTS ) { CloseHandle(hMutex); hMutex = NULL; dwSleepCount++; Sleep(SYSPREP_LOCK_SLEEP); } else { // Application successfully created lock // bReturn = TRUE; } } else { bBail = TRUE; } } } else if ( hMutex ) { CloseHandle(hMutex); hMutex = NULL; bReturn = TRUE; } // Return whether or not the lock/unlock was successful // return bReturn; } // // Shutdown or Reboot the machine // VOID ShutdownOrReboot(UINT uFlags, DWORD dwReserved) { // Enable privileges for shutdown // EnablePrivilege(SE_SHUTDOWN_NAME, TRUE); // Shutdown or Reboot the machine // ExitWindowsEx(uFlags|EWX_FORCE, dwReserved); } // Remember the Screen Saver state and to disable it during Sysprep // void DisableScreenSaver(BOOL *pScreenSaver) { SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, (PVOID)pScreenSaver, 0); if (*pScreenSaver == TRUE) { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, FALSE, 0, SPIF_SENDWININICHANGE); } } // Remember the Screen Saver state and to re-enable it after Sysprep // void EnableScreenSaver(BOOL *pScreenSaver) { if (*pScreenSaver == TRUE) { SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, TRUE, 0, SPIF_SENDWININICHANGE); } } // Rename the old winbom when going into factory or reseal so that we don't use it again by mistake. static BOOL RenameWinbom() { BOOL bRet = TRUE; static LPTSTR lpszWinbom = NULL; static BOOL bInitialized = FALSE; if ( !bInitialized ) { // Only bother to try if we are in audit mode and there // is a winbom in use. // if ( RegCheck(HKLM, _T("SYSTEM\\Setup"), _T("AuditInProgress")) ) { lpszWinbom = RegGetExpand(HKLM, _T("SOFTWARE\\Microsoft\\Factory\\State"), _T("Winbom")); } bInitialized = TRUE; } else if ( lpszWinbom ) { // Make sure the winbom in the registry exists. // if ( *lpszWinbom && FileExists(lpszWinbom) ) { LPTSTR lpszExtension; TCHAR szBackup[MAX_PATH]; DWORD dwExtra; // At this point, if we don't rename the file then it // means there was an error. // bRet = FALSE; // Copy the full path to the winbom into our own buffer. // lstrcpyn(szBackup, lpszWinbom, AS(szBackup)); // Get a pointer to the extension of the file name. // if ( lpszExtension = StrRChr(szBackup, NULL, _T('.')) ) { // Set the extension pointer to after the '.' character. // lpszExtension = CharNext(lpszExtension); // See how many characters are in the current extension. // if ( (dwExtra = lstrlen(lpszExtension)) < 3 ) { // There is less then a 3 character extension, so // we need some extra space for our 3 digit one. // dwExtra = 3 - dwExtra; } else { // If there are already at least 3 characters in // the exension, then no more space is required. // dwExtra = 0; } } else { // No extension, so we need 4 characters extra for // the '.' and the 3 digit extension. // dwExtra = 4; } // Make sure there is enough room for our extension to be // added to our buffer. // if ( ( lstrlen(lpszWinbom) < AS(szBackup) ) && ( lstrlen(szBackup) + dwExtra < AS(szBackup) ) ) { DWORD dwNum = 0; // If there is no extension, add the dot. // if ( NULL == lpszExtension ) { // Add our '.' to the end of the string, and set the // extension pointer past it. // lpszExtension = szBackup + lstrlen(szBackup); *lpszExtension = _T('.'); lpszExtension = CharNext(lpszExtension); } // Try to find out new file name. Keep increasing our // number from 000 until we find a name that doesn't exist. // do { StringCchPrintf ( lpszExtension, AS ( szBackup ) - ( szBackup - lpszExtension), _T("%3.3d"), dwNum); } while ( ( FileExists(szBackup) ) && ( ++dwNum < 1000 ) ); // If we found a name that doesn't exist, rename // the winbom. // if ( dwNum < 1000 ) { // If the move works, then return success. // bRet = MoveFile(lpszWinbom, szBackup); } } } // Free the buffer allocated. // FREE(lpszWinbom); } // Return TRUE if we didn't need to rename the winbom, // or we were able to do so successfully. // return bRet; } #if !defined(_WIN64) static BOOL SaveDiskSignature() { BOOL bRet = FALSE; WCHAR szBuf[MAX_PATH] = NULLSTR; HANDLE hDisk; DWORD dwBytesReturned = 0; TCHAR cDriveLetter; szBuf[0] = NULLCHR; if ( GetWindowsDirectory(szBuf, AS(szBuf)) && szBuf[0] ) { // We only need the drive letter from this. cDriveLetter = szBuf[0]; StringCchPrintf ( szBuf, AS ( szBuf ), _T("\\\\.\\%c:"), cDriveLetter); } else { return FALSE; } // Attempt to open the file // hDisk = CreateFile( szBuf, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); // Check to see if we were able to open the disk // if ( INVALID_HANDLE_VALUE == hDisk ) { bRet = FALSE; DbgPrint("SaveDiskSignature(): Unable to open file on %ws. Error (%lx)\n", szBuf, GetLastError()); } else { PDRIVE_LAYOUT_INFORMATION_EX pLayoutInfoEx = NULL; ULONG lengthLayoutEx = 0; DbgPrint("SaveDiskSignature(): Successfully opened file on %ws\n", szBuf); lengthLayoutEx = sizeof(DRIVE_LAYOUT_INFORMATION_EX) + (sizeof(PARTITION_INFORMATION_EX) * 128); pLayoutInfoEx = (PDRIVE_LAYOUT_INFORMATION_EX) MALLOC( lengthLayoutEx ); if ( pLayoutInfoEx ) { // Attempt to get the drive layout // bRet = DeviceIoControl( hDisk, IOCTL_DISK_GET_DRIVE_LAYOUT_EX, NULL, 0, pLayoutInfoEx, lengthLayoutEx, &dwBytesReturned, NULL ); // Check the status of the drive layout // if ( bRet ) { // Only do this on MBR disks // if ( PARTITION_STYLE_MBR == pLayoutInfoEx->PartitionStyle ) { // Only set this value on MBR disks. // if ( !RegSetDword(HKEY_LOCAL_MACHINE, REGSTR_PATH_SYSTEM_SETUP, REGSTR_VAL_DISKSIG, pLayoutInfoEx->Mbr.Signature) ) { DbgPrint("SaveDiskSignature(): Cannot write disk signature to registry\n."); bRet = FALSE; } } else { // bRet = TRUE at this point. DbgPrint("SaveDiskSignature(): Not supported on GPT disks.\n"); } } else { DbgPrint("SaveDiskSignature(): Unable to open IOCTL on %ws. Error (%lx)\n", szBuf, GetLastError()); } // Clean up. Macro checks for NULL; // FREE( pLayoutInfoEx ); } else { bRet = FALSE; } CloseHandle( hDisk ); } return bRet; } #endif // !defined(_WIN64) // // Helper function for CleanupPhantomDevices. Decides whether it is ok to remove // certain PNP devices. // BOOL CanDeviceBeRemoved( HDEVINFO DeviceInfoSet, PSP_DEVINFO_DATA DeviceInfoData, PTSTR DeviceInstanceId ) { BOOL bCanBeRemoved = TRUE; if (_tcsicmp(DeviceInstanceId, TEXT("HTREE\\ROOT\\0")) == 0) { // // The device has the DeviceInstanceId of HTREE\ROOT\0 then it is the // root of the device tree and can NOT be removed! // bCanBeRemoved = FALSE; } else if (_tcsnicmp(DeviceInstanceId, TEXT("SW\\"), lstrlen(TEXT("SW\\"))) == 0) { // // If the DeviceInstanceId starts with SW\\ then it is a swenum (software // enumerated) device and should not be removed. // bCanBeRemoved = FALSE; } else if (IsEqualGUID(&(DeviceInfoData->ClassGuid), &GUID_DEVCLASS_LEGACYDRIVER)) { // // If the device is of class GUID_DEVCLASS_LEGACYDRIVER then do not // uninstall it. // bCanBeRemoved = FALSE; } return bCanBeRemoved; } // // Cleans up phantom PNP devices. This is useful for cleaning up devices that existed // on the machine that was imaged but do not exist on the target machine. // static INT CleanupPhantomDevices( VOID ) { HDEVINFO DeviceInfoSet; HDEVINFO InterfaceDeviceInfoSet; SP_DEVINFO_DATA DeviceInfoData; SP_DEVICE_INTERFACE_DATA DeviceInterfaceData; INT DevicesRemoved = 0; INT MemberIndex, InterfaceMemberIndex; DWORD Status, Problem; CONFIGRET cr; TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN]; // // Get a list of all the devices on this machine, including present (live) // and not present (phantom) devices. // DeviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_ALLCLASSES ); if (DeviceInfoSet != INVALID_HANDLE_VALUE) { DeviceInfoData.cbSize = sizeof(DeviceInfoData); MemberIndex = 0; // // Enumerate through the list of devices. // while (SetupDiEnumDeviceInfo(DeviceInfoSet, MemberIndex++, &DeviceInfoData )) { // // Check if this device is a Phantom // cr = CM_Get_DevNode_Status(&Status, &Problem, DeviceInfoData.DevInst, 0 ); if ((cr == CR_NO_SUCH_DEVINST) || (cr == CR_NO_SUCH_VALUE)) { // // This is a phantom. Now get the DeviceInstanceId so we // can display/log this as output. // if (SetupDiGetDeviceInstanceId(DeviceInfoSet, &DeviceInfoData, DeviceInstanceId, sizeof(DeviceInstanceId)/sizeof(TCHAR), NULL)) { if (CanDeviceBeRemoved(DeviceInfoSet, &DeviceInfoData, DeviceInstanceId)) { #ifdef DEBUG_LOGLOG LOG_Write(L"CLEANUP: %s will be removed.\n", DeviceInstanceId); #endif // // Call DIF_REMOVE to remove the device's hardware // and software registry keys. // if (SetupDiCallClassInstaller(DIF_REMOVE, DeviceInfoSet, &DeviceInfoData )) { DevicesRemoved++; } else { #ifdef DEBUG_LOGLOG LOG_Write(L"CLEANUP: Error 0x%X removing phantom\n", GetLastError()); #endif } } } } } SetupDiDestroyDeviceInfoList(DeviceInfoSet); } return DevicesRemoved; } // Cleans unused services and phantom PNP devices. // static VOID CleanUpDevices() { // Cleanup the services that we installed in the [SysprepMassStorage] section. // CleanDeviceDatabase(); // Cleanup phantom devices. // CleanupPhantomDevices(); }