windows-nt/Source/XPSP1/NT/base/ntsetup/syssetup/syspnp.c

7025 lines
252 KiB
C
Raw Permalink Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (c) 1995-2001 Microsoft Corporation
Module Name:
syspnp.c
Abstract:
Device installation routines.
Author:
Jaime Sasson (jaimes) 6-Mar-1997
Revision History:
--*/
#include "setupp.h"
#pragma hdrstop
//
// Provide extern references to device (setup) class GUIDs instantiated in
// clasinst.c.
//
#include <devguid.h>
//
// Define and initialize a global variable, GUID_NULL
// (from coguid.h)
//
#include <initguid.h>
//
// UpdateDriverForPlugAndPlayDevices constants
//
#include <newdev.h>
DEFINE_GUID(GUID_NULL, 0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
#define PNP_NEW_HW_PIPE TEXT("\\\\.\\pipe\\PNP_New_HW_Found")
#define PNP_CREATE_PIPE_EVENT TEXT("PNP_Create_Pipe_Event")
#define PNP_BATCH_PROCESSED_EVENT TEXT("PNP_Batch_Processed_Event")
#define PNP_PIPE_TIMEOUT 180000
#ifdef PRERELEASE
//
// legacy-phase1 can take a long time in certain cases
//
#define PNP_LEGACY_PHASE1_TIMEOUT 2*60*1000
#define PNP_LEGACY_PHASE2_TIMEOUT 1*60*1000
#define PNP_LEGACY_PHASE3_TIMEOUT 1*60*1000
#define PNP_ENUM_TIMEOUT 1*60*1000
#define RUNONCE_TIMEOUT 1*60*1000
#define RUNONCE_THRESHOLD 20 // * RUNONCE_TIMEOUT
#else // PRERELEASE
//
// legacy-phase1 can take a long time in certain cases
//
#define PNP_LEGACY_PHASE1_TIMEOUT 4*60*1000
#define PNP_LEGACY_PHASE2_TIMEOUT 2*60*1000
#define PNP_LEGACY_PHASE3_TIMEOUT 2*60*1000
#define PNP_ENUM_TIMEOUT 2*60*1000
#define RUNONCE_TIMEOUT 2*60*1000
#define RUNONCE_THRESHOLD 20 // * RUNONCE_TIMEOUT
#endif // PRERELEASE
//
// Declare a private INF key string recogized only by syssetup during device
// installation...
//
PWSTR szSyssetupPnPFlags = L"SyssetupPnPFlags";
//
// ...and define the flags that are valid for this value
//
#define PNPFLAG_DONOTCALLCONFIGMG 0x00000001
#define SETUP_OEM_LDR_DEVICE 0x00000001
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Remove the #define below, after the network guys (jameelh) fix
// the network class installer. This should happen before beta2
//
#define BB_PNP_NETWORK_TIMEOUT 10*60*1000
#define BB_NETWORK_GUID_STRING L"{4D36E972-E325-11CE-BFC1-08002BE10318}"
#ifdef PNP_DEBUG_UI
#define UNKNOWN_DEVICE_ICON_INDEX 18
typedef struct _LISTBOX_ITEM {
HDEVINFO DevInfoSet;
SP_DEVINFO_DATA DeviceInfoData;
INT IconIndex;
WCHAR DeviceDescription[ LINE_LEN ];
} *PLISTBOX_ITEM, LISTBOX_ITEM;
typedef struct _DEVINFOSET_ELEMENT {
struct _DEVINFOSET_ELEMENT *Next;
HDEVINFO DevInfoSet;
GUID SetGuid;
} *PDEVINFOSET_ELEMENT, DEVINFOSET_ELEMENT;
PDEVINFOSET_ELEMENT DevInfoSetList = NULL;
PWSTR szUnknownDevice = NULL;
#endif // PNP_DEBUG_UI
BOOL PrivilegeAlreadySet = FALSE;
//
// pnplog.txt is the file that lists the class installers
// that hang during GUI setup, so that when GUI setup is restarted,
// the offendig class installers will not be invoked again.
// This file is created during GUI setup on %SystemRoot%, and is always
// deleted during textmode setup (the file is listed on txtsetup.sif as
// "delete on upgrade").
// This file will never exist when the system first boots into GUI setup,
// immediately after the completion of textmode setup. This is
// to ensure that an old version of pnplog.txt will not affect the
// upgrade case, or a clean install of a system that is installed on
// a directory that already contains an NT system.
//
PWSTR szPnpLogFile = L"pnplog.txt";
PWSTR szEnumDevSection = L"EnumeratedDevices";
PWSTR szLegacyClassesSection = L"LegacyClasses";
PWSTR szLegacyDevSection = L"LegacyDevices";
//
// multi-sz list of files that is passed to SfcInitProt that the initial scan
// will not replace. This is used for non-signed drivers that are specified
// by F6 during textmode setup.
//
MULTISZ EnumPtrSfcIgnoreFiles = {NULL, NULL, 0};
//
// Structure for thread parameter
//
typedef struct _PNP_THREAD_PARAMS {
HWND Window;
HWND ProgressWindow;
DWORD ThreadId;
HINF InfHandle;
UINT ProgressWindowStartAtPercent;
UINT ProgressWindowStopAtPercent;
BOOL SendWmQuit;
} PNP_THREAD_PARAMS, *PPNP_THREAD_PARAMS;
typedef struct _INF_FILE_NAME {
struct _INF_FILE_NAME *Next;
PWSTR InfName;
} *PINF_FILE_NAME, INF_FILE_NAME;
typedef struct _PNP_ENUM_DEV_THREAD_PARAMS {
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
PWSTR pDeviceDescription;
PWSTR pDeviceId;
} PNP_ENUM_DEV_THREAD_PARAMS, *PPNP_ENUM_DEV_THREAD_PARAMS;
typedef struct _PNP_PHASE1_LEGACY_DEV_THREAD_PARAMS {
HDEVINFO hDevInfo;
GUID Guid;
PWSTR pClassDescription;
HWND hwndParent;
} PNP_PHASE1_LEGACY_DEV_THREAD_PARAMS, *PPNP_PHASE1_LEGACY_DEV_THREAD_PARAMS;
typedef struct _PNP_PHASE2_LEGACY_DEV_THREAD_PARAMS {
HDEVINFO hDevInfo;
HSPFILEQ FileQ;
SP_DEVINFO_DATA DeviceInfoData;
PWSTR pClassDescription;
PWSTR pDeviceId;
} PNP_PHASE2_LEGACY_DEV_THREAD_PARAMS, *PPNP_PHASE2_LEGACY_DEV_THREAD_PARAMS;
typedef struct _PNP_PHASE3_LEGACY_DEV_THREAD_PARAMS {
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
PWSTR pDeviceId;
} PNP_PHASE3_LEGACY_DEV_THREAD_PARAMS, *PPNP_PHASE3_LEGACY_DEV_THREAD_PARAMS;
//
// private cfgmgr32 API that we use
//
DWORD
CMP_WaitNoPendingInstallEvents(
IN DWORD dwTimeout
);
//
// for run-time loading of newdev.dll
//
typedef BOOL (WINAPI *ExternalUpdateDriverForPlugAndPlayDevicesW)(
HWND hwndParent,
LPCWSTR HardwareId,
LPCWSTR FullInfPath,
DWORD InstallFlags,
PBOOL bRebootRequired OPTIONAL
);
//
// Private routine prototypes
//
VOID
SortClassGuidListForDetection(
IN OUT LPGUID GuidList,
IN ULONG GuidCount,
OUT PULONG LastBatchedDetect
);
DWORD
pPhase1InstallPnpLegacyDevicesThread(
PPNP_PHASE1_LEGACY_DEV_THREAD_PARAMS ThreadParams
);
DWORD
pPhase2InstallPnpLegacyDevicesThread(
PPNP_PHASE2_LEGACY_DEV_THREAD_PARAMS ThreadParams
);
DWORD
pPhase3InstallPnpLegacyDevicesThread(
PPNP_PHASE3_LEGACY_DEV_THREAD_PARAMS ThreadParams
);
DWORD
pInstallPnpEnumeratedDeviceThread(
PPNP_ENUM_DEV_THREAD_PARAMS ThreadParams
);
BOOL
GetDeviceConfigFlags(
HDEVINFO hDevInfo,
PSP_DEVINFO_DATA pDeviceInfoData,
DWORD* pdwConfigFlags
);
BOOL
SetDeviceConfigFlags(
HDEVINFO hDevInfo,
PSP_DEVINFO_DATA pDeviceInfoData,
DWORD dwConfigFlags
);
BOOL
InstallOEMInfs(
VOID
);
#if defined(_X86_)
VOID
SfcExcludeMigratedDrivers (
VOID
);
#endif
BOOL
CallRunOnceAndWait();
BOOL
MarkPnpDevicesAsNeedReinstall(
);
ULONG
SyssetupGetPnPFlags(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PSP_DRVINFO_DATA pDriverInfoData
);
BOOL
IsInstalledInfFromOem(
IN PCWSTR InfFileName
);
BOOL
IsInfInLayoutInf(
IN PCWSTR InfFileName
);
#ifdef PNP_DEBUG_UI
VOID
FreeDevInfoSetList(
IN PDEVINFOSET_ELEMENT DevInfoSetList
)
/*++
Routine Description:
This function frees each element of a device info set list.
Arguments:
DevInfoSetList - The array of info sets that is to be freed.
Return Value:
None.
--*/
{
PDEVINFOSET_ELEMENT p, q;
p = DevInfoSetList;
while( p != NULL ) {
if( ( p->DevInfoSet != NULL ) &&
( p->DevInfoSet != INVALID_HANDLE_VALUE )
) {
SetupDiDestroyDeviceInfoList(p->DevInfoSet);
}
q = p->Next;
MyFree( p );
p = q;
}
}
#endif // PNP_DEBUG_UI
#ifdef PNP_DEBUG_UI
BOOL
AddDevInfoDataToDevInfoSet(
IN OUT PDEVINFOSET_ELEMENT* DevInfoSetList,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN PWSTR DeviceId,
IN HWND hwndParent
)
/*++
Routine Description:
This function adds a device info data to a device info set that contains
other device info data of the same GUID. If such info set doesn't exist,
then one is created, and it will be added to the array of info sets passed
as parameter.
Arguments:
DevInfoSetList - Pointer to the array of info sets. If this function creates
a new info set, then it will be added to this array.
DeviceInfoData - Device info data to be added to an info set that has elements
with the same GUID as the device info data.
DeviceId - Id of the device being added to an info set.
hwndParent - Handle to a top level window that may be used for UI purposes
Return Value:
BOOL - This function returns TRUE if the device info data was successfully added
to a device info set. Otherwise, it returns FALSE.
--*/
{
BOOL b;
HDEVINFO hDevInfo;
PDEVINFOSET_ELEMENT p;
for( p = *DevInfoSetList; p != NULL; p = p->Next ) {
//
// Find an info set, whose elements have the same GUID as the device
// that we want to add to the list.
//
if( IsEqualGUID( &(p->SetGuid), &(DeviceInfoData->ClassGuid) ) ) {
//
// If an info set was found, then add the device to the set.
//
if( !SetupDiOpenDeviceInfo( p->DevInfoSet,
DeviceId,
hwndParent,
DIOD_INHERIT_CLASSDRVS,
NULL ) ) {
SetupDebugPrint1( L"SETUP: SetupDiOpenDeviceInfo() failed. Error = %d", GetLastError() );
return( FALSE );
}
return( TRUE );
}
}
//
// If an info set was not foud, then create one.
//
if((hDevInfo = SetupDiCreateDeviceInfoList(&(DeviceInfoData->ClassGuid), hwndParent))
== INVALID_HANDLE_VALUE) {
SetupDebugPrint1( L"SETUP: SetupDiCreateDeviceInfoList() failed. Error = %d", GetLastError );
return( FALSE );
}
//
// Add a device info to the info set
//
if( !SetupDiOpenDeviceInfo( hDevInfo,
DeviceId,
hwndParent,
DIOD_INHERIT_CLASSDRVS,
NULL ) ) {
SetupDebugPrint1( L"SETUP: SetupDiOpenDeviceInfo() failed. Error = %d", GetLastError() );
SetupDiDestroyDeviceInfoList(hDevInfo);
return( FALSE );
}
//
// Add the newly created info set to the info set list
//
p = MyMalloc( sizeof(DEVINFOSET_ELEMENT) );
if( p == NULL ) {
SetupDebugPrint( L"SETUP: Out of memory - AddDevInfoDataToDevInfoSet()" );
SetupDiDestroyDeviceInfoList(hDevInfo);
return( FALSE );
}
p->DevInfoSet = hDevInfo;
p->SetGuid = DeviceInfoData->ClassGuid;
p->Next = *DevInfoSetList;
*DevInfoSetList = p;
return( TRUE );
}
#endif // PNP_DEBUG_UI
UINT
pOemF6ScanQueueCallback(
PVOID Context,
UINT Notification,
UINT_PTR Param1,
UINT_PTR Param2
)
{
PFILEPATHS FilePaths = (PFILEPATHS)Param1;
//
// Add the Target filename to the list of files that Sfc should ignore when
// doing it's final scan at the end of GUI setup.
//
if (Notification == SPFILENOTIFY_QUEUESCAN_EX) {
if (FilePaths->Target) {
MultiSzAppendString(&EnumPtrSfcIgnoreFiles, FilePaths->Target);
}
}
return NO_ERROR;
}
void
AddOemF6DriversToSfcIgnoreFilesList(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData
)
/*++
Routine Description:
Arguments:
hDevInfo -
pDeviceInfoData -
Return Value:
--*/
{
HSPFILEQ FileQueue = INVALID_HANDLE_VALUE;
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINSTALL_PARAMS DriverInstallParams;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
DWORD ScanResult;
//
// First check that the selected driver that we just installed is an OEM F6
// driver.
//
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
DriverInstallParams.cbSize = sizeof(SP_DRVINSTALL_PARAMS);
if (SetupDiGetSelectedDriver(hDevInfo,
pDeviceInfoData,
&DriverInfoData) &&
SetupDiGetDriverInstallParams(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInstallParams) &&
(DriverInstallParams.Flags & DNF_OEM_F6_INF)) {
//
// This is an OEM F6 driver.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if (!SetupDiGetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams)) {
goto clean0;
}
FileQueue = SetupOpenFileQueue();
if (FileQueue == INVALID_HANDLE_VALUE) {
goto clean0;
}
DeviceInstallParams.FileQueue = FileQueue;
DeviceInstallParams.Flags |= DI_NOVCP;
//
// Set the device install params to use our file queue and call
// DIF_INSTALLDEVICEFILES to build up a list of files for this device.
//
if (SetupDiSetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams) &&
SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES,
hDevInfo,
pDeviceInfoData)) {
SetupScanFileQueue(FileQueue,
SPQ_SCAN_USE_CALLBACKEX,
NULL,
pOemF6ScanQueueCallback,
NULL,
&ScanResult);
}
}
clean0:
if (FileQueue != INVALID_HANDLE_VALUE) {
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if (SetupDiGetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams)) {
DeviceInstallParams.Flags &= ~DI_NOVCP;
DeviceInstallParams.FileQueue = INVALID_HANDLE_VALUE;
SetupDiSetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams);
}
SetupCloseFileQueue(FileQueue);
}
}
BOOL
GetClassGuidForInf(
IN PCTSTR InfFileName,
OUT LPGUID ClassGuid
)
{
TCHAR ClassName[MAX_CLASS_NAME_LEN];
DWORD NumGuids;
if(!SetupDiGetINFClass(InfFileName,
ClassGuid,
ClassName,
SIZECHARS(ClassName),
NULL)) {
return FALSE;
}
if(pSetupIsGuidNull(ClassGuid)) {
//
// Then we need to retrieve the GUID associated with the INF's class name.
// (If this class name isn't installed (i.e., has no corresponding GUID),
// or if it matches with multiple GUIDs, then we abort.
//
if(!SetupDiClassGuidsFromName(ClassName, ClassGuid, 1, &NumGuids) || !NumGuids) {
return FALSE;
}
}
return TRUE;
}
BOOL
InstallPnpClassInstallers(
IN HWND hwndParent,
IN HINF InfHandle,
IN HSPFILEQ FileQ
)
{
INFCONTEXT InfContext;
UINT LineCount,LineNo;
PCWSTR SectionName = L"DeviceInfsToInstall";
PCWSTR IfExistsSectionName = L"DeviceInfsToInstallIfExists";
PCWSTR InfFileName;
GUID InfClassGuid;
HKEY hClassKey;
BOOL b = TRUE;
SC_HANDLE SCMHandle, ServiceHandle;
SERVICE_STATUS ServiceStatus;
//
// Before we do anything else, we have to make sure that the Plug&Play service is up and
// running, otherwise our ConfigMgr calls will fail, and we won't be able to migrate devices.
//
if(SCMHandle = OpenSCManager(NULL, NULL, GENERIC_READ)) {
if(ServiceHandle = OpenService(SCMHandle, L"PlugPlay", SERVICE_QUERY_STATUS)) {
while(TRUE) {
if(!QueryServiceStatus(ServiceHandle, &ServiceStatus)) {
//
// Couldn't find out the status of the Plug&Play service--hope for the best
// and trudge on ahead.
//
SetupDebugPrint1( L"SETUP: QueryServiceStatus() failed. Error = %d", GetLastError() );
SetupDebugPrint( L"SETUP: Couldn't find out the status of the Plug&Play service" );
break;
}
if(ServiceStatus.dwCurrentState == SERVICE_RUNNING) {
//
// The service is up and running, we can do our business.
//
break;
}
//
// The service hasn't started yet--print a message, then wait a second
// and try again.
//
SetupDebugPrint( L"SETUP: PlugPlay service isn't running yet--sleeping 1 second..." );
Sleep(1000);
}
CloseServiceHandle(ServiceHandle);
}
CloseServiceHandle(SCMHandle);
}
//
// Get the number of lines in the section that contains the infs whose
// classes are to be installed.
// The section may be empty or non-existant; this is not an error condition.
//
LineCount = (UINT)SetupGetLineCount(InfHandle,SectionName);
if((LONG)LineCount > 0) {
for(LineNo=0; LineNo<LineCount; LineNo++) {
if(SetupGetLineByIndex(InfHandle,SectionName,LineNo,&InfContext)
&& (InfFileName = pSetupGetField(&InfContext,1))) {
if( !SetupDiInstallClass( hwndParent,
InfFileName,
DI_NOVCP | DI_FORCECOPY,
FileQ ) ) {
SetupDebugPrint2( L"SETUP: SetupDiInstallClass() failed. Filename = %ls Error = %lx.", InfFileName, GetLastError() );
b = FALSE;
}
}
}
}
//
// Get the number of lines in the section that contains the infs whose
// classes are to be installed if they already exist.
// The section may be empty or non-existant; this is not an error condition.
//
LineCount = (UINT)SetupGetLineCount(InfHandle,IfExistsSectionName);
if((LONG)LineCount > 0) {
for(LineNo=0; LineNo<LineCount; LineNo++) {
if(SetupGetLineByIndex(InfHandle,IfExistsSectionName,LineNo,&InfContext)
&& (InfFileName = pSetupGetField(&InfContext,1))) {
//
// Check to see if this section already exists in the registry
//
if (GetClassGuidForInf(InfFileName, &InfClassGuid)) {
if (CM_Open_Class_Key(&InfClassGuid,
NULL,
KEY_READ,
RegDisposition_OpenExisting,
&hClassKey,
CM_OPEN_CLASS_KEY_INSTALLER
) == CR_SUCCESS) {
RegCloseKey(hClassKey);
//
// This class already exists so we need to reinstall it
//
if( !SetupDiInstallClass( hwndParent,
InfFileName,
DI_NOVCP | DI_FORCECOPY,
FileQ ) ) {
SetupDebugPrint2( L"SETUP: SetupDiInstallClass() failed. Filename = %ls Error = %lx.", InfFileName, GetLastError() );
b = FALSE;
}
}
}
}
}
}
return( b );
}
ULONG
FindNumberOfElementsInInfoSet(
IN HDEVINFO hDevInfo
)
{
SP_DEVINFO_DATA DeviceInfoData;
ULONG Index;
ULONG Error;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for( Index = 0;
SetupDiEnumDeviceInfo( hDevInfo, Index, &DeviceInfoData );
Index++ );
if( ( Error = GetLastError() ) != ERROR_NO_MORE_ITEMS ) {
//
// We were enable to enumerated all devices in the info set.
// This should not have happened.
//
SetupDebugPrint2( L"SETUP: SetupDiEnumDeviceInfo() failed. Index = %d, Error = %d", Index, Error );
}
return( Index );
}
BOOL
FlushFilesToDisk(
IN PWSTR RootPath
)
/*++
Routine Description:
This function flushes the cache of a particular drive, to disk.
Arguments:
Path to the root directory of the drive whose cache is to be flushed.
Return Value:
Returns TRUE if the operation succeeds, or FALSE otherwise.
--*/
{
HANDLE RootHandle;
LONG Error;
//
// Enable backup privilege.
//
if( !PrivilegeAlreadySet ) {
PrivilegeAlreadySet = pSetupEnablePrivilege(SE_BACKUP_NAME,TRUE) && pSetupEnablePrivilege(SE_RESTORE_NAME,TRUE);
}
RootHandle = CreateFile( RootPath,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
0
);
if( RootHandle == INVALID_HANDLE_VALUE ) {
SetupDebugPrint2( L"SETUP: Failed to open %ls. Error = %d", RootPath, GetLastError() );
return( FALSE );
}
//
// Flush the cache
//
Error = ( FlushFileBuffers( RootHandle ) )? ERROR_SUCCESS : GetLastError();
CloseHandle( RootHandle );
if( Error != ERROR_SUCCESS ) {
SetupDebugPrint2( L"SETUP: FlushFileBuffers() failed. Root = %ls, Error = %d", RootPath, Error );
}
return( Error == ERROR_SUCCESS );
}
BOOL
SyssetupInstallNullDriver(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData
)
/*++
Routine Description:
This function installs a the null driver for a particular device.
Arguments:
hDevInfo -
pDeviceInfoData -
Return Value:
Returns TRUE if the null driver was successfully installed.
--*/
{
SP_DEVINSTALL_PARAMS DeviceInstallParams;
DWORD Error;
PWSTR GUIDUnknownString = L"{4D36E97E-E325-11CE-BFC1-08002BE10318}";
Error = ERROR_SUCCESS;
//
// Find out if the GUID of this device is GUID_NULL.
// If it is then set it to GUID_DEVCLASS_UNKNOWN, so that after the sysstem is installed,
// the Device Manager can include this device in the device tree.
//
if(IsEqualGUID(&(pDeviceInfoData->ClassGuid), &GUID_NULL)) {
SetupDebugPrint( L"SETUP: Setting GUID_DEVCLASS_UNKNOWN for this device" );
if( !SetupDiSetDeviceRegistryProperty( hDevInfo,
pDeviceInfoData,
SPDRP_CLASSGUID,
(PBYTE)GUIDUnknownString,
(wcslen(GUIDUnknownString) + 1)*sizeof(WCHAR) ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceRegistryProperty(SPDRP_CLASSGUID) failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceRegistryProperty(SPDRP_CLASSGUID) failed. Error = %d", Error );
}
//
// In case of error we just ignore the error
//
Error = ERROR_SUCCESS;
}
} else {
WCHAR GUIDString[ 64 ];
pSetupStringFromGuid( &(pDeviceInfoData->ClassGuid), GUIDString, sizeof( GUIDString ) / sizeof( WCHAR ) );
SetupDebugPrint1( L"SETUP: GUID = %ls", GUIDString );
}
if( !SetupDiSetSelectedDriver( hDevInfo,
pDeviceInfoData,
NULL ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiSetSelectedDriver() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiSetSelectedDriver() failed. Error = %d", Error );
}
return( FALSE );
}
//
// Let the class installer/co-installers know they should be quiet.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if( !SetupDiGetDeviceInstallParams( hDevInfo,
pDeviceInfoData,
&DeviceInstallParams ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %d", Error );
}
} else {
DeviceInstallParams.Flags |= DI_QUIETINSTALL;
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_RESTART_DEVICE_ONLY;
if( !SetupDiSetDeviceInstallParams( hDevInfo,
pDeviceInfoData,
&DeviceInstallParams ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d", Error );
}
}
}
//
// First, attempt to install the null driver without setting DI_FLAGSEX_SETFAILEDINSTALL.
// Installation of LEGACY_* devices should succeed in this case.
//
// We do this through the class installer, in case it needs to clean-up
// turds from a previous installation (see RAID #266793).
//
if(SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
hDevInfo,
pDeviceInfoData)) {
//
// Istallation succeeded.
//
return( TRUE );
}
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed on first attempt. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed on first attempt. Error = %d", Error );
}
SetupDebugPrint( L"SETUP: Trying a second time with DI_FLAGSEX_SETFAILEDINSTALL set." );
//
// The first attempt to install the null driver (without setting DI_FLAGSEX_SETFAILEDINSTALL)
// failed.
// So we set the flag and try it again.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if( !SetupDiGetDeviceInstallParams( hDevInfo,
pDeviceInfoData,
&DeviceInstallParams ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %d", Error );
}
return( FALSE );
}
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_SETFAILEDINSTALL;
if( !SetupDiSetDeviceInstallParams( hDevInfo,
pDeviceInfoData,
&DeviceInstallParams ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d", Error );
}
return( FALSE );
}
if(!SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
hDevInfo,
pDeviceInfoData)) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed. Error = %d", Error );
}
return( FALSE );
}
return( TRUE );
}
BOOL
RebuildListWithoutOldInternetDrivers(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData
)
/*++
Routine Description:
This function determins whether SetupDiBuildDriverInfoList will need to be
called again with the DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS flag set. We first
call SetupDiBuildDriverInfoList without this flag to allow old Internet drivers
to be included in the best driver selection. If an old Internet driver is
selected as the best then we need to do a validity check to verify that all
the destination files are present on the system and correctly digitally signed.
If both of these are true then we can allow this old internet driver to stay
the best driver since it won't prompt for source files.
We need to do this for the case when a user is running a previous OS and they
get a better driver from Windows Update. We can't blindly replace the Windows
Update driver with the drivers in the new OS since they might not be better.
Arguments:
hDevInfo -
pDeviceInfoData -
Return Value:
Returns TRUE if the list needs to be rebuilt with the DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS
flag and FALSE otherwise.
Note if this API returns FALSE it either means the best driver was not an old
internet driver or that it was but all of it's destination files are present
and correctly digitally signed so no file copy will need to take place.
--*/
{
BOOL RebuildList = FALSE;
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINSTALL_PARAMS DriverInstallParams;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
HSPFILEQ FileQueue = INVALID_HANDLE_VALUE;
DWORD ScanResult = 0;
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
if (SetupDiGetSelectedDriver(hDevInfo, pDeviceInfoData, &DriverInfoData)) {
DriverInstallParams.cbSize = sizeof(SP_DRVINSTALL_PARAMS);
if (SetupDiGetDriverInstallParams(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInstallParams
) &&
(DriverInstallParams.Flags & DNF_OLD_INET_DRIVER)) {
//
// At this point we know that the best driver is an old Internet driver.
// Now do a validity check which will verify all the source files are
// present and digitally signed. We will also assume we need to rebuild
// the list at this point unless we pass the validity check below.
//
RebuildList = TRUE;
FileQueue = SetupOpenFileQueue();
if (FileQueue != INVALID_HANDLE_VALUE) {
//
// Tell setupapi to use our file queue.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if (SetupDiGetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams
)) {
DeviceInstallParams.Flags |= DI_NOVCP;
DeviceInstallParams.FileQueue = FileQueue;
if (SetupDiSetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams
)) {
if (SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES,
hDevInfo,
pDeviceInfoData
) &&
SetupScanFileQueue(FileQueue,
SPQ_SCAN_FILE_VALIDITY,
NULL,
NULL,
NULL,
&ScanResult
) &&
((ScanResult == 1) ||
(ScanResult == 2))) {
//
// if ScanResult is 1 or 2 then no copying needs to
// take place because all the destination files are
// in their place and digitally signed.
//
RebuildList = FALSE;
}
}
}
//
// Clear out the file queue handle from the device install params
// so that we can close the file queue.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if (SetupDiGetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams
)) {
DeviceInstallParams.Flags &= ~DI_NOVCP;
DeviceInstallParams.FileQueue = INVALID_HANDLE_VALUE;
SetupDiSetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams
);
}
SetupCloseFileQueue(FileQueue);
}
}
}
return RebuildList;
}
BOOL
pDoesExistingDriverNeedBackup(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PWSTR InfPath,
IN DWORD InfPathSize
)
/*++
Routine Description:
This function determins whether the currently install driver needs to be
backed up or not. It does this by checking if the current driver is an
oem driver, and verifying that it is not the same as the new driver
we are about to install.
Arguments:
hDevInfo -
pDeviceInfoData -
Return Value:
TRUE if the current driver needs to be backed up, FALSE otherwise.
--*/
{
BOOL bBackupCurrentDriver = FALSE;
HKEY Key = INVALID_HANDLE_VALUE;
DWORD dwType, dwData;
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
if (InfPath) {
InfPath[0] = TEXT('\0');
}
//
// Open the driver key for this device.
//
Key = SetupDiOpenDevRegKey ( hDevInfo,
pDeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_READ );
if (Key != INVALID_HANDLE_VALUE) {
//
// Get the 'InfPath' value from the registry.
//
dwType = REG_SZ;
dwData = InfPathSize;
if (RegQueryValueEx(Key,
REGSTR_VAL_INFPATH,
NULL,
&dwType,
(LPBYTE)InfPath,
&dwData) == ERROR_SUCCESS) {
//
// Check if this is an oem inf
//
if (IsInstalledInfFromOem(InfPath)) {
//
// Retrieve the name of the INF associated with the selected driver
// node.
//
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
if (SetupDiGetSelectedDriver(hDevInfo, pDeviceInfoData, &DriverInfoData)) {
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if (SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInfoDetailData,
sizeof(DriverInfoDetailData),
NULL) ||
(GetLastError() == ERROR_INSUFFICIENT_BUFFER)) {
//
// If the two INFs are not the same then this means we
// should backup the current drivers before installing
// the new drivers.
//
if (lstrcmpi(pSetupGetFileTitle(DriverInfoDetailData.InfFileName),
InfPath) != 0) {
bBackupCurrentDriver = TRUE;
}
} else {
SetupDebugPrint1( L"SETUP: SetupDiGetDriverInfoDetail() failed. Error = %d", GetLastError() );
}
} else {
SetupDebugPrint1( L"SETUP: SetupDiGetSelectedDriver() failed. Error = %d", GetLastError() );
}
}
}
RegCloseKey(Key);
}
return bBackupCurrentDriver;
}
BOOL
SelectBestDriver(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
OUT PBOOL pbOemF6Driver
)
/*++
Routine Description:
This function selects the best driver for the specified device.
It is assumed that SetupDiBuildDriverInfoList was called before calling
this API.
This API will first check the list of driver nodes for this device and
see if any have the DNF_OEM_F6_INF flag. If they do then this INF was
specified by the user during text mode setup by doing an F6. We always
want to use these drivers, even if they are not the 'best' according to
setupapi. If there are no drivers in the list with this flag then we
fall back to the default behavior of DIF_SELECTBESTCOMPATDRV.
Arguments:
hDevInfo -
pDeviceInfoData -
Return Value:
Returns the result SetupDiCallClassInstaller with DIF_SELECTBESTCOMPATDRV.
--*/
{
DWORD index;
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINSTALL_PARAMS DriverInstallParams;
BOOL bFoundOemF6Driver = FALSE;
*pbOemF6Driver = FALSE;
DriverInfoData.cbSize = sizeof(DriverInfoData);
index = 0;
//
// First go through the list of drivers and see if there is an OEM F6 driver
// in the list.
//
while (SetupDiEnumDriverInfo(hDevInfo,
pDeviceInfoData,
SPDIT_COMPATDRIVER,
index++,
&DriverInfoData
)) {
DriverInstallParams.cbSize = sizeof(SP_DRVINSTALL_PARAMS);
if (SetupDiGetDriverInstallParams(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInstallParams
) &&
(DriverInstallParams.Flags & DNF_OEM_F6_INF)) {
bFoundOemF6Driver = TRUE;
SetupDebugPrint( L"SETUP: Using Oem F6 driver for this device." );
break;
}
}
//
// If we found an Oem driver that was specified by F6 during text mode setup,
// then we will go through the list again and mark all drivers that are
// not OEM F6 drivers and BAD drivers. This way when we call
// DIF_SELECTBESTCOMPATDRV it will only select from the OEM F6 drivers.
//
if (bFoundOemF6Driver) {
*pbOemF6Driver = TRUE;
DriverInfoData.cbSize = sizeof(DriverInfoData);
index = 0;
while (SetupDiEnumDriverInfo(hDevInfo,
pDeviceInfoData,
SPDIT_COMPATDRIVER,
index++,
&DriverInfoData
)) {
DriverInstallParams.cbSize = sizeof(SP_DRVINSTALL_PARAMS);
if (SetupDiGetDriverInstallParams(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInstallParams
)) {
//
// If this driver node does not have the DNF_OEM_F6_INF flag
// then set the DNF_BAD_DRIVER flag so we will skip this
// driver later when we do DIF_SELECTBESTCOMPATDRV
//
if (!(DriverInstallParams.Flags & DNF_OEM_F6_INF)) {
DriverInstallParams.Flags |= DNF_BAD_DRIVER;
SetupDiSetDriverInstallParams(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInstallParams
);
}
}
}
}
return (SetupDiCallClassInstaller( DIF_SELECTBESTCOMPATDRV,
hDevInfo,
pDeviceInfoData ) );
}
BOOL
SkipDeviceInstallation(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN HINF InfHandle,
IN PCWSTR GUIDString
)
/*++
Routine Description:
This function determines whether or not the installation of a particular device should be skipped.
It should be skipped if the following conditions are met:
- The device is already installed;
- The GUID for the device is listed in [InstalledDevicesToSkip], in syssetup.inf
Arguments:
hDevInfo -
pDeviceInfoData -
InfHandle - System setup inf handle (syssetup.inf).
GUIDString - GUID associated to the device being checked (in string format).
Return Value:
Returns TRUE if the installation of the device should be skipped. Otherwise, it returns FALSE.
--*/
{
BOOL DeviceAlreadyInstalled;
BOOL SafeClassInstaller = TRUE;
WCHAR PropertyBuffer[ MAX_PATH + 1 ];
HKEY Key;
//
// Attempt to open the dev node's driver key
//
Key = SetupDiOpenDevRegKey ( hDevInfo,
pDeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
MAXIMUM_ALLOWED );
if( Key == INVALID_HANDLE_VALUE ) {
DeviceAlreadyInstalled = FALSE;
SetupDebugPrint( L"SETUP: Device not yet installed." );
} else {
RegCloseKey( Key );
DeviceAlreadyInstalled = TRUE;
SetupDebugPrint( L"SETUP: Device already installed." );
}
//
// In the case of MiniSetup, we're not doing an
// upgrade, so all we care about is if the device is
// already installed.
//
if( MiniSetup ) {
return( DeviceAlreadyInstalled );
}
if( DeviceAlreadyInstalled ) {
//
// If the device is already installed, then check if the class installer for this
// device is considered safe.
//
SafeClassInstaller = !SetupGetLineText( NULL,
InfHandle,
L"InstalledDevicesToSkip",
GUIDString,
PropertyBuffer,
sizeof( PropertyBuffer )/sizeof( WCHAR ),
NULL );
}
return( DeviceAlreadyInstalled && !SafeClassInstaller );
}
BOOL
PrecompileInfFiles(
IN HWND ProgressWindow,
IN ULONG StartAtPercent,
IN ULONG StopAtPercent
)
/*++
Routine Description:
This function precompiles all the infs in %SystemRoot%\inf.
and then builds the cache.
Arguments:
ProgressWindow - Handle to the progress bar.
StartAtPercent - Starting position in the progress bar.
It indicates that from position 0 to this position
the gauge is already filled.
StopAtPercent - Ending position of the progress bar.
The pnp thread should not fill the progress bar beyond
this position
Return Value:
Returns TRUE if at least one inf was pre-compiled. Otherwise, returns FALSE.
--*/
{
WCHAR SavedDirectory[ MAX_PATH + 1 ];
WCHAR InfDirectory[ MAX_PATH + 1 ];
UINT GaugeRange;
BOOL AlwaysFalse = FALSE;
PINF_FILE_NAME InfList = NULL;
PINF_FILE_NAME p;
WIN32_FIND_DATA FindData;
HANDLE FindHandle;
ULONG InfCount;
ULONG i = 0;
DWORD Result;
SetupDebugPrint( L"SETUP: Entering PrecompileInfFiles()" );
//
// Save current directory
//
GetCurrentDirectory( sizeof(SavedDirectory)/sizeof(WCHAR), SavedDirectory );
//
// Change current directory to %SystemRoot%\inf
//
Result = GetWindowsDirectory( InfDirectory, sizeof(InfDirectory)/sizeof(WCHAR) );
if( Result == 0) {
MYASSERT(FALSE);
return FALSE;
}
wcscat( InfDirectory, L"\\inf" );
SetCurrentDirectory( InfDirectory );
//
// Find total number of inf files
//
InfCount = 0;
FindHandle = FindFirstFile( L"*.inf", &FindData );
if( FindHandle != INVALID_HANDLE_VALUE ) {
do {
//
// Skip directories
//
if( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
continue;
}
p = MyMalloc( sizeof( INF_FILE_NAME ) ) ;
if( p != NULL ) {
p->InfName = pSetupDuplicateString( &FindData.cFileName[0] );
p->Next = InfList;
InfList = p;
InfCount++;
}
} while( FindNextFile( FindHandle, &FindData ) );
FindClose( FindHandle );
} else {
SetupDebugPrint1( L"SETUP: FindFirstFile( *.inf ) failed. Error = %d", GetLastError() );
}
//
// Initialize the gauge allow for pSetupInfCacheBuild step
//
GaugeRange = ((InfCount+1)*100/(StopAtPercent-StartAtPercent));
SendMessage(ProgressWindow, WMX_PROGRESSTICKS, (InfCount+1), 0);
SendMessage(ProgressWindow,PBM_SETRANGE,0,MAKELPARAM(0,GaugeRange));
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StartAtPercent/100,0);
SendMessage(ProgressWindow,PBM_SETSTEP,1,0);
//
// Prcompile each inf file
//
for( i = 0;
(
//
// Tick the gauge after we finished pre-compiling an inf.
// Note that we don't tick the gauge when i == 0 (no inf was yet processed),
// but we do tick the gauge when i == InfCount (all infs were pre-compiled).
// Note also that we use the flag AlwaysFalse, to enforce that all inf will be processed,
// even when SendMessage(PBM_SETPBIT) returns a non-zero value.
//
(i != 0) &&
SendMessage(ProgressWindow,PBM_STEPIT,0,0) &&
AlwaysFalse
) ||
(i < InfCount);
i++
) {
HINF hInf;
SetupDebugPrint1( L"SETUP: Pre-compiling file: %ls", InfList->InfName );
MYASSERT(InfList);
hInf = SetupOpenInfFile( InfList->InfName,
NULL,
INF_STYLE_WIN4,
NULL );
if( hInf != INVALID_HANDLE_VALUE ) {
SetupCloseInfFile( hInf );
} else {
DWORD Error;
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint2( L"SETUP: SetupOpenInfFile() failed. FileName = %ls, Error = %lx", InfList->InfName, Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint2( L"SETUP: SetupOpenInfFile() failed. FileName = %ls, Error = %d", InfList->InfName, Error );
}
}
//
// The file name is no longer needed
//
p = InfList;
InfList = InfList->Next;
if( p->InfName != NULL ) {
MyFree( p->InfName );
}
MyFree( p );
}
SetupDebugPrint2( L"SETUP: Total inf files = %d, total precompiled: %d", InfCount, i );
SetupDebugPrint( L"SETUP: Calling pSetupInfCacheBuild()" );
pSetupInfCacheBuild(INFCACHEBUILD_REBUILD);
//
// Make sure that at this point, the gauge area is filled up to the end of
// the area reserved for the pre-compilation of inf files.
//
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StopAtPercent/100,0);
//
// Restore current directory
//
SetCurrentDirectory( SavedDirectory );
SetupDebugPrint( L"SETUP: Leaving PrecompileInfFiles()" );
return( i != 0 );
}
BOOL
InstallLegacyDevices(
IN HWND hwndParent,
IN HWND ProgressWindow,
IN ULONG StartAtPercent,
IN ULONG StopAtPercent
)
{
ULONG Index;
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA DeviceInfoData;
ULONG Error;
WCHAR GUIDString[ 64 ];
BOOL b = TRUE;
HSPFILEQ FileQ = INVALID_HANDLE_VALUE;
LPGUID GuidList = NULL;
ULONG GuidCount = 32;
ULONG GuidIndex;
ULONG LastBatchedDetect;
ULONG GuidLB, GuidUB;
HDEVINFO* InfoSetArray = NULL;
BOOL AlwaysFalse = FALSE;
UINT GaugeRange;
HANDLE ThreadHandle = NULL;
DWORD ThreadId;
PPNP_PHASE1_LEGACY_DEV_THREAD_PARAMS Phase1Context;
PPNP_PHASE2_LEGACY_DEV_THREAD_PARAMS Phase2Context;
WCHAR PnpLogPath[ MAX_PATH + 1 ];
WCHAR LoggedDescription[ LINE_LEN + 1 ];
DWORD ScanQueueResult;
SP_DRVINFO_DATA DriverInfoData;
ULONG PnPFlags;
DWORD Result;
SetupDebugPrint( L"SETUP: Entering InstallLegacyDevices()" );
//
// Build path to pnp log file
//
Result = GetWindowsDirectory( PnpLogPath, sizeof(PnpLogPath)/sizeof(WCHAR) );
if( Result == 0) {
MYASSERT(FALSE);
return FALSE;
}
pSetupConcatenatePaths( PnpLogPath, szPnpLogFile, sizeof(PnpLogPath)/sizeof(WCHAR), NULL );
//
// Do the migration of legacy devices.
// This is a quick operation and doesn't need to use the progress window.
//
// This is now performed before installation of true PnP devices
//
// PnPInitializationThread(NULL);
GuidList = ( LPGUID )MyMalloc( sizeof( GUID ) * GuidCount );
if( !GuidList ) {
return( FALSE );
}
if ( !SetupDiBuildClassInfoList( 0,
GuidList,
GuidCount,
&GuidCount ) ) {
Error = GetLastError();
if( Error != ERROR_INSUFFICIENT_BUFFER ) {
SetupDebugPrint1( L"SETUP: SetupDiBuildClassInfoList() failed. Error = %d", Error );
MyFree( GuidList );
//
// Fill the gauge up to the end of the area reserved for legacy devices.
//
GaugeRange = 100;
SendMessage(ProgressWindow,PBM_SETRANGE,0,MAKELPARAM(0,GaugeRange));
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StopAtPercent/100,0);
SetupDebugPrint( L"SETUP: Leaving InstallLegacyDevices()" );
return( FALSE );
}
GuidList = ( LPGUID )MyRealloc( GuidList, sizeof( GUID ) * GuidCount );
if( !SetupDiBuildClassInfoList( 0,
GuidList,
GuidCount,
&GuidCount ) ) {
MyFree( GuidList );
SetupDebugPrint1( L"SETUP: SetupDiBuildClassInfoList() failed. Error = %d", Error );
//
// Fill the gauge up to the end of the area reserved for legacy devices.
//
GaugeRange = 100;
SendMessage(ProgressWindow,PBM_SETRANGE,0,MAKELPARAM(0,GaugeRange));
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StopAtPercent/100,0);
SetupDebugPrint( L"SETUP: Leaving InstallLegacyDevices()" );
return( FALSE );
}
}
//
// Sort the class GUID list based on the detection ordering specified in syssetup.inf
//
SortClassGuidListForDetection(GuidList, GuidCount, &LastBatchedDetect);
InfoSetArray = (HDEVINFO*)MyMalloc( sizeof(HDEVINFO) * GuidCount );
//
// Initialize the gauge.
// Note that since we process the device classes twice (two big 'for
// loops'), we divide the area of the gauge reserved for the gauge
// reserved for the legacy devices in 2 pieces, one for each 'for loop'.
//
GaugeRange = (2*GuidCount*100/(StopAtPercent-StartAtPercent));
SendMessage(ProgressWindow, WMX_PROGRESSTICKS, 2*GuidCount, 0);
SendMessage(ProgressWindow,PBM_SETRANGE,0,MAKELPARAM(0,GaugeRange));
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StartAtPercent/100,0);
SendMessage(ProgressWindow,PBM_SETSTEP,1,0);
//
// On our first pass, we process all the detections that can be batched
// together. Then, on subsequent passes, we process any non-batchable
// detections individually...
//
for(GuidLB = 0, GuidUB = LastBatchedDetect; GuidLB < GuidCount; GuidLB = ++GuidUB) {
//
// First, create a file queue
//
FileQ = SetupOpenFileQueue();
if( FileQ == INVALID_HANDLE_VALUE ) {
SetupDebugPrint1( L"SETUP: Failed to create file queue. Error = %d", GetLastError() );
}
for( GuidIndex = GuidLB;
(
//
// Tick the gauge after we finished processing all the devices of a particular class.
// Note that we don't tick the gauge when GuidIndex == 0 (no device was yet processed),
// but we do tick the gauge when GuidIndex == GuidCount (all devices of the last class
// were processed).
// Note also that we use the flag AlwaysFalse, to enforce that all classes will be processed,
// even when SendMessage(PBM_SETPBIT) returns a non-zero value.
//
(GuidIndex != GuidLB) &&
SendMessage(ProgressWindow,PBM_STEPIT,0,0) &&
AlwaysFalse
) ||
(GuidIndex <= GuidUB);
GuidIndex++ ) {
HDEVINFO hEmptyDevInfo = INVALID_HANDLE_VALUE;
WCHAR ClassDescription[ LINE_LEN + 1 ];
InfoSetArray[ GuidIndex ] = INVALID_HANDLE_VALUE;
ClassDescription[0] = (WCHAR)'\0';
if( !SetupDiGetClassDescription( &GuidList[ GuidIndex ],
ClassDescription,
sizeof(ClassDescription)/sizeof(WCHAR),
NULL ) ) {
SetupDebugPrint1( L"SETUP: SetupDiGetClassDescription() failed. Error = %lx", GetLastError() );
ClassDescription[0] = (WCHAR)'\0';
}
pSetupStringFromGuid( &GuidList[ GuidIndex ], GUIDString, sizeof( GUIDString ) / sizeof( WCHAR ) );
SetupDebugPrint1( L"SETUP: Installing legacy devices of class: %ls ", ClassDescription );
SetupDebugPrint2( L"SETUP: GuidIndex = %d, Guid = %ls", GuidIndex, GUIDString );
#ifndef DBG
//
// Check if this class of devices is listed as a bad class
//
LoggedDescription[0] = L'\0';
if( (GetPrivateProfileString( szLegacyClassesSection,
GUIDString,
L"",
LoggedDescription,
sizeof(LoggedDescription)/sizeof(WCHAR),
PnpLogPath ) != 0) &&
( wcslen( LoggedDescription ) != 0 )
) {
//
// Skip the installation of this class of devices
//
SetupDebugPrint1( L"SETUP: Skipping installation of devices of class: %ls", ClassDescription );
continue;
}
#endif
//
// Start the thread that actually does the initial part of the installation of the legacy devices (DIF_FIRSTTIMESETUP)
//
Phase1Context = MyMalloc( sizeof( PNP_PHASE1_LEGACY_DEV_THREAD_PARAMS ) );
Phase1Context->hDevInfo = INVALID_HANDLE_VALUE;
Phase1Context->Guid = GuidList[ GuidIndex ];
Phase1Context->pClassDescription = pSetupDuplicateString(ClassDescription);
Phase1Context->hwndParent = hwndParent;
ThreadHandle = NULL;
ThreadHandle = CreateThread( NULL,
0,
pPhase1InstallPnpLegacyDevicesThread,
Phase1Context,
0,
&ThreadId );
if(ThreadHandle) {
DWORD WaitResult;
DWORD ExitCode;
BOOL KeepWaiting;
KeepWaiting = TRUE;
while( KeepWaiting ) {
int Result;
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Fix the network timeout after the network guys fix their class installer
//
WaitResult = WaitForSingleObject( ThreadHandle,
(_wcsicmp( GUIDString, BB_NETWORK_GUID_STRING ) == 0)? BB_PNP_NETWORK_TIMEOUT :
PNP_LEGACY_PHASE1_TIMEOUT );
if( WaitResult == WAIT_TIMEOUT ) {
HANDLE hDialogEvent;
if( hDialogEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, SETUP_HAS_OPEN_DIALOG_EVENT ) ) {
//
// Setupapi is prompting the user for a file. Don't timeout.
//
CloseHandle( hDialogEvent );
KeepWaiting = TRUE;
continue;
} else {
}
//
// Class installer is hung
//
SetupDebugPrint1( L"SETUP: Class Installer appears to be hung (phase1). ClassDescription = %ls", ClassDescription );
#ifdef PRERELEASE
//
// Ask the user if he wants to skip the installation of this class of devices
//
if( !Unattended ) {
Result = MessageBoxFromMessage( hwndParent,
MSG_CLASS_INSTALLER_HUNG_FIRSTTIMESETUP,
NULL,
IDS_WINNT_SETUP,
MB_YESNO | MB_ICONWARNING,
ClassDescription );
} else {
Result = IDYES;
}
#else
Result = IDYES;
#endif
if(Result == IDYES) {
//
// User wants to skip this class of devices.
// First find out if the thread has already returned.
//
WaitResult = WaitForSingleObject( ThreadHandle, 0 );
if( WaitResult != WAIT_OBJECT_0 ) {
//
// Thread hasn't returned yet. Skip the installation of this class of devices
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: Skipping installation of legacy devices of class: %ls", ClassDescription );
b = FALSE;
//
// Remember this class so that it won't be installed if GUI setup is
// restarted
//
WritePrivateProfileString( szLegacyClassesSection,
GUIDString,
ClassDescription,
PnpLogPath );
} else{
//
// Thread has already returned.
// There is no need to skip the installation of this class of devices.
// We assume that the user decided not to skip the installation of this class,
// and next call to WaitForSingleObject will immediately return.
//
}
}
} else if( WaitResult == WAIT_OBJECT_0 ) {
//
// Device Installation Thread completed.
//
KeepWaiting = FALSE;
if( GetExitCodeThread( ThreadHandle, &ExitCode ) ) {
if( ExitCode == ERROR_SUCCESS ) {
//
// The installation was successful
//
InfoSetArray[ GuidIndex ] = Phase1Context->hDevInfo;
} else {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
b = FALSE;
}
} else {
//
// Unable to retrieve exit code. Assume success.
//
InfoSetArray[ GuidIndex ] = Phase1Context->hDevInfo;
SetupDebugPrint1( L"SETUP: GetExitCode() failed. Error = %d", GetLastError() );
SetupDebugPrint( L"SETUP: Unable to retrieve thread exit code. Assuming devices successfully installed (phase1)." );
}
//
// Deallocate all the memory that was passed to the thread.
//
MyFree(Phase1Context->pClassDescription);
MyFree(Phase1Context);
} else {
//
// Should not occur.
// In this case we don't deallocate any memory, since the thread may be running.
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: WaitForSingleObject() returned %d", WaitResult );
b = FALSE;
}
}
//
// The thread handle is no longer needed at this point
//
CloseHandle(ThreadHandle);
} else {
//
// Just do it synchronously.
//
Error = GetLastError();
SetupDebugPrint1( L"SETUP: CreateThread() failed (phase1). Error = %d", Error );
if( pPhase1InstallPnpLegacyDevicesThread(Phase1Context) != ERROR_SUCCESS ) {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
b = FALSE;
} else {
InfoSetArray[ GuidIndex ] = Phase1Context->hDevInfo;
}
//
// Deallocate the memory passed as argument
//
MyFree( Phase1Context->pClassDescription );
MyFree( Phase1Context );
}
//
// Find out if we should install devices of this class.
//
if( InfoSetArray[ GuidIndex ] == INVALID_HANDLE_VALUE ) {
//
// If we should not install this class of devices, then go process the next class.
//
continue;
}
//
// Now enumerate each device information element added to this set, registering
// and then installing the files for each one.
//
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for( Index = 0;
SetupDiEnumDeviceInfo( InfoSetArray[ GuidIndex ], Index, &DeviceInfoData );
Index++ ) {
WCHAR DevInstId[ MAX_DEVICE_ID_LEN ];
Error = ERROR_SUCCESS;
//
// Make sure the device is marked as 'Do not install'
//
if( !pSetupDiSetDeviceInfoContext( InfoSetArray[ GuidIndex ], &DeviceInfoData, FALSE ) ) {
SetupDebugPrint2( L"SETUP: pSetupDiSetDeviceInfoContext() failed. Error = %lx, Index = %d", GetLastError(), Index );
}
//
// Retrieve the string id for this device
//
DevInstId[0] = L'\0';
if( !SetupDiGetDeviceInstanceId( InfoSetArray[ GuidIndex ],
&DeviceInfoData,
DevInstId,
sizeof( DevInstId ) / sizeof( WCHAR ),
NULL ) ) {
SetupDebugPrint2( L"SETUP: Index = %d, SetupDiGetDeviceInstanceId() failed. Error = ", Index, GetLastError() );
}
#ifndef DBG
//
// Find out if this device is marked as a bad device
//
LoggedDescription[0] = L'\0';
if( (GetPrivateProfileString( szLegacyClassesSection,
DevInstId,
L"",
LoggedDescription,
sizeof(LoggedDescription)/sizeof(WCHAR),
PnpLogPath ) != 0) &&
( wcslen( LoggedDescription ) != 0 )
) {
//
// Skip the installation of this device
//
SetupDebugPrint1( L"SETUP: Skipping installation of legacy device: %ls", DevInstId );
continue;
}
#endif
SetupDebugPrint2( L"SETUP: Index = %d, DeviceId = %ls", Index, DevInstId );
Phase2Context = MyMalloc( sizeof( PNP_PHASE2_LEGACY_DEV_THREAD_PARAMS ) );
Phase2Context->hDevInfo = InfoSetArray[ GuidIndex ];
Phase2Context->FileQ = FileQ;
Phase2Context->DeviceInfoData = DeviceInfoData;
Phase2Context->pClassDescription = pSetupDuplicateString(ClassDescription);
Phase2Context->pDeviceId = pSetupDuplicateString(DevInstId);
ThreadHandle = NULL;;
ThreadHandle = CreateThread( NULL,
0,
pPhase2InstallPnpLegacyDevicesThread,
Phase2Context,
0,
&ThreadId );
if( ThreadHandle ) {
DWORD WaitResult;
DWORD ExitCode;
BOOL KeepWaiting;
KeepWaiting = TRUE;
while( KeepWaiting ) {
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Fix the network timeout after the network guys fix their class installer
//
WaitResult = WaitForSingleObject( ThreadHandle,
(_wcsicmp( GUIDString, BB_NETWORK_GUID_STRING ) == 0)? BB_PNP_NETWORK_TIMEOUT :
PNP_LEGACY_PHASE2_TIMEOUT );
if( WaitResult == WAIT_TIMEOUT ) {
int Result;
HANDLE hDialogEvent;
if( hDialogEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, SETUP_HAS_OPEN_DIALOG_EVENT ) ) {
//
// Setupapi is prompting the user for a file. Don't timeout.
//
CloseHandle( hDialogEvent );
KeepWaiting = TRUE;
continue;
} else {
}
//
// Class installer is hung
//
SetupDebugPrint1( L"SETUP: Class Installer appears to be hung (phase2). DeviceId = %ls", DevInstId );
#ifdef PRERELEASE
//
// Ask the user if he wants to skip the installation of this device
//
if( !Unattended ) {
Result = MessageBoxFromMessage( hwndParent,
MSG_CLASS_INSTALLER_HUNG,
NULL,
IDS_WINNT_SETUP,
MB_YESNO | MB_ICONWARNING,
DevInstId );
} else {
Result = IDYES;
}
#else
Result = IDYES;
#endif
if(Result == IDYES) {
//
// User wants to skip this class of devices.
// First find out if the thread has already returned.
//
WaitResult = WaitForSingleObject( ThreadHandle, 0 );
if( WaitResult != WAIT_OBJECT_0 ) {
//
// Thread hasn't returned yet. Skip the installation of this device
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: Skipping installation of legacy device (phase2). Device = %ls", DevInstId );
b = FALSE;
//
// Remember this device so that it won't be installed if GUI setup is
// restarted
//
WritePrivateProfileString( szLegacyDevSection,
DevInstId,
ClassDescription,
PnpLogPath );
} else{
//
// Thread has already returned.
// There is no need to skip the installation of this device.
// We assume that the user decided not to skip the installation of this device,
// and next call to WaitForSingleObject will immediately return.
//
}
}
} else if( WaitResult == WAIT_OBJECT_0 ) {
//
// Device Installation Thread completed.
// Find out the outcome of this phase of the installation.
//
KeepWaiting = FALSE;
if( GetExitCodeThread( ThreadHandle, &ExitCode ) ) {
if( ExitCode ) {
//
// The installation was successful
//
} else {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
b = FALSE;
}
} else {
//
// Unable to retrieve exit code. Assume success.
//
SetupDebugPrint1( L"SETUP: GetExitCode() failed. Error = %d", GetLastError() );
SetupDebugPrint( L"SETUP: Unable to retrieve thread exit code. Assuming device successfully installed (phase2)." );
}
//
// Deallocate the memory passed to the thread.
//
MyFree(Phase2Context->pClassDescription);
MyFree(Phase2Context->pDeviceId);
MyFree(Phase2Context);
} else {
//
// Should not occur.
// In this case we do not deallocate the memory passed to the thread, since the thread may be running.
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: WaitForSingleObject() returned %d", WaitResult );
b = FALSE;
}
}
//
// The thread handle is no longer needed at this point
//
CloseHandle(ThreadHandle);
} else {
//
// Unable to create the thread. Just do it syncronously
//
Error = GetLastError();
SetupDebugPrint1( L"SETUP: CreateThread() failed (phase2). Error = %d", Error );
if( !pPhase2InstallPnpLegacyDevicesThread(Phase2Context) ) {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
SetupDebugPrint( L"SETUP: Device not successfully installed (phase2)." );
b = FALSE;
}
//
// Deallocate the memory that was passed as argument.
//
MyFree( Phase2Context->pClassDescription );
MyFree( Phase2Context->pDeviceId );
MyFree( Phase2Context );
}
}
//
// Find out why SetupDiEnumDeviceInfo() failed.
//
Error = GetLastError();
if( Error != ERROR_NO_MORE_ITEMS ) {
SetupDebugPrint2( L"SETUP: Device = %d, SetupDiEnumDeviceInfo() failed. Error = %d", Index, Error );
b = FALSE;
} else {
if( Index == 0 ) {
SetupDebugPrint1( L"SETUP: DeviceInfoSet is empty. ClassDescription = %ls", ClassDescription );
}
}
}
//
// If the file queue exists, then commit it.
//
if( FileQ != INVALID_HANDLE_VALUE ) {
PVOID Context;
//
// Commit file queue
//
if(Context = InitSysSetupQueueCallbackEx(hwndParent,
INVALID_HANDLE_VALUE,
0,
0,
NULL)) {
WCHAR RootPath[ MAX_PATH + 1];
if(!SetupScanFileQueue(
FileQ,
SPQ_SCAN_FILE_VALIDITY |
SPQ_SCAN_PRUNE_COPY_QUEUE |
SPQ_SCAN_PRUNE_DELREN,
hwndParent,
NULL,
NULL,
&ScanQueueResult)) {
//
// SetupScanFileQueue should really never
// fail when you don't ask it to call a
// callback routine, but if it does, just
// go ahead and commit the queue.
//
ScanQueueResult = 0;
}
if( ScanQueueResult != 1 ){
if( !SetupCommitFileQueue(hwndParent,FileQ,SysSetupQueueCallback,Context) ) {
b = FALSE;
}
}
TermSysSetupQueueCallback(Context);
GetWindowsDirectory(RootPath,sizeof(RootPath)/sizeof(WCHAR));
RootPath[3] = L'\0';
FlushFilesToDisk( RootPath );
}
}
//
// Now that the files were already copied, call the class installer
// with DIF_INSTALLDEVICE so that we can complete the installation
// of the device.
//
for( GuidIndex = GuidLB;
(
//
// Tick the gauge after we finished processing all the devices of a particular class.
// Note that we don't tick the gauge when GuidIndex == 0 (no device was yet processed),
// but we do tick the gauge when GuidIndex == GuidCount (all devices of the last class
// were processed).
// Note also that we use the flag AlwaysFalse, to enforce that all classes will be processed,
// even when SendMessage(PBM_SETPBIT) returns a non-zero value.
//
(GuidIndex != GuidLB) &&
SendMessage(ProgressWindow,PBM_STEPIT,0,0) &&
AlwaysFalse
) ||
(GuidIndex <= GuidUB);
GuidIndex++ ) {
SP_DEVINFO_DATA TempDeviceInfoData;
if( InfoSetArray[ GuidIndex ] == INVALID_HANDLE_VALUE ) {
continue;
}
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Remove the line below after the network guys fix their class installer
//
pSetupStringFromGuid( &GuidList[ GuidIndex ], GUIDString, sizeof( GUIDString ) / sizeof( WCHAR ) );
TempDeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for( Index = 0; SetupDiEnumDeviceInfo( InfoSetArray[ GuidIndex ], Index, &TempDeviceInfoData ); Index++ ) {
WCHAR DevInstId[ MAX_DEVICE_ID_LEN ];
DWORD InstallDevice;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
PPNP_PHASE3_LEGACY_DEV_THREAD_PARAMS Phase3Context;
#ifdef PNP_DEBUG_UI
BOOL DeviceSuccessfullyInstalled;
#endif
//
// Retrieve the string id for this device
//
DevInstId[0] = L'\0';
if( !SetupDiGetDeviceInstanceId( InfoSetArray[ GuidIndex ],
&TempDeviceInfoData,
DevInstId,
sizeof( DevInstId ) / sizeof( WCHAR ),
NULL ) ) {
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstanceId() failed. Error = ", GetLastError() );
}
//
// Find out if we need to call DIF_INSTALLDEVICE for this device
//
InstallDevice = 0;
if( !pSetupDiGetDeviceInfoContext( InfoSetArray[ GuidIndex ], &TempDeviceInfoData, &InstallDevice ) ) {
SetupDebugPrint2( L"SETUP: pSetupDiSetDeviceInfoContext() failed. Error = %lx, DeviceId = %ls ", GetLastError(), DevInstId );
continue;
}
if( !InstallDevice ) {
//
// No need to install the device
//
SetupDebugPrint1( L"SETUP: Skipping device. DeviceId = %ls ", DevInstId );
continue;
}
#ifndef DBG
//
// Find out if this device is marked as a bad device
//
LoggedDescription[0] = L'\0';
if( (GetPrivateProfileString( szLegacyClassesSection,
DevInstId,
L"",
LoggedDescription,
sizeof(LoggedDescription)/sizeof(WCHAR),
PnpLogPath ) != 0) &&
( wcslen( LoggedDescription ) != 0 )
) {
//
// Skip the installation of this device
//
SetupDebugPrint1( L"SETUP: Skipping installation of legacy device: %ls", DevInstId );
continue;
}
#endif
//
// Retrieve information about the driver node selected above.
//
DriverInfoData.cbSize = sizeof( SP_DRVINFO_DATA );
if( !SetupDiGetSelectedDriver( InfoSetArray[ GuidIndex ],
&TempDeviceInfoData,
&DriverInfoData ) ) {
SetupDebugPrint1( L"SETUP: SetupDiGetSelectedDriver() failed. Error = %d", GetLastError() );
b = FALSE;
continue;
}
//
// Retrieve syssetup PnP flags (if any) the INF specifies for this
// device.
//
PnPFlags = SyssetupGetPnPFlags(InfoSetArray[ GuidIndex ],
&TempDeviceInfoData,
&DriverInfoData
);
SetupDebugPrint3( L"SETUP: Installing device: %ls, GuidIndex = %d, Index = %d", DevInstId, GuidIndex, Index );
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(!SetupDiGetDeviceInstallParams(InfoSetArray[ GuidIndex ], &TempDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %d", GetLastError() );
b = FALSE;
continue;
}
DeviceInstallParams.Flags &= ~DI_FORCECOPY;
DeviceInstallParams.Flags &= ~DI_NOVCP;
DeviceInstallParams.Flags |= DI_NOFILECOPY;
DeviceInstallParams.FileQueue = NULL;
if(PnPFlags & PNPFLAG_DONOTCALLCONFIGMG) {
//
// We _do not_ honor this flag for devices reported via
// DIF_FIRSTTIMESETUP. The intent of this flag is to
// prevent us from perturbing correctly-functioning devices
// and potentially causing problems (e.g., moving PCI
// bridges or COM ports off of their boot configs). This
// concern does not apply to legacy-detected devnodes. We
// definitely _do_ want to bring these devices on-line now,
// or else devices attached to them (whether PnP or legacy)
// won't be found and installed.
//
SetupDebugPrint( L"SETUP: Clearing PNPFLAG_DONOTCALLCONFIGMG since this is a detected device." );
PnPFlags &= ~PNPFLAG_DONOTCALLCONFIGMG;
}
if(!SetupDiSetDeviceInstallParams(InfoSetArray[ GuidIndex ], &TempDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d", GetLastError() );
b = FALSE;
continue;
}
Phase3Context = MyMalloc( sizeof( PNP_PHASE3_LEGACY_DEV_THREAD_PARAMS ) );
Phase3Context->hDevInfo = InfoSetArray[ GuidIndex ];
Phase3Context->DeviceInfoData = TempDeviceInfoData;
Phase3Context->pDeviceId = pSetupDuplicateString( DevInstId );
#ifdef PNP_DEBUG_UI
DeviceSuccessfullyInstalled = FALSE;
#endif
ThreadHandle = NULL;
ThreadHandle = CreateThread( NULL,
0,
pPhase3InstallPnpLegacyDevicesThread,
Phase3Context,
0,
&ThreadId );
if( ThreadHandle ) {
DWORD WaitResult;
DWORD ExitCode;
BOOL KeepWaiting;
KeepWaiting = TRUE;
while( KeepWaiting ) {
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Fix the network timeout after the network guys fix their class installer
//
WaitResult = WaitForSingleObject( ThreadHandle,
(_wcsicmp( GUIDString, BB_NETWORK_GUID_STRING ) == 0)? BB_PNP_NETWORK_TIMEOUT :
PNP_LEGACY_PHASE3_TIMEOUT );
if( WaitResult == WAIT_TIMEOUT ) {
int Result;
HANDLE hDialogEvent;
if( hDialogEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, SETUP_HAS_OPEN_DIALOG_EVENT ) ) {
//
// Setupapi is prompting the user for a file. Don't timeout.
//
CloseHandle( hDialogEvent );
KeepWaiting = TRUE;
continue;
} else {
}
//
// Class installer is hung
//
SetupDebugPrint1( L"SETUP: Class Installer appears to be hung (phase3). DeviceId = %ls", DevInstId );
#ifdef PRERELEASE
//
// Ask the user if he wants to skip the installation of this class of devices
//
if( !Unattended ) {
Result = MessageBoxFromMessage( hwndParent,
MSG_CLASS_INSTALLER_HUNG,
NULL,
IDS_WINNT_SETUP,
MB_YESNO | MB_ICONWARNING,
DevInstId );
} else {
Result = IDYES;
}
#else
Result = IDYES;
#endif
if(Result == IDYES) {
//
// User wants to skip this device.
// First find out if the thread has already returned.
//
WaitResult = WaitForSingleObject( ThreadHandle, 0 );
if( WaitResult != WAIT_OBJECT_0 ) {
//
// Thread hasn't returned yet. Skip the installation of this device
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: Skipping installation of legacy device (phase3). Device = %ls", DevInstId );
b = FALSE;
if( !SetupDiGetClassDescription( &GuidList[ GuidIndex ],
LoggedDescription,
sizeof(LoggedDescription)/sizeof(WCHAR),
NULL ) ) {
SetupDebugPrint1( L"SETUP: SetupDiGetClassDescription() failed. Error = %lx", GetLastError() );
//
// Assume anything for class description, since we don't really care about it,
// for logging purposes. The class description only helps identify the problematic
// device.
//
wcscpy( LoggedDescription, L"1" );
}
//
// Remember this device so that it won't be installed if GUI setup is
// restarted
//
WritePrivateProfileString( szLegacyDevSection,
DevInstId,
LoggedDescription,
PnpLogPath );
} else{
//
// Thread has already returned.
// There is no need to skip the installation of this device.
// We assume that the user decided not to skip the installation of this device,
// and next call to WaitForSingleObject will immediately return.
//
}
}
} else if( WaitResult == WAIT_OBJECT_0 ) {
//
// Device Installation Thread completed.
// Find out the outcome of this phase of the installation.
//
KeepWaiting = FALSE;
if( GetExitCodeThread( ThreadHandle, &ExitCode ) ) {
if( ExitCode ) {
//
// The installation was successful
//
#ifdef PNP_DEBUG_UI
DeviceSuccessfullyInstalled = TRUE;
#endif
} else {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
b = FALSE;
}
} else {
//
// Unable to retrieve exit code. Assume success.
//
SetupDebugPrint1( L"SETUP: GetExitCode() failed. Error = %d", GetLastError() );
SetupDebugPrint( L"SETUP: Unable to retrieve thread exit code. Assuming device successfully installed (phase3)." );
}
MyFree(Phase3Context->pDeviceId);
MyFree(Phase3Context);
} else {
//
// Should not occur
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: WaitForSingleObject() returned %d", WaitResult );
b = FALSE;
}
}
//
// The thread handle is no longer needed at this point
//
CloseHandle(ThreadHandle);
} else {
//
// Unable to create the thread. Just do it syncronously.
//
Error = GetLastError();
SetupDebugPrint1( L"SETUP: CreateThread() failed (phase3). Error = %d", Error );
if( !pPhase3InstallPnpLegacyDevicesThread(Phase3Context) ) {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
b = FALSE;
} else {
#ifdef PNP_DEBUG_UI
DeviceSuccessfullyInstalled = TRUE;
#endif
}
MyFree( Phase3Context->pDeviceId );
MyFree( Phase3Context );
}
#ifdef PNP_DEBUG_UI
if( DeviceSuccessfullyInstalled ) {
//
// Add the device to an info set that has the same class as this device.
//
if( !AddDevInfoDataToDevInfoSet( &DevInfoSetList,
&TempDeviceInfoData,
DevInstId,
hwndParent ) ) {
SetupDebugPrint1( L"SETUP: AddDevInfoDataToDevInfoSet() failed. DevId = %ls", DevInstId );
b = FALSE;
continue;
}
}
#endif // PNP_DEBUG_UI
}
Error = GetLastError();
if( Error != ERROR_NO_MORE_ITEMS ) {
SetupDebugPrint2( L"SETUP: Device = %d, SetupDiEnumDeviceInfo() failed. Error = %d", Index, Error );
b = FALSE;
}
}
//
// Get rid of the file queue, if it exists
//
if( FileQ != INVALID_HANDLE_VALUE) {
SetupCloseFileQueue(FileQ);
}
}
//
// Make sure that at this point, the gauge area is filled up to the end of
// the area reserved for the installation of legacy devices.
//
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StopAtPercent/100,0);
//
// Get rid of the list of GUIDs.
//
if( GuidList != NULL ) {
MyFree( GuidList );
}
//
// Get rid of InfoSetArray, and all info sets stored on it.
//
if( InfoSetArray != NULL ) {
for( GuidIndex = 0; GuidIndex < GuidCount; GuidIndex++ ) {
if( InfoSetArray[ GuidIndex ] != INVALID_HANDLE_VALUE ) {
SetupDiDestroyDeviceInfoList( InfoSetArray[ GuidIndex ] );
}
}
MyFree( InfoSetArray );
}
SetupDebugPrint( L"SETUP: Leaving InstallLegacyDevices()" );
return( b );
}
BOOL
InstallEnumeratedDevices(
IN HWND hwndParent,
IN HINF InfHandle,
IN HWND ProgressWindow,
IN ULONG StartAtPercent,
IN ULONG StopAtPercent
)
/*++
Routine Description:
This function enumerates and install all new PnP devices detected during
GUI setup.
Arguments:
hwndParent - Handle to a top level window that may be used for UI purposes.
InfHandle - System setup inf handle (syssetup.inf).
Return Value:
Returns TRUE if all enumerated hardware were installed successfully.
--*/
{
HANDLE hPipeEvent = NULL;
HANDLE hPipe = INVALID_HANDLE_VALUE;
ULONG Index = 0;
ULONG Error;
ULONG ulSize = 0;
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
WCHAR szBuffer[MAX_PATH];
BOOL b = TRUE;
UINT GaugeRange = 100;
WCHAR RootPath[ MAX_PATH + 1];
WCHAR PnpLogPath[ MAX_PATH + 1];
PAF_DRIVERS AfDrivers;
PAF_DRIVER_ATTRIBS SelectedAfDriver;
PSP_DEVINFO_DATA pDeviceInfoData = NULL;
ULONG PnPFlags;
HANDLE hBatchEvent = NULL;
DWORD Result;
SetupDebugPrint( L"SETUP: Entering InstallEnumeratedDevices()" );
//
// Build path to pnp log file
//
Result = GetWindowsDirectory( PnpLogPath, sizeof(PnpLogPath)/sizeof(WCHAR) );
if( Result == 0) {
MYASSERT(FALSE);
return FALSE;
}
pSetupConcatenatePaths( PnpLogPath, szPnpLogFile, sizeof(PnpLogPath)/sizeof(WCHAR), NULL );
//
// Initialize RootPath with empty string
//
RootPath[0] = L'\0';
//
// Initialize answer file table
//
AfDrivers = CreateAfDriverTable();
//
// Attempt to create the event that will be used to signal the successfull
// creation of the named pipe
//
hPipeEvent = CreateEvent( NULL, TRUE, FALSE, PNP_CREATE_PIPE_EVENT );
if (hPipeEvent == NULL) {
Error = GetLastError();
if( Error != ERROR_ALREADY_EXISTS ) {
SetupDebugPrint1( L"SETUP: CreateEvent() failed. Error = %d", Error );
b = FALSE;
goto Clean0;
}
//
// If umpnpmgr has already created the event, then just open the event
//
hPipeEvent = OpenEvent(EVENT_MODIFY_STATE,
FALSE,
PNP_CREATE_PIPE_EVENT);
if (hPipeEvent == NULL) {
SetupDebugPrint1( L"SETUP: OpenEvent() failed. Error = %d", GetLastError() );
b = FALSE;
goto Clean0;
}
}
//
// Attempt to create the event that will be used to signal the completion of
// processing of the last batch of devices.
//
hBatchEvent = CreateEvent( NULL, TRUE, FALSE, PNP_BATCH_PROCESSED_EVENT );
if (hBatchEvent == NULL) {
Error = GetLastError();
if( Error != ERROR_ALREADY_EXISTS ) {
SetupDebugPrint1( L"SETUP: CreateEvent() failed. Error = %d", Error );
b = FALSE;
goto Clean0;
}
//
// If umpnpmgr has already created the event, then just open the event
//
hBatchEvent = OpenEvent(EVENT_MODIFY_STATE,
FALSE,
PNP_BATCH_PROCESSED_EVENT);
if (hBatchEvent == NULL) {
SetupDebugPrint1( L"SETUP: OpenEvent() failed. Error = %d", GetLastError() );
b = FALSE;
goto Clean0;
}
}
//
// create the named pipe, umpnpmgr will write requests to
// this pipe if new hardware is found
//
hPipe = CreateNamedPipe(PNP_NEW_HW_PIPE,
PIPE_ACCESS_INBOUND,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE,
1, // only one connection
sizeof(szBuffer), // out buffer size
sizeof(szBuffer), // in buffer size
PNP_PIPE_TIMEOUT, // default timeout
NULL // default security
);
//
// signal the event now
//
SetEvent(hPipeEvent);
if (hPipe == INVALID_HANDLE_VALUE) {
SetupDebugPrint1( L"SETUP: CreateNamedPipe() failed. Error = %d", GetLastError() );
b = FALSE;
goto Clean0;
}
//
// Connect to the newly created named pipe.
// If umpnpmgr is not already connected to the named pipe, then ConnectNamedPipe()
// will succeed. Otherwise, it will fail with ERROR_PIPE_CONNECTED. Note however that
// this is not an error condition.
//
if (ConnectNamedPipe(hPipe, NULL) ||
((Error = GetLastError()) == ERROR_PIPE_CONNECTED) ) {
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// This is gauge related and needs to be fixed.
// We are assuming a total of 50 enumerated device.
//
BOOL AlwaysFalse = FALSE;
UINT BogusValue = 50;
//
// Initialize the gauge
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Fix this - We are assuming a fixed number of devices
//
GaugeRange = (BogusValue*100/(StopAtPercent-StartAtPercent));
SendMessage(ProgressWindow, WMX_PROGRESSTICKS, BogusValue, 0);
SendMessage(ProgressWindow,PBM_SETRANGE,0,MAKELPARAM(0,GaugeRange));
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StartAtPercent/100,0);
SendMessage(ProgressWindow,PBM_SETSTEP,1,0);
//
// listen to the named pipe by submitting read
// requests until the named pipe is broken on the
// other end.
//
for( Index = 0;
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// This is gauge related and needs to be fixed.
// We are assuming a constant number of enumerated devices
//
( (Index != 0) &&
(Index < BogusValue) &&
SendMessage(ProgressWindow,PBM_STEPIT,0,0) &&
AlwaysFalse
) || // This is a trick to force the gauge to be upgraded after a
// device is processed, and the pipe to be read.
ReadFile( hPipe,
(LPBYTE)szBuffer, // device instance id
sizeof(szBuffer),
&ulSize,
NULL );
Index++ ) {
SP_DRVINFO_DATA DriverInfoData;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
WCHAR GUIDString[ 64 ];
WCHAR ClassDescription[ LINE_LEN + 1 ];
HANDLE ThreadHandle = NULL;
DWORD ThreadId;
PPNP_ENUM_DEV_THREAD_PARAMS Context;
BOOL DeviceInstalled;
WCHAR LoggedDescription[ LINE_LEN + 1 ];
BOOL bOemF6Driver;
if (lstrlen(szBuffer) == 0) {
SetEvent(hBatchEvent);
continue;
}
SetupDebugPrint2( L"SETUP: Index = %d, DeviceId = %ls", Index, szBuffer );
//
// Check if this device is listed as a bad device
//
LoggedDescription[0] = L'\0';
if( (GetPrivateProfileString( szEnumDevSection,
szBuffer,
L"",
LoggedDescription,
sizeof(LoggedDescription)/sizeof(WCHAR),
PnpLogPath ) != 0) &&
( wcslen( LoggedDescription ) != 0 )
) {
#ifndef DBG
//
// Skip the installation of this device
//
SetupDebugPrint1( L"SETUP: Skipping installation of device %ls", szBuffer );
continue;
#endif
}
BEGIN_SECTION(LoggedDescription);
//
// Find out if we need to create an hDevinfo.
// We will need to create one if this is the first device that we are installing (Index == 0),
// or the class installer for the previous device (Index == Index - 1) is hung. If the class
// installer is hung then we cannot use the same hDevinfo, since the class installer has a lock
// on it. So we just create a new one.
// If the class installer for the previous device didn't hang, then there is no need to create
// a new hDevinfo, since we can re-use it. We do this for performance reasons.
//
if( hDevInfo == INVALID_HANDLE_VALUE ) {
//
// create a devinfo handle and device info data set to
// pass to DevInstall
//
if((hDevInfo = SetupDiCreateDeviceInfoList(NULL, hwndParent))
== INVALID_HANDLE_VALUE) {
b = FALSE;
SetupDebugPrint1( L"SETUP: SetupDiCreateDeviceInfoList() failed. Error = %d", GetLastError() );
goto Clean0;
}
pDeviceInfoData = MyMalloc(sizeof(SP_DEVINFO_DATA));
if( pDeviceInfoData == NULL ) {
b = FALSE;
SetupDebugPrint( L"SETUP: Unable to create pDeviceInfoData. MyMalloc() failed." );
goto Clean0;
}
pDeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
}
if(!SetupDiOpenDeviceInfo(hDevInfo, szBuffer, hwndParent, 0, pDeviceInfoData)) {
SetupDebugPrint1( L"SETUP: SetupDiOpenDeviceInfo() failed. Error = %d", GetLastError() );
b = FALSE;
END_SECTION(LoggedDescription);
continue;
}
//
// Answer file support: Test the answer file to see if it has a driver
// in its [DeviceDrivers] section. This overrides the NT-supplied driver,
// if one exists.
//
if (!SyssetupInstallAnswerFileDriver (
AfDrivers,
hDevInfo,
pDeviceInfoData,
&SelectedAfDriver
)) {
//
// No answer file driver, proceed with the standard device install
//
SetupDebugPrint( L"SETUP: Device was NOT installed via answer file driver" );
//
// Build a list of compatible drivers for this device
// (This call can be time consuming the first time it is called)
//
if( !SetupDiBuildDriverInfoList( hDevInfo, pDeviceInfoData, SPDIT_COMPATDRIVER ) ) {
SetupDebugPrint1( L"SETUP: SetupDiBuildDriverInfoList() failed. Error = %d", GetLastError() );
b = FALSE;
continue;
}
//
// Select the best compatible driver for this device.
//
if( !SelectBestDriver( hDevInfo,
pDeviceInfoData,
&bOemF6Driver ) ) {
Error = GetLastError();
if( Error != ERROR_NO_COMPAT_DRIVERS ) {
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed. Error = %d", Error );
b = FALSE;
END_SECTION(LoggedDescription);
continue;
}
SetupDebugPrint( L"SETUP: Compatible driver List is empty" );
SetupDebugPrint( L"SETUP: Installing the null driver for this device" );
//
// Install the null driver for this device.
// This is to avoid the "Found New Hardware" popup when the
// user first logs on after the system is installed.
//
if( !SyssetupInstallNullDriver( hDevInfo, pDeviceInfoData ) ) {
SetupDebugPrint( L"SETUP: Unable to install null driver" );
}
END_SECTION(LoggedDescription);
continue;
}
//
// Check if we need to rebuild the driver list without including the
// old Internet drivers.
//
if (RebuildListWithoutOldInternetDrivers(hDevInfo, pDeviceInfoData)) {
SetupDiDestroyDriverInfoList( hDevInfo, pDeviceInfoData, SPDIT_COMPATDRIVER );
//
// OR in the DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS flag so that we don't include
// old internet drivers in the list that build.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if (SetupDiGetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams
))
{
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS;
SetupDiSetDeviceInstallParams(hDevInfo,
pDeviceInfoData,
&DeviceInstallParams
);
}
//
// Build a list of compatible drivers for this device
// (This call can be time consuming the first time it is called)
//
if( !SetupDiBuildDriverInfoList( hDevInfo, pDeviceInfoData, SPDIT_COMPATDRIVER ) ) {
SetupDebugPrint1( L"SETUP: SetupDiBuildDriverInfoList() failed. Error = %d", GetLastError() );
b = FALSE;
continue;
}
//
// Select the best compatible driver for this device.
//
if( !SelectBestDriver( hDevInfo,
pDeviceInfoData,
&bOemF6Driver ) ) {
Error = GetLastError();
if( Error != ERROR_NO_COMPAT_DRIVERS ) {
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV) failed. Error = %d", Error );
b = FALSE;
END_SECTION(LoggedDescription);
continue;
}
SetupDebugPrint( L"SETUP: Compatible driver List is empty" );
SetupDebugPrint( L"SETUP: Installing the null driver for this device" );
//
// Install the null driver for this device.
// This is to avoid the "Found New Hardware" popup when the
// user first logs on after the system is installed.
//
if( !SyssetupInstallNullDriver( hDevInfo, pDeviceInfoData ) ) {
SetupDebugPrint( L"SETUP: Unable to install null driver" );
}
END_SECTION(LoggedDescription);
continue;
}
}
} else {
SetupDebugPrint( L"SETUP: Device was installed via answer file driver" );
}
//
// Retrieve information about the driver node selected above.
//
DriverInfoData.cbSize = sizeof( SP_DRVINFO_DATA );
if( !SetupDiGetSelectedDriver( hDevInfo,
pDeviceInfoData,
&DriverInfoData ) ) {
SetupDebugPrint1( L"SETUP: SetupDiGetSelectedDriver() failed. Error = %d", GetLastError() );
b = FALSE;
continue;
}
//
// Get the GUID string for this device
//
GUIDString[0] = (WCHAR)'\0';
pSetupStringFromGuid( &(pDeviceInfoData->ClassGuid), GUIDString, sizeof( GUIDString ) / sizeof( WCHAR ) );
SetupDebugPrint1( L"SETUP: DriverType = %lx", DriverInfoData.DriverType );
SetupDebugPrint1( L"SETUP: Description = %ls", &(DriverInfoData.Description[0]) );
SetupDebugPrint1( L"SETUP: MfgName = %ls", &(DriverInfoData.MfgName[0]) );
SetupDebugPrint1( L"SETUP: ProviderName = %ls", &(DriverInfoData.ProviderName[0]) );
SetupDebugPrint1( L"SETUP: GUID = %ls", GUIDString );
ClassDescription[0] = (WCHAR)'\0';
if( !SetupDiGetClassDescription( &(pDeviceInfoData->ClassGuid),
ClassDescription,
sizeof(ClassDescription)/sizeof(WCHAR),
NULL ) ) {
SetupDebugPrint1( L"SETUP: SetupDiGetClassDescription() failed. Error = %lx", GetLastError() );
ClassDescription[0] = (WCHAR)'\0';
}
SetupDebugPrint1( L"SETUP: DeviceClass = %ls", ClassDescription );
//
// Retrieve syssetup PnP flags (if any) the INF specifies for this
// device.
//
PnPFlags = SyssetupGetPnPFlags(hDevInfo, pDeviceInfoData, &DriverInfoData);
if( SkipDeviceInstallation( hDevInfo,
pDeviceInfoData,
InfHandle,
GUIDString ) ) {
SetupDebugPrint( L"SETUP: Skipping installation of this device" );
END_SECTION(LoggedDescription);
continue;
}
//
// If the PnP flag was set that said we shouldn't call ConfigMgr
// for this device, then set the appropriate flag in the device's
// install params.
//
if(PnPFlags & PNPFLAG_DONOTCALLCONFIGMG) {
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if( !SetupDiGetDeviceInstallParams( hDevInfo,
pDeviceInfoData,
&DeviceInstallParams ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %d", Error );
}
} else {
DeviceInstallParams.Flags |= DI_DONOTCALLCONFIGMG;
if( !SetupDiSetDeviceInstallParams( hDevInfo,
pDeviceInfoData,
&DeviceInstallParams ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d", Error );
}
}
}
}
//
// Start the thread that actually does the installation of the device
//
Context = MyMalloc( sizeof(PNP_ENUM_DEV_THREAD_PARAMS) );
Context->hDevInfo = hDevInfo;
Context->DeviceInfoData = *pDeviceInfoData;
Context->pDeviceDescription = pSetupDuplicateString(&(DriverInfoData.Description[0]));
Context->pDeviceId = pSetupDuplicateString(szBuffer);
DeviceInstalled = FALSE;
ThreadHandle = CreateThread( NULL,
0,
pInstallPnpEnumeratedDeviceThread,
Context,
0,
&ThreadId );
if(ThreadHandle) {
DWORD WaitResult;
DWORD ExitCode;
BOOL KeepWaiting;
KeepWaiting = TRUE;
while( KeepWaiting ) {
//
// REVIEW 2000/11/08 seanch - Old behavior that we don't want to regress.
// Fix the network timeout after the network guys fix their class installer
//
WaitResult = WaitForSingleObject( ThreadHandle,
(_wcsicmp( GUIDString, BB_NETWORK_GUID_STRING ) == 0)? BB_PNP_NETWORK_TIMEOUT :
PNP_ENUM_TIMEOUT );
if( WaitResult == WAIT_TIMEOUT ) {
int Result;
HANDLE hDialogEvent;
if( hDialogEvent = OpenEvent( EVENT_MODIFY_STATE, FALSE, SETUP_HAS_OPEN_DIALOG_EVENT ) ) {
//
// Setupapi is prompting the user for a file. Don't timeout.
//
CloseHandle( hDialogEvent );
KeepWaiting = TRUE;
continue;
} else {
}
//
// Class installer is hung
//
SetupDebugPrint1( L"SETUP: Class Installer appears to be hung. Device = %ls", &(DriverInfoData.Description[0]) );
#ifdef PRERELEASE
//
// Ask the user if he wants to skip the installation of this device.
//
if( !Unattended ) {
Result = MessageBoxFromMessage( hwndParent,
MSG_CLASS_INSTALLER_HUNG,
NULL,
IDS_WINNT_SETUP,
MB_YESNO | MB_ICONWARNING,
&(DriverInfoData.Description[0]) );
} else {
Result = IDYES;
}
#else
Result = IDYES;
#endif
if(Result == IDYES) {
//
// User wants to skip this device.
// First find out if the thread has already returned.
//
WaitResult = WaitForSingleObject( ThreadHandle, 0 );
if( WaitResult != WAIT_OBJECT_0 ) {
//
// Thread hasn't returned yet. Skip the installation of this device.
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: Skipping installation of enumerated device. Device = %ls", &(DriverInfoData.Description[0]) );
b = FALSE;
//
// Remember this device so that it won't be installed if GUI setup is
// restarted
//
WritePrivateProfileString( szEnumDevSection,
szBuffer,
&(DriverInfoData.Description[0]),
PnpLogPath );
//
// Since the class installer is hung, we cannot re-use the hDevInfo that was passed
// to the class installer. So we just ignore this one. We will create a new one for
// the next device to be installed.
//
hDevInfo = INVALID_HANDLE_VALUE;
pDeviceInfoData = NULL;
} else{
//
// Thread has already returned.
// There is no need to skip the installation of this device.
// We assume that the user decided not to skip the installation of this device,
// and next call to WaitForSingleObject will immediately return.
//
}
}
} else if( WaitResult == WAIT_OBJECT_0 ) {
//
// Device Installation Thread completed.
//
KeepWaiting = FALSE;
//
// Deallocate memory passed to the thread.
//
MyFree( Context->pDeviceDescription );
MyFree( Context->pDeviceId );
MyFree( Context );
if( GetExitCodeThread( ThreadHandle, &ExitCode ) ) {
if( ExitCode == ERROR_SUCCESS ) {
//
// The installation was successful
//
DeviceInstalled = TRUE;
SetupDebugPrint( L"SETUP: Device successfully installed." );
} else {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
SetupDebugPrint( L"SETUP: Device not successfully installed." );
b = FALSE;
}
} else {
//
// Unable to retrieve exit code. Assume success.
//
SetupDebugPrint1( L"SETUP: GetExitCode() failed. Error = %d", GetLastError() );
SetupDebugPrint( L"SETUP: Unable to retrieve thread exit code. Assuming device successfully installed." );
}
} else {
//
// Should not occur
//
KeepWaiting = FALSE;
SetupDebugPrint1( L"SETUP: WaitForSingleObject() returned %d", WaitResult );
// MyFree( Context->pDeviceDescription );
// MyFree( Context->pDeviceId );
// MyFree( Context );
b = FALSE;
}
}
//
// The thread handle is no longer needed.
//
CloseHandle(ThreadHandle);
} else {
//
// Just do it synchronously.
//
SetupDebugPrint1( L"SETUP: CreateThread() failed (enumerated device). Error = %d", GetLastError() );
if( pInstallPnpEnumeratedDeviceThread(Context) != ERROR_SUCCESS ) {
//
// The installation was not successful.
// There is no need to log the error, since the thread has already done it.
//
SetupDebugPrint( L"SETUP: Device not successfully installed." );
b = FALSE;
} else {
DeviceInstalled = TRUE;
}
MyFree( Context->pDeviceDescription );
MyFree( Context->pDeviceId );
MyFree( Context );
}
if( DeviceInstalled ) {
#ifdef PNP_DEBUG_UI
//
// Add the device to an info set that has the same class as this device.
//
if( !AddDevInfoDataToDevInfoSet( &DevInfoSetList,
pDeviceInfoData,
szBuffer,
hwndParent ) ) {
SetupDebugPrint1( L"SETUP: AddDevInfoDataToDevInfoSet() failed. DevId = %ls", szBuffer );
b = FALSE;
continue;
}
#endif // PNP_DEBUG_UI
//
// If this device came from the answer file, then change the original
// media path from the local temp dir to the original path (typically
// the floppy drive).
//
if (SelectedAfDriver) {
SyssetupFixAnswerFileDriverPath (
SelectedAfDriver,
hDevInfo,
pDeviceInfoData
);
}
//
// If the driver we just installed was an Oem F6 driver then
// we need to add it to the list of files that SfcInitProt
// will leave alone during its scan.
//
if (bOemF6Driver) {
AddOemF6DriversToSfcIgnoreFilesList(hDevInfo, pDeviceInfoData);
}
}
END_SECTION(LoggedDescription);
}
//
// Find out why we stopped reading the pipe. If it was because the connetion
// to the pipe was broken by umpnp, then there was nothing else to read from
// the pipe. Otherwise, there was an error condition.
//
if( ( Error = GetLastError() ) != ERROR_BROKEN_PIPE ) {
SetupDebugPrint1( L"SETUP: ReadFile( hPipe ) failed. Error = %d", GetLastError() );
b = FALSE;
goto Clean0;
}
} else {
SetupDebugPrint1( L"SETUP: ConnectNamedPipe() failed. Error = %d", GetLastError() );
b = FALSE;
goto Clean0;
}
Clean0:
BEGIN_SECTION(L"InstallEnumeratedDevices cleanup");
//
// Make sure that at this point the gauge is filled up to the end of the
// region reserved for the installation of the enumerated devices.
//
SendMessage(ProgressWindow,PBM_SETPOS,GaugeRange*StopAtPercent/100,0);
if (hPipe != INVALID_HANDLE_VALUE) {
DisconnectNamedPipe(hPipe);
CloseHandle(hPipe);
}
if (hPipeEvent != NULL) {
CloseHandle(hPipeEvent);
}
if (hBatchEvent != NULL) {
CloseHandle(hBatchEvent);
}
if( hDevInfo != INVALID_HANDLE_VALUE ) {
SetupDiDestroyDeviceInfoList( hDevInfo );
}
DestroyAfDriverTable (AfDrivers);
if( pDeviceInfoData != NULL ) {
MyFree( pDeviceInfoData );
}
SetupDebugPrint( L"SETUP: Leaving InstallEnumeratedDevices()" );
END_SECTION(L"InstallEnumeratedDevices cleanup");
return( b );
}
DWORD
pInstallPnpDevicesThread(
PPNP_THREAD_PARAMS ThreadParams
)
/*++
Routine Description:
This is the thread that does the installation of the PnP devices.
Arguments:
ThreadParams - Points to a structure that contains the information
passed to this thread.
Return Value:
Returnd TRUE if the operation succeeds, or FALSE otherwise.
--*/
{
BOOL b;
PPNP_THREAD_PARAMS Context;
ULONG StartAtPercent;
ULONG StopAtPercent;
ULONG DeltaPercent;
Context = ThreadParams;
//
// Assume success.
//
b = TRUE;
//
// We don't want SetupAPI to do any RunOnce calls, as we'll do it manually
//
pSetupSetGlobalFlags(pSetupGetGlobalFlags()|PSPGF_NO_RUNONCE);
//
// Initialize some variables that are related to the progress bar.
// We divide the progress window area reserved for pnp installation in 3 regions
// of equal size, and they will be used in the following steps:
// . Pre-compilation of infs
// . Installation of enumerated devices
// . Installation of legacy devices
// . Installation of enumerated devices that may have appeared after the installation of legacy devices
//
DeltaPercent = (Context->ProgressWindowStopAtPercent - Context->ProgressWindowStartAtPercent) / 4;
StartAtPercent = Context->ProgressWindowStartAtPercent;
StopAtPercent = Context->ProgressWindowStartAtPercent + DeltaPercent;
//
// Pre-compile inf files
//
//
// Before we can start precompiling inf files, let's "seed" the INF
// directory with any OEM-supplied INFs that need to be present during
// detection. This should be really quick and doesn't require the
// updating the progress window
//
RemainingTime = CalcTimeRemaining(Phase_PrecompileInfs);
SetRemainingTime(RemainingTime);
BEGIN_SECTION(L"Installing OEM infs");
InstallOEMInfs();
//
// Add migrated drivers to the SFC exclusion list
//
#if defined(_X86_)
SfcExcludeMigratedDrivers ();
#endif
END_SECTION(L"Installing OEM infs");
BEGIN_SECTION(L"Precompiling infs");
PrecompileInfFiles( Context->ProgressWindow,
StartAtPercent,
StopAtPercent );
END_SECTION(L"Precompiling infs");
//
// This operation is very fast, so we don't need to upgrade the progress bar.
//
if( !MiniSetup ) {
BEGIN_SECTION(L"Mark PnP devices for reinstall");
MarkPnpDevicesAsNeedReinstall();
END_SECTION(L"Mark PnP devices for reinstall");
}
//
// Do the migration of legacy devices...at the moment all that PnPInit does
// is migrate all the device nodes who belong to the vid load order group to
// be put into the display class. This is the desired result.
//
// This is a quick operation and doesn't need to use the progress window.
//
PnPInitializationThread(NULL);
//
// Do installation of enumerated devices
//
StartAtPercent += DeltaPercent;
StopAtPercent += DeltaPercent;
RemainingTime = CalcTimeRemaining(Phase_InstallEnumDevices1);
SetRemainingTime(RemainingTime);
BEGIN_SECTION(L"Installing enumerated devices");
b = InstallEnumeratedDevices( Context->Window,
Context->InfHandle,
Context->ProgressWindow,
StartAtPercent,
StopAtPercent );
//
// devices installs may exist in RunOnce entries
// they are processed as a batch, as opposed to per-device
// during syssetup
//
CallRunOnceAndWait();
END_SECTION(L"Installing enumerated devices");
//
// Do the installation of legacy pnp devices.
//
StartAtPercent += DeltaPercent;
StopAtPercent += DeltaPercent;
BEGIN_SECTION(L"Installing legacy devices");
RemainingTime = CalcTimeRemaining(Phase_InstallLegacyDevices);
SetRemainingTime(RemainingTime);
b = InstallLegacyDevices( Context->Window,
Context->ProgressWindow,
StartAtPercent,
StopAtPercent ) && b;
//
// devices installs may exist in RunOnce entries
// they are processed as a batch, as opposed to per-device
// during syssetup
//
CallRunOnceAndWait();
END_SECTION(L"Installing legacy devices");
// Install the remaining enumerated devices that may have appeared after the
// installation of the legacy devices.
// Since this step uses the last region of the progress window,
// use as StopAtPercent, the value that was passed to this function,
// instead of the calulated value (by adding DeltaPorcent). This will
// ensure that at the end of this step, the gauge will be filled up
// completely. If we use the calculated value, then rounding error
// can cause the gauge not to be completelly filled up at the end of
// this step.
//
StartAtPercent += DeltaPercent;
StopAtPercent = Context->ProgressWindowStopAtPercent;
BEGIN_SECTION(L"Install enumerated devices triggered by legacy devices");
RemainingTime = CalcTimeRemaining(Phase_InstallEnumDevices2);
SetRemainingTime(RemainingTime);
b = InstallEnumeratedDevices( Context->Window,
Context->InfHandle,
Context->ProgressWindow,
StartAtPercent,
StopAtPercent ) && b;
//
// devices installs may exist in RunOnce entries
// they are processed as a batch, as opposed to per-device
// during syssetup
//
// since we will not be calling RunOnce again after this
// allow devices to call RunOnce immediately
// (other device install threads may be still running)
//
pSetupSetGlobalFlags(pSetupGetGlobalFlags()&~PSPGF_NO_RUNONCE);
CallRunOnceAndWait();
END_SECTION(L"Install enumerated devices triggered by legacy devices");
//
// Mark all non-present devices as needing re-install
// we do this a 2nd time in case a device "disappeared" due to the
// re-installation of a parent device
// This operation is very fast, so we don't need to upgrade the progress bar.
//
if( !MiniSetup ) {
MarkPnpDevicesAsNeedReinstall();
}
if( Context->SendWmQuit ) {
// #if 0
ULONG Error = ERROR_SUCCESS;
// #endif
//
// We send WM_QUIT only if this routine was started as a separate thread.
// Otherwise, the WM_QUIT will be processed by the wizard, and it will make it stop.
//
// PostThreadMessage(Context->ThreadId,WM_QUIT,b,0);
// #if 0
do {
if( !PostThreadMessage(Context->ThreadId,WM_QUIT,b,0) ) {
Error = GetLastError();
SetupDebugPrint1( L"SETUP: PostThreadMessage(WM_QUIT) failed. Error = %d", Error );
}
} while ( Error != ERROR_SUCCESS );
// #endif
}
return( b );
}
BOOL
InstallPnpDevices(
IN HWND hwndParent,
IN HINF InfHandle,
IN HWND ProgressWindow,
IN ULONG StartAtPercent,
IN ULONG StopAtPercent
)
/*++
Routine Description:
This function creates and starts the thread responsible for installation of
PnP devices.
Arguments:
hwndParent - Handle to a top level window that may be used for UI purposes
InfHandle - System setup inf handle (syssetup.inf).
ProgressWindow - Handle to the progress bar.
StartAtPercent - Starting position in the progress bar.
It indicates that from position 0 to this position
the gauge is already filled.
StopAtPercent - Ending position of the progress bar.
The pnp thread should not fill the progress bar beyond
this position
Return Value:
Returns TRUE if all the PnP devices installed successfully.
--*/
{
BOOL Success = TRUE;
DWORD ThreadId;
HANDLE ThreadHandle = NULL;
PNP_THREAD_PARAMS Context;
MSG msg;
Context.ThreadId = GetCurrentThreadId();
Context.Window = hwndParent;
Context.ProgressWindow = ProgressWindow;
Context.InfHandle = InfHandle;
Context.ProgressWindowStartAtPercent = StartAtPercent;
Context.ProgressWindowStopAtPercent = StopAtPercent;
Context.SendWmQuit = TRUE;
ThreadHandle = CreateThread(
NULL,
0,
pInstallPnpDevicesThread,
&Context,
0,
&ThreadId
);
if(ThreadHandle) {
CloseHandle(ThreadHandle);
//
// Pump the message queue and wait for the thread to finish.
//
do {
GetMessage(&msg,NULL,0,0);
if(msg.message != WM_QUIT) {
DispatchMessage(&msg);
}
} while(msg.message != WM_QUIT);
Success = (BOOL)msg.wParam;
} else {
//
// Just do it synchronously.
//
Context.SendWmQuit = FALSE;
Success = pInstallPnpDevicesThread(&Context);
}
return(Success);
}
BOOL
UpdatePnpDeviceDrivers(
)
/*++
Routine Description:
This function goes through all the installed devices and makes sure
it has the latest and greatest driver.
Arguments:
Return Value:
Returns TRUE if there are no fatal errors.
--*/
{
BOOL bRet = FALSE;
HINSTANCE hInstNewDev;
ExternalUpdateDriverForPlugAndPlayDevicesW pUpdateDriverForPlugAndPlayDevicesW = NULL;
HDEVINFO DeviceInfoSet;
// We need the "UpdateDriverForPlugAndPlayDevices" function from newdev.dll.
//
if ( NULL == (hInstNewDev = LoadLibrary(L"newdev.dll")) )
{
SetupDebugPrint1(L"SETUP: Failed to load newdev.dll. Error = %d", GetLastError());
return bRet;
}
pUpdateDriverForPlugAndPlayDevicesW =
(ExternalUpdateDriverForPlugAndPlayDevicesW) GetProcAddress(hInstNewDev, "UpdateDriverForPlugAndPlayDevicesW");
if ( NULL == pUpdateDriverForPlugAndPlayDevicesW )
{
SetupDebugPrint1(L"SETUP: Failed to get UpdateDriverForPlugAndPlayDevicesW. Error = %d", GetLastError());
}
// Create a device information set that will be the container for
// the device interfaces.
//
else if ( INVALID_HANDLE_VALUE == (DeviceInfoSet = SetupDiCreateDeviceInfoList(NULL, NULL)) )
{
SetupDebugPrint1(L"SETUP: Failed SetupDiCreateDeviceInfoList(). Error = %d", GetLastError());
}
else
{
HDEVINFO NewDeviceInfoSet;
// Get the list of all present devices.
//
NewDeviceInfoSet = SetupDiGetClassDevsEx(NULL,
NULL,
NULL,
DIGCF_ALLCLASSES | DIGCF_PRESENT,
DeviceInfoSet,
NULL,
NULL);
if ( INVALID_HANDLE_VALUE == NewDeviceInfoSet )
{
SetupDebugPrint1(L"SETUP: Failed SetupDiGetClassDevsEx(). Error = %d", GetLastError());
}
else
{
SP_DEVINFO_DATA DeviceInfoData;
DWORD dwDevice;
// Once we get this far, the default return is TRUE.
//
bRet = TRUE;
// Setup the device info data structutre.
//
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
// Loop through all the devices.
//
for ( dwDevice = 0; SetupDiEnumDeviceInfo(NewDeviceInfoSet, dwDevice, &DeviceInfoData); dwDevice++ )
{
SP_DEVINSTALL_PARAMS DeviceInstallParams;
SP_DRVINFO_DATA NewDriverInfoData;
PSP_DRVINFO_DETAIL_DATA pNewDriverInfoDetailData = NULL;
DWORD cbBytesNeeded = 0;
TCHAR szDeviceID[MAX_DEVICE_ID_LEN];
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
NewDriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
if ( SetupDiGetDeviceInstallParams(NewDeviceInfoSet,
&DeviceInfoData,
&DeviceInstallParams) )
{
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_EXCLUDE_OLD_INET_DRIVERS;
SetupDiSetDeviceInstallParams(NewDeviceInfoSet,
&DeviceInfoData,
&DeviceInstallParams);
}
// Build the list of possible drivers for this device.
// Select the best compatible driver for this device.
// Retrieve information about the driver node selected above.
// Get driver info details.
//
if ( ( SetupDiBuildDriverInfoList(NewDeviceInfoSet,
&DeviceInfoData,
SPDIT_COMPATDRIVER ) ) &&
( SetupDiCallClassInstaller(DIF_SELECTBESTCOMPATDRV,
NewDeviceInfoSet,
&DeviceInfoData ) ) &&
( SetupDiGetSelectedDriver(NewDeviceInfoSet,
&DeviceInfoData,
&NewDriverInfoData ) ) &&
( ( SetupDiGetDriverInfoDetail(NewDeviceInfoSet,
&DeviceInfoData,
&NewDriverInfoData,
NULL,
0,
&cbBytesNeeded) ) ||
( GetLastError() == ERROR_INSUFFICIENT_BUFFER ) ) &&
( cbBytesNeeded ) &&
( pNewDriverInfoDetailData = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, cbBytesNeeded) ) &&
( pNewDriverInfoDetailData->cbSize = sizeof(SP_DRVINFO_DETAIL_DATA) ) &&
( SetupDiGetDriverInfoDetail(NewDeviceInfoSet,
&DeviceInfoData,
&NewDriverInfoData,
pNewDriverInfoDetailData,
cbBytesNeeded,
NULL) ) &&
( SetupDiGetDeviceRegistryProperty(NewDeviceInfoSet,
&DeviceInfoData,
SPDRP_HARDWAREID,
NULL,
(LPBYTE) szDeviceID,
sizeof(szDeviceID),
NULL) ) )
{
HKEY hDevRegKey;
BOOL bUpdate = TRUE,
bRebootFlag;
// Get the device's regkey, so that we can get the
// version of the currently installed driver.
//
if ( INVALID_HANDLE_VALUE != (hDevRegKey = SetupDiOpenDevRegKey(NewDeviceInfoSet,
&DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
KEY_READ)) )
{
TCHAR szInfPath[MAX_PATH],
szInfName[MAX_PATH];
DWORD dwSize = sizeof(szInfName),
dwType;
szInfPath[0] = L'\0';
GetSystemWindowsDirectory(szInfPath, sizeof(szInfPath) / sizeof(TCHAR));
if ( ( szInfPath[0] ) &&
( pSetupConcatenatePaths(szInfPath,
L"INF",
sizeof(szInfPath) / sizeof(TCHAR),
NULL) ) &&
( ERROR_SUCCESS == RegQueryValueEx(hDevRegKey,
REGSTR_VAL_INFPATH,
NULL,
&dwType,
(LPBYTE) &szInfName,
&dwSize) ) &&
( pSetupConcatenatePaths(szInfPath,
szInfName,
sizeof(szInfPath) / sizeof(TCHAR),
NULL) ) &&
( CSTR_EQUAL == CompareString(LOCALE_SYSTEM_DEFAULT,
NORM_IGNORECASE,
pNewDriverInfoDetailData->InfFileName,
-1,
szInfPath,
-1) ) )
{
// The inf we found is already in the %windir%\inf folder and is already
// installed. So we don't want to install this inf again.
//
bUpdate = FALSE;
}
// Make sure we close the key.
//
RegCloseKey(hDevRegKey);
}
// Check to see if we have the possibility of a better driver and
// try to install it if we do.
//
if ( bUpdate &&
!pUpdateDriverForPlugAndPlayDevicesW(NULL,
szDeviceID,
pNewDriverInfoDetailData->InfFileName,
0,
&bRebootFlag) )
{
SetupDebugPrint1(L"SETUP: Failed to install updated driver. Error = %d", GetLastError());
bRet = FALSE;
}
}
//else
//{
//
// SetupDebugPrint1(L"SETUP: No installed Driver. Error = %d", GetLastError());
// SetupDebugPrint(L"SETUP: No best compatible driver.");
// SetupDebugPrint(L"SETUP: Unable to get new driver info.");
// SetupDebugPrint1(L"SETUP: Unable to get new driver detail data. Error = %d", GetLastError());
//
//}
// Free this if allocated.
//
if ( pNewDriverInfoDetailData )
{
HeapFree(GetProcessHeap(), 0, pNewDriverInfoDetailData);
}
}
// Make sure we clean up the list.
//
SetupDiDestroyDeviceInfoList(NewDeviceInfoSet);
}
// Make sure we clean up the list.
//
SetupDiDestroyDeviceInfoList(DeviceInfoSet);
}
FreeLibrary(hInstNewDev);
return bRet;
}
#ifdef PNP_DEBUG_UI
INT
CompareListboxItems(
IN PCOMPAREITEMSTRUCT Items
)
/*++
Routine Description:
This routine is called in response to a WM_COMPAREITEM.
It compares two item of type LISTBOX_ITEM.
Arguments:
Items - Pointer to a structure that contains the information
about the items to compare.
Return Value:
-1 ... Item1 precedes Item2 in the sorted order.
0 ... Item1 and Item2 are identical in the sorted order.
1 ... Item2 precedes Item1 in the sorted order.
--*/
{
PLISTBOX_ITEM ListBoxItem1;
PLISTBOX_ITEM ListBoxItem2;
ListBoxItem1 = (PLISTBOX_ITEM)(Items->itemData1);
ListBoxItem2 = (PLISTBOX_ITEM)(Items->itemData2);
// SetupDebugPrint( L"SETUP: Entering CompareListboxItems()" );
// SetupDebugPrint( L"SETUP: IconIdex1 = %d, DeviceDescription1 = %ls", ListBoxItem1->IconIndex, ListBoxItem1->IconIndex );
// SetupDebugPrint( L"SETUP: IconIdex2 = %d, DeviceDescription1 = %ls", ListBoxItem2->IconIndex, ListBoxItem2->IconIndex );
if( ( ListBoxItem1->IconIndex > ListBoxItem2->IconIndex ) ||
( ( ListBoxItem1->IconIndex == ListBoxItem2->IconIndex ) &&
( _wcsicmp( ListBoxItem1->DeviceDescription, ListBoxItem2->DeviceDescription ) > 0 )
)
) {
// SetupDebugPrint( L"SETUP: Leaving CompareListboxItems(). Return value = 1" );
return( 1 );
} else if( ( ListBoxItem1->IconIndex < ListBoxItem2->IconIndex ) ||
( ( ListBoxItem1->IconIndex == ListBoxItem2->IconIndex ) &&
( _wcsicmp( ListBoxItem1->DeviceDescription, ListBoxItem2->DeviceDescription ) < 0 )
)
) {
// SetupDebugPrint( L"SETUP: Leaving CompareListboxItems(). Return value = -1" );
return( -1 );
} else {
// SetupDebugPrint( L"SETUP: Leaving CompareListboxItems(). Return value = 0" );
return( 0 );
}
}
#endif // PNP_DEBUG_UI
#ifdef PNP_DEBUG_UI
VOID
DrawItem(
LPDRAWITEMSTRUCT lpdis
)
/*++
Routine Description:
Draw a device description in an owner drawn list box.
Arguments:
lpdis - Pointer to the sructure that contains the item to
be drawn in the listbox.
Return Value:
None.
--*/
{
HDC hDc = lpdis->hDC;
int dxString, bkModeSave;
DWORD dwBackColor, dwTextColor;
UINT itemState=lpdis->itemState;
RECT rcItem=lpdis->rcItem;
SIZE size;
PLISTBOX_ITEM ListBoxItem;
PWSTR DeviceDescription;
MYASSERT( lpdis->CtlType & ODT_LISTBOX );
if((int)lpdis->itemID < 0) {
return;
}
//
// extract the DeviceDescription from DRAWITEM struct
//
ListBoxItem = (PLISTBOX_ITEM)lpdis->itemData;
DeviceDescription = ListBoxItem->DeviceDescription;
GetTextExtentPoint32(hDc,DeviceDescription,wcslen(DeviceDescription),&size);
if(lpdis->itemAction != ODA_FOCUS) {
bkModeSave = GetBkMode(hDc);
dwBackColor = SetBkColor(hDc, GetSysColor((itemState & ODS_SELECTED) ?
COLOR_HIGHLIGHT : COLOR_WINDOW));
dwTextColor = SetTextColor(hDc, GetSysColor((itemState & ODS_SELECTED) ?
COLOR_HIGHLIGHTTEXT : COLOR_WINDOWTEXT));
//
// fill in the background; do this before mini-icon is drawn
//
ExtTextOut(hDc, 0, 0, ETO_OPAQUE, &rcItem, NULL, 0, NULL);
//
// draw mini-icon for this device and move string accordingly
//
dxString = SetupDiDrawMiniIcon(
hDc,
rcItem,
ListBoxItem->IconIndex,
(itemState & ODS_SELECTED) ? MAKELONG(DMI_BKCOLOR, COLOR_HIGHLIGHT) : 0
);
//
// draw the text transparently on top of the background
//
SetBkMode(hDc, TRANSPARENT);
ExtTextOut(hDc, dxString + rcItem.left, rcItem.top +
((rcItem.bottom - rcItem.top) - size.cy) / 2,
0, NULL, DeviceDescription, wcslen(DeviceDescription), NULL);
//
// Restore hdc colors.
//
SetBkColor(hDc, dwBackColor);
SetTextColor(hDc, dwTextColor);
SetBkMode(hDc, bkModeSave);
}
if(lpdis->itemAction == ODA_FOCUS || (itemState & ODS_FOCUS)) {
DrawFocusRect(hDc, &rcItem);
}
}
#endif // PNP_DEBUG_UI
#ifdef PNP_DEBUG_UI
BOOL
CALLBACK
InstalledHardwareDlgProc(
IN HWND hdlg,
IN UINT msg,
IN WPARAM wParam,
IN LPARAM lParam
)
/*++
Routine Description:
Dialog procedure for the installed hardware dialog.
Arguments:
hWnd - a handle to the dialog proceedure.
msg - the message passed from Windows.
wParam - extra message dependent data.
lParam - extra message dependent data.
Return Value:
TRUE if the value was edited. FALSE if cancelled or if no
changes were made.
--*/
{
NMHDR *NotifyParams;
switch(msg) {
case WM_INITDIALOG: {
PDEVINFOSET_ELEMENT p;
// SetupDebugPrint( L"SETUP: InstalledHardwareDlgProc() received WM_INITDIALOG" );
if( UiTest ) {
//
// If testing the wizard, make sure that the page is
// displayed
//
break;
}
szUnknownDevice = MyLoadString( IDS_DEVNAME_UNK );
for( p = DevInfoSetList; p != NULL; p = p->Next ) {
ULONG Index;
ULONG Error;
for( Index = 0; ; Index++ ) {
LONG i;
PLISTBOX_ITEM ListBoxItem;
PWSTR TempString;
ListBoxItem = MyMalloc( sizeof( LISTBOX_ITEM ) );
if( ListBoxItem == NULL ) {
SetupDebugPrint( L"SETUP: Out of memory: InstalledHardwareDlgProc()" );
continue;
}
ListBoxItem->DevInfoSet = p->DevInfoSet;
ListBoxItem->DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
ListBoxItem->IconIndex = UNKNOWN_DEVICE_ICON_INDEX;
*(ListBoxItem->DeviceDescription) = L'\0';
if( szUnknownDevice != NULL ) {
wcscpy( ListBoxItem->DeviceDescription, szUnknownDevice );
}
if( SetupDiEnumDeviceInfo( p->DevInfoSet, Index, &(ListBoxItem->DeviceInfoData) ) ) {
ULONG Size;
if( SetupDiGetDeviceRegistryProperty( ListBoxItem->DevInfoSet,
&(ListBoxItem->DeviceInfoData),
SPDRP_FRIENDLYNAME,
NULL,
(PBYTE)(ListBoxItem->DeviceDescription),
LINE_LEN * sizeof( WCHAR ), // sizeof( DeviceDescription ),
NULL ) ) {
SetupDebugPrint( L"SETUP: Device = %d, Description = %ls", Index, ListBoxItem->DeviceDescription );
} else {
SetupDebugPrint( L"SETUP: Device = %d, No friendly name", Index );
if( SetupDiGetDeviceRegistryProperty( ListBoxItem->DevInfoSet,
&(ListBoxItem->DeviceInfoData),
SPDRP_DEVICEDESC,
NULL,
(PBYTE)(ListBoxItem->DeviceDescription),
LINE_LEN * sizeof( WCHAR ), // sizeof( DeviceDescription ),
NULL ) ) {
SetupDebugPrint( L"SETUP: Device = %d, Description = %ls", Index, ListBoxItem->DeviceDescription );
} else {
SetupDebugPrint( L"SETUP: Device = %d, Description = Unknown device", Index );
}
}
if( !SetupDiLoadClassIcon( &(ListBoxItem->DeviceInfoData.ClassGuid),
NULL,
&(ListBoxItem->IconIndex) ) ) {
SetupDebugPrint( L"SETUP: SetupDiLoadClassIcon() failed. Error = %d, Description = %ls", GetLastError(), ListBoxItem->DeviceDescription );
}
} else {
Error = GetLastError();
MyFree( ListBoxItem );
if( Error == ERROR_NO_MORE_ITEMS ) {
break;
} else {
SetupDebugPrint( L"SETUP: Device = %d, Description = Unknown device", Index );
}
}
i = SendDlgItemMessage( hdlg, IDC_LIST1, LB_ADDSTRING, 0, (LPARAM)ListBoxItem );
SetupDebugPrint( L"SETUP: SendDlgItemMessage() returned i = %d", i );
}
}
//
// Disable the 'Properties' button
//
// EnableWindow( GetDlgItem( hdlg, IDB_BUTTON_1 ), FALSE );
break;
}
case WM_IAMVISIBLE:
break;
case WM_SIMULATENEXT:
// Simulate the next button somehow
PropSheet_PressButton( GetParent(hdlg), PSBTN_NEXT);
break;
case WM_NOTIFY:
NotifyParams = (NMHDR *)lParam;
switch(NotifyParams->code) {
case PSN_SETACTIVE:
TESTHOOK(509);
BEGIN_SECTION(L"Installed Hardware Page");
// Make page visible
SendMessage(GetParent(hdlg), WMX_BBTEXT, (WPARAM)FALSE, 0);
SetWizardButtons(hdlg,WizPageInstalledHardware);
if(Unattended) {
UnattendSetActiveDlg(hdlg,IDD_HARDWARE);
}
break;
case PSN_WIZNEXT:
case PSN_WIZFINISH:
//
// Allow next page to be activated.
//
SetWindowLong(hdlg,DWL_MSGRESULT,0);
break;
case PSN_KILLACTIVE:
WizardKillHelp(hdlg);
SetWindowLong(hdlg,DWL_MSGRESULT, FALSE);
END_SECTION(L"Installed Hardware Page");
break;
case PSN_HELP:
WizardBringUpHelp(hdlg,WizPageInstalledHardware);
break;
default:
break;
}
break;
case WM_DRAWITEM:
DrawItem( (LPDRAWITEMSTRUCT)lParam );
break;
case WM_DESTROY:
{
LONG Count;
LONG i;
PLISTBOX_ITEM ListBoxItem;
// SetupDebugPrint( L"SETUP: InstalledHardwareDlgProc() received WM_DESTROY" );
Count = SendDlgItemMessage( hdlg, IDC_LIST1, LB_GETCOUNT, 0, 0);
if( Count != LB_ERR ) {
for( i = 0; i < Count; i++ ) {
ListBoxItem = (PLISTBOX_ITEM)SendDlgItemMessage( hdlg, IDC_LIST1, LB_GETITEMDATA, (WPARAM)i, 0);
if( (LONG)ListBoxItem != LB_ERR ) {
MyFree( ListBoxItem );
} else {
SetupDebugPrint( L"SETUP: SendDlgItemMessage( LB_GETITEMDATA ) failed. Index = %d, Error = %d", i, GetLastError() );
}
}
} else {
SetupDebugPrint( L"SETUP: SendDlgItemMessage( LB_GETCOUNT ) failed. Error = %d", GetLastError() );
}
return( FALSE );
}
case WM_COMPAREITEM:
return( CompareListboxItems( (PCOMPAREITEMSTRUCT)lParam ) );
case WM_COMMAND:
switch( LOWORD( wParam ) ) {
case IDC_LIST1:
{
switch( HIWORD( wParam )) {
case LBN_SELCHANGE:
{
//
// Enable the 'Properties' button
//
EnableWindow( GetDlgItem( hdlg, IDB_BUTTON_1 ),
TRUE );
return( FALSE );
}
case LBN_DBLCLK:
{
//
// Simulate that the 'Properties' button was pushed
//
SendMessage( hdlg,
WM_COMMAND,
MAKEWPARAM( IDB_BUTTON_1, BN_CLICKED ),
( LPARAM ) GetDlgItem( hdlg, IDB_BUTTON_1 ) );
return( FALSE );
}
}
break;
}
break;
case IDB_BUTTON_1:
return( FALSE );
}
break;
default:
return(FALSE);
}
return(TRUE);
}
#endif // PNP_DEBUG_UI
VOID
SortClassGuidListForDetection(
IN OUT LPGUID GuidList,
IN ULONG GuidCount,
OUT PULONG LastBatchedDetect
)
/*++
Routine Description:
This routine sorts the supplied list of GUID based on a (partial)
ordering specified in the [DetectionOrder] and [NonBatchedDetection]
sections of syssetup.inf. This allows us to maintain a detection
ordering similar to previous versions of NT, and also to allow for
class installers that may depend upon the successful installation of
devices detected by other class installers.
Arguments:
GuidList - address of the array of GUIDs to be sorted.
GuidCount - the number of GUIDs in the array. This number must be > 0.
LastBatchedDetect - Supplies the address of a variable that will
receive the index of the last GUID in the array that may be batched
together (i.e., all detections are run, all files queued into one
big queue, etc.). Any GUIDs existing at higher indices must be
processed individually, and such processing will happen _after_ the
batched detection is completed.
Return Value:
none.
--*/
{
LONG LineCount, LineIndex, GuidIndex, NextTopmost;
PCWSTR CurGuidString;
INFCONTEXT InfContext;
GUID CurGuid;
MYASSERT(GuidCount > 0);
*LastBatchedDetect = GuidCount - 1;
//
// First, sort the classes in syssetup.inf's [DetectionOrder] list to the
// front...
//
LineCount = SetupGetLineCount(SyssetupInf, L"DetectionOrder");
NextTopmost = 0;
for(LineIndex = 0; LineIndex < LineCount; LineIndex++) {
if(!SetupGetLineByIndex(SyssetupInf, L"DetectionOrder", LineIndex, &InfContext) ||
((CurGuidString = pSetupGetField(&InfContext, 1)) == NULL) ||
(pSetupGuidFromString(CurGuidString, &CurGuid) != NO_ERROR)) {
continue;
}
//
// Search through the GUID list looking for this GUID. If found, move the GUID from
// it's current position to the next topmost position.
//
for(GuidIndex = 0; GuidIndex < (LONG)GuidCount; GuidIndex++) {
if(IsEqualGUID(&CurGuid, &(GuidList[GuidIndex]))) {
if(NextTopmost != GuidIndex) {
//
// We should never be moving the GUID _down_ the list.
//
MYASSERT(NextTopmost < GuidIndex);
MoveMemory(&(GuidList[NextTopmost + 1]),
&(GuidList[NextTopmost]),
(GuidIndex - NextTopmost) * sizeof(GUID)
);
CopyMemory(&(GuidList[NextTopmost]),
&CurGuid,
sizeof(GUID)
);
}
NextTopmost++;
break;
}
}
}
//
// Now, move any classes in syssetup.inf's [NonBatchedDetection] list to
// the end...
//
LineCount = SetupGetLineCount(SyssetupInf, L"NonBatchedDetection");
for(LineIndex = 0; LineIndex < LineCount; LineIndex++) {
if(!SetupGetLineByIndex(SyssetupInf, L"NonBatchedDetection", LineIndex, &InfContext) ||
((CurGuidString = pSetupGetField(&InfContext, 1)) == NULL) ||
(pSetupGuidFromString(CurGuidString, &CurGuid) != NO_ERROR)) {
continue;
}
//
// Search through the GUID list looking for this GUID. If found, move
// the GUID from it's current position to the end of the list.
//
for(GuidIndex = 0; GuidIndex < (LONG)GuidCount; GuidIndex++) {
if(IsEqualGUID(&CurGuid, &(GuidList[GuidIndex]))) {
//
// We found a non-batched class--decrement our index that
// points to the last batched detection class.
//
(*LastBatchedDetect)--;
//
// Now shift all the GUIDs after this one up, and move this one
// to the last position in the array (unless, of course, it was
// already at the last position in the array).
//
if(GuidIndex < (LONG)(GuidCount - 1)) {
MoveMemory(&(GuidList[GuidIndex]),
&(GuidList[GuidIndex+1]),
(GuidCount - (GuidIndex+1)) * sizeof(GUID)
);
CopyMemory(&(GuidList[GuidCount-1]),
&CurGuid,
sizeof(GUID)
);
}
break;
}
}
}
}
DWORD
pPhase1InstallPnpLegacyDevicesThread(
PPNP_PHASE1_LEGACY_DEV_THREAD_PARAMS ThreadParams
)
/*++
Routine Description:
This thread does the initial part of the installation installation
of legacy Pnp devices of a particular class.
It invokes a class installer of a particular class with:
- DIF_FIRSTTIMESETUP
If it successds, it returns in the structure passed as argument
a device info list that contains the detected legacy devices.
Arguments:
ThreadParams - Points to a structure that contains the information
passed to this thread.
Return Value:
Returns a Win32 error code.
--*/
{
PPNP_PHASE1_LEGACY_DEV_THREAD_PARAMS Context;
LPGUID pGuid;
PWSTR pClassDescription;
HDEVINFO hEmptyDevInfo;
ULONG Error;
//
// Initialize variables
//
Context = ThreadParams;
Context->hDevInfo = INVALID_HANDLE_VALUE;
pGuid = &(Context->Guid);
pClassDescription = Context->pClassDescription;
//
// Assume success
//
Error = ERROR_SUCCESS;
//
// DIF_FIRSTTIME
//
if((hEmptyDevInfo = SetupDiCreateDeviceInfoList(pGuid,
Context->hwndParent))
== INVALID_HANDLE_VALUE) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiCreateDeviceInfoList() failed (phase1). Error = %d, ClassDescription = %ls", Error, pClassDescription );
goto phase1_legacy_dev_thread_exit;
}
if( !SetupDiCallClassInstaller( DIF_FIRSTTIMESETUP,
hEmptyDevInfo,
NULL ) ) {
Error = GetLastError();
if( Error != ERROR_DI_DO_DEFAULT ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_FIRSTTIMESETUP) failed (phase1). Error = %lx, ClassDescription = %ls", Error, pClassDescription );
} else {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_FIRSTTIMESETUP) failed (phase1). Error = %lx, ClassDescription = %ls", Error, pClassDescription );
}
SetupDiDestroyDeviceInfoList(hEmptyDevInfo);
goto phase1_legacy_dev_thread_exit;
}
//
// Save the info set after DIF_FIRSTTIMESETUP
//
Context->hDevInfo = hEmptyDevInfo;
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_FIRSTTIMESETUP) succeeded (phase1). ClassDescription = %ls", pClassDescription );
phase1_legacy_dev_thread_exit:
return(Error);
}
DWORD
pPhase2InstallPnpLegacyDevicesThread(
PPNP_PHASE2_LEGACY_DEV_THREAD_PARAMS ThreadParams
)
/*++
Routine Description:
This thread does a partial installation of a legacy device.
It invokes a class installer with:
- DIF_REGISTERDEVICE
- DIF_ALLOW_INSTALL
- DIF_INSTALLDEVICEFILES
Note that the call with DIF_INSTALLDEVICEFILES only queue the files on the queue
created by the parent of this handle.
Arguments:
ThreadParams - Points to a structure that contains the information
passed to this thread.
Return Value:
Returns TRUE is all calls to the class installer were successfull.
Otherwise, returns FALSE.
--*/
{
PPNP_PHASE2_LEGACY_DEV_THREAD_PARAMS Context;
HDEVINFO hDevInfo;
HSPFILEQ FileQ;
HSPFILEQ TempFileQ = INVALID_HANDLE_VALUE;
PSP_DEVINFO_DATA pDeviceInfoData;
PWSTR pClassDescription;
PWSTR pDeviceId;
BOOL b;
ULONG Error;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
DWORD ScanQueueResult;
//
// Initialize variables
//
Context = ThreadParams;
hDevInfo = Context->hDevInfo;
FileQ = Context->FileQ;
pDeviceInfoData = &(Context->DeviceInfoData);
pClassDescription = Context->pClassDescription;
pDeviceId = Context->pDeviceId;
//
// Assume success
//
Error = ERROR_SUCCESS;
b = TRUE;
//
// Let the class installer/co-installers know they should be quiet.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(!SetupDiGetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint2( L"SETUP: SetupDiGetDeviceInstallParams() failed (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
DeviceInstallParams.Flags |= DI_QUIETINSTALL;
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
//
// Register the device
//
if( !SetupDiCallClassInstaller( DIF_REGISTERDEVICE,
hDevInfo,
pDeviceInfoData ) ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_REGISTERDEVICE) failed (phase2). Error = %lx, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
//
// Verify with the class installer and class-specific co-installers that the
// driver we're about to install isn't blacklisted.
//
if( !SetupDiCallClassInstaller( DIF_ALLOW_INSTALL,
hDevInfo,
pDeviceInfoData ) ) {
Error = GetLastError();
if( Error != ERROR_DI_DO_DEFAULT ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed (phase2). Error = %d, DeviceId = %ls", Error, pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
}
//
// In general, any devices being installed as a result of DIF_FIRSTTIMESETUP
// will be using in-box drivers, and as such, will be signed. However, it
// is possible that a built-in class installer will report a detected device
// that's using a 3rd-party, unsigned driver. The ScsiAdapter class is such
// a case. If we _do_ encounter unsigned driver packages, we want to avoid
// media prompting, just like we do when installing PnP-enumerated devices.
// Unfortunately, this is complicated in the legacy case by the fact that we
// queue all files into one big queue, then commit the queue in an all-or-
// nothing fashion. Thus, we don't have the same granularity that we have
// when installing PnP-enumerated devices in a one-at-a-time fashion.
//
// To address this, we will first queue up all files to a "temporary" queue,
// which we will then examine in a similar fashion to the way we handle the
// per-device queues for PnP-enumerated devices. If the catalog nodes
// associated with the queue are all signed, then we'll queue up those same
// files to our "real" queue. If one or more catalog nodes are not signed,
// we'll then do a queue scan based on presence checking. If all files
// required are present, then we'll add nothing to the "real" queue, but
// allow the device to be subsequently installed. If one or more required
// files are found to be missing, we're in a bad state, because queueing
// these files up to our "real" queue means the user will (potentially) see
// a driver signing warning popup and/or media prompt, either of which they
// may cancel out of. Since the legacy device install queue is one big
// queue containing fileops for all such device installs, cancelling its
// committal would result in none of the files being copied for any phase2
// install. Thus, multimedia codecs, non-motherboard legacy COM ports, and
// other (signed) system devices wouldn't get installed. Since this is
// obviously unacceptable, we instead simply skip installation for this
// device, just as if DIF_ALLOWINSTALL had failed. This isn't as bad as it
// sounds, because it would be _exceedingly_ rare if a 3rd-party driver's
// device were reported, yet all necessary files weren't present.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(!SetupDiGetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint2( L"SETUP: SetupDiGetDeviceInstallParams() failed for TempFileQueue (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
//
// Note: this code has had the following comment (and behavior) for a _long_
// time...
//
// "we may rely on this flag being set this early"
//
// The DI_FORCECOPY flag actually has no effect on any setupapi activity
// except for installation of class installer files via
// SetupDiInstallClass(Ex). However, it's possible some class-/co-installer
// has developed a dependency on its presence, and since it doesn't hurt
// anything, we'll continue to set it.
//
DeviceInstallParams.Flags |= DI_FORCECOPY;
TempFileQ = SetupOpenFileQueue();
if(TempFileQ == INVALID_HANDLE_VALUE) {
SetupDebugPrint2( L"SETUP: SetupOpenFileQueue() failed for TempFileQueue (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
DeviceInstallParams.Flags |= DI_NOVCP;
DeviceInstallParams.FileQueue = TempFileQ;
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed for TempFileQueue (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
//
// Queue the device files into our temporary file queue
//
if(SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES,
hDevInfo,
pDeviceInfoData)) {
Error = ERROR_SUCCESS;
} else {
Error = GetLastError();
if(Error == ERROR_DI_DO_DEFAULT) {
//
// This isn't actually an error
//
Error = ERROR_SUCCESS;
} else {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed for TempFileQueue (phase2). Error = %lx, DeviceId = %ls", Error, pDeviceId );
}
}
//
// Disassociate the temporary file queue from the device information
// element so that we can free it later...
//
DeviceInstallParams.Flags &= ~DI_NOVCP;
DeviceInstallParams.FileQueue = INVALID_HANDLE_VALUE;
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed for disassociating TempFileQueue (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
if(Error == ERROR_SUCCESS) {
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) suceeded for TempFileQueue (phase2). DeviceId = %ls", pDeviceId );
} else {
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
//
// Now that we've retrieved all files operations into our temporary file
// queue, we can "pre-verify" the catalog nodes in the queue. If an OEM
// INF in %windir%\Inf is unsigned, we scan the queue to see if all
// required files are present (although not validated) in their target
// locations. If we're doing an upgrade, and all files are in-place, we'll
// forego queue committal. If we're doing a fresh-install, and all files
// are in-place, we'll commit the empty queue on-the-spot, so that the user
// will get the driver signing popup (based on policy), thus may
// potentially abort the installation of this device. If all files aren't
// already in-place, we silently abort the device installation, as we can't
// run the risk of queueing up potentially-cancellable fileops to the
// "real" legacy device install queue.
//
if(NO_ERROR != pSetupVerifyQueuedCatalogs(TempFileQ)) {
//
// We only want to prune based on presence check for OEM INFs living in
// %windir%\Inf.
//
SP_DRVINFO_DATA DriverInfoData;
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
//
// Retrieve the name of the INF associated with the selected driver
// node.
//
DriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
if(!SetupDiGetSelectedDriver(hDevInfo, pDeviceInfoData, &DriverInfoData)) {
SetupDebugPrint2( L"SETUP: SetupDiGetSelectedDriver() failed. Error = %d, Device = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if(!SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
&DriverInfoData,
&DriverInfoDetailData,
sizeof(DriverInfoDetailData),
NULL) &&
(GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
SetupDebugPrint2( L"SETUP: SetupDiGetDriverInfoDetail() failed. Error = %d, Device = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
if(pSetupInfIsFromOemLocation(DriverInfoDetailData.InfFileName, TRUE) ||
IsInfInLayoutInf(DriverInfoDetailData.InfFileName)) {
//
// Either the INF lives somewhere other than %windir%\Inf, or its
// an in-box unsigned INF. In either case, we want to
// abort installation of this device, otherwise we risk having the
// user cancel the queue committal, and wipe out all the other
// detected device installs as well.
//
SetupDebugPrint2( L"SETUP: Skipping unsigned driver install for detected device (phase2). DeviceId = %ls, Inf = %ls", pDeviceId, DriverInfoDetailData.InfFileName );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
} else {
//
// Note: it doesn't really matter whether we do an "all-or-nothing"
// scan or a pruning scan here, because we're going to abort the
// install on anything other than a fully empty (post-scan) queue.
// We request pruning here, because that fits better with the
// semantics of SPQ_SCAN_PRUNE_DELREN (this flag's semantics
// don't really fit with a non-pruning scan, as discussed in RAID
// #280543).
//
if(!SetupScanFileQueue(TempFileQ,
SPQ_SCAN_FILE_PRESENCE |
SPQ_SCAN_PRUNE_COPY_QUEUE |
SPQ_SCAN_PRUNE_DELREN,
NULL,
NULL,
NULL,
&ScanQueueResult)) {
//
// SetupScanFileQueue failed for some reason (rare). We'll
// just commit the whole file queue...
//
ScanQueueResult = 0;
}
if(ScanQueueResult != 1) {
//
// Abort installation of this device, otherwise we risk having
// the user cancel the queue committal, and wipe out all the
// other detected device installs as well.
//
SetupDebugPrint1( L"SETUP: Skipping unsigned driver install for detected device due to missing files (phase2). DeviceId = %ls", pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
if(!Upgrade) {
PVOID QCBContext;
//
// No fileops left in queue, but we're doing a fresh install,
// so we still want to give user driver signing popup (logging
// the event to setupapi.log), and let them potentially abort
// the unsigned installation...
//
QCBContext = InitSysSetupQueueCallbackEx(
DeviceInstallParams.hwndParent,
INVALID_HANDLE_VALUE,
0,
0,
NULL
);
if(!QCBContext) {
SetupDebugPrint1( L"SETUP: Failed to allocate queue callback context (phase2). DeviceId = %ls", pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
if(!SetupCommitFileQueue(DeviceInstallParams.hwndParent,
TempFileQ,
SysSetupQueueCallback,
QCBContext)) {
//
// User elected not to proceed with the unsigned installation.
//
SetupDebugPrint2( L"SETUP: SetupCommitFileQueue() failed (phase2). Error = %d, Device = %ls", GetLastError(), pDeviceId );
b = FALSE;
}
TermSysSetupQueueCallback(QCBContext);
if(!b) {
goto phase2_legacy_dev_thread_exit;
}
}
}
} else {
//
// Queue the files to be copied for this device to the "real" file queue
//
if( FileQ != INVALID_HANDLE_VALUE ) {
DeviceInstallParams.Flags |= DI_NOVCP;
DeviceInstallParams.FileQueue = FileQ;
}
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed (phase2). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
//
// Install the device files (queue the files)
//
Error = ERROR_SUCCESS;
if( !SetupDiCallClassInstaller( DIF_INSTALLDEVICEFILES,
hDevInfo,
pDeviceInfoData ) &&
( ( Error = GetLastError() ) != ERROR_DI_DO_DEFAULT )
) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed (phase2). Error = %lx, DeviceId = %ls", Error, pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) suceeded (phase2). DeviceId = %ls", pDeviceId );
}
//
// Mark the device as 'Do install'
//
if( !pSetupDiSetDeviceInfoContext( hDevInfo, pDeviceInfoData, TRUE ) ) {
SetupDebugPrint2( L"SETUP: pSetupDiSetDeviceInfoContext() failed (phase2). Error = %lx, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase2_legacy_dev_thread_exit;
}
phase2_legacy_dev_thread_exit:
if(TempFileQ != INVALID_HANDLE_VALUE) {
SetupCloseFileQueue(TempFileQ);
}
return(b);
}
BOOL
CheckIfDeviceHasWizardPages( HDEVINFO hDevInfo,
PSP_DEVINFO_DATA pDeviceInfoData
)
/*++
Routine Description:
This routine invokes the class installer with
DIF_NEWDEVICEWIZARD_FINISHINSTALL to determine if the device has wizard
pages to display.
Arguments:
hDevInfo - The device info set.
pDeviceInfoData - The device that needs to be marked
Return Value:
Returns TRUE if the device has FINISHINSTALL wizard pages, FALSE otherwise
--*/
{
SP_NEWDEVICEWIZARD_DATA ndwd = {0};
BOOL b;
// Check if this device has wizard pages that need to be shown as
// part of the install. If so, we will mark the device as
// nneding a reinstall so the UI can be display later
//
ndwd.ClassInstallHeader.cbSize = sizeof( SP_CLASSINSTALL_HEADER );
ndwd.ClassInstallHeader.InstallFunction = DIF_NEWDEVICEWIZARD_FINISHINSTALL;
// Set the install params for the function
b = SetupDiSetClassInstallParams( hDevInfo, pDeviceInfoData,
(PSP_CLASSINSTALL_HEADER) ( &ndwd ),
sizeof( ndwd ) );
if ( b ) {
// Invoke the class installer (and co-installers)
b = SetupDiCallClassInstaller( DIF_NEWDEVICEWIZARD_FINISHINSTALL,
hDevInfo,
pDeviceInfoData );
if ( b || (ERROR_DI_DO_DEFAULT == GetLastError())) {
// Retrieve the install params
b = SetupDiGetClassInstallParams( hDevInfo,
pDeviceInfoData,
(PSP_CLASSINSTALL_HEADER)&ndwd,
sizeof(ndwd),
NULL );
if ( b ) {
// Are there any pages?
if ( 0 == ndwd.NumDynamicPages ) {
b = FALSE;
}
else {
// b is already TRUE if we made it here so no need to set
UINT i;
// We don't need the pages so destroy them
for ( i = 0; i < ndwd.NumDynamicPages; i++ ) {
DestroyPropertySheetPage( ndwd.DynamicPages[i] );
}
}
}
else {
SetupDebugPrint1( L"SETUP: SetupDiGetClassInstallParams failed (phase3). Error = %lx", GetLastError() );
}
}
else if ( ERROR_DI_DO_DEFAULT != GetLastError() ) {
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_NEWDEVICEWIZARD_FINISHINSTALL) failed (phase3). Error = %lx", GetLastError() );
}
}
else {
SetupDebugPrint1( L"SETUP: SetupDiSetClassInstallParams failed. Error = %lx", GetLastError() );
}
return b;
}
BOOL
MarkDeviceAsNeedsReinstallIfNeeded(
HDEVINFO hDevInfo,
PSP_DEVINFO_DATA pDeviceInfoData
)
/*++
Routine Description:
This function checks if a device has wizard pages
(DIF_NEWDEVICEWIZARD_FINISHINSTALL pages) and sets the REINSTALL
config flag if it does.
Arguments:
hDevInfo - The device info set.
pDeviceInfoData - The device whose config flags will set.
Return Value:
Returns TRUE if successful, FALSE on error.
--*/
{
DWORD ConfigFlags;
BOOL b = TRUE;
if (CheckIfDeviceHasWizardPages( hDevInfo, pDeviceInfoData ) ) {
SetupDebugPrint( L"SETUP: Device has wizard pages, marking as need reinstall." );
//
// Get the config flags for the device and set the reinstall bit
//
if ( !( b = GetDeviceConfigFlags(hDevInfo, pDeviceInfoData, &ConfigFlags ) ) ) {
SetupDebugPrint( L"SETUP: GetDeviceConfigFlags failed. " );
}
if ( b ) {
ConfigFlags |= CONFIGFLAG_REINSTALL;
if ( !( b = SetDeviceConfigFlags(hDevInfo, pDeviceInfoData, ConfigFlags ) ) ) {
SetupDebugPrint( L"SETUP: SetDeviceConfigFlags failed. " );
}
}
}
return b;
}
DWORD
pPhase3InstallPnpLegacyDevicesThread(
PPNP_PHASE3_LEGACY_DEV_THREAD_PARAMS ThreadParams
)
/*++
Routine Description:
This thread completes the installation of a legacy device.
It invokes a class installer with:
- DIF_REGISTER_COINSTALLERS
- DIF_INSTALLINTERFACES
- DIF_INSTALLDEVICE
Arguments:
ThreadParams - Points to a structure that contains the information
passed to this thread.
Return Value:
Returns TRUE if all calls to the class installer were successfull.
Otherwise, returns FALSE.
--*/
{
PPNP_PHASE3_LEGACY_DEV_THREAD_PARAMS Context;
HDEVINFO hDevInfo;
PSP_DEVINFO_DATA pDeviceInfoData;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
PWSTR pDeviceId;
BOOL b;
WCHAR DeviceDescription[MAX_PATH];
DWORD Status;
DWORD Problem;
BOOL fNewDevice = FALSE;
Context = ThreadParams;
hDevInfo = Context->hDevInfo;
pDeviceInfoData = &(Context->DeviceInfoData);
pDeviceId = Context->pDeviceId;
b = TRUE;
//
// Register any device-specific co-installers for this device.
//
if( !SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS, hDevInfo, pDeviceInfoData ) ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed (phase3). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase3_legacy_dev_thread_exit;
}
//
// Install any INF/class installer-specified interfaces.
//
if( !SetupDiCallClassInstaller(DIF_INSTALLINTERFACES, hDevInfo, pDeviceInfoData) ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_REGISTER_INSTALLINTERFACES) failed (phase3). Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase3_legacy_dev_thread_exit;
}
//
// Before we install this device, we need to find out if it is a new
// device or a reinstall. If the problem CM_PROB_NOT_CONFIGURED is set
// then we will consider it as a new device and check if it has wizard
// pages after DIF_INSTALLDEVICE
//
if ( CR_SUCCESS == CM_Get_DevInst_Status(&Status,
&Problem,
(DEVINST)pDeviceInfoData->DevInst,
0 ) && (Problem & CM_PROB_NOT_CONFIGURED) )
{
fNewDevice = TRUE;
}
//
// Set the DI_FLAGSEX_RESTART_DEVICE_ONLY for legacy device installs. This
// flag tells setupapi to only stop/start this one device and not all
// devices that share the same drivers with this device.
//
// This is not critical if this fails since by default setupapi will just
// stop/start all devices that share drivers with this device, including
// the device itself. This can cause stop/start to take a little longer
// if there are lots of devices sharing drivers with this device.
//
DeviceInstallParams.cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(SetupDiGetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams)) {
DeviceInstallParams.FlagsEx |= DI_FLAGSEX_RESTART_DEVICE_ONLY;
SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, &DeviceInstallParams);
}
if( !SetupDiCallClassInstaller( DIF_INSTALLDEVICE,
hDevInfo,
pDeviceInfoData ) ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed (phase3). Error = %lx, DeviceId = %ls", GetLastError(), pDeviceId );
b = FALSE;
goto phase3_legacy_dev_thread_exit;
}
DeviceDescription[0] = L'\0';
if( !SetupDiGetDeviceRegistryProperty( hDevInfo,
pDeviceInfoData,
SPDRP_DEVICEDESC,
NULL,
(PBYTE)DeviceDescription,
sizeof( DeviceDescription ),
NULL ) ) {
SetupDebugPrint2( L"SETUP: SetupDiGetDeviceRegistryProperty() failed. Error = %d, DeviceId = %ls", GetLastError(), pDeviceId );
}
SetupDebugPrint2( L"SETUP: Device installed. DeviceId = %ls, Description = %ls", pDeviceId, DeviceDescription );
//
// If the device has wizard pages to show (in response to
// DIF_NEWDEVICEWIZARD_FINISHINSTALL) then it needs to be marked as need
// reinstall so that pages get a chance to be shown at a later time
//
if ( fNewDevice ) {
b = MarkDeviceAsNeedsReinstallIfNeeded( hDevInfo, pDeviceInfoData);
}
phase3_legacy_dev_thread_exit:
return( b );
}
BOOL
GetDeviceConfigFlags(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDeviceInfoData,
DWORD* pdwConfigFlags)
/*++
Routine Description:
This function gets the configflags of a device.
Arguments:
hDevInfo - The device info set.
pDeviceInfoData - The device whose config flags will be retrieved.
pdwConfigFlags - The buffer that will receive the current flags.
Return Value:
Returns TRUE if successful, FALSE on error.
--*/
{
BOOL b = TRUE;
DWORD Error;
//
// Clear the output parameter
//
*pdwConfigFlags = 0;
// Get the config flags for the device
if( !SetupDiGetDeviceRegistryProperty( hDevInfo,
pDeviceInfoData,
SPDRP_CONFIGFLAGS,
NULL,
(PBYTE)pdwConfigFlags,
sizeof( *pdwConfigFlags ),
NULL ) ) {
Error = GetLastError();
//
// ERROR_INVALID_DATA is ok. It means that the device doesn't have config flags set yet.
//
if( Error != ERROR_INVALID_DATA ) {
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: GetDeviceConfigFlags failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: GetDeviceConfigFlags failed. Error = %d", Error );
}
b = FALSE;
}
}
return b;
}
BOOL
SetDeviceConfigFlags(HDEVINFO hDevInfo, PSP_DEVINFO_DATA pDeviceInfoData,
DWORD dwConfigFlags)
/*++
Routine Description:
This function sets the configflags of a device.
Arguments:
hDevInfo - The device info set.
pDeviceInfoData - The device whose config flags will set.
dwConfigFlags - The config flags to set.
Return Value:
Returns TRUE if successful, FALSE on error.
--*/
{
BOOL b = TRUE;
DWORD Error;
if( !SetupDiSetDeviceRegistryProperty( hDevInfo,
pDeviceInfoData,
SPDRP_CONFIGFLAGS,
(PBYTE)&dwConfigFlags,
sizeof( dwConfigFlags ) ) ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetDeviceConfigFlags failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetDeviceConfigFlags failed. Error = %d", Error );
}
b = FALSE;
}
return b;
}
DWORD
pInstallPnpEnumeratedDeviceThread(
PPNP_ENUM_DEV_THREAD_PARAMS ThreadParams
)
/*++
Routine Description:
This is the thread that does the installation of an enumerated PnP device.
Arguments:
ThreadParams - Points to a structure that contains the information
passed to this thread.
Return Value:
Returns a Win32 error code.
--*/
{
PPNP_ENUM_DEV_THREAD_PARAMS Context;
HDEVINFO hDevInfo;
PSP_DEVINFO_DATA pDeviceInfoData;
PSP_DEVINSTALL_PARAMS pDeviceInstallParams;
PWSTR pDeviceDescription;
PWSTR pDeviceId;
ULONG Error;
WCHAR RootPath[ MAX_PATH + 1];
SP_DEVINSTALL_PARAMS DeviceInstallParams;
DWORD Status;
DWORD Problem;
BOOL fNewDevice = FALSE;
DWORD ConfigFlags;
HSPFILEQ FileQ;
PVOID QContext;
HSPFILEQ SavedFileQ;
DWORD SavedFlags;
DWORD ScanQueueResult;
HWND hwndParent;
SP_DRVINFO_DATA pDriverInfoData;
HKEY hClassKey;
WCHAR InfPath[MAX_PATH];
BOOL fCommitFileQueue = TRUE;
BOOL fDriversChanged = FALSE;
DWORD FileQueueFlags;
Context = ThreadParams;
hDevInfo = Context->hDevInfo;
pDeviceInfoData = &(Context->DeviceInfoData);
pDeviceInstallParams = &DeviceInstallParams;
pDeviceDescription = Context->pDeviceDescription;
pDeviceId = Context->pDeviceId;
InfPath[0] = TEXT('\0');
Error = ERROR_SUCCESS;
//
// Queue all files to be copied into our own file queue.
//
FileQ = SetupOpenFileQueue();
if ( FileQ == (HSPFILEQ)INVALID_HANDLE_VALUE ) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupOpenFileQueue() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
pDeviceInstallParams->cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(!SetupDiGetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// Let the class installer/co-installers know that they should be quiet
//
pDeviceInstallParams->Flags |= DI_QUIETINSTALL;
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// Install the class if it does not exist.
//
if (CM_Open_Class_Key(&pDeviceInfoData->ClassGuid,
NULL,
KEY_READ,
RegDisposition_OpenExisting,
&hClassKey,
CM_OPEN_CLASS_KEY_INSTALLER
) != CR_SUCCESS) {
HSPFILEQ ClassFileQ;
PVOID ClassQContext;
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
ClassFileQ = SetupOpenFileQueue();
if ( ClassFileQ == (HSPFILEQ)INVALID_HANDLE_VALUE ) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupOpenFileQueue() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// First, we have to retrieve the name of the INF associated with the
// selected driver node.
//
pDriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
if (!SetupDiGetSelectedDriver(hDevInfo, pDeviceInfoData, &pDriverInfoData)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiGetSelectedDriver() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if (!SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
&pDriverInfoData,
&DriverInfoDetailData,
sizeof(DriverInfoDetailData),
NULL) &&
(GetLastError() != ERROR_INSUFFICIENT_BUFFER)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiGetDriverInfoDetail() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
if (!SetupDiInstallClass(NULL,
DriverInfoDetailData.InfFileName,
DI_NOVCP | DI_FORCECOPY,
ClassFileQ)) {
Error = GetLastError();
SetupDebugPrint3( L"SETUP: SetupDiInstallClass(%s) failed. Error = %d, Device = %ls", DriverInfoDetailData.InfFileName, Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// Commit the file queue.
//
ClassQContext = InitSysSetupQueueCallbackEx(
NULL,
INVALID_HANDLE_VALUE,
0,0,NULL);
if( ClassQContext == NULL) {
Error = GetLastError();
SetupDebugPrint1( L"SETUP: InitSysSetupQueueCallbackEx() failed. Error = %d", Error );
goto enum_dev_thread_exit;
}
if (!SetupCommitFileQueue(
NULL,
ClassFileQ,
SysSetupQueueCallback,
ClassQContext
)) {
Error = GetLastError();
}
TermSysSetupQueueCallback(ClassQContext);
if ( ClassFileQ != (HSPFILEQ)INVALID_HANDLE_VALUE ) {
SetupCloseFileQueue( ClassFileQ );
}
if (Error == NO_ERROR) {
SetupDebugPrint1( L"SETUP: SetupDiInstallClass() succeeded. Device = %ls", pDeviceDescription );
} else {
//
// We failed while installing the class so don't bother installing
// the device.
//
SetupDebugPrint3( L"SETUP: SetupCommitFileQueue(%s) failed while installing Class. Error = %d, Device = %ls", DriverInfoDetailData.InfFileName, Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
} else {
//
// The class already exists.
//
RegCloseKey(hClassKey);
}
//
// Verify with the class installer and class-specific co-installers that the
// driver we're about to install isn't blacklisted.
//
if( !SetupDiCallClassInstaller(DIF_ALLOW_INSTALL,
hDevInfo,
pDeviceInfoData ) ) {
Error = GetLastError();
if( Error != ERROR_DI_DO_DEFAULT ) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
}
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_ALLOW_INSTALL) succeeded. Device = %ls", pDeviceDescription );
//
// Everything checks out. We're ready to pre-copy the driver files for this device.
//
pDeviceInstallParams->cbSize = sizeof(SP_DEVINSTALL_PARAMS);
if(!SetupDiGetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiGetDeviceInstallParams() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
pDeviceInstallParams->Flags |= DI_FORCECOPY;
SavedFileQ = pDeviceInstallParams->FileQueue;
SavedFlags = pDeviceInstallParams->Flags;
pDeviceInstallParams->FileQueue = FileQ;
pDeviceInstallParams->Flags |= DI_NOVCP;
//
// If the old, or existing, driver is an 3rd party driver and not the same
// as the current driver we are about to install, then back up the existing
// drivers.
//
if (pDoesExistingDriverNeedBackup(hDevInfo, pDeviceInfoData, InfPath, sizeof(InfPath)/sizeof(WCHAR))) {
SetupDebugPrint1( L"SETUP: Backing up 3rd party drivers for Device = %ls", pDeviceDescription );
pDeviceInstallParams->FlagsEx |= DI_FLAGSEX_PREINSTALLBACKUP;
}
//
// Remember the parent HWND because we may need it later...
//
hwndParent = pDeviceInstallParams->hwndParent;
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// Install the device files
//
Error = ERROR_SUCCESS;
if( !SetupDiCallClassInstaller( DIF_INSTALLDEVICEFILES,
hDevInfo,
pDeviceInfoData ) &&
( ( Error = GetLastError() ) != ERROR_DI_DO_DEFAULT )
) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) failed. Error = %lx, Device = %ls ", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICEFILES) succeeded. Device = %ls", pDeviceDescription );
//
// Commit the file queue.
//
QContext = InitSysSetupQueueCallbackEx(
NULL,
INVALID_HANDLE_VALUE,
0,0,NULL);
//
// "Pre-verify" the catalog nodes in the file queue. If an OEM INF in
// %windir%\Inf is unsigned, we scan the queue to see if all required files
// are present (although not validated) in their target locations. If
// we're doing an upgrade, and all files are in-place, we'll forego queue
// committal. If we're doing a fresh install, we'll still commit the
// queue, even if all files were present. This will result in a driver
// signing popup (based on policy). We do this to prevent subversion of
// driver signing due to someone "sneaking" all the files into place prior
// to GUI setup.
//
if(NO_ERROR != pSetupVerifyQueuedCatalogs(FileQ)) {
//
// We only want to prune based on presence check for OEM INFs living in
// %windir%\Inf.
//
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
//
// Retrieve the name of the INF associated with the selected driver
// node.
//
pDriverInfoData.cbSize = sizeof(SP_DRVINFO_DATA);
if (!SetupDiGetSelectedDriver(hDevInfo, pDeviceInfoData, &pDriverInfoData)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiGetSelectedDriver() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if (!SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
&pDriverInfoData,
&DriverInfoDetailData,
sizeof(DriverInfoDetailData),
NULL) &&
((Error = GetLastError()) != ERROR_INSUFFICIENT_BUFFER)) {
SetupDebugPrint2( L"SETUP: SetupDiGetDriverInfoDetail() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// There is only one case where we want to skip commiting the file queue
// for an unsigned drivers, and all of the following must be true:
//
// - This is an upgrade.
// - The INF lives under %windir%\INF
// - The INF is NOT an in-box INF
// - The INF has the same name as the previous INF (if there was
// a previous INF)
// - No file copy operations (copy, delete, rename) need to be done.
//
if(Upgrade &&
!pSetupInfIsFromOemLocation(DriverInfoDetailData.InfFileName, TRUE) &&
!IsInfInLayoutInf(DriverInfoDetailData.InfFileName) &&
((InfPath[0] == TEXT('\0')) ||
(lstrcmpi(InfPath, pSetupGetFileTitle(DriverInfoDetailData.InfFileName)) == 0)) &&
SetupScanFileQueue(FileQ,
SPQ_SCAN_FILE_PRESENCE |
SPQ_SCAN_PRUNE_DELREN,
hwndParent,
NULL,
NULL,
&ScanQueueResult) &&
(ScanQueueResult == 1)) {
fCommitFileQueue = FALSE;
}
} else {
//
// Prun the file queue.
//
SetupScanFileQueue(FileQ,
SPQ_SCAN_FILE_VALIDITY |
SPQ_SCAN_PRUNE_COPY_QUEUE |
SPQ_SCAN_PRUNE_DELREN,
NULL,
NULL,
NULL,
&ScanQueueResult);
}
//
// If this device is the computer itself (i.e., the HAL, kernel, and other
// platform-specific files), then we shouldn't need to copy anything, since
// all the right files were copied during textmode setup. However, these
// files will _not_ have been pruned from the file queue if they came from
// HAL.INF because they're marked as COPYFLG_NOPRUNE in that INF. This is
// done so that doing an "update driver" from a UP HAL to an MP one works
// properly. (Pruning gets in the way here, since we consider the properly-
// signed UP files on the system to be perfectly acceptable, thus don't
// bother with copying over the MP versions.)
//
// In order to avoid re-copying the HAL, kernel, etc., we just always skip
// this queue committal if the device is of class "Computer".
//
if(IsEqualGUID(&(pDeviceInfoData->ClassGuid), &GUID_DEVCLASS_COMPUTER)) {
fCommitFileQueue = FALSE;
}
Error = ERROR_SUCCESS;
if (fCommitFileQueue) {
if (!SetupCommitFileQueue(
NULL,
FileQ,
SysSetupQueueCallback,
QContext
)) {
Error = GetLastError();
}
}
if (SetupGetFileQueueFlags(FileQ, &FileQueueFlags) &&
(FileQueueFlags & SPQ_FLAG_FILES_MODIFIED)) {
//
// One of the driver files has changed for this device. This means
// a full restart of the device, and all other devices shareing
// one of its drivers is in order.
//
fDriversChanged = TRUE;
}
TermSysSetupQueueCallback(QContext);
if (Error != ERROR_SUCCESS) {
SetupDebugPrint2( L"SETUP: SetupCommitFileQueue() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// If no driver files changed then we only need to restart this device.
//
if (!fDriversChanged) {
pDeviceInstallParams->FlagsEx |= DI_FLAGSEX_RESTART_DEVICE_ONLY;
}
pDeviceInstallParams->FileQueue = SavedFileQ;
pDeviceInstallParams->Flags = (SavedFlags | DI_NOFILECOPY) ;
if(!SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams)) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiSetDeviceInstallParams() failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
//
// Make sure that the files get flushed to disk
//
GetWindowsDirectory(RootPath,sizeof(RootPath)/sizeof(WCHAR));
RootPath[3] = L'\0';
FlushFilesToDisk( RootPath );
//
// Register any device-specific co-installers for this device.
//
if( !SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS,
hDevInfo,
pDeviceInfoData ) ) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_REGISTER_COINSTALLERS) succeeded. Device = %ls", pDeviceDescription );
//
// Install any INF/class installer-specified interfaces.
//
if( !SetupDiCallClassInstaller(DIF_INSTALLINTERFACES,
hDevInfo,
pDeviceInfoData) ) {
Error = GetLastError();
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_REGISTER_INSTALLINTERFACES) failed. Error = %d, Device = %ls", Error, pDeviceDescription );
goto enum_dev_thread_exit;
}
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLINTERFACES) succeeded. Device = %ls", pDeviceDescription );
//
// Before we install this device, we need to find out if it is a new
// device or a reinstall. If the problem CM_PROB_NOT_CONFIGURED is set
// then we will consider it as a new device and check if it has wizard
// pages after DIF_INSTALLDEVICE
//
if ( CR_SUCCESS == CM_Get_DevInst_Status(&Status,
&Problem,
(DEVINST)pDeviceInfoData->DevInst,
0 ) && (Problem & CM_PROB_NOT_CONFIGURED) )
{
fNewDevice = TRUE;
}
//
// Install the device
//
Error = ERROR_SUCCESS;
if( !SetupDiCallClassInstaller( DIF_INSTALLDEVICE,
hDevInfo,
pDeviceInfoData ) &&
( ( Error = GetLastError() ) != ERROR_DI_DO_DEFAULT )
) {
SetupDebugPrint2( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) failed. Error = %lx, Device = %ls ", Error, pDeviceDescription );
goto enum_dev_thread_exit;
} else {
SetupDebugPrint1( L"SETUP: SetupDiCallClassInstaller(DIF_INSTALLDEVICE) suceeded. Device = %ls ", pDeviceDescription );
}
//
// If the device has wizard pages to show (in response to
// DIF_NEWDEVICEWIZARD_FINISHINSTALL) then it needs to be marked as need
// reinstall so that pages get a chance to be shown at a later time
// note that we have to do this even for a re-install
//
if (!MarkDeviceAsNeedsReinstallIfNeeded( hDevInfo, pDeviceInfoData) ) {
Error = GetLastError();
}
enum_dev_thread_exit:
if ( FileQ != (HSPFILEQ)INVALID_HANDLE_VALUE ) {
if(SetupDiGetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams)) {
pDeviceInstallParams->FileQueue = INVALID_HANDLE_VALUE;
pDeviceInstallParams->Flags &= ~DI_NOVCP;
SetupDiSetDeviceInstallParams(hDevInfo, pDeviceInfoData, pDeviceInstallParams);
}
SetupCloseFileQueue( FileQ );
}
if (Error != ERROR_SUCCESS) {
//
// Device installed failed, so install the NULL driver on this device.
//
SetupDebugPrint( L"SETUP: Installing the null driver for this device" );
if( !SyssetupInstallNullDriver( hDevInfo, pDeviceInfoData ) ) {
SetupDebugPrint( L"SETUP: Unable to install null driver" );
}
}
return( Error );
}
BOOL
MarkPnpDevicesAsNeedReinstall(
)
/*++
Routine Description:
This function marks all non-present Pnp devices as 'need reinstall'.
Arguments:
None.
Return Value:
Returns TRUE if the null if all devices were marked successfull.
--*/
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
ULONG Index = 0;
BOOL b;
DWORD Error;
SetupDebugPrint( L"SETUP: Entering MarkPnpDevicesAsNeedReinstall()." );
Error = ERROR_SUCCESS;
//
// Get a list of all devices
//
hDevInfo = SetupDiGetClassDevs( NULL,
NULL,
NULL,
DIGCF_ALLCLASSES );
if( hDevInfo == INVALID_HANDLE_VALUE ) {
Error = GetLastError();
if( ((LONG)Error) < 0 ) {
//
// Setupapi error code, display it in hex
//
SetupDebugPrint1( L"SETUP: SetupDiGetClassDevs(DIGCF_ALLCLASSES) failed. Error = %lx", Error );
} else {
//
// win32 error code, display it in decimal
//
SetupDebugPrint1( L"SETUP: SetupDiGetClassDevs(DIGCF_ALLCLASSES) failed. Error = %d", Error );
}
SetupDebugPrint( L"SETUP: Leaving MarkPnpDevicesAsNeedReinstall(). No devices marked." );
return( FALSE );
}
//
// Assume success
//
b = TRUE;
//
// Now enumerate each device information element added to this set, and
// mark it as 'need reinstall' if it isn't a live devnode.
//
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for( Index = 0;
SetupDiEnumDeviceInfo( hDevInfo, Index, &DeviceInfoData );
Index++ ) {
DWORD ConfigFlags;
ULONG Status, Problem;
if(CR_SUCCESS == CM_Get_DevInst_Status(&Status,
&Problem,
(DEVINST)DeviceInfoData.DevInst,
0))
{
//
// Since we were able to retrieve the devnode's status, we know that
// it is a live devnode (not necessarily started, but at least
// present in the hardware tree). Thus, we don't want to mark it
// as needing reinstall--it'll automatically get reinstalled as part
// of our installation of all present devices (i.e., that we get
// from the UMPNPMGR named pipe).
//
continue;
}
//
// Get the config flags for the device and set the reinstall bit
//
if ( !( b = GetDeviceConfigFlags(hDevInfo, &DeviceInfoData, &ConfigFlags ) ) )
{
SetupDebugPrint1( L"SETUP: GetDeviceConfigFlags failed. Index = %d", Index );
continue;
}
ConfigFlags |= CONFIGFLAG_REINSTALL;
if ( !( b = SetDeviceConfigFlags(hDevInfo, &DeviceInfoData, ConfigFlags ) ) ) {
SetupDebugPrint1( L"SETUP: SetDeviceConfigFlags failed. Index = %d", Index );
continue;
}
}
//
// Find out why SetupDiEnumDeviceInfo() failed.
//
Error = GetLastError();
if( Error != ERROR_NO_MORE_ITEMS ) {
SetupDebugPrint2( L"SETUP: Device = %d, SetupDiEnumDeviceInfo() failed. Error = %d", Index, Error );
b = FALSE;
}
SetupDebugPrint1( L"SETUP: Leaving MarkPnpDevicesAsNeedReinstall(). Devices marked = %d", Index );
SetupDiDestroyDeviceInfoList( hDevInfo );
return( b );
}
//
// devices installs may exist in RunOnce entries
// they are processed as a batch, as opposed to per-device
// during syssetup
//
BOOL
CallRunOnceAndWait(
)
/*++
Routine Description:
This function calls RunOnce and waits for a "reasonable" amount of time for it to complete
if we don't return within a reasonable amount of time, we leave RunOnce going
and continue with rest of install process
if we underestimate timeout, we can cause a whole series of "class installer appears to have hung" messages
Arguments:
None.
Return Value:
Returns TRUE if we completed successfully
FALSE should not be considered a fatal error, and can be ignored
--*/
{
static CONST TCHAR pszPathRunOnce[] = REGSTR_PATH_RUNONCE;
BOOL Success = FALSE;
STARTUPINFO StartupInfo;
PROCESS_INFORMATION ProcessInformation;
BOOL started;
TCHAR cmdline[MAX_PATH];
HKEY hKey = NULL;
LONG l;
DWORD nValues = 0;
DWORD timeout;
SetupDebugPrint( L"SETUP: Entering CallRunOnceAndWait. ");
try {
//
// First, open the key "HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce"
// to see if we have anything to do
//
if((l = RegOpenKeyEx(HKEY_LOCAL_MACHINE,pszPathRunOnce,0,KEY_QUERY_VALUE,&hKey)) != ERROR_SUCCESS) {
//
// not considered an error
//
SetupDebugPrint( L"SETUP: CallRunOnceAndWait: could not open RunOnce registry, assuming no entries. ");
Success = TRUE;
leave;
}
//
// we want to know how many items we'll be executing in RunOnce to estimate a timeout
//
l = RegQueryInfoKey(hKey,NULL,NULL,NULL,
NULL, NULL, NULL,
&nValues,
NULL, NULL, NULL, NULL);
if ( l != ERROR_SUCCESS ) {
SetupDebugPrint( L"SETUP: CallRunOnceAndWait: could not get number of entries, assuming no entries. ");
nValues = 0;
}
RegCloseKey(hKey);
//
// estimating timeout is a black art
// we can try and guess for any entries in the HKLM\Software\Microsoft\Windows\CurrentVersion\RunOnce key
// but we're in the dark if any new keys are added
// we add '5' items to the timeout to "account" for this uncertainty. We also always run RunOnce
//
if (nValues == 0) {
SetupDebugPrint( L"SETUP: CallRunOnceAndWait: calling RunOnce (no detected entries). ");
nValues = 5;
} else {
SetupDebugPrint1( L"SETUP: CallRunOnceAndWait: calling RunOnce (%u known entries). ", nValues);
nValues += 5;
}
if (nValues < RUNONCE_THRESHOLD) {
timeout = nValues * RUNONCE_TIMEOUT;
} else {
timeout = RUNONCE_THRESHOLD * RUNONCE_TIMEOUT;
}
ZeroMemory(&StartupInfo,sizeof(StartupInfo));
ZeroMemory(&ProcessInformation,sizeof(ProcessInformation));
StartupInfo.cb = sizeof(StartupInfo);
lstrcpy(cmdline,TEXT("runonce -r"));
started = CreateProcess(NULL, // use application name below
cmdline, // command to execute
NULL, // default process security
NULL, // default thread security
FALSE, // don't inherit handles
0, // default flags
NULL, // inherit environment
NULL, // inherit current directory
&StartupInfo,
&ProcessInformation);
if(started) {
DWORD WaitProcStatus;
do {
WaitProcStatus = WaitForSingleObjectEx(ProcessInformation.hProcess, timeout , TRUE);
} while (WaitProcStatus == WAIT_IO_COMPLETION);
if (WaitProcStatus == WAIT_TIMEOUT) {
//
// RunOnce is still running
//
SetupDebugPrint( L"SETUP: CallRunOnceAndWait: RunOnce may have hung and has been abandoned. ");
} else if (WaitProcStatus == (DWORD)(-1)) {
//
// huh?
//
DWORD WaitProcError = GetLastError();
SetupDebugPrint1( L"SETUP: CallRunOnceAndWait: WaitForSingleObjectEx failed. Error = %lx ", WaitProcError );
} else {
//
// we ran, we waited, we returned
//
Success = TRUE;
}
CloseHandle(ProcessInformation.hThread);
CloseHandle(ProcessInformation.hProcess);
} else {
DWORD CreateProcError;
//
// The runonce task should get picked up later by someone else (e.g., at next
// login).
//
CreateProcError = GetLastError();
SetupDebugPrint1( L"SETUP: CallRunOnceAndWait: start RunOnce failed. Error = %lx ", CreateProcError );
}
} except(EXCEPTION_EXECUTE_HANDLER) {
SetupDebugPrint( L"SETUP: CallRunOnceAndWait: Exception! ");
Success = FALSE;
}
SetupDebugPrint( L"SETUP: Leaving CallRunOnceAndWait. ");
return Success;
}
//
// obsolete function from devmgr.c. keep the export in place for backwards compatibility
//
BOOL
DevInstallW(
HDEVINFO hDevInfo,
PSP_DEVINFO_DATA pDeviceInfoData
)
{
UNREFERENCED_PARAMETER(hDevInfo);
UNREFERENCED_PARAMETER(pDeviceInfoData);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return FALSE;
}
ULONG
SyssetupGetPnPFlags(
IN HDEVINFO hDevInfo,
IN PSP_DEVINFO_DATA pDeviceInfoData,
IN PSP_DRVINFO_DATA pDriverInfoData
)
{
DWORD Err;
BOOL b;
SP_DRVINFO_DETAIL_DATA DriverInfoDetailData;
HINF hInf;
WCHAR InfSectionWithExt[255]; // MAX_SECT_NAME_LEN from setupapi\inf.h
INFCONTEXT InfContext;
ULONG ret = 0;
//
// First retrieve the name of the INF for this driver node.
//
DriverInfoDetailData.cbSize = sizeof(SP_DRVINFO_DETAIL_DATA);
if(!SetupDiGetDriverInfoDetail(hDevInfo,
pDeviceInfoData,
pDriverInfoData,
&DriverInfoDetailData,
sizeof(DriverInfoDetailData),
NULL)) {
//
// If we failed with ERROR_INSUFFICIENT_BUFFER, that's OK. We're
// guaranteed to have gotten all the static fields in the driver info
// detail structure filled in (including the INF name and section
// name fields).
//
Err = GetLastError();
MYASSERT(Err == ERROR_INSUFFICIENT_BUFFER);
if(Err != ERROR_INSUFFICIENT_BUFFER) {
return ret;
}
}
//
// Now open up the INF associated with this driver node.
//
hInf = SetupOpenInfFile(DriverInfoDetailData.InfFileName,
NULL,
INF_STYLE_WIN4,
NULL
);
if(hInf == INVALID_HANDLE_VALUE) {
//
// This will fail, for example, if the INF is an old-style INF.
//
return ret;
}
//
// Get the potentially decorated install section name.
//
b = SetupDiGetActualSectionToInstall(hInf,
DriverInfoDetailData.SectionName,
InfSectionWithExt,
SIZECHARS(InfSectionWithExt),
NULL,
NULL
);
MYASSERT(b);
if(!b) {
goto clean0;
}
//
// Now look to see if there's a "SyssetupPnPFlags" entry in that section.
//
if(!SetupFindFirstLine(hInf,
InfSectionWithExt,
szSyssetupPnPFlags,
&InfContext)) {
//
// We didn't find such a line in this section.
//
goto clean0;
}
if(!SetupGetIntField(&InfContext, 1, (PINT)&ret)) {
//
// We failed--ensure return value is still zero.
//
ret = 0;
goto clean0;
}
clean0:
SetupCloseInfFile(hInf);
return ret;
}
//
// this function will tell umpnpmgr to stop server-side installs
//
VOID
PnpStopServerSideInstall(
VOID
)
/*++
Routine Description:
After phase-2, server-side installs kick in to pick up software-enumerated drivers
Call this when it's critical that we need to stop installing
Arguments:
None.
Return Value:
None.
Returns when it is safe to proceed.
--*/
{
//
// since when we're called there should be nobody generating new devnodes, we're pretty safe
//
CMP_WaitNoPendingInstallEvents(INFINITE);
}
//
// this function will update HAL+kernel from NewInf
//
VOID
PnpUpdateHAL(
VOID
)
/*++
Routine Description:
At very end of MiniSetup, OEM may indicate (via Unattend) that a different HAL should be installed
This must be done very last due to that way that HAL's need to be installed, if we do this earlier
then an app or service may pick the wrong HAL/Kernel32/other
Arguments:
None
Return Value:
None.
There is nothing meaningful that can be done if this fails
--*/
{
HDEVINFO hDevInfo = INVALID_HANDLE_VALUE;
SP_DEVINFO_DATA DevInfoData;
SP_DEVINSTALL_PARAMS DeviceInstallParams;
SP_DRVINFO_DATA DrvInfoData;
WCHAR HardwareID[MAX_PATH+2];
WCHAR InfPath[MAX_PATH];
WCHAR Answer[MAX_PATH];
WCHAR AnswerFile[2*MAX_PATH];
DWORD HardwareIdSize;
DWORD dwLen;
DWORD Error;
ULONG flags = 0;
HINSTANCE hInstNewDev = NULL;
ExternalUpdateDriverForPlugAndPlayDevicesW pUpdateDriverForPlugAndPlayDevicesW = NULL;
PWSTR pSrch = NULL;
PWSTR pHwID = NULL;
PWSTR pInfPath = NULL;
BOOL RebootFlag = FALSE;
SYSTEM_INFO info;
GetSystemDirectory(AnswerFile,MAX_PATH);
pSetupConcatenatePaths(AnswerFile,WINNT_GUI_FILE,MAX_PATH,NULL);
//
// If we determine we are only running on one processor (based on System Info)
// allow use of UpdateUPHAL
//
GetSystemInfo(&info);
Answer[0]=L'\0';
if(info.dwNumberOfProcessors==1) {
if( GetPrivateProfileString( WINNT_UNATTENDED,
WINNT_U_UPDATEUPHAL,
pwNull,
Answer,
MAX_PATH,
AnswerFile )) {
SetupDebugPrint1( L"SETUP: UpdateUPHAL=\"%ls\"",Answer);
}
}
if(!Answer[0]) {
//
// we didn't explicitly get an answer based on being UP - try catch-all
//
if( GetPrivateProfileString( WINNT_UNATTENDED,
WINNT_U_UPDATEHAL,
pwNull,
Answer,
MAX_PATH,
AnswerFile )) {
SetupDebugPrint1( L"SETUP: UpdateHAL=\"%ls\"",Answer);
}
}
if(!Answer[0]) {
//
// no update request
//
return;
}
//
// split Answer into HardwareID & INF
//
pHwID = Answer;
pSrch = wcschr(Answer,L',');
if(pSrch == NULL) {
SetupDebugPrint( L"SETUP: Required Syntax: \"hwid,inffile\"");
return;
}
pInfPath = pSrch+1;
//
// trim HardwareID & prepare as a MULTI-SZ
//
while(pHwID[0]==L' '||pHwID[0]==L'\t') {
pHwID++;
}
while(pSrch != pHwID && (pSrch[-1]==L' '||pSrch[-1]==L'\t')) {
pSrch--;
}
pSrch[0]=0;
if(!pHwID[0]) {
SetupDebugPrint( L"SETUP: Required Syntax: \"hwid,inffile\"");
return;
}
lstrcpy(HardwareID,pHwID);
HardwareIdSize = wcslen(HardwareID)+1;
HardwareID[HardwareIdSize++]=L'\0';
//
// now pre-process the INF, trim & allow %windir% expansion
//
while(pInfPath[0]==L' '||pInfPath[0]==L'\t') {
pInfPath++;
}
pSrch = pInfPath+wcslen(pInfPath);
while(pSrch != pInfPath && (pSrch[-1]==L' '||pSrch[-1]==L'\t')) {
pSrch--;
}
pSrch[0]=0;
if(!pInfPath[0]) {
SetupDebugPrint( L"SETUP: Required Syntax: \"hwid,inffile\"");
return;
}
dwLen=ExpandEnvironmentStrings(pInfPath,InfPath,MAX_PATH);
if(dwLen==0 || dwLen > MAX_PATH) {
SetupDebugPrint1( L"SETUP: Expansion of \"%ls\" failed",InfPath);
return;
}
SetupDebugPrint2( L"SETUP: Preparing to install new HAL %ls from %ls",HardwareID,InfPath);
// we need "UpdateDriverForPlugAndPlayDevices"
// make sure we can get this before changing hardware ID
//
hInstNewDev = LoadLibrary(L"newdev.dll");
if(hInstNewDev == NULL) {
SetupDebugPrint1( L"SETUP: Failed to load newdev.dll. Error = %d", GetLastError() );
goto clean0;
}
pUpdateDriverForPlugAndPlayDevicesW = (ExternalUpdateDriverForPlugAndPlayDevicesW)
GetProcAddress(hInstNewDev,
"UpdateDriverForPlugAndPlayDevicesW");
if(pUpdateDriverForPlugAndPlayDevicesW==NULL) {
SetupDebugPrint1( L"SETUP: Failed to get UpdateDriverForPlugAndPlayDevicesW. Error = %d", GetLastError() );
goto clean0;
}
//
// we enumerate the Computer class, GUID={4D36E966-E325-11CE-BFC1-08002BE10318}
// and should find a single DevNode, which is the one we need to update
// when we actually update, we consider this a safe thing to do
// since it should not involve any Co-Installers
//
hDevInfo = SetupDiGetClassDevs(&GUID_DEVCLASS_COMPUTER,NULL,NULL,DIGCF_PRESENT|DIGCF_PROFILE);
if(hDevInfo == INVALID_HANDLE_VALUE) {
SetupDebugPrint1( L"SETUP: SetupDiGetClassDevs() failed. Error = %d", GetLastError() );
goto clean0;
}
DevInfoData.cbSize = sizeof(DevInfoData);
if(!SetupDiEnumDeviceInfo(hDevInfo,0,&DevInfoData)) {
SetupDebugPrint1( L"SETUP: SetupDiEnumDeviceInfo() failed. Error = %d", GetLastError() );
SetupDiDestroyDeviceInfoList(hDevInfo);
goto clean0;
}
//
// change the harware ID
//
if(!SetupDiSetDeviceRegistryProperty(hDevInfo,
&DevInfoData,
SPDRP_HARDWAREID,
(PBYTE)HardwareID,
sizeof(HardwareID[0])*HardwareIdSize
)) {
SetupDebugPrint1( L"SETUP: SetupDiSetDeviceRegistryProperty() failed. Error = %d", GetLastError() );
SetupDiDestroyDeviceInfoList(hDevInfo);
goto clean0;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
//
// now effect an update
//
if(!pUpdateDriverForPlugAndPlayDevicesW(NULL,HardwareID,InfPath,INSTALLFLAG_FORCE,&RebootFlag)) {
SetupDebugPrint1( L"SETUP: UpdateDriverForPlugAndPlayDevices() failed. Error = %d", GetLastError() );
} else {
SetupDebugPrint( L"SETUP: ... new HAL installed and will be active on reboot");
}
clean0:
if(hInstNewDev != NULL) {
FreeLibrary(hInstNewDev);
}
}
BOOL
InstallOEMInfs(
VOID
)
/*++
Routine Description:
This routine will install any OEM supplied INFs (and their corresponding
catalogs) that may have been supplied to the system during an earlier phase
of setup. For example, the OEM can currently supply an INF for unsupported
hardware during the textmode phase of setup by pressing "F6".
The list of OEM INFs to be installed is supplied to us via the answer
file in the following format:
[Data]
OEMDrivers=<driver-section-1>,<driver-section-2>,...
[driver-section-1]
OemDriverPathName=<path> (path to the driver (may use environment variables)
OemInfName=<inf name> name of the inf to be installed from the above
directory (there can be one or more infs in this directory, so
this is a comma separated list)
OemDriverFlags=<flags>
valid flags are:
SETUP_OEM_LDR_DEVICE 0x00000001
//indicates that the driver was supplied via the textmode "F6" mechanism
This function is really just a wrapper for SetupCopyOEMInf, which does
everything we need
Arguments:
None.
Return Value:
TRUE indicates that all answer file-supplied drivers were installed properly
--*/
{
HINF hAnswerFile;
BOOL RetVal;
INFCONTEXT LineContext;
DWORD FieldCount, InfNameCount;
DWORD Field, InfCount;
DWORD d;
PCTSTR SectionName;
INFCONTEXT FirstLineContext,InfLineContext;
PCWSTR OemDriverPathName;
PCWSTR OemInfName;
DWORD OemFlags;
WCHAR FullInfPathBuffer[MAX_PATH];
WCHAR FullInfPathBufferWithInf[MAX_PATH];
RetVal = TRUE;
hAnswerFile = pOpenAnswerFile();
if (hAnswerFile == INVALID_HANDLE_VALUE) {
//
// if there is no answer file, then we can't continue.
//
RetVal = FALSE;
goto e0;
}
if (!SetupFindFirstLine(hAnswerFile,WINNT_DATA,WINNT_OEMDRIVERS,&LineContext)) {
//
// we were successful in doing nothing
//
RetVal = TRUE;
goto e1;
}
do {
//
// Each value on the line in the given section
// is the name of another section.
//
FieldCount = SetupGetFieldCount(&LineContext);
for(Field=1; Field<=FieldCount; Field++) {
OemDriverPathName = NULL;
OemInfName = NULL;
OemFlags = 0;
FullInfPathBuffer[0] = '\0';
FullInfPathBufferWithInf[0] = '\0';
if((SectionName = pSetupGetField(&LineContext,Field))
&& SetupFindFirstLine(hAnswerFile,SectionName,WINNT_OEMDRIVERS_PATHNAME,&FirstLineContext)) {
//
// the section points to a valid section, so process it.
//
OemDriverPathName = pSetupGetField(&FirstLineContext,1);
if (SetupFindFirstLine(hAnswerFile,SectionName,WINNT_OEMDRIVERS_FLAGS,&FirstLineContext)) {
SetupGetIntField(&FirstLineContext,1,&OemFlags);
}
if (OemDriverPathName) {
ExpandEnvironmentStrings( OemDriverPathName,
FullInfPathBuffer,
sizeof(FullInfPathBuffer)/sizeof(WCHAR) );
}
if (SetupFindFirstLine(hAnswerFile,SectionName,WINNT_OEMDRIVERS_INFNAME,&InfLineContext)) {
InfNameCount = SetupGetFieldCount(&InfLineContext);
for (InfCount = 1; InfCount <= InfNameCount; InfCount++) {
OemInfName = pSetupGetField(&InfLineContext,InfCount);
if (OemDriverPathName && OemInfName) {
wcscpy( FullInfPathBufferWithInf, FullInfPathBuffer );
pSetupConcatenatePaths(
FullInfPathBufferWithInf,
OemInfName,
sizeof(FullInfPathBufferWithInf)/sizeof(WCHAR),
0 );
if (!SetupCopyOEMInf(
FullInfPathBufferWithInf,
FullInfPathBuffer,
SPOST_PATH,
(OemFlags & SETUP_OEM_LDR_DEVICE) ? SP_COPY_OEM_F6_INF : 0,
NULL,
0,
NULL,
NULL)) {
RetVal = FALSE;
}
if (OemFlags & SETUP_OEM_LDR_DEVICE) {
//
// if this flag is set, we know that there is an
// additional oemXXX##.inf file in the system32
// directory which corresponds to the INF file we
// have already copied from this directory. We need
// to seek out that file and remove it, since the INF
// file we have just copied is "better" than that file.
//
WIN32_FIND_DATA fd;
HANDLE hFind;
WCHAR OldInfBuffer[MAX_PATH];
PWSTR p;
ExpandEnvironmentStringsW(
L"%SystemRoot%\\system32\\",
OldInfBuffer,
sizeof(OldInfBuffer)/sizeof(WCHAR));
p = wcsrchr( OldInfBuffer, L'\\' );
p += 1;
pSetupConcatenatePaths(
OldInfBuffer,
L"oem?????.inf",
sizeof(OldInfBuffer)/sizeof(WCHAR),
0 );
if ((hFind = FindFirstFile(OldInfBuffer, &fd)) != INVALID_HANDLE_VALUE) {
do {
wcscpy( p, fd.cFileName );
if (DoFilesMatch( FullInfPathBufferWithInf, OldInfBuffer )) {
SetFileAttributes(OldInfBuffer, FILE_ATTRIBUTE_NORMAL );
DeleteFile( OldInfBuffer );
}
} while(FindNextFile( hFind, &fd ));
FindClose( hFind );
}
}
} else {
RetVal = FALSE;
}
}
}
}
}
} while(SetupFindNextMatchLine(&LineContext,NULL,&LineContext));
e1:
SetupCloseInfFile( hAnswerFile );
e0:
return(RetVal);
}
#if defined(_X86_)
VOID
SfcExcludeMigratedDrivers (
VOID
)
/*++
Routine Description:
Adds all OEM migrated boot drivers (via unsupdrv.inf) to the SFC exclusion list
Arguments:
None
Return Value:
None
--*/
{
TCHAR unsupInfPath[MAX_PATH];
TCHAR windir[MAX_PATH];
HINF unsupdrvInf;
INFCONTEXT ic;
TCHAR driverId[MAX_PATH];
TCHAR sectionFiles[MAX_PATH];
INFCONTEXT ic2;
TCHAR driverFilename[MAX_PATH];
TCHAR driverSubDir[MAX_PATH];
TCHAR driverPath[MAX_PATH];
if (!FloppylessBootPath[0] ||
!BuildPath (unsupInfPath, ARRAYSIZE(unsupInfPath), FloppylessBootPath, TEXT("$WIN_NT$.~BT")) ||
!pSetupConcatenatePaths (unsupInfPath, TEXT("unsupdrv.inf"), ARRAYSIZE(unsupInfPath), NULL)) {
return;
}
unsupdrvInf = SetupOpenInfFile (unsupInfPath, NULL, INF_STYLE_WIN4, NULL);
if (unsupdrvInf == INVALID_HANDLE_VALUE) {
return;
}
if (!GetWindowsDirectory (windir, ARRAYSIZE(windir))) {
return;
}
if(SetupFindFirstLine (
unsupdrvInf,
TEXT("Devices"),
NULL,
&ic)) {
do {
if (!SetupGetStringField (&ic, 1, driverId, ARRAYSIZE(driverId), NULL)) {
continue;
}
if (_sntprintf (sectionFiles, ARRAYSIZE(sectionFiles) - 1, TEXT("Files.%s"), driverId) < 0) {
continue;
}
sectionFiles[ARRAYSIZE(sectionFiles) - 1] = 0;
if(SetupFindFirstLine (
unsupdrvInf,
sectionFiles,
NULL,
&ic2)) {
do {
if (!SetupGetStringField (&ic2, 1, driverFilename, ARRAYSIZE(driverFilename), NULL)) {
continue;
}
if (!SetupGetStringField (&ic2, 2, driverSubDir, ARRAYSIZE(driverSubDir), NULL)) {
continue;
}
if (_sntprintf (driverPath, ARRAYSIZE(driverPath) - 1, TEXT("%s\\%s\\%s"), windir, driverSubDir, driverFilename) < 0) {
continue;
}
driverPath[ARRAYSIZE(driverPath) - 1] = 0;
if (FileExists (driverPath, NULL)) {
MultiSzAppendString(&EnumPtrSfcIgnoreFiles, driverPath);
}
} while (SetupFindNextLine(&ic2, &ic2));
}
} while (SetupFindNextLine(&ic, &ic));
}
SetupCloseInfFile (unsupdrvInf);
}
#endif
BOOL
IsInstalledInfFromOem(
IN PCWSTR InfFileName
)
/*++
Routine Description:
Determine if an INF file is OEM-supplied (i.e., it's name is of the form
"OEM<n>.INF").
Arguments:
InfFileName - supplies name (may include path) of INF. No checking is done
to ensure INF lives in %windir%\Inf--this is caller's responsibility.
Return Value:
If TRUE, this is an OEM INF. If FALSE, it's an in-box INF (or possibly one
that was illegally copied directly into %windir%\Inf).
--*/
{
PCWSTR p = pSetupGetFileTitle(InfFileName);
//
// First check that the first 3 characters are OEM
//
if((*p != L'o') && (*p != L'O')) {
return FALSE;
}
p++;
if((*p != L'e') && (*p != L'E')) {
return FALSE;
}
p++;
if((*p != L'm') && (*p != L'M')) {
return FALSE;
}
p++;
//
// Now make sure that all subsequent characters up to the dot (.) are
// numeric.
//
while((*p != L'\0') && (*p != L'.')) {
if((*p < L'0') || (*p > L'9')) {
return FALSE;
}
p++;
}
//
// Finally, verify that the last 4 characters are ".inf"
//
if(lstrcmpi(p, L".inf")) {
return FALSE;
}
//
// This is an OEM INF
//
return TRUE;
}
BOOL
IsInfInLayoutInf(
IN PCWSTR InfFileName
)
/*++
Routine Description:
Determine if an INF file is shiped in-box with the operating system.
This is acomplished by looking up the INF name in the [SourceDisksFiles]
section of layout.inf
Arguments:
InfFileName - supplies name (may include path) of INF. No checking is done
to ensure INF lives in %windir%\Inf--this is caller's responsibility.
Return Value:
If TRUE, this is an in-box INF. If FALSE, it's not an in-box INF, this
could be an OEM<n>.INF or an inf illegaly copied into the INF directory.
--*/
{
BOOL bInBoxInf = FALSE;
HINF hInf = INVALID_HANDLE_VALUE;
UINT SourceId;
hInf = SetupOpenInfFile(TEXT("layout.inf"), NULL, INF_STYLE_WIN4, NULL);
if (hInf != INVALID_HANDLE_VALUE) {
if(SetupGetSourceFileLocation(hInf,
NULL,
pSetupGetFileTitle(InfFileName),
&SourceId,
NULL,
0,
NULL)) {
bInBoxInf = TRUE;
}
SetupCloseInfFile(hInf);
}
return bInBoxInf;
}