Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

5248 lines
187 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
devinstd.c
Abstract:
Default install handlers for SetupDiCallClassInstaller DIF_* functions.
Author:
Lonny McMichael (lonnym) 1-Aug-1995
Revision History:
--*/
#include "setupntp.h"
#pragma hdrstop
//
// Global strings for use inside this file only.
//
CONST TCHAR pszAddService[] = SZ_KEY_ADDSERVICE,
pszDelService[] = SZ_KEY_DELSERVICE,
pszDisplayName[] = INFSTR_KEY_DISPLAYNAME,
pszServiceType[] = INFSTR_KEY_SERVICETYPE,
pszStartType[] = INFSTR_KEY_STARTTYPE,
pszErrorControl[] = INFSTR_KEY_ERRORCONTROL,
pszServiceBinary[] = INFSTR_KEY_SERVICEBINARY,
pszLoadOrderGroup[] = INFSTR_KEY_LOADORDERGROUP,
pszDependencies[] = INFSTR_KEY_DEPENDENCIES,
pszStartName[] = INFSTR_KEY_STARTNAME,
pszSystemRoot[] = TEXT("%SystemRoot%\\");
//
// Define function prototype for legacy INF interpreter supplied
// by setupdll.dll
//
typedef BOOL (WINAPI *LEGACY_INF_INTERP_PROC)(
IN HWND OwnerWindow,
IN PCSTR InfFilename,
IN PCSTR InfSection,
IN PCHAR ExtraVariables,
OUT PSTR InfResult,
IN DWORD BufferSize,
OUT INT *InterpResult,
IN PCSTR InfSourceDir OPTIONAL
);
//
// Define function prototype for legacy INF routine that returns a list
// of all services modified during an INF 'run' via LegacyInfInterpret().
//
typedef DWORD (WINAPI *LEGACY_INF_GETSVCLIST_PROC)(
IN LPSTR SvcNameBuffer,
IN UINT SvcNameBufferSize,
OUT PUINT RequiredSize
);
//
// Define the legacy INF interpreter exit codes (copied from setup\legacy\dll\_shell.h
//
#define SETUP_ERROR_SUCCESS 0
#define SETUP_ERROR_USERCANCEL 1
#define SETUP_ERROR_GENERAL 2
//
// Private function prototypes
//
BOOL
_SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall
);
DWORD
InstallHW(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HINF hDeviceInf,
IN PCTSTR szSectionName,
OUT PBOOL DeleteDevKey
);
BOOL
AssociateDevInstWithDefaultService(
IN PSP_DEVINFO_DATA DeviceInfoData,
OUT PTSTR ServiceName,
IN OUT PDWORD ServiceNameSize
);
VOID
CheckIfDevStarted(
IN PDEVINFO_ELEM DevInfoElem
);
DWORD
pSetupAddService(
IN PINFCONTEXT LineContext,
OUT PSVCNAME_NODE *SvcListHead,
IN DWORD Flags,
IN DEVINST DevInst OPTIONAL
);
DWORD
pSetupDeleteService(
IN PINFCONTEXT LineContext
);
VOID
DeleteServicesInList(
IN PSVCNAME_NODE ServicesToDelete
);
BOOL
IsDevRemovedFromAllHwProfiles(
IN PCTSTR DeviceInstanceId
);
DWORD
GetDevInstConfigFlags(
IN DEVINST DevInst,
IN DWORD Default
);
DWORD
pSetupRunLegacyInf(
IN DEVINST DevInst,
IN HWND OwnerWindow,
IN PCTSTR InfFileName,
IN PCTSTR InfOptionName,
IN PCTSTR InfLanguageName,
IN HINF InfHandle
);
PTSTR
pSetupCmdLineAppendString(
IN PTSTR CmdLine,
IN PCTSTR Key,
IN PCTSTR Value, OPTIONAL
IN OUT PUINT StrLen,
IN OUT PUINT BufSize
);
PTSTR
DoServiceModsForLegacyInf(
IN PTSTR ServiceList
);
BOOL
WINAPI
SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default hander for DIF_INSTALLDEVICE
This routine will install a device by performing a SetupInstallFromInfSection
for the install section of the selected driver for the specified device
information element. The device will then be started (if possible).
NOTE: This API actually supports an OS/architecture-specific extension that
may be used to specify multiple installation behaviors for a single device,
based on the environment we're running under. The algorithm is as follows:
We take the install section name, as specified in the driver node (for this
example, it's "InstallSec"), and attempt to find one of the following INF
sections (searched for in the order specified):
If we're running under Windows 95:
1. InstallSec.Win
2. InstallSec
If we're running under Windows NT:
1. InstallSec.NT<platform> (platform is "x86", "MIPS", "Alpha", or "PPC")
2. InstallSec.NT
3. InstallSec
The first section that we find is the one we'll use to do the installation. This
section name is also what we'll base our ".Hw" and ".Services" installation against.
(E.g., if we match on "InstallSec.NTAlpha", then the service install section must be
named "InstallSec.NTAlpha.Services".)
The original install section name (i.e., the one specified in the driver node), will
be written as-is to the driver key's "InfSection" value entry, just as it was in the
past. The extension that we use (if any) will be stored in the device's driver key
as the REG_SZ value, "InfSectionExt". E.g.,
InfSection : REG_SZ : "InstallSec"
InfSectionExt : REG_SZ : ".NTMIPS"
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
driver is to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
a driver is to be installed. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
If no driver is selected for the specified device information element, then a
NULL driver will be installed.
Upon return, the install parameters Flags will indicate whether the system
needs to be rebooted or restarted in order for the device to be started.
--*/
{
return _SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData, TRUE);
}
BOOL
_SetupDiInstallDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData,
IN BOOL DoFullInstall
)
/*++
Routine Description:
Worker routine for both SetupDiInstallDevice and SetupDiInstallDriverFiles.
See the description of SetupDiInstallDevice for more information.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
driver is to be installed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for which
a driver is to be installed. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return.
DoFullInstall - If TRUE, then an entire device installation is performed,
otherwise, only the driver files are copied.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err, ScanQueueResult;
PDEVINFO_ELEM DevInfoElem = NULL;
PTSTR szInfFileName, szInfSectionName;
PTSTR szInfSectionExt = NULL;
TCHAR InfSectionWithExt[MAX_PATH];
DWORD InfSectionWithExtLength;
HINF hDeviceInf = INVALID_HANDLE_VALUE;
HKEY hkDrv = INVALID_HANDLE_VALUE;
PSP_FILE_CALLBACK MsgHandler;
PVOID MsgHandlerContext;
BOOL MsgHandlerIsNativeCharWidth;
HSPFILEQ UserFileQ;
INFCONTEXT InfContext;
DWORD dwConfigFlags=0;
ULONG cbData;
PTSTR DevIdBuffer = NULL;
PCTSTR TempString;
ULONG ulStatus, ulProblem;
DEVINST dnReenum = 0;
TCHAR szNewName[MAX_PATH];
BOOL OemInfFileToCopy = FALSE;
BOOL DeleteDevKey = FALSE;
PSVCNAME_NODE DeleteServiceList = NULL;
BOOL FreeMsgHandlerContext = FALSE;
BOOL CloseUserFileQ = FALSE;
HWND hwndParent;
#if MAX_SECT_NAME_LEN > MAX_PATH
#error MAX_SECT_NAME_LEN is larger than MAX_PATH--fix InfSectionWithExt!
#endif
//
// We can only install a device if a device was specified.
//
if(!DeviceInfoData) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
try {
if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL))) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// Make sure we only use the devinfo element's window if it's valid.
//
if(hwndParent = DevInfoElem->InstallParamBlock.hwndParent) {
if(!IsWindow(hwndParent)) {
hwndParent = NULL;
}
}
//
// If we are installing a driver, then the selected driver pointer will be
// non-NULL, otherwise we are actually removing the driver (i.e., installing the
// NULL driver)
//
if(DevInfoElem->SelectedDriver) {
if(DoFullInstall) {
//
// Create the Driver Reg Key.
//
if((hkDrv = SetupDiCreateDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DRV,
NULL,
NULL)) == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
} else {
//
// Make sure we aren't trying to do a copy-only install on a legacy
// driver--we don't know how to do that!
//
if(DevInfoElem->SelectedDriver->Flags & DNF_LEGACYINF) {
Err = ERROR_WRONG_INF_STYLE;
goto clean0;
}
}
szInfFileName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfFileName
);
szInfSectionName = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->InfSectionName
);
if((hDeviceInf = SetupOpenInfFile(szInfFileName,
NULL,
((DevInfoElem->SelectedDriver->Flags & DNF_LEGACYINF)
? INF_STYLE_OLDNT
: INF_STYLE_WIN4),
NULL)) == INVALID_HANDLE_VALUE) {
Err = GetLastError();
goto clean0;
}
//
// Unless we happen to be installing from a legacy INF, we want to find out the 'real'
// install section we should be using (i.e., the potentially OS/architecture-specific
// one.
//
if(!(DevInfoElem->SelectedDriver->Flags & DNF_LEGACYINF)) {
SetupDiGetActualSectionToInstall(hDeviceInf,
szInfSectionName,
InfSectionWithExt,
SIZECHARS(InfSectionWithExt),
&InfSectionWithExtLength,
&szInfSectionExt
);
}
//
// First, copy the files. (Ignore the DI_NOFILECOPY flag if we're doing a
// copy-only installation--that's what setupx does.)
//
if(!(DevInfoElem->InstallParamBlock.Flags & DI_NOFILECOPY) || !DoFullInstall) {
//
// We handle 'file copying' differently depending on whether we're installing a Win95-style
// driver node, or an old-style INF one.
//
if(DevInfoElem->SelectedDriver->Flags & DNF_LEGACYINF) {
PTSTR szLegacyInfLangName;
//
// We're doing a script-driven install using a legacy INF. Since we can't control
// what takes place when this INF gets run, we can't split this out into various
// phases (file copying, registry modification, etc.). So we consider the legacy
// INF installation action to be a file copy action, therefore, we only run it if
// the user specified that we should do file copying. No other actions are performed
// with this INF throughout the rest of the installation.
//
szLegacyInfLangName = pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->LegacyInfLang
);
Err = pSetupRunLegacyInf(DevInfoElem->DevInst,
hwndParent,
szInfFileName,
szInfSectionName,
szLegacyInfLangName,
hDeviceInf
);
} else {
//
// Append the layout INF, if necessary.
//
SetupOpenAppendInfFile(NULL, hDeviceInf, NULL);
//
// If the DI_NOVCP flag is set, then just queue up the file
// copy/rename/delete operations. Otherwise, perform the
// actions.
//
if(DevInfoElem->InstallParamBlock.Flags & DI_NOVCP) {
//
// We must have a user-supplied file queue.
//
MYASSERT(DevInfoElem->InstallParamBlock.UserFileQ);
UserFileQ = DevInfoElem->InstallParamBlock.UserFileQ;
} else {
//
// Since we may need to check the queued files to determine whether file copy
// is necessary, we have to open our own queue, and commit it ourselves.
//
if((UserFileQ = SetupOpenFileQueue()) != INVALID_HANDLE_VALUE) {
CloseUserFileQ = TRUE;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// If the parameter block contains an install message handler, then use it,
// otherwise, initialize our default one.
//
if(DevInfoElem->InstallParamBlock.InstallMsgHandler) {
MsgHandler = DevInfoElem->InstallParamBlock.InstallMsgHandler;
MsgHandlerContext = DevInfoElem->InstallParamBlock.InstallMsgHandlerContext;
MsgHandlerIsNativeCharWidth = DevInfoElem->InstallParamBlock.InstallMsgHandlerIsNativeCharWidth;
} else {
if(!(MsgHandlerContext = SetupInitDefaultQueueCallbackEx(
hwndParent,
(DevInfoElem->InstallParamBlock.Flags & DI_QUIETINSTALL)
? INVALID_HANDLE_VALUE : NULL,
0,
0,
NULL))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
SetupCloseFileQueue(UserFileQ);
CloseUserFileQ = FALSE;
goto clean0;
}
FreeMsgHandlerContext = TRUE;
MsgHandler = SetupDefaultQueueCallback;
MsgHandlerIsNativeCharWidth = TRUE;
}
}
Err = pSetupInstallFiles(hDeviceInf,
NULL,
InfSectionWithExt,
NULL,
NULL,
NULL,
(DevInfoElem->InstallParamBlock.Flags & DI_NOBROWSE)
? SP_COPY_NOBROWSE : 0,
NULL,
UserFileQ,
MsgHandlerIsNativeCharWidth
);
if(CloseUserFileQ) {
if(Err == NO_ERROR) {
//
// We successfully queued up the file operations--now we need to commit
// the queue. First off, though, we should check to see if the files are
// already there.
//
if(!(DevInfoElem->InstallParamBlock.Flags & DI_FORCECOPY) &&
!InfIsFromOemLocation(szInfFileName)) {
//
// Determine whether the queue actually needs to be committed.
//
// ScanQueueResult can have 1 of 3 values:
//
// 0: User wants new files or some files were missing;
// Must commit queue.
//
// 1: User wants to use existing files and queue is empty;
// Can skip committing queue.
//
// 2: User wants to use existing files but del/ren queues not empty.
// Must commit queue. The copy queue will have been emptied,
// so only del/ren functions will be performed.
//
//
if(!SetupScanFileQueue(UserFileQ,
SPQ_SCAN_FILE_VALIDITY | SPQ_SCAN_INFORM_USER,
hwndParent,
NULL,
NULL,
&ScanQueueResult)) {
Err = GetLastError();
ScanQueueResult = 1; // skip queue commit.
}
} else {
ScanQueueResult = 0; // always commit the queue.
}
if(ScanQueueResult != 1) {
//
// Copy enqueued files.
//
if(!_SetupCommitFileQueue(hwndParent,
UserFileQ,
MsgHandler,
MsgHandlerContext,
MsgHandlerIsNativeCharWidth
)) {
Err = GetLastError();
}
}
}
//
// Close our file queue handle.
//
SetupCloseFileQueue(UserFileQ);
CloseUserFileQ = FALSE;
}
//
// Terminate the default queue callback, if it was created. (Do this before
// checking the return status of the file copying.
//
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
FreeMsgHandlerContext = FALSE;
}
}
if(Err != NO_ERROR) {
goto clean0;
}
}
//
// If the copy succeeded (or in setup's case was queued), then
// it's time to update the registry and ini files.
//
if(Err == NO_ERROR) {
//
// We've got some registry modifications to do, but we don't want to
// do them if this is a copy-only installation.
//
if(DoFullInstall) {
//
// Do every thing left over (but only if we're not installing from a
// legacy INF).
//
if(!(DevInfoElem->SelectedDriver->Flags & DNF_LEGACYINF)) {
//
// Skip installation of log configs if this is an enumerated device
//
if((CM_Get_DevInst_Status(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0) != CR_SUCCESS) ||
(ulStatus & DN_ROOT_ENUMERATED)) {
LOG_CONF LogConf;
//
// Clean out all existing BASIC_LOG_CONF LogConfigs before writing out new ones from
// the INF.
//
while(CM_Get_First_Log_Conf(&LogConf, DevInfoElem->DevInst, BASIC_LOG_CONF) == CR_SUCCESS) {
CM_Free_Log_Conf(LogConf, 0);
CM_Free_Log_Conf_Handle(LogConf);
}
//
// Now write out the new basic log configs.
//
if(!SetupInstallFromInfSection(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_LOGCONFIG,
NULL,
NULL,
0,
NULL,
NULL,
DeviceInfoSet,
DeviceInfoData)) {
Err = GetLastError();
}
}
if((Err == NO_ERROR) && !(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NO_DRVREG_MODIFY)) {
//
// We don't pass a msg handler so no need to worry about ansi
// vs. unicode issues here.
//
if(SetupInstallFromInfSection(NULL,
hDeviceInf,
InfSectionWithExt,
SPINST_INIFILES
| SPINST_REGISTRY
| SPINST_INI2REG,
hkDrv,
NULL,
0,
NULL,
NULL,
INVALID_HANDLE_VALUE,
NULL)) {
//
// Install extra HardWare registry section (if any).
//
Err = InstallHW(DeviceInfoSet,
DeviceInfoData,
hDeviceInf,
InfSectionWithExt,
&DeleteDevKey
);
} else {
Err = GetLastError();
}
}
//
// Set appropriate flags if we need to reboot or restart after
// this installation.
//
if(SetupFindFirstLine(hDeviceInf, InfSectionWithExt, pszReboot, &InfContext)) {
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
} else if(SetupFindFirstLine(hDeviceInf, InfSectionWithExt, pszRestart, &InfContext)) {
//
// NOTE: This behavior is taken from setupx. In both "Reboot"
// and "Restart" cases, it sets the DI_NEEDREBOOT flag.
//
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
}
}
//
// Set the value to write for the config flags, only if there
// are no config flags yet. If they exist, i.e., we are updating
// an existing device, just clear the re-install flag.
//
dwConfigFlags = GetDevInstConfigFlags(
DevInfoElem->DevInst,
(DevInfoElem->InstallParamBlock.Flags & DI_INSTALLDISABLED)
? CONFIGFLAG_DISABLED
: 0
);
//
// Always clear the REINSTALL bit and the FAILEDINSTALL bit
// when installing a device.
//
dwConfigFlags &= ~(CONFIGFLAG_REINSTALL | CONFIGFLAG_FAILEDINSTALL);
}
//
// If this is an OEM INF, then we must get the name of
// the file we will create (i.e., "oemxxxx.inf").
//
if((DevInfoElem->InstallParamBlock.DriverPath != -1) &&
!(DevInfoElem->InstallParamBlock.Flags & DI_ENUMSINGLEINF) &&
InfIsFromOemLocation(szInfFileName)) {
if(GetNewInfName(szInfFileName,
szNewName,
SIZECHARS(szNewName),
NULL,
&OemInfFileToCopy) == NO_ERROR) {
//
// Get just the INF file name, stripping the path.
//
TempString = MyGetFileTitle(szNewName);
} else {
TempString = NULL;
}
} else {
//
// Get just the INF file name, stripping the path, since
// we do not want to save the Setup Temp path.
//
// NOTE: If the DI_ENUMSINGLEINF flag was set, then we assume
// the INF we are using is located somewhere in the driver
// search path. If this is not the case, then we're broken,
// because we won't copy it into our INF directory. This is
// probably OK, since there's no UI that allows a user to point
// at a particular INF (only at a directory containing INFs).
//
TempString = MyGetFileTitle(szInfFileName);
}
if(!DoFullInstall) {
//
// If we're doing a copy-only installation, then we can skip
// down to where we copy the OEM INF into the Inf directory.
//
goto clean1;
}
//
// Insert Driver Specific strings into the registry.
//
if(TempString) {
RegSetValueEx(hkDrv,
pszInfPath,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
}
RegSetValueEx(hkDrv,
pszInfSection,
0,
REG_SZ,
(PBYTE)szInfSectionName,
(lstrlen(szInfSectionName) + 1) * sizeof(TCHAR)
);
if(szInfSectionExt) {
RegSetValueEx(hkDrv,
pszInfSectionExt,
0,
REG_SZ,
(PBYTE)szInfSectionExt,
(lstrlen(szInfSectionExt) + 1) * sizeof(TCHAR)
);
} else {
//
// This wasn't an OS/architecture-specific install section, _or_ we are
// installing from a legacy INF. Make sure there's no value hanging
// around from a previous installation.
//
RegDeleteValue(hkDrv, pszInfSectionExt);
}
if(DevInfoElem->SelectedDriver->ProviderDisplayName == -1) {
//
// No provider specified--delete any previously existing value entry.
//
RegDeleteValue(hkDrv, pszProviderName);
} else {
//
// Retrieve the Provider name, and store it in the driver key.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->ProviderDisplayName
);
RegSetValueEx(hkDrv,
pszProviderName,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
}
//
// Set the MFG device registry property.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->MfgDisplayName
);
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_MFG,
TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR),
0
);
//
// Add hardware and compatible IDs to the hardware key if they exist
// in the driver node and don't already exist in the registry. This
// sets up an ID for manually installed devices.
//
if(!(DevInfoElem->InstallParamBlock.Flags & DI_NOWRITE_IDS) && // Want IDs written?
(DevInfoElem->SelectedDriver->HardwareId != -1)) // ID in driver node?
{
//
// Don't write IDs if either Hardware or Compatible IDs already
// exist in the registry. Note that I use cbData as an IN/OUT parameter
// to both CM calls. This is OK, however, since cbSize will not be modified
// on a CR_NO_SUCH_VALUE return, and I won't try to re-use it otherwise.
//
cbData = 0;
if((DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_ALWAYSWRITEIDS) ||
((CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_HARDWAREID,
NULL,
NULL,
&cbData,
0) == CR_NO_SUCH_VALUE) &&
(CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_COMPATIBLEIDS,
NULL,
NULL,
&cbData,
0) == CR_NO_SUCH_VALUE)))
{
DWORD CurStringLen, TotalStringLen, i;
//
// Compute the maximum buffer size needed to hold our REG_MULTI_SZ
// ID lists.
//
TotalStringLen = (((DevInfoElem->SelectedDriver->NumCompatIds) ?
DevInfoElem->SelectedDriver->NumCompatIds : 1)
* MAX_DEVICE_ID_LEN) + 1;
if(!(DevIdBuffer = MyMalloc(TotalStringLen * sizeof(TCHAR)))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Build a multi-sz list of the (single) HardwareID, and set it.
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->HardwareId);
TotalStringLen = lstrlen(TempString) + 1;
CopyMemory(DevIdBuffer, TempString, TotalStringLen * sizeof(TCHAR));
DevIdBuffer[TotalStringLen++] = TEXT('\0'); // Add extra terminating NULL;
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_HARDWAREID,
DevIdBuffer,
TotalStringLen * sizeof(TCHAR),
0
);
//
// Build a multi-sz list of the zero or more CompatibleIDs, and set it
//
TotalStringLen = 0;
for(i = 0; i < DevInfoElem->SelectedDriver->NumCompatIds; i++) {
TempString = pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->CompatIdList[i]);
CurStringLen = lstrlen(TempString) + 1;
CopyMemory(&(DevIdBuffer[TotalStringLen]),
TempString,
CurStringLen * sizeof(TCHAR));
TotalStringLen += CurStringLen;
}
if(TotalStringLen) {
DevIdBuffer[TotalStringLen++] = TEXT('\0'); // Add extra terminating NULL;
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_COMPATIBLEIDS,
DevIdBuffer,
TotalStringLen * sizeof(TCHAR),
0
);
}
}
}
//
// If we're running under Windows NT, and we've successfully installed the device instance,
// then we need to install any required services.
//
if((Err == NO_ERROR) && (OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)) {
PTSTR pServiceInstallSection;
if(DevInfoElem->SelectedDriver->Flags & DNF_LEGACYINF) {
pServiceInstallSection = NULL;
} else {
//
// The install section name is of the form:
//
// <InfSectionWithExt>.Services
//
CopyMemory(&(InfSectionWithExt[InfSectionWithExtLength - 1]),
pszServicesSectionSuffix,
sizeof(pszServicesSectionSuffix)
);
pServiceInstallSection = InfSectionWithExt;
}
Err = InstallNtService(DeviceInfoSet,
DeviceInfoData,
hDeviceInf,
pServiceInstallSection,
&DeleteServiceList,
0
);
}
}
} else {
//
// Installing the NULL driver.
// This means to set the Config flags, and nothing else.
// Config Flags get set to enabled in this case, so the device
// gets assigned the correct config. (Win95 bug 26320)
//
if(DoFullInstall) {
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst, 0) &
~(CONFIGFLAG_DISABLED | CONFIGFLAG_REINSTALL);
//
// Delete all driver (software) keys associated with the device, and clear
// the "Driver" registry property.
//
SetupDiDeleteDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGGENERAL,
0,
DIREG_DRV
);
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst, CM_DRP_DRIVER, NULL, 0, 0);
} else {
//
// It is an error to not have a selected driver in the copy-only case.
//
Err = ERROR_NO_DRIVER_SELECTED;
}
}
//
// If all went well above, then write some configflags, and re-enumerate
// the parent device instance if necessary
//
if(Err == NO_ERROR) {
//
// Write the Driver Description to the Registry, if there
// is an lpSelectedDriver, and the Device Description also
//
if(DevInfoElem->SelectedDriver) {
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DrvDescription
);
RegSetValueEx(hkDrv,
pszDrvDesc,
0,
REG_SZ,
(PBYTE)TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR)
);
//
// (setupx BUG 12721) always update the DevDesc in the registry with the
// value from the INF (ie only do this if we have a SELECTED driver)
// The semantics are weird, but the SelectedDriver NODE contains the
// INF Device description, and DRV description
//
TempString = pStringTableStringFromId(pDeviceInfoSet->StringTable,
DevInfoElem->SelectedDriver->DevDescriptionDisplayName
);
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_DEVICEDESC,
TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR),
0
);
} else {
//
// No driver is selected, so use the description stored with the device
// information element itself for the device description. However, only set this
// if it isn't already present.
//
cbData = 0;
if(CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_DEVICEDESC,
NULL,
NULL,
&cbData,
0) == CR_NO_SUCH_VALUE) {
if(DevInfoElem->DeviceDescriptionDisplayName != -1) {
TempString = pStringTableStringFromId(
pDeviceInfoSet->StringTable,
DevInfoElem->DeviceDescriptionDisplayName
);
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_DEVICEDESC,
TempString,
(lstrlen(TempString) + 1) * sizeof(TCHAR),
0
);
}
}
}
//
// Unless the caller explicitly requested that this device be installed disabled, clear
// the CONFIGFLAG_DISABLED bit.
//
if(!(DevInfoElem->InstallParamBlock.Flags & DI_INSTALLDISABLED)) {
dwConfigFlags &= ~CONFIGFLAG_DISABLED;
}
//
// Write the config flags. If no selected driver, then set the Install Failed flag.
//
if((!DevInfoElem->SelectedDriver) &&
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_SETFAILEDINSTALL)) {
dwConfigFlags |= CONFIGFLAG_FAILEDINSTALL;
}
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0
);
if(!(DevInfoElem->InstallParamBlock.Flags & (DI_DONOTCALLCONFIGMG | DI_NEEDREBOOT | DI_NEEDRESTART))) {
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
//
// Retrieve the name of the device instance. This is necessary, because
// we may be about to remove the DEVINST so that it can be re-enumerated. This
// should never fail.
//
CM_Get_Device_ID(DevInfoElem->DevInst,
DeviceInstanceId,
SIZECHARS(DeviceInstanceId),
0
);
//
// If the device instance has a problem but is not disabled. Just
// restart it. This should be 90% of the cases.
//
if((CM_Get_DevInst_Status(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0) == CR_SUCCESS) &&
(ulStatus & DN_HAS_PROBLEM)) {
//
// Poke at Config Manager to make it load the driver.
//
CM_Setup_DevInst(DevInfoElem->DevInst, CM_SETUP_DEVINST_READY);
CheckIfDevStarted(DevInfoElem);
} else {
//
// If there is a device instance with no problem , then we should remove it, and
// re-enumerate its parent to recreate it. This is because its driver has changed
// in this case. If the device instance refuses to remove, then set the flags that
// say we need to reboot.
//
if(CM_Query_Remove_SubTree(
DevInfoElem->DevInst,
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_QUERY_REMOVE_UI_NOT_OK : CM_QUERY_REMOVE_UI_OK) == CR_SUCCESS)
{
CM_Get_Parent(&dnReenum, DevInfoElem->DevInst, 0);
CM_Remove_SubTree(DevInfoElem->DevInst,
(DevInfoElem->InstallParamBlock.FlagsEx & DI_FLAGSEX_NOUIONQUERYREMOVE)
? CM_REMOVE_UI_NOT_OK : CM_REMOVE_UI_OK
);
DevInfoElem->DevInst = 0;
DevInfoElem->DiElemFlags &= ~DIE_IS_PHANTOM;
} else {
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
}
}
//
// Only re-enumerate the device instance if we installed something, and we sucessfully
// removed the old one.
//
if(DevInfoElem->SelectedDriver && !DevInfoElem->DevInst) {
//
// Did we get DnReenum above?
//
if(!dnReenum) {
//
// Use the Root
//
CM_Locate_DevInst(&dnReenum, NULL, CM_LOCATE_DEVINST_NORMAL);
if(CM_Create_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
dnReenum,
CM_CREATE_DEVINST_NORMAL) != CR_SUCCESS)
{
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
//
// Retrieve the devinst as a phantom.
//
CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
} else {
//
// Reenumerate Syncronous
//
CM_Reenumerate_DevInst(dnReenum, CM_REENUMERATE_SYNCHRONOUS);
CheckIfDevStarted(DevInfoElem);
}
} else {
CM_Reenumerate_DevInst(dnReenum, CM_REENUMERATE_SYNCHRONOUS);
if(CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_NORMAL) != CR_SUCCESS) {
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
//
// Retrieve the devinst as a phantom
//
CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
}
CheckIfDevStarted(DevInfoElem);
}
} else {
//
// We installed the NULL driver, and we successfully removed the device
// instance. Retrieve it again as a phantom.
//
CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
}
//
// Update the DevInst field of the DeviceInfoData structure to reflect the
// current value of the devinst handle.
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
}
}
clean1:
//
// If the install was being done out of an OEM path, copy
// the INF file over to the real tree.
//
if((Err == NO_ERROR) && OemInfFileToCopy) {
PLOADED_INF PrecompiledNewInf;
UINT ErrorLineNumber;
WIN32_FIND_DATA FindData;
PTSTR OemSourcePathEndPos;
CopyFile(szInfFileName, szNewName, FALSE);
//
// While we're at it, let's go ahead and open this new INF, so that it will be
// precompiled. This helps us keep from getting a build-up of non-precompiled
// INFs in our directory that can slow down driver searching.
//
// Another very handy thing that this provides is the ability to store in the PNF
// form the SourcePath where the INF actually came from. That way, if the user
// does an install using this INF in the future, we'll prompt them for source at
// the same location where they provided it last time--pretty cool, huh?
//
lstrcpy(InfSectionWithExt, szInfFileName); // re-use this buffer to hold our path.
OemSourcePathEndPos = (PTSTR)MyGetFileTitle(InfSectionWithExt);
if(((OemSourcePathEndPos - InfSectionWithExt) == 3) &&
(InfSectionWithExt[1] == TEXT(':'))) {
//
// This path is a root path (e.g., 'A:\'), so don't strip the trailing backslash.
//
*OemSourcePathEndPos = TEXT('\0');
} else {
//
// Strip the trailing backslash.
//
if((OemSourcePathEndPos > InfSectionWithExt) &&
(*(OemSourcePathEndPos - 1) == TEXT('\\'))) {
OemSourcePathEndPos--;
}
*OemSourcePathEndPos = TEXT('\0');
}
if(FileExists(szNewName, &FindData) &&
(LoadInfFile(szNewName,
&FindData,
INF_STYLE_WIN4 | INF_STYLE_OLDNT,
LDINF_FLAG_ALWAYS_TRY_PNF,
NULL,
InfSectionWithExt,
NULL,
&PrecompiledNewInf,
&ErrorLineNumber) == NO_ERROR)) {
//
// That's all we need to do to precompile the INF.
//
FreeInfFile(PrecompiledNewInf);
}
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
if(FreeMsgHandlerContext) {
SetupTermDefaultQueueCallback(MsgHandlerContext);
}
if(CloseUserFileQ) {
SetupCloseFileQueue(UserFileQ);
}
//
// Access the following variables so that the compiler will respect our statement
// ordering w.r.t. these values. Otherwise, we may not be able to know with
// certainty whether or not we should release their corresponding resources.
//
DevInfoElem = DevInfoElem;
hDeviceInf = hDeviceInf;
hkDrv = hkDrv;
DevIdBuffer = DevIdBuffer;
DeleteServiceList = DeleteServiceList;
OemInfFileToCopy = OemInfFileToCopy;
}
//
// Clean up the registry if the install didn't go well. Along with other
// error paths, this handles the case where the user cancels the install
// while copying files
//
if(Err != NO_ERROR) {
if(DevInfoElem && DoFullInstall) {
//
// Disable the device if the error wasn't a user cancel.
//
if(Err != ERROR_CANCELLED) {
//
// The device is in an unknown state. Disable it by setting the
// CONFIGFLAG_DISABLED config flag.
//
// NOTE: The setupx implementation distinguishes between
// success and failure of the ConfigFlags retrieval, and sets both
// the CONFIGFLAG_DISABLED _and_ CONFIGFLAG_REINSTALL flags if retrieval
// is successful. The original code is shown below:
//
// dwConfigFlagsSize = sizeof(DWORD);
// if(CM_Get_DevInst_Registry_Property(DevInfoElem->DevInst,
// CM_DRP_CONFIGFLAGS,
// NULL,
// &dwConfigFlags,
// &dwConfigFlagsSize,
// 0) == CR_SUCCESS) {
//
// dwConfigFlags |= (CONFIGFLAG_DISABLED | CONFIGFLAG_REINSTALL);
// } else {
// dwConfigFlags = CONFIGFLAG_DISABLED;
// }
//
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst, 0) |
(CONFIGFLAG_DISABLED | CONFIGFLAG_REINSTALL);
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0
);
//
// Delete the Driver= entry from the Dev Reg Key and delete the
// DrvRegKey (as well as the DevRegKey if it didn't previously exist).
//
if(DevInfoElem->SelectedDriver) {
SetupDiDeleteDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGGENERAL,
0,
(DeleteDevKey ? DIREG_BOTH : DIREG_DRV)
);
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst, CM_DRP_DRIVER, NULL, 0, 0);
}
//
// If necessary, delete any service entries created for this device instance.
//
if(DeleteServiceList) {
DeleteServicesInList(DeleteServiceList);
}
}
}
//
// If we generated a 0-length OEMxxx.INF placeholder file, delete it now.
//
if(OemInfFileToCopy) {
DeleteFile(szNewName);
}
}
UnlockDeviceInfoSet(pDeviceInfoSet);
if(hDeviceInf != INVALID_HANDLE_VALUE) {
SetupCloseInfFile(hDeviceInf);
}
if(hkDrv != INVALID_HANDLE_VALUE) {
RegCloseKey(hkDrv);
}
if(DevIdBuffer) {
MyFree(DevIdBuffer);
}
if(DeleteServiceList) {
PSVCNAME_NODE TmpSvcNode;
for(TmpSvcNode = DeleteServiceList; TmpSvcNode; TmpSvcNode = DeleteServiceList) {
DeleteServiceList = DeleteServiceList->Next;
MyFree(TmpSvcNode);
}
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiInstallDriverFiles(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData OPTIONAL
)
/*++
Routine Description:
Default hander for DIF_INSTALLDEVICEFILES
This routine is similiar to SetupDiInstallDevice, but it only performs
the file copy commands in the install sections, and will not attempt to
configure the device in any way. This API is useful for pre-copying a
device's driver files.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
driver is to be installed.
DeviceInfoData - Optionally, supplies the address of a SP_DEVINFO_DATA
structure for which a driver is to be installed. If this parameter is not
specified, then a driver will be installed for the global class driver list
associated with the device information set itself.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
A driver must be selected for the specified device information set or element
before calling this API.
--*/
{
return _SetupDiInstallDevice(DeviceInfoSet, DeviceInfoData, FALSE);
}
BOOL
WINAPI
SetupDiRemoveDevice(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default hander for DIF_REMOVE
This routine removes a device from the system.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
device is to be removed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure for
which a device is to be removed. This is an IN OUT parameter, since the
DevInst field of the structure may be updated with a new handle value upon
return. (If this is a global removal, or the last hardware profile-specific
removal, then all traces of the devinst are removed from the registry, and
the handle becomes NULL at that point.)
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
Remarks:
This routine will remove the device from the system, deleting both of its
registry keys, and dynamically stopping the device if its DevInst is 'live'.
If the device cannot be dynamically stopped, then flags will be set in the
install parameter block that will eventually cause the user to be prompted
to shut the system down. The removal is either global to all hardware
profiles, or specific to one hardware profile depending on the contents of
the ClassInstallParams field.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err, ConfigFlags;
PDEVINFO_ELEM DevInfoElem;
PDEVINSTALL_PARAM_BLOCK dipb;
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
PSP_REMOVEDEVICE_PARAMS RemoveDevParams;
BOOL IsCurrentHwProfile = FALSE;
ULONG HwProfFlags;
DWORD HwProfileToRemove;
HWPROFILEINFO HwProfileInfo;
BOOL RemoveDevInst = FALSE, NukeDevInst = FALSE;
BOOL RemoveGlobally;
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
try {
//
// Locate the devinfo element to be removed.
//
if(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL)) {
dipb = &(DevInfoElem->InstallParamBlock);
} else {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// Retrieve the name of the device instance. This is necessary, because
// we're about to remove the DEVINST, but we need to be able to locate it
// again, as a phantom. This should never fail.
//
CM_Get_Device_ID(DevInfoElem->DevInst,
DeviceInstanceId,
SIZECHARS(DeviceInstanceId),
0
);
//
// See if there's a SP_REMOVEDEVICE_PARAMS structure we need to pay
// attention to.
//
if((dipb->Flags & DI_CLASSINSTALLPARAMS) &&
(dipb->ClassInstallHeader->InstallFunction == DIF_REMOVE)) {
RemoveDevParams = (PSP_REMOVEDEVICE_PARAMS)(dipb->ClassInstallHeader);
if(RemoveGlobally = (RemoveDevParams->Scope == DI_REMOVEDEVICE_GLOBAL)) {
//
// We are doing a global removal. We still want to set CSCONFIGFLAG_DO_NOT_CREATE
// for this device in the current hardware profile, so that someone else happening
// to do an enumeration won't turn this guy back on before we get a chance to
// remove it.
//
HwProfileToRemove = 0;
} else {
//
// Remove device from a particular hardware profile.
//
HwProfileToRemove = RemoveDevParams->HwProfile;
//
// Set the CSCONFIGFLAG_DO_NOT_CREATE flag for the specified hardware profile.
//
if(CM_Get_HW_Prof_Flags(DeviceInstanceId,
HwProfileToRemove,
&HwProfFlags,
0) == CR_SUCCESS) {
HwProfFlags |= CSCONFIGFLAG_DO_NOT_CREATE;
} else {
HwProfFlags = CSCONFIGFLAG_DO_NOT_CREATE;
}
if(CM_Set_HW_Prof_Flags(DeviceInstanceId,
HwProfileToRemove,
HwProfFlags,
0) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
}
//
// Determine if we are deleting the device from the current hw profile.
//
if((HwProfileToRemove == 0) ||
((CM_Get_Hardware_Profile_Info((ULONG)-1, &HwProfileInfo, 0) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == HwProfileToRemove))) {
IsCurrentHwProfile = TRUE;
}
}
//
// Is this the current hardware profile or a global removal AND
// is there a present device?
//
if((IsCurrentHwProfile || RemoveGlobally) &&
!(DevInfoElem->DiElemFlags & DIE_IS_PHANTOM) &&
!(dipb->Flags & DI_DONOTCALLCONFIGMG)) {
RemoveDevInst = TRUE;
}
} else {
//
// No device removal params given, so do a global removal.
//
RemoveGlobally = TRUE;
HwProfileToRemove = 0;
if(!(dipb->Flags & DI_DONOTCALLCONFIGMG)) {
RemoveDevInst = TRUE;
}
}
#if 0 // (lonnym): move this piece of code to up above (only set the hardware profile-specific
// flag when removing the device from a particular profile). Otherwise, if we remove and
// then re-add a device, it doesn't work, because no one is clearing this flag!
//
// Set the CSCONFIGFLAG_DO_NOT_CREATE flag for the specified hardware profile.
//
if(CM_Get_HW_Prof_Flags(DeviceInstanceId,
HwProfileToRemove,
&HwProfFlags,
0) == CR_SUCCESS) {
HwProfFlags |= CSCONFIGFLAG_DO_NOT_CREATE;
} else {
HwProfFlags = CSCONFIGFLAG_DO_NOT_CREATE;
}
if(CM_Set_HW_Prof_Flags(DeviceInstanceId,
HwProfileToRemove,
HwProfFlags,
0) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
}
#endif
//
// If this is a global removal, or the last hardware profile-specific one, then clean up
// the registry.
//
if(RemoveGlobally || IsDevRemovedFromAllHwProfiles(DeviceInstanceId)) {
NukeDevInst = TRUE;
}
if(RemoveDevInst) {
if((CM_Query_Remove_SubTree(DevInfoElem->DevInst, CM_QUERY_REMOVE_UI_OK) == CR_SUCCESS) &&
(CM_Remove_SubTree(DevInfoElem->DevInst, CM_REMOVE_UI_OK) == CR_SUCCESS)) {
//
// Device instance successfully removed--now locate it as a phantom.
//
CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
} else {
dipb->Flags |= DI_NEEDREBOOT;
}
}
if(NukeDevInst) {
//
// Remove all traces of this device from the registry.
//
pSetupDeleteDevRegKeys(DevInfoElem->DevInst,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGSPECIFIC,
(DWORD)-1,
DIREG_BOTH,
TRUE
);
CM_Uninstall_DevInst(DevInfoElem->DevInst, 0);
//
// Mark this device information element as unregistered, and set its
// devinst handle to NULL.
//
DevInfoElem->DiElemFlags &= ~DIE_IS_REGISTERED;
DevInfoElem->DevInst = (DEVINST)0;
}
//
// Now update the DevInst field of the DeviceInfoData structure with the new
// value of the devinst handle (possibly NULL).
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
UnlockDeviceInfoSet(pDeviceInfoSet);
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiMoveDuplicateDevice(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DestinationDeviceInfoData
)
/*++
Routine Description:
Default hander for DIF_MOVEDEVICE
This routine moves a device to a new location in the Enum branch.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which
a device is to be moved.
DestinationDeviceInfoData - Supplies the address of a SP_DEVINFO_DATA
structure for the device instance that is the destination of the move.
This device must contain class install parameters for DIF_MOVEDEVICE,
or the API will fail with ERROR_NO_CLASSINSTALL_PARAMS.
Return Value:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error
information, call GetLastError.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err;
PDEVINFO_ELEM SourceDevInfoElem, DestDevInfoElem;
PDEVINSTALL_PARAM_BLOCK dipb;
PSP_MOVEDEV_PARAMS MoveDevParams;
BOOL bUnlockDestDevInfoElem, bUnlockSourceDevInfoElem, bRestoreConfigMgrBehavior;
DWORD ConfigFlags;
TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN];
//
// A device information element must be specified for this routine.
//
if(!DestinationDeviceInfoData) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
bUnlockDestDevInfoElem = bUnlockSourceDevInfoElem = bRestoreConfigMgrBehavior = FALSE;
try {
//
// Locate the destination devinfo element.
//
if(DestDevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DestinationDeviceInfoData,
NULL)) {
dipb = &(DestDevInfoElem->InstallParamBlock);
} else {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// We'd better have DIF_MOVEDEVICE class install params
//
if(!(dipb->Flags & DI_CLASSINSTALLPARAMS) ||
(dipb->ClassInstallHeader->InstallFunction != DIF_MOVEDEVICE)) {
Err = ERROR_NO_CLASSINSTALL_PARAMS;
goto clean0;
}
MoveDevParams = (PSP_MOVEDEV_PARAMS)(dipb->ClassInstallHeader);
//
// Find the source device.
//
if(!(SourceDevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
&(MoveDevParams->SourceDeviceInfoData),
NULL))) {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
//
// We're about to call the class installer to handle device install (and possibly
// device selection as well). We don't want the class installer to do something
// slimy like delete the devices out from under us, so we'll lock 'em down.
//
if(!(DestDevInfoElem->DiElemFlags & DIE_IS_LOCKED)) {
DestDevInfoElem->DiElemFlags |= DIE_IS_LOCKED;
bUnlockDestDevInfoElem = TRUE;
}
if(!(SourceDevInfoElem->DiElemFlags & DIE_IS_LOCKED)) {
SourceDevInfoElem->DiElemFlags |= DIE_IS_LOCKED;
bUnlockSourceDevInfoElem = TRUE;
}
//
// We don't want the following calls to cause the ConfigMgr to do re-enumeration, etc.
//
if(!(dipb->Flags & DI_DONOTCALLCONFIGMG)) {
dipb->Flags |= DI_DONOTCALLCONFIGMG;
bRestoreConfigMgrBehavior = TRUE;
}
//
// We need to unlock the HDEVINFO before calling the class installer.
//
UnlockDeviceInfoSet(pDeviceInfoSet);
pDeviceInfoSet = NULL;
//
// If the destination device doesn't have a selected driver, then get one.
//
if(!DestDevInfoElem->SelectedDriver) {
if(!SetupDiCallClassInstaller(DIF_SELECTDEVICE,
DeviceInfoSet,
DestinationDeviceInfoData)) {
Err = GetLastError();
goto clean0;
}
}
if(!SetupDiCallClassInstaller(DIF_INSTALLDEVICE,
DeviceInfoSet,
DestinationDeviceInfoData)) {
Err = GetLastError();
goto clean0;
}
//
// Re-acquire the HDEVINFO lock.
//
pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet);
MYASSERT(pDeviceInfoSet);
//
// Retrieve the name of the device instance. This is necessary, because
// we may attempt to remove the DEVINST, but we need to be able to locate
// it again, as a phantom. This should never fail.
//
CM_Get_Device_ID(SourceDevInfoElem->DevInst,
DeviceInstanceId,
SIZECHARS(DeviceInstanceId),
0
);
//
// Delete all the user-accessible registry keys associated with the source
// device in preparation for the move.
//
pSetupDeleteDevRegKeys(SourceDevInfoElem->DevInst,
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGSPECIFIC,
(DWORD)-1,
DIREG_BOTH,
TRUE
);
//
// Check to see if we can remove the source device dynamically.
//
// NOTE: The ConfigFlags of the _destination_ device are retrieved, and checked
// for the presence of the CONFIGFLAG_CANTSTOPACHILD flag. PierreYs assures me
// that this is the correct behavior.
//
ConfigFlags = GetDevInstConfigFlags(DestDevInfoElem->DevInst, 0);
if(!(ConfigFlags & CONFIGFLAG_CANTSTOPACHILD) &&
(CM_Query_Remove_SubTree(SourceDevInfoElem->DevInst, CM_QUERY_REMOVE_UI_NOT_OK) == CR_SUCCESS) &&
(CM_Remove_SubTree(SourceDevInfoElem->DevInst, CM_REMOVE_UI_NOT_OK) == CR_SUCCESS)) {
//
// Source device instance successfully removed--now locate it as a phantom.
//
CM_Locate_DevInst(&(SourceDevInfoElem->DevInst),
(DEVINSTID)DeviceInstanceId,
CM_LOCATE_DEVINST_PHANTOM
);
SourceDevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
//
// Totally remove the source device from the system.
//
CM_Uninstall_DevInst(SourceDevInfoElem->DevInst, 0);
//
// Tell ConfigMgr to start the new (destination) device instance.
//
CM_Setup_DevInst(DestDevInfoElem->DevInst, CM_SETUP_DEVINST_READY);
CheckIfDevStarted(DestDevInfoElem);
} else {
//
// OK, can't remove on this boot, so just move the old one to the
// new one.
//
CM_Move_DevNode(SourceDevInfoElem->DevInst, DestDevInfoElem->DevInst, 0);
//
// Mark the source device instance as a phantom.
//
SourceDevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
}
//
// Mark the source device instance as unregistered, and clear its DevInst
// handle.
//
SourceDevInfoElem->DiElemFlags &= ~DIE_IS_REGISTERED;
SourceDevInfoElem->DevInst = (DEVINST)0;
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
//
// Reference the following variables so that the compiler will respect our statement
// ordering w.r.t. assignment.
//
pDeviceInfoSet = pDeviceInfoSet;
bUnlockDestDevInfoElem = bUnlockDestDevInfoElem;
bUnlockSourceDevInfoElem = bUnlockSourceDevInfoElem;
}
if(bUnlockDestDevInfoElem || bUnlockSourceDevInfoElem || bRestoreConfigMgrBehavior) {
if(!pDeviceInfoSet) {
pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet);
MYASSERT(pDeviceInfoSet);
}
try {
if(bUnlockDestDevInfoElem) {
DestDevInfoElem->DiElemFlags &= ~DIE_IS_LOCKED;
}
if(bUnlockSourceDevInfoElem) {
SourceDevInfoElem->DiElemFlags &= ~DIE_IS_LOCKED;
}
if(bRestoreConfigMgrBehavior) {
dipb->Flags &= ~DI_DONOTCALLCONFIGMG;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
; // nothing to do
}
}
if(pDeviceInfoSet) {
UnlockDeviceInfoSet(pDeviceInfoSet);
}
SetLastError(Err);
return(Err == NO_ERROR);
}
BOOL
WINAPI
SetupDiChangeState(
IN HDEVINFO DeviceInfoSet,
IN OUT PSP_DEVINFO_DATA DeviceInfoData
)
/*++
Routine Description:
Default hander for DIF_PROPERTYCHANGE
This routine is used to change the state of an installed device.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set for which a
device's state is to be changed.
DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure identifying
the device whose state is to be changed. This is an IN OUT parameter, since
the DevInst field of the structure may be updated with a new handle value upon
return.
Return Value:
If the function succeeds, and there are files to be copied, the return value is TRUE.
If the function succeeds, and there are no files to be copied, the return value is
FALSE, and GetLastError returns ERROR_DI_NOFILECOPY.
If the function fails, the return value is FALSE, and GetLastError returns some
other ERROR_* code.
--*/
{
PDEVICE_INFO_SET pDeviceInfoSet;
DWORD Err;
PDEVINFO_ELEM DevInfoElem;
PDEVINSTALL_PARAM_BLOCK dipb;
DWORD dwConfigFlags;
HKEY hk;
DEVINST dnToReenum;
ULONG ulStatus, ulProblem;
DWORD dwStateChange;
DWORD dwFlags;
ULONG lParam;
TCHAR szDevID[MAX_DEVICE_ID_LEN];
DWORD dwHWProfFlags;
HWPROFILEINFO HwProfileInfo;
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
Err = NO_ERROR;
try {
//
// Locate the devinfo element whose state is to be changed.
//
if(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet,
DeviceInfoData,
NULL)) {
dipb = &(DevInfoElem->InstallParamBlock);
} else {
Err = ERROR_INVALID_PARAMETER;
goto clean0;
}
if(!(dipb->Flags & DI_CLASSINSTALLPARAMS) ||
(dipb->ClassInstallHeader->InstallFunction != DIF_PROPERTYCHANGE)) {
//
// Don't have any class install parameters to tell us what needs to be done!
//
Err = ERROR_NO_CLASSINSTALL_PARAMS;
goto clean0;
}
dwStateChange = ((PSP_PROPCHANGE_PARAMS)(dipb->ClassInstallHeader))->StateChange;
dwFlags = ((PSP_PROPCHANGE_PARAMS)(dipb->ClassInstallHeader))->Scope;
lParam = ((PSP_PROPCHANGE_PARAMS)(dipb->ClassInstallHeader))->HwProfile;
switch(dwStateChange) {
case DICS_ENABLE:
if(dwFlags == DICS_FLAG_GLOBAL) {
//
// Clear the Disabled config flag, and attempt to enumerate the
// device. Presumably it has a device node, it is just dormant (ie
// prob 80000001).
//
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst, 0) & ~CONFIGFLAG_DISABLED;
//
// Set the New config flags value
//
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0
);
} else {
//
// Get the hardware profile-specific flags
//
CM_Get_Device_ID(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0
);
if(CM_Get_HW_Prof_Flags(szDevID, lParam, &dwHWProfFlags, 0) == CR_SUCCESS) {
//
// Clear the Disabled bit.
//
dwHWProfFlags &= ~CSCONFIGFLAG_DISABLED;
//
// Set the profile Flags for this device to Enabled
//
if(CM_Set_HW_Prof_Flags(szDevID, lParam, dwHWProfFlags, 0) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
}
} else {
//
// No flags. so set them to 0.
//
if(CM_Set_HW_Prof_Flags(szDevID, lParam, 0, 0) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
}
}
}
//
// Enable the disabled device instance
//
if((dwFlags == DICS_FLAG_GLOBAL) ||
((dwFlags == DICS_FLAG_CONFIGSPECIFIC) && (lParam == 0))) {
if(DevInfoElem->DevInst && !(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
if(CM_Enable_DevNode(DevInfoElem->DevInst, 0) == CR_SUCCESS) {
//
// Process this devnode now.
//
CM_Reenumerate_DevNode(DevInfoElem->DevInst, CM_REENUMERATE_SYNCHRONOUS);
//
// See if we sucessfully started dynamically.
//
CheckIfDevStarted(DevInfoElem);
} else {
//
// We could not enable so we should restart
//
dipb->Flags |= DI_NEEDREBOOT;
}
}
}
break;
case DICS_DISABLE:
if(dwFlags == DICS_FLAG_GLOBAL) {
//
// Set the Disabled config flag, and if there is a devnode, then
// try to remove it.
//
dwConfigFlags = GetDevInstConfigFlags(DevInfoElem->DevInst, 0) | CONFIGFLAG_DISABLED;
//
// Set the New config flags value
//
CM_Set_DevInst_Registry_Property(DevInfoElem->DevInst,
CM_DRP_CONFIGFLAGS,
&dwConfigFlags,
sizeof(dwConfigFlags),
0
);
} else {
//
// Get the hardware profile-specific flags
//
CM_Get_Device_ID(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0
);
if(CM_Get_HW_Prof_Flags(szDevID, lParam, &dwHWProfFlags, 0) == CR_SUCCESS) {
//
// Set the Disabled bit.
//
dwHWProfFlags |= CSCONFIGFLAG_DISABLED;
//
// Set the profile Flags for this device to Disabled
//
if(CM_Set_HW_Prof_Flags(szDevID, lParam, dwHWProfFlags, 0) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
}
} else {
//
// No flags. so set them to CSCONFIGFLAG_DISABLED.
//
if(CM_Set_HW_Prof_Flags(szDevID, lParam, CSCONFIGFLAG_DISABLED, 0) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
}
}
}
//
// Remove the device instance of the device being Disabled, if we can actually remove it.
// Also only remove iff this is a Global Disable, or a config specific disable for
// the current config (ie dwConfigID == 0)
//
if((dwFlags == DICS_FLAG_GLOBAL) ||
((dwFlags == DICS_FLAG_CONFIGSPECIFIC) && (lParam == 0))) {
//
// If there is a device instance, then we should disable it.
//
if(DevInfoElem->DevInst && !(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
if(CM_Disable_DevNode(DevInfoElem->DevInst, CM_DISABLE_POLITE) != CR_SUCCESS) {
//
// Disable Failed, so we need to reboot.
//
dipb->Flags |= DI_NEEDREBOOT;
}
}
}
break;
case DICS_PROPCHANGE:
//
// Properties have changed, so we need to remove the Devnode, and
// re-enumerate its parent.
//
if(DevInfoElem->DevInst) {
//
// Get the device instance ID for this device. We gotta have this to find
// the device instance again after we remove it from the hardware tree.
//
CM_Get_Device_ID(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0
);
if(CM_Query_Remove_SubTree(DevInfoElem->DevInst, 0) == CR_SUCCESS) {
CM_Get_Parent(&dnToReenum, DevInfoElem->DevInst, 0);
CM_Remove_SubTree(DevInfoElem->DevInst, 0);
CM_Reenumerate_DevInst(dnToReenum, CM_REENUMERATE_SYNCHRONOUS);
DevInfoElem->DevInst = 0;
if(CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)szDevID,
CM_LOCATE_DEVINST_NORMAL) != CR_SUCCESS) {
dipb->Flags |= DI_NEEDREBOOT;
//
// Retrieve the devinst as a phantom
//
CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)szDevID,
CM_LOCATE_DEVINST_PHANTOM
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
//
// Update the caller's buffer to reflect the new device instance handle
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
}
//
// Make Sure the device instance started OK
//
if((CM_Get_DevNode_Status(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0) == CR_SUCCESS) && !(ulStatus & DN_STARTED)) {
dipb->Flags |= DI_NEEDREBOOT;
}
} else {
dipb->Flags |= DI_NEEDREBOOT;
}
} else {
//
// HMMM no device instance handle, well let's just restart then
//
dipb->Flags |= DI_NEEDREBOOT;
}
break;
case DICS_START:
//
// DICS_START is always config specific (we enforce this in SetupDiSetClassInstallParams).
//
MYASSERT(dwFlags == DICS_FLAG_CONFIGSPECIFIC);
//
// Get the Profile Flags.
//
CM_Get_Device_ID(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0
);
if(CM_Get_HW_Prof_Flags(szDevID, lParam, &dwHWProfFlags, 0) == CR_SUCCESS) {
//
// Clear the "don't start" bit.
//
dwHWProfFlags &= ~CSCONFIGFLAG_DO_NOT_START;
CM_Set_HW_Prof_Flags(szDevID, lParam, dwHWProfFlags, 0);
} else {
//
// No flags?. Set them to 0.
//
CM_Set_HW_Prof_Flags(szDevID, lParam, 0, 0);
}
//
// Start the device instance if this is for the current config (ie dwConfigID/lparam == 0)
//
if((lParam == 0) ||
((CM_Get_Hardware_Profile_Info((ULONG)-1, &HwProfileInfo, 0) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == lParam)))
{
//
// If there is a device instance, then we should start it.
//
if(DevInfoElem->DevInst && !(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT)) ) {
CM_Setup_DevNode(DevInfoElem->DevInst, CM_SETUP_DEVNODE_READY);
CheckIfDevStarted(DevInfoElem);
}
}
break;
case DICS_STOP:
//
// DICS_STOP is always config specific (we enforce this in SetupDiSetClassInstallParams).
//
MYASSERT(dwFlags == DICS_FLAG_CONFIGSPECIFIC);
//
// Get the Profile Flags.
//
CM_Get_Device_ID(DevInfoElem->DevInst,
szDevID,
SIZECHARS(szDevID),
0
);
if(CM_Get_HW_Prof_Flags(szDevID, lParam, &dwHWProfFlags, 0) == CR_SUCCESS) {
//
// Set the "don't start" bit.
//
dwHWProfFlags |= CSCONFIGFLAG_DO_NOT_START;
CM_Set_HW_Prof_Flags(szDevID, lParam, dwHWProfFlags, 0);
} else {
//
// No flags. so set them to CSCONFIGFLAG_DO_NOT_START.
//
CM_Set_HW_Prof_Flags(szDevID, lParam, CSCONFIGFLAG_DO_NOT_START, 0);
}
//
// Stop the device instance if this is for the current config (ie dwConfigID/lparam == 0)
//
if((lParam == 0) ||
((CM_Get_Hardware_Profile_Info((ULONG)-1, &HwProfileInfo, 0) == CR_SUCCESS) &&
(HwProfileInfo.HWPI_ulHWProfile == lParam)))
{
//
// If there is a device instance, then we should stop it.
//
if(DevInfoElem->DevInst && !(dipb->Flags & (DI_NEEDRESTART | DI_NEEDREBOOT))) {
//
// Remove the device instance in order to stop the device.
//
if((CM_Query_Remove_SubTree(DevInfoElem->DevInst, CM_QUERY_REMOVE_UI_OK) == CR_SUCCESS) &&
(CM_Remove_SubTree(DevInfoElem->DevInst, CM_REMOVE_UI_OK) == CR_SUCCESS)) {
//
// Device instance successfully removed--now locate it as a phantom.
//
CM_Locate_DevInst(&(DevInfoElem->DevInst),
(DEVINSTID)szDevID,
CM_LOCATE_DEVINST_PHANTOM
);
DevInfoElem->DiElemFlags |= DIE_IS_PHANTOM;
//
// Update the caller's buffer to reflect the new device instance handle.
//
DeviceInfoData->DevInst = DevInfoElem->DevInst;
} else {
dipb->Flags |= DI_NEEDREBOOT;
}
}
}
break;
default:
return ERROR_DI_DO_DEFAULT;
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
UnlockDeviceInfoSet(pDeviceInfoSet);
SetLastError(Err);
return(Err == NO_ERROR);
}
DWORD
GetNewInfName(
IN PCTSTR OemInfName,
OUT PTSTR NewInfName, OPTIONAL
IN DWORD NewInfNameSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PBOOL CopyNeeded
)
/*++
Routine Description:
This routine finds a unique INF name of the form "<systemroot>\Inf\OEM<n>.INF",
and returns in in the supplied buffer. It leaves an (empty) file of that name
in the INF directory, so that anyone else who attempts to generate a unique
filename won't pick the same name.
Arguments:
OemInfName - Supplies the name of the OEM INF that needs to be copied into the Inf
directory (under a unique name).
NewInfName - Optionally, supplies the address of a character buffer to store
the unique name in. If this parameter is not specified, then the variable
pointed to by RequiredSize will be filled in the buffer size required to
hold the filename, and the routine will return NO_ERROR.
NewInfNameSize - Specifies the size, in characters, of the NewInfName buffer.
RequiredSize - Optionally, supplies the address of a variable that receives the
size, in characters, required to store the full new filename.
CopyNeeded - Supplies the address of a boolean variable that is set upon return
to indicate whether or not the OEM INF actually needs to be copied. If this
flag is FALSE, then the specified INF was already in the Inf directory, and
NewInfName specifies what the INF's name is in that directory.
Return Value:
If the function succeeds, the return value is NO_ERROR, otherwise, it is a Win32
error code.
--*/
{
INT i;
HANDLE h;
DWORD Err;
TCHAR lpszNewName[MAX_PATH];
DWORD TempLen;
WIN32_FIND_DATA FindData;
HANDLE FindHandle;
PTSTR FilenamePart;
DWORD OemInfFileSize;
HANDLE OemInfFileHandle, OemInfMappingHandle;
PVOID OemInfBaseAddress;
DWORD CurInfFileSize;
HANDLE CurInfFileHandle, CurInfMappingHandle;
PVOID CurInfBaseAddress;
//
// Initially, assume that the specified INF isn't already present in the Inf directory.
//
*CopyNeeded = TRUE;
//
// First, examine all the existing OEM INFs in the Inf directory, to see if this
// INF already exists there. If so, we'll just return the name of the previously-
// existing file.
//
lstrcpy(lpszNewName, InfDirectory);
ConcatenatePaths(lpszNewName, pszOemInfWildcard, SIZECHARS(lpszNewName), NULL);
if((FindHandle = FindFirstFile(lpszNewName, &FindData)) != INVALID_HANDLE_VALUE) {
//
// We have at least one OEM INF to compare against, so open our INF in preparation.
//
if(OpenAndMapFileForRead(OemInfName,
&OemInfFileSize,
&OemInfFileHandle,
&OemInfMappingHandle,
&OemInfBaseAddress) == NO_ERROR) {
//
// Find the location in the wildcard path where the filename part begins, so that
// we can easily build a fully-qualified path for each OEM INF we enumerate.
//
FilenamePart = (PTSTR)MyGetFileTitle(lpszNewName);
do {
//
// Only bother opening this INF if the file sizes match.
//
if(FindData.nFileSizeLow != OemInfFileSize) {
continue;
}
//
// Build the fully-qualified path to the INF being compared.
//
lstrcpy(FilenamePart, FindData.cFileName);
if(OpenAndMapFileForRead(lpszNewName,
&CurInfFileSize,
&CurInfFileHandle,
&CurInfMappingHandle,
&CurInfBaseAddress) == NO_ERROR) {
MYASSERT(CurInfFileSize == FindData.nFileSizeLow);
//
// Surround the following in try/except, in case we get an inpage error.
//
try {
if(!memcmp(OemInfBaseAddress, CurInfBaseAddress, OemInfFileSize)) {
//
// We've found a match (i.e., the specified OEM INF is already
// present in the Inf directory.
//
*CopyNeeded = FALSE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
; // nothing to do
}
UnmapAndCloseFile(CurInfFileHandle, CurInfMappingHandle, CurInfBaseAddress);
}
} while(*CopyNeeded && (FindNextFile(FindHandle, &FindData)));
UnmapAndCloseFile(OemInfFileHandle, OemInfMappingHandle, OemInfBaseAddress);
}
FindClose(FindHandle);
}
if(!(*CopyNeeded)) {
//
// Then this INF already exists in the Inf directory, return its name now.
//
TempLen = lstrlen(lpszNewName) + 1;
if(RequiredSize) {
*RequiredSize = TempLen;
}
if(NewInfName) {
if(TempLen < NewInfNameSize) {
CopyMemory(NewInfName, lpszNewName, TempLen * sizeof(TCHAR));
return NO_ERROR;
} else {
return ERROR_INSUFFICIENT_BUFFER;
}
} else {
//
// The caller just wanted to find out how big a buffer was needed to store the
// filename (or maybe simply whether the INF already existed in the Inf directory).
//
return NO_ERROR;
}
}
//
// OK, so the INF isn't presently in the Inf directory--find a unique name for it.
//
for(i = 0; i < 10000; i++) {
wsprintf(lpszNewName, pszOemInfGenerate, InfDirectory, i);
if((h = CreateFile(lpszNewName,
GENERIC_READ | GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL)) != INVALID_HANDLE_VALUE) {
//
// Then we either opened an existing file (that we need to leave alone),
// or we created a new file (in which case, we've found our unique name).
// These two cases are identified by the value of GetLastError().
//
Err = GetLastError();
//
// Before we decide what to do, close the file handle.
//
CloseHandle(h);
if(Err != ERROR_ALREADY_EXISTS) {
//
// Then we created a new file. Determine whether the filename fits
// in the caller-supplied buffer.
//
TempLen = lstrlen(lpszNewName) + 1;
if(RequiredSize) {
*RequiredSize = TempLen;
}
if(NewInfName) {
if(TempLen < NewInfNameSize) {
CopyMemory(NewInfName, lpszNewName, TempLen * sizeof(TCHAR));
return NO_ERROR;
} else {
//
// The caller's buffer isn't large enough. We have to delete the
// file we created.
//
DeleteFile(lpszNewName);
return ERROR_INSUFFICIENT_BUFFER;
}
} else {
//
// The caller just wanted to find out how big a buffer was needed to store
// the filename. Delete the file, and return success.
//
DeleteFile(lpszNewName);
return NO_ERROR;
}
}
}
}
//
// We didn't find a unique OEM INF name to use!
//
return ERROR_FILE_NOT_FOUND;
}
DWORD
InstallHW(
IN HDEVINFO DeviceInfoSet,
IN PSP_DEVINFO_DATA DeviceInfoData,
IN HINF hDeviceInf,
IN PCTSTR szSectionName,
OUT PBOOL DeleteDevKey
)
/*++
Routine Description:
This routine appends a ".Hw" to the end of the install section name for the
specified device, and attempts to find that section name in the specified INF.
If found, it does a performs a registry installation against it.
Arguments:
DeviceInfoSet - Supplies a handle to the device information set to call
SetupInstallFromInfSection for.
DeviceInfoData - Supplies the address of a device information element structure
for which the installation action is to be performed.
hDeviceInf - Supplies a handle to the opened INF containing the device install
section.
szSectionName - Supplies the address of a string specifying the install section
name for this device. This string will be appended with ".Hw" to create
the corresponding hardware section name.
DeleteDevKey - Supplies the address of a variable that receives a boolean value
indicating whether or not a user-accessible device key was created as a
result of calling this routine. This output may be used to indicate whether
or not the key should be destroyed if the caller encounters some error later
on that requires clean-up.
Return Value:
If the function succeeds, the return value is NO_ERROR, otherwise it is an
ERROR_* code.
--*/
{
HKEY hKey;
DWORD Err;
TCHAR szHwSection[MAX_SECT_NAME_LEN];
INFCONTEXT InfContext;
//
// Initially, assume the device key is already there, and therefore shouldn't be
// deleted during error clean-up.
//
*DeleteDevKey = FALSE;
//
// Form the hardware INF section name, and see if that section exists in the INF.
//
wsprintf(szHwSection, pszHwSectionFormat, szSectionName);
if(!SetupFindFirstLine(hDeviceInf, szHwSection, NULL, &InfContext)) {
return NO_ERROR;
}
if((hKey = SetupDiOpenDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
KEY_ALL_ACCESS)) == INVALID_HANDLE_VALUE) {
//
// Open failed--try create.
//
if((hKey = SetupDiCreateDevRegKey(DeviceInfoSet,
DeviceInfoData,
DICS_FLAG_GLOBAL,
0,
DIREG_DEV,
NULL,
NULL)) == INVALID_HANDLE_VALUE) {
return GetLastError();
} else {
*DeleteDevKey = TRUE;
}
}
Err = NO_ERROR;
try {
Err = pSetupInstallRegistry(hDeviceInf, szHwSection, hKey, DeviceInfoData->DevInst);
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
RegCloseKey(hKey);
return Err;
}
VOID
CheckIfDevStarted(
IN PDEVINFO_ELEM DevInfoElem
)
/*++
Routine Description:
This routine calls CM_Get_DevInst_Status to see if the specified device
instance has been started. If the device hasn't been started, and it
isn't disabled, the DI_NEEDREBOOT flag is set in the device information
element.
Arguments:
DevInfoElem - Supplies the address of the device information element to
check.
Return Value:
None.
--*/
{
ULONG ulStatus, ulProblem;
if(CM_Get_DevInst_Status(&ulStatus, &ulProblem, DevInfoElem->DevInst, 0) == CR_SUCCESS) {
if(!(ulStatus & DN_STARTED)) {
if(ulProblem != CM_PROB_DISABLED) {
DevInfoElem->InstallParamBlock.Flags |= DI_NEEDREBOOT;
}
}
}
}
DWORD
InstallNtService(
IN HDEVINFO DeviceInfoSet, OPTIONAL
IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL
IN HINF hDeviceInf,
IN PCTSTR szSectionName, OPTIONAL
OUT PSVCNAME_NODE *ServicesToDelete, OPTIONAL
IN DWORD Flags
)
/*++
Routine Description:
This routine looks for the specified INF section, and if found, it installs any
services specified in "AddService" entries, and deletes any services specified
in "DelService" entries. These entries have the following form:
AddService = <ServiceName>, [<Flags>], <ServiceInstallSection>[, <EventLogInstallSection>]
DelService = <ServiceName>
A linked list is built of newly-created services, and optionally returned to the
caller (in case a subsequent installation failure requires all modifications to
be undone).
After all service modifications are complete, this routine checks the device
parameters to see if we're in the context of a device installation. If so, then
it checks to see if the device instance specifies a valid controlling service,
and that the service is not disabled (disabled services are assumed to be uninstalled).
Arguments:
DeviceInfoSet - Optionally, supplies a handle to the device information set
containing the device information element being installed. If this parameter
is not specified or is INVALID_HANDLE_VALUE, then the service is not being
installed in relation to a device instance, and DeviceInfoData is ignored.
DeviceInfoData - Optionally, supplies the address of a device information element
structure for which the installation action is to be performed. This parameter
is ignored if DeviceInfoSet is not specified.
hDeviceInf - Supplies a handle to the opened INF containing the service install
section.
szSectionName - Optionally, supplies the name of the service install section in a
Win95-style device INF. If this parameter is NULL, then no AddService or
DelService lines will be processed.
ServicesToDelete - Optionally, supplies the address of a linked list head pointer,
that receives a list of services that were newly-created by this routine, and
as such, should be deleted if the installation fails later on. The caller must
free the memory allocated for the nodes in this list by calling MyFree() on each
one.
Flags - Supplies flags controlling how the services are to be installed. May be a
combination of the following values:
SPSVCINST_TAGTOFRONT - For every kernel or filesystem driver installed (that
has an associated LoadOrderGroup), always move this service's tag to the
front of the ordering list.
Return Value:
If the function succeeds, the return value is NO_ERROR, otherwise it is an
ERROR_* code.
--*/
{
CONFIGRET cr;
TCHAR ServiceName[MAX_SERVICE_NAME_LEN];
ULONG ServiceNameSize;
DWORD Err = NO_ERROR, i;
SC_HANDLE SCMHandle, ServiceHandle;
LPQUERY_SERVICE_CONFIG ServiceConfig;
DWORD ServiceConfigSize;
PCTSTR Key;
INFCONTEXT LineContext;
PSVCNAME_NODE SvcListHead = NULL;
PSVCNAME_NODE TmpSvcNode;
SC_LOCK SCLock;
DWORD NewTag;
//
// Treat invalid handle value and NULL the same.
//
if(DeviceInfoSet == INVALID_HANDLE_VALUE) {
DeviceInfoSet = NULL;
}
if(szSectionName) {
//
// Surround the following in try/except, in case we get an inpage error.
//
try {
//
// Make two passes through the section--once for deletions, and a
// second time for additions.
//
for(i = 0; i < 2; i++) {
//
// Find the relevent line (if there is one) in the given install section.
//
Key = (i) ? pszAddService : pszDelService;
if(!SetupFindFirstLine(hDeviceInf, szSectionName, Key, &LineContext)) {
continue;
}
do {
//
// We have a line to act upon.
//
Err = (i) ? pSetupAddService(&LineContext,
&SvcListHead,
Flags,
(DeviceInfoSet ? DeviceInfoData->DevInst : 0))
: pSetupDeleteService(&LineContext);
if(Err != NO_ERROR) {
goto clean0;
}
} while(SetupFindNextMatchLine(&LineContext, Key, &LineContext));
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
}
if((Err != NO_ERROR) || !DeviceInfoSet) {
goto FinalClean0;
}
}
//
// Find out if the device instance already has an associated service.
//
ServiceNameSize = sizeof(ServiceName);
if((cr = CM_Get_DevInst_Registry_Property(DeviceInfoData->DevInst,
CM_DRP_SERVICE,
NULL,
ServiceName,
&ServiceNameSize,
0)) != CR_SUCCESS) {
//
// Either the device instance has gone sour (in which case we return an error),
// or we couldn't retrieve an associated service name. In the latter case, we
// will make the association based on the default service for the class.
//
if(cr == CR_INVALID_DEVINST) {
Err = ERROR_NO_SUCH_DEVINST;
} else {
ServiceNameSize = sizeof(ServiceName);
Err = AssociateDevInstWithDefaultService(DeviceInfoData, ServiceName, &ServiceNameSize)
? NO_ERROR : ERROR_NO_ASSOCIATED_SERVICE;
}
//
// Regardless of whether we were able to successfully associate a default service
// with this device, we want to skip the service controller querying/modification
// that follows. The assumption is that if we successfully retrieved the default
// service, then that service is correctly installed, and has a proper tag value.
//
goto FinalClean0;
}
//
// At this point, we have the name of the service with which the device instance is
// associated. Attempt to locate this service in the SCM database.
//
if(!(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
Err = GetLastError();
goto FinalClean0;
}
if(!(ServiceHandle = OpenService(SCMHandle, ServiceName, SERVICE_ALL_ACCESS))) {
//
// We couldn't access the service--probably because it doesn't exist.
// Bail now.
//
Err = GetLastError();
goto FinalClean1;
}
//
// The service exists. Make sure that it's not disabled.
//
if((Err = RetrieveServiceConfig(ServiceHandle, &ServiceConfig)) == NO_ERROR) {
if(ServiceConfig->dwStartType == SERVICE_DISABLED) {
Err = ERROR_SERVICE_DISABLED;
} else {
//
// If this service has a load order group, and is a kernel or filesystem
// driver, then make sure that it has a tag.
//
// NOTE: We have to do this here, even though we ensure that all new services we install
// have their tags set up properly in pSetupAddService(). The reason is that the device may
// using an existing service that wasn't installed via a Win95-style INF.
//
if(ServiceConfig->lpLoadOrderGroup && *(ServiceConfig->lpLoadOrderGroup) &&
(ServiceConfig->dwServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER))) {
//
// This service needs a tag--does it have one???
//
if(!(NewTag = ServiceConfig->dwTagId)) {
//
// Attempt to lock the service database before generating a tag. We'll go ahead
// and make the change, even if this fails.
//
SCLock = LockServiceDatabase(SCMHandle);
if(!ChangeServiceConfig(ServiceHandle,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
NULL,
ServiceConfig->lpLoadOrderGroup, // have to specify this to generate new tag.
&NewTag,
NULL,
NULL,
NULL,
NULL)) {
NewTag = 0;
}
if(SCLock) {
UnlockServiceDatabase(SCLock);
}
}
//
// Make sure that the tag exists in the service's corresponding GroupOrderList entry.
//
if(NewTag) {
AddTagToGroupOrderListEntry(ServiceConfig->lpLoadOrderGroup,
NewTag,
Flags & SPSVCINST_TAGTOFRONT
);
}
}
}
MyFree(ServiceConfig);
}
CloseServiceHandle(ServiceHandle);
FinalClean1:
CloseServiceHandle(SCMHandle);
FinalClean0:
if(Err == NO_ERROR) {
//
// If requested, store the linked-list of newly-created service nodes in the output
// parameter, otherwise, delete the list.
//
if(ServicesToDelete) {
*ServicesToDelete = SvcListHead;
} else {
for(TmpSvcNode = SvcListHead; TmpSvcNode; TmpSvcNode = SvcListHead) {
SvcListHead = SvcListHead->Next;
MyFree(TmpSvcNode);
}
}
} else {
//
// Something failed along the way, so we need to clean up any newly-created
// services.
//
if(SvcListHead) {
DeleteServicesInList(SvcListHead);
for(TmpSvcNode = SvcListHead; TmpSvcNode; TmpSvcNode = SvcListHead) {
SvcListHead = SvcListHead->Next;
MyFree(TmpSvcNode);
}
}
}
return Err;
}
BOOL
AssociateDevInstWithDefaultService(
IN PSP_DEVINFO_DATA DeviceInfoData,
OUT PTSTR ServiceName,
IN OUT PDWORD ServiceNameSize
)
/*++
Routine Description:
This routine attempts to find out the default service with which to associate
the specified device. The default service (if there is one) is associated with
the device's class. If a default is found, the device instance is associated
with that service.
Arguments:
DeviceInfoData - Supplies the address of the SP_DEVINFO_DATA structure for the
device instance to create a default service association for.
ServiceName - Supplies the address of a character buffer that receives the name
of the service with which the device instance was associated (if this routine
is successful).
ServiceNameSize - Supplies the address of a variable containing the size, in bytes,
of the ServiceName buffer. On output, this variable receives the number of
bytes actually stored in ServiceName.
Return Value:
If the function succeeds, the return value is TRUE, otherwise it is FALSE.
--*/
{
HKEY hClassKey;
DWORD RegDataType;
BOOL Success;
//
// Open up the class key for this device's class.
//
if((hClassKey = SetupDiOpenClassRegKey(&(DeviceInfoData->ClassGuid),
KEY_READ)) == INVALID_HANDLE_VALUE) {
return FALSE;
}
Success = FALSE; // assume failure
try {
//
// Retrieve the "Default Service" value from the class key. If present, this value entry
// indicates what service to associate the device with, when one isn't specified during
// installation.
//
if(RegQueryValueEx(hClassKey,
pszDefaultService,
NULL,
&RegDataType,
(PBYTE)ServiceName,
ServiceNameSize) != ERROR_SUCCESS) {
goto clean0;
}
if((RegDataType != REG_SZ) || (*ServiceNameSize < sizeof(TCHAR)) || !(*ServiceName)) {
goto clean0;
}
//
// We have successfully retrieved the default service name to be associated with this
// device instance. Perform the association now by setting the Service device registry
// property.
//
if(CM_Set_DevInst_Registry_Property(DeviceInfoData->DevInst,
CM_DRP_SERVICE,
ServiceName,
*ServiceNameSize,
0) == CR_SUCCESS) {
Success = TRUE;
}
clean0: ; // nothing to do
} except(EXCEPTION_EXECUTE_HANDLER) {
Success = FALSE;
}
RegCloseKey(hClassKey);
return Success;
}
VOID
DeleteServicesInList(
IN PSVCNAME_NODE ServicesToDelete
)
/*++
Routine Description:
This routine deletes each service entry in the supplied linked list. This is
typically called to clean up if something goes wrong during a device's installation.
If the 'DeleteEventLog' flag for a particular node is TRUE, then the corresponding
event log entry under HKLM\System\CurrentControlSet\Services\EventLog\System is
also deleted.
Arguments:
ServicesToDelete - supplies a pointer to the head of a linked list of service names
to be deleted.
Return Value:
None.
--*/
{
SC_HANDLE SCMHandle, ServiceHandle;
SC_LOCK SCLock;
HKEY hKey = NULL;
TCHAR RegistryPath[CSTRLEN(REGSTR_PATH_SERVICES) + CSTRLEN(DISTR_EVENTLOG_SYSTEM) + MAX_SERVICE_NAME_LEN];
if(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS)) {
if(SCLock = LockServiceDatabase(SCMHandle)) {
for(; ServicesToDelete; ServicesToDelete = ServicesToDelete->Next) {
if(ServiceHandle = OpenService(SCMHandle,
ServicesToDelete->Name,
SERVICE_ALL_ACCESS)) {
DeleteService(ServiceHandle);
CloseServiceHandle(ServiceHandle);
if(ServicesToDelete->DeleteEventLog) {
//
// We need to delete the associated event log for this service.
//
if(!hKey) {
//
// We haven't opened up the System EventLog registry key
// yet, so do that now.
//
CopyMemory(RegistryPath, pszServicesRegPath, sizeof(pszServicesRegPath));
CopyMemory(RegistryPath + CSTRLEN(REGSTR_PATH_SERVICES),
pszEventLogSystem,
sizeof(pszEventLogSystem)
);
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE,
RegistryPath,
0,
KEY_ALL_ACCESS,
&hKey) != ERROR_SUCCESS) {
hKey = NULL; // make sure this value is still NULL!
continue;
}
}
RegistryDelnode(hKey, ServicesToDelete->Name);
}
}
}
if(hKey) {
RegCloseKey(hKey);
}
UnlockServiceDatabase(SCLock);
}
CloseServiceHandle(SCMHandle);
}
}
BOOL
IsDevRemovedFromAllHwProfiles(
IN PCTSTR DeviceInstanceId
)
/*++
Routine Description:
This routine determines whether the specified device instance has been removed from
every hardware profile. The device has been removed from a particular profile if
its corresponding CsConfigFlags has the CSCONFIGFLAG_DO_NOT_CREATE bit set.
Arguments:
DeviceInstanceId - Supplies the name of the device instance to check.
Return Value:
If the device exists in only the specified profile, the return value is TRUE,
otherwise, it is FALSE.
--*/
{
CONFIGRET cr;
ULONG i = 0;
HWPROFILEINFO HwProfileInfo;
ULONG HwProfFlags;
//
// Enumerate all the hardware profiles.
//
do {
if((cr = CM_Get_Hardware_Profile_Info(i, &HwProfileInfo, 0)) == CR_SUCCESS) {
if((CM_Get_HW_Prof_Flags((DEVINSTID)DeviceInstanceId,
HwProfileInfo.HWPI_ulHWProfile,
&HwProfFlags,
0) != CR_SUCCESS) ||
!(HwProfFlags & CSCONFIGFLAG_DO_NOT_CREATE))
{
//
// If we couldn't retrieve the CSConfigFlags, or if the
// CSCONFIGFLAG_DO_NOT_CREATE bit was not set, then we've found
// a profile where the device still exists, so we can bail here.
//
return FALSE;
}
}
i++;
} while(cr != CR_NO_MORE_HW_PROFILES);
//
// We didn't find any hardware profile where the device wasn't removed.
//
return TRUE;
}
DWORD
GetDevInstConfigFlags(
IN DEVINST DevInst,
IN DWORD Default
)
/*++
Routine Description:
This routine retrieves the ConfigFlags for the specified device instance. If the
value can not be retrieved, the specified default is returned.
Arguments:
DevInst - Supplies the handle of the device instance for which the ConfigFlags value
is to be retrieved.
Default - Supplies the default value that should be returned if for some reason the
ConfigFlags cannot be retrieved.
Return Value:
The ConfigFlags value for the specified device instance.
--*/
{
DWORD ConfigFlags;
ULONG ConfigFlagsSize = sizeof(ConfigFlags);
if(CM_Get_DevInst_Registry_Property(DevInst,
CM_DRP_CONFIGFLAGS,
NULL,
&ConfigFlags,
&ConfigFlagsSize,
0) != CR_SUCCESS) {
ConfigFlags = Default;
}
return ConfigFlags;
}
DWORD
pSetupDeleteService(
IN PINFCONTEXT LineContext
)
/*++
Routine Description:
This routine processes the specified DelService line in an INF's Service
install section. The line has the form:
DelService = <ServiceName>
Arguments:
LineContext - Supplies the context of the DelService line to be processed.
Return Value:
If field 1 on the specified line could not be retrieved, then an error
is returned. Otherwise, the routine returns NO_ERROR (i.e., the routine
is considered successful regardless of whether the service to delete
actually existed).
--*/
{
SVCNAME_NODE TempSvcNode;
//
// Initialize a service name node for a call to DeleteServicesInList.
//
if(!SetupGetStringField(LineContext,
1,
TempSvcNode.Name,
SIZECHARS(TempSvcNode.Name),
NULL)) {
return GetLastError();
}
TempSvcNode.Next = NULL;
TempSvcNode.DeleteEventLog = FALSE;
DeleteServicesInList(&TempSvcNode);
return NO_ERROR;
}
DWORD
pSetupAddService(
IN PINFCONTEXT LineContext,
OUT PSVCNAME_NODE *SvcListHead,
IN DWORD Flags,
IN DEVINST DevInst OPTIONAL
)
/*++
Routine Description:
This routine processes the specified AddService line in an INF's Service
install section. The line has the form:
AddService = <ServiceName>, [<Flags>], <ServiceInstallSection>[, <EventLogInstallSection>]
Currently, the following flags are defined:
SPSVCINST_TAGTOFRONT (0x1) - Move the tag for this service to the front of its
group order list
SPSVCINST_ASSOCSERVICE (0x2) - Associate this service with the device instance
being installed (only used if DevInst is non-zero)
A service with the name <ServiceName> is created. The parameters used in the
call to CreateService are retrieved from the <ServiceInstallSection>, and are
in the following format (lines not marked as optional must be present or the
routine will fail):
DisplayName = <string> ; (optional) 'Friendly name' for the service
ServiceType = <number> ; one of the SERVICE_* type codes
StartType = <number> ; one of the SERVICE_* start codes
ErrorControl = <number> ; one of the SERVICE_ERROR_* error control codes
ServiceBinary = <string> ; path to binary
LoadOrderGroup = <string> ; (optional) group to which this service belongs
Dependencies = <string>[[, <string>]...] ; (optional) list of groups (prefixed with '+')
; and services this service depends on
StartName = <string> ; (optional) driver object name used to load the
; driver--only used for drivers & filesystems
SetupInstallFromInfSection is then called for the <ServiceInstallSection>, which may
also contain registry modifications (SPINST_REGISTRY is the only flag used). HKR is
the service entry key.
Finally, if <EventLogInstallSection> is specified, then a key for this service is
created under HKLM\System\CurrentControlSet\Services\EventLog\System, and
SetupInstallFromInfSection is invoked to do registry modifications specified in
that section, with HKR being the event log entry (again, only SPINST_REGISTRY is used).
Arguments:
LineContext - Supplies the context of the AddService line to be processed.
SvcListHead - Supplies the address of the linked-list head containing a list of
all services newly created as a result of the current installation. This
routine first checks for the presence of the service, and if it already exists,
then it simply modifies the existing one. If the service doesn't already exist,
then this routine creates a new SVCNAME_NODE, and fills it in with the name of
the newly-created service. Likewise, if an EventLog entry is given, then the
presence of an existing one is checked first, and the service node's
'DeleteEventLog' field is set to TRUE only if the event log entry didn't
previously exist. This list is kept to allow for proper clean-up in case
of a later failure.
Flags - Specifies how the service should be installed. These flags are basically
overrides of what the AddService flags field specifies. May be a combination of the
following values:
SPSVCINST_TAGTOFRONT - For every kernel or filesystem driver installed (that
has an associated LoadOrderGroup), always move this service's tag to the
front of the ordering list.
DevInst - If specified (i.e., non-zero), and if the AddService flags field has the
SPSVCINST_ASSOCSERVICE flag set, then we will store this service name in the
device instance's 'Service' registry property.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
Remarks:
Note that we don't do anything special for SERVICE_ADAPTER and SERVICE_RECOGNIZER_DRIVER
service types. These types are invalid as far as the service contoller is concerned, so
we just let the create/change service APIs do the validation on them.
--*/
{
PCTSTR ServiceName, InstallSection;
HINF hInf;
INFCONTEXT InstallSectionContext;
DWORD ServiceType, StartType, ErrorControl, ServiceInstallFlags;
PCTSTR ServiceBinary;
TCHAR ServiceBinaryBuffer[MAX_PATH];
PCTSTR DisplayName = NULL, LoadOrderGroup = NULL, StartName = NULL;
PTSTR DependenciesBuffer;
DWORD TagId;
PDWORD NewTag;
DWORD Err;
SC_HANDLE SCMHandle, ServiceHandle;
SC_LOCK SCLock;
HKEY hKeyService, hKeyEventLog;
TCHAR RegistryPath[CSTRLEN(REGSTR_PATH_SERVICES) + CSTRLEN(DISTR_EVENTLOG_SYSTEM) + MAX_SERVICE_NAME_LEN];
DWORD EventLogKeyDisposition;
SVCNAME_NODE NewSvcNameNode;
PSVCNAME_NODE TmpNode;
BOOL NewService;
INT PathLen;
BOOL b, BinaryInSysRoot, ServiceHasTag;
LPQUERY_SERVICE_CONFIG ServiceConfig;
//
// First, get the service name and service install section.
//
if(!(ServiceName = pSetupGetField(LineContext, 1)) ||
!(InstallSection = pSetupGetField(LineContext, 3))) {
return GetLastError();
}
//
// Get the flags field.
//
if(!SetupGetIntField(LineContext, 2, (PINT)&ServiceInstallFlags)) {
ServiceInstallFlags = 0;
}
//
// Allow the caller-supplied flags to override the INF (currently, only the 'tag-to-front'
// flag is overrideable).
//
ServiceInstallFlags |= (Flags & SPSVCINST_TAGTOFRONT);
//
// Locate the service install section.
//
hInf = LineContext->Inf;
//
// Retrieve the required values from this section. Don't do validation on them--leave
// that up to the Service Control Manager.
//
if(!SetupFindFirstLine(hInf, InstallSection, pszServiceType, &InstallSectionContext) ||
!SetupGetIntField(&InstallSectionContext, 1, (PINT)&ServiceType)) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
if(!SetupFindFirstLine(hInf, InstallSection, pszStartType, &InstallSectionContext) ||
!SetupGetIntField(&InstallSectionContext, 1, (PINT)&StartType)) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
if(!SetupFindFirstLine(hInf, InstallSection, pszErrorControl, &InstallSectionContext) ||
!SetupGetIntField(&InstallSectionContext, 1, (PINT)&ErrorControl)) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
BinaryInSysRoot = FALSE;
if(SetupFindFirstLine(hInf, InstallSection, pszServiceBinary, &InstallSectionContext) &&
(ServiceBinary = pSetupGetField(&InstallSectionContext, 1)) && *ServiceBinary) {
//
// Compare the initial part of this path with the WindowsDirectory path. If they're
// the same, then we strip off that part (including the dividing backslash), and use
// the rest of the path for the subsequent calls to SCM. This allows SCM to assign
// the special path to the binary, that is accessible, at any time (i.e, boot-loader on).
//
PathLen = lstrlen(WindowsDirectory);
MYASSERT(PathLen);
//
// Make sure that the it is possible for the WindowsDirectory to fit in the ServiceBinary
// path string.
//
if(PathLen < lstrlen(ServiceBinary)) {
//
// There will never be a trailing backslash in the WindowsDirectory path, unless the
// installation is at the root of a drive (e.g., C:\). Check this, just to be
// on the safe side.
//
b = (WindowsDirectory[PathLen - 1] == TEXT('\\'));
if(b || (ServiceBinary[PathLen] == TEXT('\\'))) {
//
// The path prefix is in the right format--now we need to see if the two
// paths actually match. Copy just the prefix part to another buffer, so
// that we can do the comparison.
//
CopyMemory(ServiceBinaryBuffer, ServiceBinary, PathLen * sizeof(TCHAR));
ServiceBinaryBuffer[PathLen] = TEXT('\0');
if(!lstrcmpi(WindowsDirectory, ServiceBinaryBuffer)) {
//
// We have a match--take the relative part of the path (relative to SystemRoot),
// and do one of the following:
//
// 1. If it's a driver, simply use the relative part (no preceding backslash).
// This tells the bootloader/NtLoadDriver that the path is relative to the
// SystemRoot, so the driver can be loaded no matter what phase it's loaded in.
//
// 2. If it's a Win32 service, prepend a %SystemRoot%, so that the service will
// still be able to start if the drive letter mappings change.
//
ServiceBinary += PathLen;
if(!b) {
ServiceBinary++;
}
if(ServiceType & SERVICE_WIN32) {
CopyMemory(ServiceBinaryBuffer, pszSystemRoot, sizeof(pszSystemRoot) - sizeof(TCHAR));
lstrcpy(ServiceBinaryBuffer + CSTRLEN(pszSystemRoot), ServiceBinary);
ServiceBinary = ServiceBinaryBuffer;
}
BinaryInSysRoot = TRUE;
}
}
}
} else {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
//
// If this is a driver, then it has to be located under SystemRoot.
//
if((ServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER)) && !BinaryInSysRoot) {
return ERROR_BAD_SERVICE_INSTALLSECT;
}
//
// Now check for the other, optional, parameters.
//
if(SetupFindFirstLine(hInf, InstallSection, pszDisplayName, &InstallSectionContext)) {
if((DisplayName = pSetupGetField(&InstallSectionContext, 1)) && !(*DisplayName)) {
DisplayName = NULL;
}
}
if(SetupFindFirstLine(hInf, InstallSection, pszLoadOrderGroup, &InstallSectionContext)) {
if((LoadOrderGroup = pSetupGetField(&InstallSectionContext, 1)) && !(*LoadOrderGroup)) {
LoadOrderGroup = NULL;
}
}
//
// Only retrieve the StartName parameter for kernel-mode drivers.
//
if(ServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER)) {
if(SetupFindFirstLine(hInf, InstallSection, pszStartName, &InstallSectionContext)) {
if((StartName = pSetupGetField(&InstallSectionContext, 1)) &&
!(*StartName)) {
StartName = NULL;
}
}
}
//
// We now need to retrieve the multi-sz list of dependencies. This requires memory allocation,
// so we include everything from here on out in try/except, so that we can do proper clean-up
// in case we encounter an inpage error.
//
DependenciesBuffer = NULL;
SCMHandle = ServiceHandle = NULL;
SCLock = NULL;
hKeyService = hKeyEventLog = NULL;
Err = NO_ERROR;
NewService = FALSE;
try {
if(!(DependenciesBuffer = GetMultiSzFromInf(hInf, InstallSection, pszDependencies, &b)) && b) {
//
// Then we failed to retrieve a dependencies list because of an out-of-memory error.
//
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// We've now retrieved all parameters necessary to create a service.
//
if(!(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
Err = GetLastError();
goto clean0;
}
//
// Only generate a tag for this service if it has a load order group, and is a kernel or
// filesystem driver.
//
ServiceHasTag = (LoadOrderGroup &&
(ServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER)));
NewTag = ServiceHasTag ? &TagId : NULL;
ServiceHandle = CreateService(SCMHandle,
ServiceName,
DisplayName,
0,
ServiceType,
StartType,
ErrorControl,
ServiceBinary,
LoadOrderGroup,
NewTag,
DependenciesBuffer,
StartName,
NULL
);
if(ServiceHandle) {
NewService = TRUE;
NewSvcNameNode.Next = NULL;
NewSvcNameNode.DeleteEventLog = FALSE;
lstrcpy(NewSvcNameNode.Name, ServiceName);
} else {
//
// If we were unable to create the service, then check to see if the service already
// exists. If so, all we need to do is change the configuration parameters in the
// service.
//
if((Err = GetLastError()) != ERROR_SERVICE_EXISTS) {
goto clean0;
}
//
// Attempt to lock the database. If we can't lock it, we'll go ahead and make the
// mods anyway, since chances are almost 0 that anyone else is mucking with this service
// at the same time.
//
SCLock = LockServiceDatabase(SCMHandle);
if(!(ServiceHandle = OpenService(SCMHandle, ServiceName, SERVICE_ALL_ACCESS))) {
Err = GetLastError();
goto clean0;
}
//
// Since this is an existing driver, then it may already have a perfectly good tag. If
// so, we don't want to disturb it.
//
if(ServiceHasTag) {
if((Err = RetrieveServiceConfig(ServiceHandle, &ServiceConfig)) != NO_ERROR) {
goto clean0;
}
if(!lstrcmpi(ServiceConfig->lpLoadOrderGroup, LoadOrderGroup) && ServiceConfig->dwTagId) {
//
// The load order group hasn't changed, and there's already a tag assigned, so
// leave it alone.
//
NewTag = NULL;
TagId = ServiceConfig->dwTagId;
}
}
if(!ChangeServiceConfig(ServiceHandle,
ServiceType,
StartType,
ErrorControl,
ServiceBinary,
LoadOrderGroup,
NewTag,
DependenciesBuffer,
StartName,
NULL,
DisplayName)) {
Err = GetLastError();
goto clean0;
}
}
//
// We've successfully created/updated the service. If this service has a load order group
// tag, then make sure it's in the appropriate GroupOrderList entry.
//
// (We ignore failure here, since the service should still work just fine without this.)
//
if(ServiceHasTag) {
AddTagToGroupOrderListEntry(LoadOrderGroup,
TagId,
ServiceInstallFlags & SPSVCINST_TAGTOFRONT);
}
//
// Now process any AddReg and DelReg entries found in this service install section.
//
CopyMemory(RegistryPath, pszServicesRegPath, sizeof(pszServicesRegPath));
ConcatenatePaths(RegistryPath,
ServiceName,
SIZECHARS(RegistryPath),
NULL
);
if((Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
RegistryPath,
0,
KEY_ALL_ACCESS,
&hKeyService)) != ERROR_SUCCESS) {
goto clean0;
}
if((Err = pSetupInstallRegistry(hInf, InstallSection, hKeyService, (DEVINST)NULL)) != NO_ERROR) {
goto clean0;
}
//
// Now, see if the INF also specifies an EventLog installation section. If so, create a
// key under HKLM\System\CurrentControlSet\Services\EventLog\System for that service, and
// run the registry modification lines in the specified install section.
//
if(InstallSection = pSetupGetField(LineContext, 4)) {
//
// We already have the services database registry path in our registry path buffer. All
// we need to do is add the \EventLog\System\<ServiceName> part.
//
CopyMemory(RegistryPath + CSTRLEN(REGSTR_PATH_SERVICES),
pszEventLogSystem,
sizeof(pszEventLogSystem)
);
ConcatenatePaths(RegistryPath,
ServiceName,
SIZECHARS(RegistryPath),
NULL
);
if((Err = RegCreateKeyEx(HKEY_LOCAL_MACHINE,
RegistryPath,
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKeyEventLog,
&EventLogKeyDisposition)) != ERROR_SUCCESS) {
goto clean0;
}
if(EventLogKeyDisposition == REG_CREATED_NEW_KEY) {
NewSvcNameNode.DeleteEventLog = TRUE;
}
if((Err = pSetupInstallRegistry(hInf, InstallSection, hKeyEventLog, (DEVINST)NULL)) != NO_ERROR) {
goto clean0;
}
}
//
// Service entry (and optional EventLog entry) were successfully installed. If the
// AddService flags field in the INF included the SPSVCINST_ASSOCSERVICE flag, _and_
// the caller supplied us with a non-zero DevInst handle, then we need to set the
// device instance's 'Service' property to indicate that it is associated with this
// service.
//
if(DevInst && (ServiceInstallFlags & SPSVCINST_ASSOCSERVICE)) {
CM_Set_DevInst_Registry_Property(DevInst,
CM_DRP_SERVICE,
ServiceName,
(lstrlen(ServiceName) + 1) * sizeof(TCHAR),
0
);
}
//
// If a new service was created, then link a new service name node into the list we
// were passed in. Don't fret about the case where we can't allocate a node--it just
// means we won't know about this new service in case clean-up is required later.
//
if(NewService) {
if(TmpNode = MyMalloc(sizeof(SVCNAME_NODE))) {
TmpNode->DeleteEventLog = NewSvcNameNode.DeleteEventLog;
lstrcpy(TmpNode->Name, NewSvcNameNode.Name);
TmpNode->Next = *SvcListHead;
*SvcListHead = TmpNode;
}
}
clean0: ; // nothing to do.
} except(EXCEPTION_EXECUTE_HANDLER) {
//
// If our exception was an AV, then use Win32 invalid param error, otherwise, assume it was
// an inpage error dealing with a mapped-in file.
//
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_PARAMETER : ERROR_READ_FAULT;
//
// Access the following variables so that the compiler will respect our statement ordering
// w.r.t. these values. Otherwise, we can't be sure that we know whether or not their
// corresponding resources should be freed.
//
DependenciesBuffer = DependenciesBuffer;
hKeyService = hKeyService;
hKeyEventLog = hKeyEventLog;
ServiceHandle = ServiceHandle;
SCLock = SCLock;
SCMHandle = SCMHandle;
NewService = NewService;
}
if(DependenciesBuffer) {
MyFree(DependenciesBuffer);
}
if(hKeyService) {
RegCloseKey(hKeyService);
}
if(hKeyEventLog) {
RegCloseKey(hKeyEventLog);
}
if(ServiceHandle) {
CloseServiceHandle(ServiceHandle);
}
if(SCLock) {
UnlockServiceDatabase(SCLock);
}
if(SCMHandle) {
CloseServiceHandle(SCMHandle);
}
if((Err != NO_ERROR) && NewService) {
//
// Then we failed part-way through, and need to clean up the service (and
// possibly event log entry) we created.
//
DeleteServicesInList(&NewSvcNameNode);
}
return Err;
}
DWORD
RetrieveServiceConfig(
IN SC_HANDLE ServiceHandle,
OUT LPQUERY_SERVICE_CONFIG *ServiceConfig
)
/*++
Routine Description:
This routine allocates a buffer for the specified service's configuration parameters,
and retrieves those parameters into the buffer. The caller is responsible for freeing
the buffer.
Arguments:
ServiceHandle - supplies a handle to the service being queried
ServiceConfig - supplies the address of a QUERY_SERVICE_CONFIG pointer that receives
the address of the allocated buffer containing the requested information.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
Remarks:
The pointer whose address is contained in ServiceConfig is guaranteed to be NULL upon
return if any error occurred.
--*/
{
DWORD ServiceConfigSize = 0, Err;
*ServiceConfig = NULL;
while(TRUE) {
if(QueryServiceConfig(ServiceHandle, *ServiceConfig, ServiceConfigSize, &ServiceConfigSize)) {
MYASSERT(*ServiceConfig);
return NO_ERROR;
} else {
Err = GetLastError();
if(*ServiceConfig) {
MyFree(*ServiceConfig);
}
if(Err == ERROR_INSUFFICIENT_BUFFER) {
//
// Allocate a larger buffer, and try again.
//
if(!(*ServiceConfig = MyMalloc(ServiceConfigSize))) {
return ERROR_NOT_ENOUGH_MEMORY;
}
} else {
*ServiceConfig = NULL;
return Err;
}
}
}
}
DWORD
AddTagToGroupOrderListEntry(
IN PCTSTR LoadOrderGroup,
IN DWORD TagId,
IN BOOL MoveToFront
)
/*++
Routine Description:
This routine first creates the specified LoadOrderGroup value entry under
HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\GroupOrderList
if the value doesn't already exist. The routine then inserts the specified
tag into the list. If MoveToFront is TRUE, the tag is inserted at the front
of the list (or moved to the front of the list if it was already present in
the list). If MoveToFront is FALSE, then the new tag is inserted at the end
of the list, or left where it is if it already exists in the list.
Arguments:
LoadOrderGroup - Specifies the name of the LoadOrderGroup to insert this new
tag into.
TagId - Specifies the tag ID to be inserted into the list.
MoveToFront - If TRUE, place the tag at the front of the list. If FALSE, then
append the tag to the end of the list, unless it was already there, in which
case it is left where it was.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
--*/
{
DWORD Err;
HKEY hKey;
PDWORD GroupOrderList, p;
DWORD GroupOrderListSize, DataType, ExtraBytes, i, NumElements;
if((Err = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
pszGroupOrderListPath,
0,
KEY_ALL_ACCESS,
&hKey)) != ERROR_SUCCESS) {
return Err;
}
if((Err = QueryRegistryValue(hKey,
LoadOrderGroup,
(PVOID)(&GroupOrderList),
&DataType,
&GroupOrderListSize)) == NO_ERROR) {
//
// Validate the list, and fix it if it's broken.
//
if(GroupOrderListSize < sizeof(DWORD)) {
if(p = MyRealloc(GroupOrderList, sizeof(DWORD))) {
GroupOrderList = p;
*GroupOrderList = 0;
GroupOrderListSize = sizeof(DWORD);
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
} else {
if(ExtraBytes = GroupOrderListSize % sizeof(DWORD)) {
if(p = MyRealloc(GroupOrderList, GroupOrderListSize + (sizeof(DWORD) - ExtraBytes))) {
GroupOrderList = p;
ZeroMemory((PBYTE)GroupOrderList + GroupOrderListSize, ExtraBytes);
GroupOrderListSize += ExtraBytes;
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
}
}
MYASSERT(!(GroupOrderListSize % sizeof(DWORD)));
//
// We now have a list that's at least in the correct format. Now validate the list count,
// and adjust if necessary.
//
NumElements = (GroupOrderListSize / sizeof(DWORD)) - 1;
if(*GroupOrderList != NumElements) {
if(*GroupOrderList > NumElements) {
*GroupOrderList = NumElements;
} else {
NumElements = *GroupOrderList;
GroupOrderListSize = (NumElements + 1) * sizeof(DWORD);
}
}
} else {
//
// If we ran out of memory, then bail, otherwise, just assume
// there wasn't a list to retrieve.
//
if(Err == ERROR_NOT_ENOUGH_MEMORY) {
goto clean0;
} else {
//
// Allocate a list containing no tags.
//
if(GroupOrderList = MyMalloc(sizeof(DWORD))) {
*GroupOrderList = 0;
GroupOrderListSize = sizeof(DWORD);
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
}
}
//
// Now we have a valid group order list to manipulate.
//
for(i = 0; i < *GroupOrderList; i++) {
if(GroupOrderList[i + 1] == TagId) {
//
// Tag already exists in the list.
//
break;
}
}
if(i == *GroupOrderList) {
//
// Then we didn't find the tag in the list. Add it either to the front, or
// the end, depending on the 'MoveToFront' flag.
//
if(p = MyRealloc(GroupOrderList, GroupOrderListSize + sizeof(DWORD))) {
GroupOrderList = p;
GroupOrderListSize += sizeof(DWORD);
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
if(MoveToFront) {
MoveMemory(&(GroupOrderList[2]), &(GroupOrderList[1]), *GroupOrderList);
GroupOrderList[1] = TagId;
} else {
GroupOrderList[*GroupOrderList + 1] = TagId;
}
(*GroupOrderList)++;
} else if(MoveToFront && i) {
MoveMemory(&(GroupOrderList[2]), &(GroupOrderList[1]), i * sizeof(DWORD));
GroupOrderList[1] = TagId;
}
//
// Now write the value back to the registry.
//
Err = RegSetValueEx(hKey,
LoadOrderGroup,
0,
REG_BINARY,
(PBYTE)GroupOrderList,
GroupOrderListSize
);
clean1:
MyFree(GroupOrderList);
clean0:
RegCloseKey(hKey);
return Err;
}
DWORD
pSetupRunLegacyInf(
IN DEVINST DevInst,
IN HWND OwnerWindow,
IN PCTSTR InfFileName,
IN PCTSTR InfOptionName,
IN PCTSTR InfLanguageName,
IN HINF InfHandle
)
/*++
Routine Description:
This routine build a command line, loads the legacy setup dll, and starts
the INF interpreter.
Arguments:
DevInst - supplies the CM device instance handle for the device being installed.
OwnerWindow - supplies the parent window for any UI that this INF generates.
InfFileName - supplies the name of the INF file to be interpreted.
InfOptionName - supplies the name of the INF section to execute.
InfLanguageName - supplies the name of the language the INF should use for any UI
(e.g., prompting, etc.)
InfHandle - supplies a handle to the legacy INF being installed from.
Return Value:
If successful, the return value is NO_ERROR, otherwise, it is a Win32 error code.
Note that this says nothing about what the INF actually did, merely that we were
actually able to launch the INF.
--*/
{
HINSTANCE LegacySetupDllModule;
LEGACY_INF_INTERP_PROC pfnLegacyInfInterpret;
LEGACY_INF_GETSVCLIST_PROC pfnLegacyInfGetModifiedSvcList;
DWORD Err;
TCHAR TempBuffer[MAX_PATH];
PTSTR CmdLine, LegacySourcePath, AssociatedService;
UINT CmdLineSize, BufSize;
PSTR AnsiLine, AnsiSourcePath;
PCSTR AnsiInfFileName;
BOOL b;
INT InterpResult;
CONFIGRET cr;
#ifdef UNICODE
CHAR AnsiBuffer[2*MAX_PATH]; // allow room for full Unicode->DBCS expansion,
// just to be on the safe side
#endif
if(!(LegacySetupDllModule = LoadLibrary(TEXT("SETUPDLL")))) {
return GetLastError();
}
if(!(pfnLegacyInfInterpret =
(LEGACY_INF_INTERP_PROC)GetProcAddress(LegacySetupDllModule,
"LegacyInfInterpret"))) {
Err = GetLastError();
goto clean0;
}
if(!(pfnLegacyInfGetModifiedSvcList =
(LEGACY_INF_GETSVCLIST_PROC)GetProcAddress(LegacySetupDllModule,
"LegacyInfGetModifiedSvcList"))) {
Err = GetLastError();
goto clean0;
}
#ifdef UNICODE
//
// Convert the Unicode INF filename to ANSI
//
WideCharToMultiByte(CP_ACP,
0,
InfFileName,
-1,
AnsiBuffer,
sizeof(AnsiBuffer),
NULL,
NULL
);
AnsiInfFileName = AnsiBuffer;
#else // else not UNICODE
//
// Filename is already ANSI--no conversion necessary.
//
AnsiInfFileName = InfFileName;
#endif // else not UNICODE
if(!(LegacySourcePath = pSetupGetDefaultSourcePath(InfHandle))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean0;
}
//
// Build the command line to be passed to the legacy INF interpreter.
//
CmdLineSize = 1;
BufSize = 1024;
if(CmdLine = MyMalloc(BufSize * sizeof(TCHAR))) {
*CmdLine = TEXT('\0');
} else {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("STF_WINDOWSPATH"),
WindowsDirectory,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
lstrcpyn(TempBuffer, WindowsDirectory, 3);
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("STF_NTDRIVE"),
TempBuffer,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("STF_NTPATH"),
SystemDirectory,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("STF_WINDOWSSYSPATH"),
SystemDirectory,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("LEGACY_DODEVINSTALL"),
TEXT("YES"),
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("LEGACY_DI_LANG"),
InfLanguageName,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("LEGACY_DI_OPTION"),
InfOptionName,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
if(!(CmdLine = pSetupCmdLineAppendString(CmdLine,
TEXT("LEGACY_DI_SRCDIR"),
LegacySourcePath,
&CmdLineSize,
&BufSize))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
#ifdef UNICODE
//
// Allocate the correct amount of space for the ANSI version of the
// command line. Leave room for DBCS chars if there are any.
//
if(!(AnsiLine = MyMalloc(CmdLineSize * 2 * sizeof(CHAR)))) {
MyFree(CmdLine);
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean1;
}
//
// Convert the command line from UNICODE to ANSI
//
WideCharToMultiByte(CP_ACP,
0,
CmdLine,
CmdLineSize,
AnsiLine,
2 * CmdLineSize * sizeof(CHAR),
NULL,
NULL
);
MyFree(CmdLine);
//
// Convert the Unicode source path to ANSI.
//
if(!(AnsiSourcePath = UnicodeToAnsi(LegacySourcePath))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean2;
}
MyFree(LegacySourcePath);
//
// Assign the new buffer back to LegacySourcePath, since that is the pointer that
// we will be freeing later.
//
LegacySourcePath = (PTSTR)AnsiSourcePath;
#else // else not UNICODE
//
// Since everything's already ANSI, no memory allocation/conversion is necessary.
//
AnsiLine = CmdLine;
AnsiSourcePath = LegacySourcePath;
#endif // else not UNICODE
//
// OK, now we're ready to call the old setup command line parser. (Do this within
// a try/except, in case setupdll falls over.)
//
try {
Err = pfnLegacyInfInterpret(OwnerWindow,
AnsiInfFileName,
"InstallOption",
AnsiLine,
(PSTR)TempBuffer,
sizeof(TempBuffer),
&InterpResult,
AnsiSourcePath)
? NO_ERROR
: ERROR_NOT_ENOUGH_MEMORY;
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = (GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ? ERROR_INVALID_DATA : ERROR_READ_FAULT;
}
if(Err != NO_ERROR) {
goto clean2;
}
//
// The interpreter successfully ran the INF. Now we need to find out what the result was.
//
// NOTE: It seems that most legacy INFs aren't very good about distinguishing between
// SETUP_ERROR_USERCANCEL and SETUP_ERROR_GENERAL. Therefore, we can't reliably set
// our error to indicate that the user cancelled (as opposed to some other INF problem).
// Since almost all of the failures we'll encounter are because the user cancelled, we
// simply lump both these errors into the same category, and return ERROR_CANCELLED in
// both cases.
//
if(InterpResult != SETUP_ERROR_SUCCESS) {
Err = ERROR_CANCELLED;
goto clean2;
}
//
// We successfully installed the legacy INF option. Now we need to find out what services
// got installed as a result of this, so that we can associate the device instance we're
// installing with its controlling service.
//
try {
Err = pfnLegacyInfGetModifiedSvcList(NULL, 0, &BufSize);
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_NO_MORE_ITEMS;
}
//
// Since we didn't pass this routine a buffer, then it should always fail.
//
MYASSERT(Err != NO_ERROR);
if(Err == ERROR_NO_MORE_ITEMS) {
//
// No service modifications were performed by this INF. This may be OK, since there may
// be a default service for this device's class. Consider our legacy INF installation a
// success (at least, for now).
//
Err = NO_ERROR;
goto clean2;
}
MYASSERT(Err == ERROR_INSUFFICIENT_BUFFER); // the only other reason we should be failing.
//
// Allocate a buffer of the size necessary, and call this routine again to retrieve the service
// list. (Re-use AnsiLine to store the ANSI multi-sz list.)
//
MyFree(AnsiLine);
if(!(AnsiLine = MyMalloc(BufSize * sizeof(CHAR)))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean2;
}
try {
Err = pfnLegacyInfGetModifiedSvcList(AnsiLine, BufSize, &BufSize);
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_NO_MORE_ITEMS;
}
if(Err != NO_ERROR) {
//
// The only time this should ever happen is if we hit an exception in setupdll.
// We'll ignore the error.
//
Err = NO_ERROR;
goto clean2;
}
#ifdef UNICODE
//
// Convert this multi-sz list to Unicode.
//
if(!(CmdLine = MyMalloc(BufSize * sizeof(WCHAR)))) {
Err = ERROR_NOT_ENOUGH_MEMORY;
goto clean2;
}
if(!MultiByteToWideChar(CP_ACP,
MB_PRECOMPOSED,
AnsiLine,
BufSize * sizeof(CHAR),
CmdLine,
BufSize)) {
Err = GetLastError();
MyFree(CmdLine);
goto clean2;
}
//
// Free the ANSI buffer, and set it equal to the Unicode one, so that we can free the same
// memory in both the ANSI and Unicode cases.
//
MyFree(AnsiLine);
AnsiLine = (PSTR)CmdLine;
#else // else not UNICODE
//
// We're not Unicode, so the ANSI list we have is just fine.
//
CmdLine = AnsiLine;
#endif // else not UNICODE
//
// OK, we now have the proper TCHAR-ized form of the multi-sz list. Process the services
// listed therein, and return the one that should be associated with this device instance.
//
if(AssociatedService = DoServiceModsForLegacyInf(CmdLine)) {
//
// Make the association between the service and the device.
//
if((cr = CM_Set_DevInst_Registry_Property(DevInst,
CM_DRP_SERVICE,
AssociatedService,
(lstrlen(AssociatedService) + 1) * sizeof(TCHAR),
0)) != CR_SUCCESS) {
if(cr == CR_INVALID_DEVINST) {
Err = ERROR_NO_SUCH_DEVINST;
} else {
Err = ERROR_INVALID_DATA;
}
}
}
clean2:
if(AnsiLine) {
MyFree(AnsiLine);
}
clean1:
MyFree(LegacySourcePath);
clean0:
//
// Clean up
//
while(GetModuleFileName(LegacySetupDllModule, TempBuffer, SIZECHARS(TempBuffer))) {
FreeLibrary(LegacySetupDllModule);
}
return Err;
}
PTSTR
pSetupCmdLineAppendString(
IN PTSTR CmdLine,
IN PCTSTR Key,
IN PCTSTR Value, OPTIONAL
IN OUT PUINT StrLen,
IN OUT PUINT BufSize
)
/*++
Routine Description:
Forms a new (multi-sz) command line by appending a list of arguments to
the current command line. For example:
CmdLine = SpSetupCmdLineAppendString(
CmdLine,
"STF_PRODUCT",
"NTWKSTA"
);
would append "STF_PRODUCT\0NTWKSTA\0\0" to CmdLine.
Arguments:
CmdLine - Original CmdLine, to be appended to. THIS BUFFER MUST CONTAIN
AT LEAST A SINGLE NULL CHARACTER!
Key - Key identifier
Value - Value of Key
StrLen - How long the current string in -- save on strlens
BufSize - Size of Current Buffer
Returns:
Pointer to the new string, or NULL if out-of-memory (in that case, the
original CmdLine buffer is freed).
--*/
{
PTSTR Ptr;
UINT NewLen;
//
// Handle special cases so we don't end up with empty strings.
//
if(!Value || !(*Value)) {
Value = TEXT("\"\"");
}
//
// "\0" -> 1 chars
// "\0\0" -> 2 char
// but we have to back up 1 character...
//
NewLen = (*StrLen + 2 + lstrlen(Key) + lstrlen(Value));
//
// Allocate more space if necessary.
//
if(NewLen >= *BufSize) {
//
// Grow the current buffer
//
*BufSize += 1024;
if(Ptr = MyRealloc(CmdLine, (*BufSize) * sizeof(TCHAR))) {
CmdLine = Ptr;
} else {
//
// Free the memory here so the caller doesn't have to worry about it.
//
MyFree(CmdLine);
return NULL;
}
}
Ptr = &(CmdLine[*StrLen-1]);
lstrcpy(Ptr, Key);
Ptr = &(CmdLine[*StrLen+lstrlen(Key)]);
lstrcpy(Ptr, Value);
CmdLine[NewLen-1] = TEXT('\0');
//
// Update the length of the buffer that we are using
//
*StrLen = NewLen;
return CmdLine;
}
PTSTR
DoServiceModsForLegacyInf(
IN PTSTR ServiceList
)
/*++
Routine Description:
This routine processes the multi-sz list of service names it is given as
input, ensuring that each one is tagged appropriately. It also keeps track
of which one is the first to load, based on its start type and membership in
one of the load order groups listed under HKLM\System\CCS\Control\ServiceGroupOrder.
The one that is first to load is returned.
Arguments:
ServiceList - supplies the address of a character buffer containing a
multi-sz list of service names to be processed.
Returns:
Pointer to the service within the list that should be associated with the
device instance, or NULL if we don't find a suitable service.
--*/
{
PTSTR CurServiceName, ServiceGroupOrderList, CurGroupName;
DWORD ServiceGroupIndex;
TCHAR NullChar;
DWORD RegDataType, RegDataSize;
PTSTR AssocServiceName = NULL;
SC_HANDLE SCMHandle, ServiceHandle;
SC_LOCK SCLock;
LPQUERY_SERVICE_CONFIG ServiceConfig;
DWORD NewTag;
DWORD AssocStartType = SERVICE_DISABLED;
DWORD AssocGroupIndex = DWORD_MAX;
HKEY hKey;
if(!(SCMHandle = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS))) {
return NULL;
}
//
// Retrieve the 'List' multi-sz value entry under HKLM\System\CCS\Control\ServiceGroupOrder.
//
if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, pszServiceGroupOrderPath, 0, KEY_READ, &hKey) == ERROR_SUCCESS) {
if(QueryRegistryValue(hKey, TEXT("List"), &ServiceGroupOrderList, &RegDataType, &RegDataSize) != NO_ERROR) {
//
// Couldn't retrieve the list--set up an empty list.
//
NullChar = TEXT('\0');
ServiceGroupOrderList = &NullChar;
}
RegCloseKey(hKey);
} else {
//
// Couldn't open the ServiceGroupOrder key--set up an empty list.
//
NullChar = TEXT('\0');
ServiceGroupOrderList = &NullChar;
}
for(CurServiceName = ServiceList;
*CurServiceName;
CurServiceName += (lstrlen(CurServiceName) + 1)) {
//
// Open this service.
//
if(!(ServiceHandle = OpenService(SCMHandle, CurServiceName, SERVICE_ALL_ACCESS))) {
//
// We couldn't access the service--possibly because it doesn't exist anymore.
// Continue on with the next service.
//
continue;
}
//
// Now retrieve the configuration for this service.
//
if(RetrieveServiceConfig(ServiceHandle, &ServiceConfig) != NO_ERROR) {
//
// There's not a lot we can do without knowing the service's configuration,
// either. Again, we'll just skip this service and continue with the next one.
//
goto clean0;
}
//
// If this service is marked as disabled, then we don't care about it.
//
if(ServiceConfig->dwStartType == SERVICE_DISABLED) {
goto clean1;
}
//
// If this service has a load order group, and is a kernel or filesystem
// driver, then make sure that it has a tag.
//
if(ServiceConfig->lpLoadOrderGroup && *(ServiceConfig->lpLoadOrderGroup) &&
(ServiceConfig->dwServiceType & (SERVICE_KERNEL_DRIVER | SERVICE_FILE_SYSTEM_DRIVER))) {
//
// This service needs a tag--does it have one???
//
if(!(NewTag = ServiceConfig->dwTagId)) {
//
// Attempt to lock the service database before generating a tag. We'll go ahead
// and make the change, even if this fails.
//
SCLock = LockServiceDatabase(SCMHandle);
if(!ChangeServiceConfig(ServiceHandle,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
SERVICE_NO_CHANGE,
NULL,
ServiceConfig->lpLoadOrderGroup, // have to specify this to generate new tag.
&NewTag,
NULL,
NULL,
NULL,
NULL)) {
NewTag = 0;
}
if(SCLock) {
UnlockServiceDatabase(SCLock);
}
}
//
// Make sure that the tag exists in the service's corresponding GroupOrderList entry.
//
if(NewTag) {
AddTagToGroupOrderListEntry(ServiceConfig->lpLoadOrderGroup, NewTag, FALSE);
}
}
//
// Determine the index that this service's load group occupies in the multi-sz ServiceGroupOrder
// list we retrieved above.
//
if(ServiceConfig->lpLoadOrderGroup) {
for(CurGroupName = ServiceGroupOrderList, ServiceGroupIndex = 0;
*CurGroupName;
CurGroupName += (lstrlen(CurGroupName) + 1), ServiceGroupIndex++) {
if(!lstrcmpi(CurGroupName, ServiceConfig->lpLoadOrderGroup)) {
break;
}
}
if(!(*CurGroupName)) {
//
// Then we didn't find this group in our list--give it the maximum index value.
//
ServiceGroupIndex = DWORD_MAX;
}
} else {
//
// This service isn't a member of a group--give it the maximum index value.
//
ServiceGroupIndex = DWORD_MAX;
}
//
// Finally, determine if this service loads before any services we've encountered so far,
// and if so, then make it our new choice for associated service.
//
if(ServiceConfig->dwStartType < AssocStartType) {
//
// Then this service loads in an earlier load phase, so we're guaranteed it loads before
// any drivers we've previously seen.
//
AssocServiceName = CurServiceName;
AssocStartType = ServiceConfig->dwStartType;
AssocGroupIndex = ServiceGroupIndex;
} else if(ServiceConfig->dwStartType == AssocStartType) {
//
// This service starts in the same load phase as the current selection, so we need to
// compare the group load order indices to see if this one comes earlier.
//
if(ServiceGroupIndex < AssocGroupIndex) {
AssocServiceName = CurServiceName;
AssocGroupIndex = ServiceGroupIndex;
}
}
clean1:
MyFree(ServiceConfig);
clean0:
CloseServiceHandle(ServiceHandle);
}
if(ServiceGroupOrderList != &NullChar) {
MyFree(ServiceGroupOrderList);
}
CloseServiceHandle(SCMHandle);
return AssocServiceName;
}
#ifdef UNICODE
//
// ANSI version
//
BOOL
WINAPI
SetupDiGetActualSectionToInstallA(
IN HINF InfHandle,
IN PCSTR InfSectionName,
OUT PSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PSTR *Extension OPTIONAL
)
{
PWSTR infsectionname;
DWORD rc;
BOOL b;
PWSTR extension;
UINT CharOffset,i;
PSTR p;
DWORD requiredsize;
WCHAR newsection[MAX_SECT_NAME_LEN];
PSTR ansi;
rc = CaptureAndConvertAnsiArg(InfSectionName,&infsectionname);
if(rc != NO_ERROR) {
SetLastError(rc);
return(FALSE);
}
b = SetupDiGetActualSectionToInstallW(
InfHandle,
infsectionname,
newsection,
MAX_SECT_NAME_LEN,
&requiredsize,
&extension
);
rc = GetLastError();
if(b) {
if(ansi = UnicodeToAnsi(newsection)) {
if(Extension) {
if(extension) {
//
// We need to figure out where the extension is
// in the converted string. To be DBCS safe we will
// count characters forward to find it.
//
CharOffset = extension - newsection;
p = ansi;
for(i=0; i<CharOffset; i++) {
p = CharNextA(p);
}
} else {
p = NULL;
}
try {
*Extension = p;
} except(EXCEPTION_EXECUTE_HANDLER) {
b = FALSE;
rc = ERROR_INVALID_PARAMETER;
}
}
if(b) {
requiredsize = lstrlenA(ansi)+1;
if(RequiredSize) {
try {
*RequiredSize = requiredsize;
} except(EXCEPTION_EXECUTE_HANDLER) {
rc = ERROR_INVALID_PARAMETER;
b = FALSE;
}
}
if(b && InfSectionWithExt) {
if(requiredsize <= InfSectionWithExtSize) {
if(!lstrcpyA(InfSectionWithExt,ansi)) {
//
// lstrcpy faulted, so InfSectionWithExt must be bad
//
rc = ERROR_INVALID_PARAMETER;
b = FALSE;
}
} else {
rc = ERROR_INSUFFICIENT_BUFFER;
b = FALSE;
}
}
}
MyFree(ansi);
} else {
rc = ERROR_NOT_ENOUGH_MEMORY;
b = FALSE;
}
}
MyFree(infsectionname);
SetLastError(rc);
return(b);
}
#else
//
// Unicode stub
//
BOOL
WINAPI
SetupDiGetActualSectionToInstallW(
IN HINF InfHandle,
IN PCWSTR InfSectionName,
OUT PWSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PWSTR *Extension OPTIONAL
)
{
UNREFERENCED_PARAMETER(InfHandle);
UNREFERENCED_PARAMETER(InfSectionName);
UNREFERENCED_PARAMETER(InfSectionWithExt);
UNREFERENCED_PARAMETER(InfSectionWithExtSize);
UNREFERENCED_PARAMETER(RequiredSize);
UNREFERENCED_PARAMETER(Extension);
SetLastError(ERROR_CALL_NOT_IMPLEMENTED);
return(FALSE);
}
#endif
BOOL
WINAPI
SetupDiGetActualSectionToInstall(
IN HINF InfHandle,
IN PCTSTR InfSectionName,
OUT PTSTR InfSectionWithExt, OPTIONAL
IN DWORD InfSectionWithExtSize,
OUT PDWORD RequiredSize, OPTIONAL
OUT PTSTR *Extension OPTIONAL
)
/*++
Routine Description:
This API finds the appropriate install section to be used when installing
a device from a Win95-style device INF. Refer to the documentation for
SetupDiInstallDevice for details on how this determination is made.
Arguments:
InfHandle - Supplies the handle of the INF to be installed from.
InfSectionName - Supplies the name of the install section, as specified by the
driver node being installed.
InfSectionWithExt - Optionally, supplies the address of a character buffer that
receives the actual install section name that should be used during installation.
If this parameter is NULL, then InfSectionWithExtSize must be zero. In that
case, the caller is only interested in retrieving the required buffer size,
so the API will return TRUE, and RequiredSize (if supplied), will be set to the
size, in characters, necessary to store the actual install section name.
InfSectionWithExtSize - Supplies the size, in characters, of the InfSectionWithExt
buffer.
RequiredSize - Optionally, supplies the address of a variable that receives the
size, in characters, required to store the actual install section name
(including terminating NULL).
Extension - Optionally, supplies the address of a variable that receives a pointer
to the extension (including '.'), or NULL if no extension is to be used. The
pointer points to the extension within the caller-supplied buffer. If the
InfSectionWithExt buffer is not supplied, then this variable will not be filled
in.
Returns:
If the function succeeds, the return value is TRUE.
If the function fails, the return value is FALSE. To get extended error information,
call GetLastError.
Remarks:
Presently, the only possible failures are ERROR_INVALID_PARAMETER (exception encountered
accessing caller-supplied pointers), and ERROR_INSUFFICIENT_BUFFER (if the caller-supplied
buffer isn't large enough). If we fall back to the baseline (i.e., non-decorated) section
name, then we simply return it, without verifying that the section actually exists.
--*/
{
TCHAR TempInfSectionName[MAX_SECT_NAME_LEN];
DWORD SectionNameLen = (DWORD)lstrlen(InfSectionName);
DWORD ExtBufferLen;
BOOL ExtFound = TRUE;
DWORD Err = NO_ERROR;
CopyMemory(TempInfSectionName, InfSectionName, SectionNameLen * sizeof(TCHAR));
if(OSVersionInfo.dwPlatformId == VER_PLATFORM_WIN32_NT) {
//
// We're running on NT, so first try the NT architecture-specific extension,
// then the generic NT extension.
//
CopyMemory(&(TempInfSectionName[SectionNameLen]),
pszNtPlatformSuffix,
sizeof(pszNtPlatformSuffix)
);
if(SetupGetLineCount(InfHandle, TempInfSectionName) != -1) {
goto clean0;
}
CopyMemory(&(TempInfSectionName[SectionNameLen]),
pszNtSuffix,
sizeof(pszNtSuffix)
);
if(SetupGetLineCount(InfHandle, TempInfSectionName) != -1) {
goto clean0;
}
} else {
//
// We're running on Windows 95, so try the Windows-specific extension
//
CopyMemory(&(TempInfSectionName[SectionNameLen]),
pszWinSuffix,
sizeof(pszWinSuffix)
);
if(SetupGetLineCount(InfHandle, TempInfSectionName) != -1) {
goto clean0;
}
}
//
// If we get to here, then we found no applicable extensions. We'll just use
// the install section specified.
//
TempInfSectionName[SectionNameLen] = TEXT('\0');
ExtFound = FALSE;
clean0:
//
// Now, determine whether the caller-supplied buffer is large enough to contain
// the section name.
//
ExtBufferLen = lstrlen(TempInfSectionName) + 1;
//
// Guard the rest of the routine in try/except, since we're dealing with caller-supplied
// memory.
//
try {
if(RequiredSize) {
*RequiredSize = ExtBufferLen;
}
if(InfSectionWithExt) {
if(ExtBufferLen > InfSectionWithExtSize) {
Err = ERROR_INSUFFICIENT_BUFFER;
} else {
CopyMemory(InfSectionWithExt, TempInfSectionName, ExtBufferLen * sizeof(TCHAR));
if(Extension) {
*Extension = ExtFound ? InfSectionWithExt + SectionNameLen : NULL;
}
}
}
} except(EXCEPTION_EXECUTE_HANDLER) {
Err = ERROR_INVALID_PARAMETER;
}
SetLastError(Err);
return (Err == NO_ERROR);
}
BOOL
InfIsFromOemLocation(
IN PCTSTR InfFileName
)
/*++
Routine Description:
This routine determines whether the specified INF came from one of the directories
in our INF search path list.
Arguments:
InfFileName - Supplies the fully-qualified path of the INF file.
Returns:
If the file is from an OEM location (i.e., _not_ in our INF search path list), then
the return value is TRUE. Otherwise, it is FALSE.
--*/
{
PCTSTR CharPos;
INT DirectoryPathLen, CurSearchPathLen;
//
// First, retrieve just the directory path part of the specified filename.
//
CharPos = MyGetFileTitle(InfFileName);
if((CharPos > InfFileName) && *(CharPos - 1) == TEXT('\\')) {
//
// Strip off the trailing backslash.
//
DirectoryPathLen = CharPos - InfFileName - 1;
} else {
DirectoryPathLen = CharPos - InfFileName;
}
//
// Now, see if this directory matches any of the ones in our search path list.
//
for(CharPos = InfSearchPaths; *CharPos; CharPos += (CurSearchPathLen + 1)) {
//
// If the current search path ends in a backslash, we want to strip it off.
//
CurSearchPathLen = lstrlen(CharPos);
if((DirectoryPathLen == CurSearchPathLen) &&
!_tcsnicmp(CharPos, InfFileName, CurSearchPathLen)) {
//
// We've found this directory in our list--we can return.
//
return FALSE;
}
}
//
// If we get to here, then we didn't find the directory in our search path list.
// Therefore, it's from an OEM location.
//
return TRUE;
}