591 lines
22 KiB
C
591 lines
22 KiB
C
|
/*++
|
||
|
|
||
|
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;
|
||
|
}
|