/*++ Copyright (c) 1995 Microsoft Corporation Module Name: winbom.c Abstract: Process the WinBOM (Windows Bill-of-Materials) file during OEM/SB pre-installation. Task performed will be: Download updated device drivers from NET Process OOBE info Process User/Customer specific settings Process OEM user specific customization Process Application pre-installations Author: Donald McNamara (donaldm) 2/8/2000 Revision History: - Added preinstall support to ProcessWinBOM: Jason Lawrence (t-jasonl) 6/7/2000 --*/ #include "factoryp.h" // // Defined Value(s): // // Time out in milliseconds to wait for the dialog thread to finish. // #define DIALOG_WAIT_TIMEOUT 2000 // // Internal Function Prototype(s): // static BOOL SetRunKey(BOOL bSet, LPDWORD lpdwTickCount); /*++ =============================================================================== Routine Description: BOOL ProcessWinBOM This routine will process all sections of the WinBOM Arguments: lpszWinBOMPath - Buffer containing the fully qualified path to the WINBOM file lpStates - Array of states that will be processed. cbStates - Number of states in the states array. Return Value: TRUE if no errors were encountered FALSE if there was an error =============================================================================== --*/ BOOL ProcessWinBOM(LPTSTR lpszWinBOMPath, LPSTATES lpStates, DWORD cbStates) { STATE stateCurrent, stateStart = stateUnknown; LPSTATES lpState; STATEDATA stateData; BOOL bQuit, bErr = FALSE, bLoggedOn = GET_FLAG(g_dwFactoryFlags, FLAG_LOGGEDON), bServer = IsServer(), bPerf = GET_FLAG(g_dwFactoryFlags, FLAG_LOG_PERF), bStatus = !GET_FLAG(g_dwFactoryFlags, FLAG_NOUI), bInit; HKEY hKey; HWND hwndStatus = NULL; DWORD dwForReal, dwStates, dwStatus = 0; LPSTATUSNODE lpsnTemp = NULL; // Get the current state from the registry. // if ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_FACTORY_STATE, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS ) { DWORD dwType = REG_DWORD, dwValue = 0, cbValue = sizeof(DWORD); if ( RegQueryValueEx(hKey, _T("CurrentState"), NULL, &dwType, (LPBYTE) &dwValue, &cbValue) == ERROR_SUCCESS ) { stateStart = (STATE) dwValue; } RegCloseKey(hKey); } // Set the static state data. // stateData.lpszWinBOMPath = lpszWinBOMPath; // We loop through the states twice. The first time is just // a quick run through to see what states to add to the status // dialog. The second time is the "real" time when we actually // run each state. // for ( dwForReal = 0; dwForReal < 2; dwForReal++ ) { // Reset these guys every time. // bInit = TRUE; bQuit = FALSE, dwStates = cbStates; lpState = lpStates; stateCurrent = stateStart; // If this is the real thing we need to handle the status dialog stuff. // if ( dwForReal ) { // Create the dialog if we want to display the UI. // if ( bStatus && dwStatus ) { STATUSWINDOW swAppList; // Start by zeroing out the structure. // ZeroMemory(&swAppList, sizeof(swAppList)); // Now copy the title into the structure. // if ( swAppList.lpszDescription = AllocateString(NULL, IDS_APPTITLE) ) { lstrcpyn(swAppList.szWindowText, swAppList.lpszDescription, AS(swAppList.szWindowText)); FREE(swAppList.lpszDescription); } // Get the description string. // swAppList.lpszDescription = AllocateString(NULL, IDS_STATUS_DESC); // Load the main icon for the status window. // swAppList.hMainIcon = LoadIcon(g_hInstance, MAKEINTRESOURCE(IDI_FACTORYPRE)); // Set the screen cordinates for the status window. // swAppList.X = -10; swAppList.Y = 10; // Now actually create the status window. // hwndStatus = StatusCreateDialog(&swAppList, lpsnTemp); // Clean up any allocated memory. // FREE(swAppList.lpszDescription); } // Delete the node list as we don't need it anymore // because the status window is already displayed. // if ( lpsnTemp ) { StatusDeleteNodes(lpsnTemp); } } // Process WINBOM states, until we reach the finished state. // do { // Set or advance to the next state. // if ( bInit ) { // Now if we are logged on, we need to advance to that state. // if ( bLoggedOn ) { while ( dwStates && ( lpState->state != stateLogon ) ) { lpState++; dwStates--; } } // If we don't know the current state, set it to the first one. // if ( stateCurrent == stateUnknown ) { stateCurrent = lpState->state; } // Make sure we don't do the init code again. // bInit = FALSE; } else { if ( stateCurrent == lpState->state ) { // Advance and save the current state. // stateCurrent = (++lpState)->state; // Set the current state in the registry (only if doing this for real). // if ( dwForReal && ( RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_FACTORY_STATE, 0, KEY_ALL_ACCESS, &hKey) == ERROR_SUCCESS ) ) { RegSetValueEx(hKey, _T("CurrentState"), 0, REG_DWORD, (LPBYTE) &stateCurrent, sizeof(DWORD)); RegCloseKey(hKey); } } else { // Just advanced to the next state. // lpState++; } } // Decrement our size counter to make sure we don't go past // the end of our array. // dwStates--; // Only execute this state if it isn't a one time only state or // our current state is the same one we are about to execute. // // Also don't execute this state if we are running on server and // it should not be. // if ( ( !GET_FLAG(lpState->dwFlags, FLAG_STATE_ONETIME) || ( stateCurrent == lpState->state ) ) && ( !GET_FLAG(lpState->dwFlags, FLAG_STATE_NOTONSERVER) || ( !bServer ) ) ) { // First reset any state data in the structure // that might be left over from a previous state call. // stateData.state = lpState->state; stateData.bQuit = FALSE; // If this is for real, then do the state stuff. // if ( dwForReal ) { LPTSTR lpStateText; BOOL bStateErr = FALSE, bSwitchCode = TRUE; DWORD dwTickStart = 0, dwTickFinish = 0; // Get the friendly name for the state if there is one. // lpStateText = lpState->nFriendlyName ? AllocateString(NULL, lpState->nFriendlyName) : NULL; // Log that we are starting this state (special case the log on case becase // we don't want to log that we are starting it twice. // if ( ( lpStateText ) && ( !bLoggedOn || ( stateLogon != lpState->state ) ) ) { FacLogFile(2, IDS_LOG_STARTINGSTATE, lpStateText); } // Get the starting tick count. // dwTickStart = GetTickCount(); // See if the state has a function. // if ( lpState->statefunc ) { // Run the state function. // bStateErr = !lpState->statefunc(&stateData); // Get the finish tick count. // dwTickFinish = GetTickCount(); } // Jump to the code for this state if there is one. We should avoid // putting code here. For now just the logon and finish states are // in the switch statement because they are very simple and crucial // to how the state loop works. All the other states just do some // work that we don't care about or need to have any knowledge of. // switch ( lpState->state ) { case stateLogon: // If we haven't logged on yet, we need to quit when we get to this state. // if ( !bLoggedOn ) { // Set the run key so we get kicked off again after we log on. // bStateErr = !SetRunKey(TRUE, &dwTickStart); bQuit = TRUE; // Don't want to log the perf yet, so set these to FALSE and zero. // bSwitchCode = FALSE; dwTickFinish = 0; } else { // Clear the run keys and get the original tick count. // if ( ( bStateErr = !SetRunKey(FALSE, &dwTickStart) ) && ( 0 == dwTickStart ) ) { // If for some reason we were not able to get the tick count // from the registry, just set these to FALSE and zero so // we don't try to log the perf. // bSwitchCode = FALSE; dwTickFinish = 0; } } break; case stateFinish: // Nothing should ever really go in the finished state. We just // set bQuit to true so we exit out. // bQuit = TRUE; break; default: bSwitchCode = FALSE; } // Check if code in the switch statement was run. // if ( bSwitchCode ) { // Get the finish tick count again. // dwTickFinish = GetTickCount(); } // Log that we are finished with this state. // if ( lpStateText ) { // Write out that we have finished this state (unless this is the // first time for the logon state, we don't want to log that it finished // twice unless there is an error the first time). // if ( bStateErr ) { FacLogFile(0 | LOG_ERR, IDS_ERR_FINISHINGSTATE, lpStateText); } else if ( bLoggedOn || ( stateLogon != lpState->state ) ) { FacLogFile(2, IDS_LOG_FINISHINGSTATE, lpStateText); } // See if we actually ran any code. // if ( dwTickFinish && bPerf ) { // Calculate the difference in milleseconds. // if ( dwTickFinish >= dwTickStart ) { dwTickFinish -= dwTickStart; } else { dwTickFinish += ( 0xFFFFFFFF - dwTickStart ); } // Write out to the log the time this state took. // FacLogFile(0, IDS_LOG_STATEPERF, lpStateText, dwTickFinish / 1000, dwTickFinish - ((dwTickFinish / 1000) * 1000)); } // Free the friendly name. // FREE(lpStateText); } // If there is status text, we should increment past it. // if ( hwndStatus && GET_FLAG(lpState->dwFlags, FLAG_STATE_DISPLAYED) ) { StatusIncrement(hwndStatus, !bStateErr); } // Check to see if the state failed. // if ( bStateErr ) { // State function failed, set the error var and see if we need // to quit proccessing states. // bErr = TRUE; if ( GET_FLAG(lpState->dwFlags, FLAG_STATE_QUITONERR) || GET_FLAG(g_dwFactoryFlags, FLAG_STOP_ON_ERROR) ) { bQuit = TRUE; } } } else { // Always reset the displayed flag first. // RESET_FLAG(lpState->dwFlags, FLAG_STATE_DISPLAYED); // Only need to do more if we want to display the UI. // if ( bStatus ) { LPTSTR lpszDisplay; // Only if there is a friendly name can we display it. // if ( lpState->nFriendlyName && ( lpszDisplay = AllocateString(NULL, lpState->nFriendlyName) ) ) { BOOL bDisplay; // First use the state display function (if there is one) to figure out // if this item is to be displayed or not. // // The function can set bQuit in the state structure just like when it actually // runs. We will catch this below and make this the last item in the list. // bDisplay = lpState->displayfunc ? lpState->displayfunc(&stateData) : FALSE; // Jump to the display code for this state to see if it is really displayed // or should be the last item in the list. We should avoid putting code // here. For now just the logon and finish states are in the switch // statement because they are very simple and crucial to how the state // loop works for the display dialog. All the other states make their own // determination if they should be displayed or not. // switch ( lpState->state ) { case stateLogon: // If we haven't logged on yet, we need to quit when we get to this state. // if ( !bLoggedOn ) { // Set the quit so this is the last item listed before logon. // bQuit = TRUE; } else { // Not going to show this state after logon, only before. We can change // this if we think we should show it as the first item after logon, but // it doesn't really matter. // bDisplay = FALSE; } break; case stateFinish: // We just set the quit so we make sure and exit out. // bQuit = TRUE; break; } // Now check to see if we really want to display this state. // if ( bDisplay ) { StatusAddNode(lpszDisplay, &lpsnTemp); SET_FLAG(lpState->dwFlags, FLAG_STATE_DISPLAYED); dwStatus++; } // Free the friendly name. // FREE(lpszDisplay); } } } // If they want to quit, set bQuit. // if ( stateData.bQuit ) { bQuit = TRUE; } } } while ( !bQuit && dwStates ); } // If we created the status window, end it now. // if ( hwndStatus ) { StatusEndDialog(hwndStatus); } return !bErr; } BOOL DisplayAlways(LPSTATEDATA lpStateData) { return TRUE; } static BOOL SetRunKey(BOOL bSet, LPDWORD lpdwTickCount) { HKEY hKey; BOOL bRet = FALSE; DWORD dwDis; // First need to open the key either way. // if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, REGSTR_PATH_RUN, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDis) == ERROR_SUCCESS ) { if ( bSet ) { TCHAR szCmd[MAX_PATH + 32]; // Set us to run after we get logged on. // lstrcpyn(szCmd, g_szFactoryPath, AS ( szCmd ) ); if ( FAILED ( StringCchCat ( szCmd, AS ( szCmd ), _T(" -logon") ) ) ) { FacLogFileStr(3, _T("StringCchCat failed %s %s\n"), szCmd, _T(" -logon") ); } if ( RegSetValueEx(hKey, _T("AuditMode"), 0, REG_SZ, (CONST LPBYTE) szCmd, ( lstrlen(szCmd) + 1 ) * sizeof(TCHAR)) == ERROR_SUCCESS ) bRet = TRUE; } else { // Delete the run key so we aren't left there. // if ( RegDeleteValue(hKey, _T("AuditMode")) == ERROR_SUCCESS ) bRet = TRUE; } RegCloseKey(hKey); } // Open the key where we save some state info. // if ( RegCreateKeyEx(HKEY_LOCAL_MACHINE, REG_FACTORY_STATE, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &hKey, &dwDis) == ERROR_SUCCESS ) { if ( bSet ) { // Set the tick count so we know how long the logon takes. // if ( RegSetValueEx(hKey, _T("TickCount"), 0, REG_DWORD, (CONST LPBYTE) lpdwTickCount, sizeof(DWORD)) != ERROR_SUCCESS ) bRet = FALSE; } else { DWORD dwType, cbValue = sizeof(DWORD); // Read and delete the run key so it isn't left there. // if ( ( RegQueryValueEx(hKey, _T("TickCount"), NULL, &dwType, (LPBYTE) lpdwTickCount, &cbValue) != ERROR_SUCCESS ) || ( REG_DWORD != dwType ) ) { *lpdwTickCount = 0; bRet = FALSE; } else RegDeleteValue(hKey, _T("TickCount")); } RegCloseKey(hKey); } return bRet; }