mirror of https://github.com/lianthony/NT4.0
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.
3386 lines
103 KiB
3386 lines
103 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
diutil.c
|
|
|
|
Abstract:
|
|
|
|
Device Installer utility routines.
|
|
|
|
Author:
|
|
|
|
Lonny McMichael (lonnym) 10-May-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "setupntp.h"
|
|
#pragma hdrstop
|
|
#include <initguid.h>
|
|
|
|
//
|
|
// Define and initialize all device class GUIDs.
|
|
// (This must only be done once per module!)
|
|
//
|
|
#include <devguid.h>
|
|
|
|
//
|
|
// Define and initialize a global variable, GUID_NULL
|
|
// (from coguid.h)
|
|
//
|
|
DEFINE_GUID(GUID_NULL, 0L, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);
|
|
|
|
//
|
|
// Declare global string variables used throughout device
|
|
// installer routines.
|
|
//
|
|
// These strings are defined in regstr.h:
|
|
//
|
|
CONST TCHAR pszNoUseClass[] = REGSTR_VAL_NOUSECLASS,
|
|
pszNoInstallClass[] = REGSTR_VAL_NOINSTALLCLASS,
|
|
pszNoDisplayClass[] = REGSTR_VAL_NODISPLAYCLASS,
|
|
pszDeviceDesc[] = REGSTR_VAL_DEVDESC,
|
|
pszDevicePath[] = REGSTR_VAL_DEVICEPATH,
|
|
pszPathSetup[] = REGSTR_PATH_SETUP,
|
|
pszKeySetup[] = REGSTR_KEY_SETUP,
|
|
pszPathRunOnce[] = REGSTR_PATH_RUNONCE,
|
|
pszSourcePath[] = REGSTR_VAL_SRCPATH,
|
|
pszBootDir[] = REGSTR_VAL_BOOTDIR,
|
|
pszInsIcon[] = REGSTR_VAL_INSICON,
|
|
pszInstaller32[] = REGSTR_VAL_INSTALLER_32,
|
|
pszEnumPropPages32[] = REGSTR_VAL_ENUMPROPPAGES_32,
|
|
pszInfPath[] = REGSTR_VAL_INFPATH,
|
|
pszInfSection[] = REGSTR_VAL_INFSECTION,
|
|
pszDrvDesc[] = REGSTR_VAL_DRVDESC,
|
|
pszHardwareID[] = REGSTR_VAL_HARDWAREID,
|
|
pszCompatibleIDs[] = REGSTR_VAL_COMPATIBLEIDS,
|
|
pszDriver[] = REGSTR_VAL_DRIVER,
|
|
pszConfigFlags[] = REGSTR_VAL_CONFIGFLAGS,
|
|
pszMfg[] = REGSTR_VAL_MFG,
|
|
pszNtDevicePaths[] = REGSTR_VAL_NTDEVICEPATHS,
|
|
pszService[] = REGSTR_VAL_SERVICE,
|
|
pszConfiguration[] = REGSTR_VAL_CONFIGURATION,
|
|
pszConfigurationVector[] = REGSTR_VAL_CONFIGURATIONVECTOR,
|
|
pszProviderName[] = REGSTR_VAL_PROVIDER_NAME,
|
|
pszFriendlyName[] = REGSTR_VAL_FRIENDLYNAME,
|
|
pszServicesRegPath[] = REGSTR_PATH_SERVICES,
|
|
pszLegacyInfOption[] = REGSTR_VAL_LEGACYINFOPT,
|
|
pszInfSectionExt[] = REGSTR_VAL_INFSECTIONEXT;
|
|
|
|
|
|
//
|
|
// Other misc. global strings (defined in devinst.h):
|
|
//
|
|
CONST TCHAR pszInfWildcard[] = DISTR_INF_WILDCARD,
|
|
pszOemInfWildcard[] = DISTR_OEMINF_WILDCARD,
|
|
pszCiDefaultProc[] = DISTR_CI_DEFAULTPROC,
|
|
pszSpaceLparen[] = DISTR_SPACE_LPAREN,
|
|
pszRparen[] = DISTR_RPAREN,
|
|
pszUniqueSubKey[] = DISTR_UNIQUE_SUBKEY,
|
|
pszOemInfGenerate[] = DISTR_OEMINF_GENERATE,
|
|
pszOemInfDefaultPath[] = DISTR_OEMINF_DEFAULTPATH,
|
|
pszUnknownClassParens[] = DISTR_UNKNOWNCLASS_PARENS,
|
|
pszDefaultService[] = DISTR_DEFAULT_SERVICE,
|
|
pszGuidNull[] = DISTR_GUID_NULL,
|
|
pszEventLogSystem[] = DISTR_EVENTLOG_SYSTEM,
|
|
pszGroupOrderListPath[] = DISTR_GROUPORDERLIST_PATH,
|
|
pszServiceGroupOrderPath[] = DISTR_SERVICEGROUPORDER_PATH,
|
|
pszOptions[] = DISTR_OPTIONS,
|
|
pszOptionsText[] = DISTR_OPTIONSTEXT,
|
|
pszLanguagesSupported[] = DISTR_LANGUAGESSUPPORTED,
|
|
pszRunOnceExe[] = DISTR_RUNONCE_EXE,
|
|
pszGrpConv[] = DISTR_GRPCONV,
|
|
pszDefaultSystemPartition[] = DISTR_DEFAULT_SYSPART;
|
|
|
|
//
|
|
// Define flag bitmask indicating which flags are controlled internally by the
|
|
// device installer routines, and thus are read-only to clients.
|
|
//
|
|
#define DI_FLAGS_READONLY ( DI_DIDCOMPAT | DI_DIDCLASS | DI_MULTMFGS )
|
|
#define DI_FLAGSEX_READONLY ( DI_FLAGSEX_DIDINFOLIST | DI_FLAGSEX_DIDCOMPATINFO )
|
|
#define DNF_FLAGS_READONLY ( DNF_DUPDESC | DNF_OLDDRIVER | DNF_LEGACYINF )
|
|
|
|
//
|
|
// Define flag bitmask indicating which flags are illegal.
|
|
//
|
|
#define DI_FLAGS_ILLEGAL ( 0x00400000L ) // setupx DI_NOSYNCPROCESSING flag
|
|
#define DI_FLAGSEX_ILLEGAL ( 0xFFFF0408L ) // all flags not currently defined
|
|
#define DNF_FLAGS_ILLEGAL ( 0xFFFFFFE0L ) // ""
|
|
|
|
#define NDW_INSTALLFLAG_ILLEGAL (~( NDW_INSTALLFLAG_DIDFACTDEFS \
|
|
| NDW_INSTALLFLAG_HARDWAREALLREADYIN \
|
|
| NDW_INSTALLFLAG_NEEDRESTART \
|
|
| NDW_INSTALLFLAG_NEEDREBOOT \
|
|
| NDW_INSTALLFLAG_NEEDSHUTDOWN \
|
|
| NDW_INSTALLFLAG_EXPRESSINTRO \
|
|
| NDW_INSTALLFLAG_SKIPISDEVINSTALLED \
|
|
| NDW_INSTALLFLAG_NODETECTEDDEVS \
|
|
| NDW_INSTALLFLAG_INSTALLSPECIFIC \
|
|
| NDW_INSTALLFLAG_SKIPCLASSLIST \
|
|
| NDW_INSTALLFLAG_CI_PICKED_OEM \
|
|
| NDW_INSTALLFLAG_PCMCIAMODE \
|
|
| NDW_INSTALLFLAG_PCMCIADEVICE \
|
|
| NDW_INSTALLFLAG_USERCANCEL \
|
|
| NDW_INSTALLFLAG_KNOWNCLASS ))
|
|
|
|
#define DYNAWIZ_FLAG_ILLEGAL (~( DYNAWIZ_FLAG_PAGESADDED \
|
|
| DYNAWIZ_FLAG_INSTALLDET_NEXT \
|
|
| DYNAWIZ_FLAG_INSTALLDET_PREV \
|
|
| DYNAWIZ_FLAG_ANALYZE_HANDLECONFLICT ))
|
|
|
|
//
|
|
// Declare data used in GUID->string conversion (from ole32\common\ccompapi.cxx).
|
|
//
|
|
static const BYTE GuidMap[] = { 3, 2, 1, 0, '-', 5, 4, '-', 7, 6, '-',
|
|
8, 9, '-', 10, 11, 12, 13, 14, 15 };
|
|
|
|
static const TCHAR szDigits[] = TEXT("0123456789ABCDEF");
|
|
|
|
|
|
PDEVICE_INFO_SET
|
|
AllocateDeviceInfoSet(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates a device information set structure, zeroes it,
|
|
and initializes the synchronization lock for it.
|
|
|
|
Arguments:
|
|
|
|
none.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is a pointer to the new
|
|
device information set.
|
|
|
|
If the function fails, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET p;
|
|
|
|
if(p = MyMalloc(sizeof(DEVICE_INFO_SET))) {
|
|
|
|
ZeroMemory(p, sizeof(DEVICE_INFO_SET));
|
|
|
|
p->InstallParamBlock.DriverPath = -1;
|
|
|
|
if(p->StringTable = pStringTableInitialize()) {
|
|
|
|
if(InitializeSynchronizedAccess(&(p->Lock))) {
|
|
return p;
|
|
}
|
|
pStringTableDestroy(p->StringTable);
|
|
}
|
|
MyFree(p);
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
|
|
|
|
VOID
|
|
DestroyDeviceInfoElement(
|
|
IN HDEVINFO hDevInfo,
|
|
IN PDEVICE_INFO_SET pDeviceInfoSet,
|
|
IN PDEVINFO_ELEM DeviceInfoElement
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys the specified device information element, freeing
|
|
all resources associated with it.
|
|
ASSUMES THAT THE CALLING ROUTINE HAS ALREADY ACQUIRED THE LOCK!
|
|
|
|
Arguments:
|
|
|
|
hDevInfo - Supplies a handle to the device information set whose internal
|
|
representation is given by pDeviceInfoSet. This opaque handle is
|
|
actually the same pointer as pDeviceInfoSet, but we want to keep this
|
|
distinction clean, so that in the future we can change our implementation
|
|
(e.g., hDevInfo might represent an offset in an array of DEVICE_INFO_SET
|
|
elements).
|
|
|
|
pDeviceInfoSet - Supplies a pointer to the device information set of which
|
|
the devinfo element is a member. This set contains the class driver list
|
|
object list that must be used in destroying the class driver list.
|
|
|
|
DeviceInfoElement - Supplies a pointer to the device information element
|
|
to be destroyed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
MYASSERT(hDevInfo && (hDevInfo != INVALID_HANDLE_VALUE));
|
|
|
|
//
|
|
// Free resources contained in the install parameters block. Do this
|
|
// before anything else, because we'll be calling the class installer
|
|
// with DIF_DESTROYPRIVATEDATA, and we want everything to be in a
|
|
// consistent state when we do (plus, it may need to destroy private
|
|
// data it's stored with individual driver nodes).
|
|
//
|
|
DestroyInstallParamBlock(hDevInfo,
|
|
pDeviceInfoSet,
|
|
DeviceInfoElement,
|
|
&(DeviceInfoElement->InstallParamBlock)
|
|
);
|
|
|
|
//
|
|
// Dereference the class driver list.
|
|
//
|
|
DereferenceClassDriverList(pDeviceInfoSet, DeviceInfoElement->ClassDriverHead);
|
|
|
|
//
|
|
// Destroy compatible driver list.
|
|
//
|
|
DestroyDriverNodes(DeviceInfoElement->CompatDriverHead);
|
|
|
|
//
|
|
// If this is a non-registered device instance, then delete any registry
|
|
// keys the caller may have created during the lifetime of this element.
|
|
//
|
|
if(DeviceInfoElement->DevInst && !(DeviceInfoElement->DiElemFlags & DIE_IS_REGISTERED)) {
|
|
|
|
pSetupDeleteDevRegKeys(DeviceInfoElement->DevInst,
|
|
DICS_FLAG_GLOBAL | DICS_FLAG_CONFIGSPECIFIC,
|
|
(DWORD)-1,
|
|
DIREG_BOTH,
|
|
TRUE
|
|
);
|
|
|
|
CM_Uninstall_DevInst(DeviceInfoElement->DevInst, 0);
|
|
}
|
|
|
|
MyFree(DeviceInfoElement);
|
|
}
|
|
|
|
|
|
DWORD
|
|
DestroyDeviceInfoSet(
|
|
IN HDEVINFO hDevInfo, OPTIONAL
|
|
IN PDEVICE_INFO_SET pDeviceInfoSet
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees a device information set, and all resources
|
|
used on its behalf.
|
|
|
|
Arguments:
|
|
|
|
hDevInfo - Optionally, supplies a handle to the device information set
|
|
whose internal representation is given by pDeviceInfoSet. This
|
|
opaque handle is actually the same pointer as pDeviceInfoSet, but
|
|
we want to keep this distinction clean, so that in the future we
|
|
can change our implementation (e.g., hDevInfo might represent an
|
|
offset in an array of DEVICE_INFO_SET elements).
|
|
|
|
This parameter will only be NULL if we're cleaning up half-way
|
|
through the creation of a device information set.
|
|
|
|
pDeviceInfoSet - supplies a pointer to the device information set
|
|
to be freed.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return code is NO_ERROR, otherwise, it is an
|
|
ERROR_* code.
|
|
|
|
--*/
|
|
{
|
|
PDEVINFO_ELEM NextElem;
|
|
PDRIVER_NODE DriverNode, NextNode;
|
|
PMODULE_HANDLE_LIST_NODE NextModuleHandleNode;
|
|
|
|
//
|
|
// We have to make sure that the wizard refcount is zero, and that
|
|
// we haven't acquired the lock more than once (i.e., we're nested
|
|
// more than one level deep in Di calls.
|
|
//
|
|
if(pDeviceInfoSet->WizPageList ||
|
|
(pDeviceInfoSet->LockRefCount > 1)) {
|
|
|
|
return ERROR_DEVINFO_LIST_LOCKED;
|
|
}
|
|
|
|
//
|
|
// Destroy all the device information elements in this set. Make sure
|
|
// that we maintain consistency while removing devinfo elements, because
|
|
// we may be calling the class installer. This means that the device
|
|
// installer APIs still have to work, even while we're tearing down the
|
|
// list.
|
|
//
|
|
while(pDeviceInfoSet->DeviceInfoHead) {
|
|
//
|
|
// We'd better not have any device info elements locked by wizard
|
|
// pages, since our wizard refcount is zero!
|
|
//
|
|
MYASSERT(!(pDeviceInfoSet->DeviceInfoHead->DiElemFlags & DIE_IS_LOCKED));
|
|
|
|
NextElem = pDeviceInfoSet->DeviceInfoHead->Next;
|
|
DestroyDeviceInfoElement(hDevInfo, pDeviceInfoSet, pDeviceInfoSet->DeviceInfoHead);
|
|
|
|
MYASSERT(pDeviceInfoSet->DeviceInfoCount > 0);
|
|
pDeviceInfoSet->DeviceInfoCount--;
|
|
|
|
//
|
|
// If this element was the currently selected device for this
|
|
// set, then reset the device selection.
|
|
//
|
|
if(pDeviceInfoSet->SelectedDevInfoElem == pDeviceInfoSet->DeviceInfoHead) {
|
|
pDeviceInfoSet->SelectedDevInfoElem = NULL;
|
|
}
|
|
|
|
pDeviceInfoSet->DeviceInfoHead = NextElem;
|
|
}
|
|
|
|
MYASSERT(pDeviceInfoSet->DeviceInfoCount == 0);
|
|
pDeviceInfoSet->DeviceInfoTail = NULL;
|
|
|
|
//
|
|
// Free resources contained in the install parameters block. Do this
|
|
// before anything else, because we'll be calling the class installer
|
|
// with DIF_DESTROYPRIVATEDATA, and we want everything to be in a
|
|
// consistent state when we do (plus, it may need to destroy private
|
|
// data it's stored with individual driver nodes).
|
|
//
|
|
DestroyInstallParamBlock(hDevInfo,
|
|
pDeviceInfoSet,
|
|
NULL,
|
|
&(pDeviceInfoSet->InstallParamBlock)
|
|
);
|
|
|
|
//
|
|
// Destroy class driver list.
|
|
//
|
|
if(pDeviceInfoSet->ClassDriverHead) {
|
|
//
|
|
// We've already destroyed all device information elements, so there should be
|
|
// exactly one driver list object remaining--the one referenced by the global
|
|
// class driver list. Also, it's refcount should be 1.
|
|
//
|
|
MYASSERT(
|
|
(pDeviceInfoSet->ClassDrvListObjectList) &&
|
|
(!pDeviceInfoSet->ClassDrvListObjectList->Next) &&
|
|
(pDeviceInfoSet->ClassDrvListObjectList->RefCount == 1) &&
|
|
(pDeviceInfoSet->ClassDrvListObjectList->DriverListHead == pDeviceInfoSet->ClassDriverHead)
|
|
);
|
|
|
|
MyFree(pDeviceInfoSet->ClassDrvListObjectList);
|
|
DestroyDriverNodes(pDeviceInfoSet->ClassDriverHead);
|
|
}
|
|
|
|
//
|
|
// Destroy the associated string table.
|
|
//
|
|
pStringTableDestroy(pDeviceInfoSet->StringTable);
|
|
|
|
//
|
|
// Destroy the lock (we have to do this after we've made all necessary calls
|
|
// to the class installer, because after the lock is freed, the HDEVINFO set
|
|
// is inaccessible).
|
|
//
|
|
DestroySynchronizedAccess(&(pDeviceInfoSet->Lock));
|
|
|
|
//
|
|
// If there are any module handles left to be freed, do that now.
|
|
//
|
|
for(; pDeviceInfoSet->ModulesToFree; pDeviceInfoSet->ModulesToFree = NextModuleHandleNode) {
|
|
NextModuleHandleNode = pDeviceInfoSet->ModulesToFree->Next;
|
|
FreeLibrary(pDeviceInfoSet->ModulesToFree->hinstClassInstaller);
|
|
MyFree(pDeviceInfoSet->ModulesToFree);
|
|
}
|
|
|
|
//
|
|
// Now, destroy the container itself.
|
|
//
|
|
MyFree(pDeviceInfoSet);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyInstallParamBlock(
|
|
IN HDEVINFO hDevInfo, OPTIONAL
|
|
IN PDEVICE_INFO_SET pDeviceInfoSet,
|
|
IN PDEVINFO_ELEM DevInfoElem, OPTIONAL
|
|
IN PDEVINSTALL_PARAM_BLOCK InstallParamBlock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees any resources contained in the specified install
|
|
parameter block. THE BLOCK ITSELF IS NOT FREED!
|
|
|
|
Arguments:
|
|
|
|
hDevInfo - Optionally, supplies a handle to the device information set
|
|
containing the element whose parameter block is to be destroyed.
|
|
|
|
If this parameter is not supplied, then we're cleaning up after
|
|
failing part-way through a SetupDiCreateDeviceInfoList.
|
|
|
|
pDeviceInfoSet - Supplies a pointer to the device information set of which
|
|
the devinfo element is a member.
|
|
|
|
DevInfoElem - Optionally, supplies the address of the device information
|
|
element whose parameter block is to be destroyed. If the parameter
|
|
block being destroyed is associated with the set itself, then this
|
|
parameter will be NULL.
|
|
|
|
InstallParamBlock - Supplies the address of the install parameter
|
|
block whose resources are to be freed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
SP_DEVINFO_DATA DeviceInfoData;
|
|
|
|
if(InstallParamBlock->UserFileQ) {
|
|
//
|
|
// If there's a user-supplied file queue stored in this installation
|
|
// parameter block, then decrement the refcount on it. Make sure we
|
|
// do this before calling the class installer with DIF_DESTROYPRIVATEDATA,
|
|
// or else they won't be able to close the queue.
|
|
//
|
|
MYASSERT(((PSP_FILE_QUEUE)(InstallParamBlock->UserFileQ))->LockRefCount);
|
|
|
|
((PSP_FILE_QUEUE)(InstallParamBlock->UserFileQ))->LockRefCount--;
|
|
}
|
|
|
|
if(InstallParamBlock->hinstClassInstaller) {
|
|
//
|
|
// If there is a class installer entry point, then call it with
|
|
// DIF_DESTROYPRIVATEDATA. NOTE: We don't unlock the HDEVINFO set
|
|
// here, so the class installer can't make any calls that disallow
|
|
// nesting levels > 1. This means that SetupDiSelectDevice, for
|
|
// example, will fail if the class installer tries to call it now.
|
|
// This is necessary, because otherwise it would deadlock.
|
|
//
|
|
if(InstallParamBlock->ClassInstallerEntryPoint) {
|
|
//
|
|
// If we have a class installer entry point, then we'd better have
|
|
// a valid HDEVINFO!
|
|
//
|
|
MYASSERT(hDevInfo && (hDevInfo != INVALID_HANDLE_VALUE));
|
|
|
|
//
|
|
// Generate an SP_DEVINFO_DATA structure from our device information
|
|
// element (if we have one).
|
|
//
|
|
if(DevInfoElem) {
|
|
//
|
|
// Lock down this element, so that the class installer can't make
|
|
// any 'dangerous' calls (e.g., SetupDiDeleteDeviceInfo), during
|
|
// the removal notification.
|
|
//
|
|
DevInfoElem->DiElemFlags |= DIE_IS_LOCKED;
|
|
|
|
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
|
|
DevInfoDataFromDeviceInfoElement(pDeviceInfoSet,
|
|
DevInfoElem,
|
|
&DeviceInfoData
|
|
);
|
|
}
|
|
|
|
InstallParamBlock->ClassInstallerEntryPoint(DIF_DESTROYPRIVATEDATA,
|
|
hDevInfo,
|
|
DevInfoElem
|
|
? &DeviceInfoData
|
|
: NULL
|
|
);
|
|
}
|
|
FreeLibrary(InstallParamBlock->hinstClassInstaller);
|
|
}
|
|
|
|
if(InstallParamBlock->ClassInstallHeader) {
|
|
MyFree(InstallParamBlock->ClassInstallHeader);
|
|
}
|
|
}
|
|
|
|
|
|
PDEVICE_INFO_SET
|
|
AccessDeviceInfoSet(
|
|
IN HDEVINFO DeviceInfoSet
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine locks the specified device information set, and returns
|
|
a pointer to the structure for its internal representation. It also
|
|
increments the lock refcount on this set, so that it can't be destroyed
|
|
if the lock has been acquired multiple times.
|
|
|
|
After access to the set is completed, the caller must call
|
|
UnlockDeviceInfoSet with the pointer returned by this function.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the pointer to the device information set
|
|
to be accessed.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is a pointer to the
|
|
device information set.
|
|
|
|
If the function fails, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET p;
|
|
|
|
try {
|
|
p = (PDEVICE_INFO_SET)DeviceInfoSet;
|
|
if(LockDeviceInfoSet(p)) {
|
|
p->LockRefCount++;
|
|
} else {
|
|
p = NULL;
|
|
}
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
p = NULL;
|
|
}
|
|
|
|
return p;
|
|
}
|
|
|
|
|
|
PDEVINFO_ELEM
|
|
FindDevInfoByDevInst(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN DEVINST DevInst,
|
|
OUT PDEVINFO_ELEM *PrevDevInfoElem OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches through all (registered) elements of a
|
|
device information set, looking for one that corresponds to the
|
|
specified device instance handle. If a match is found, a pointer
|
|
to the device information element is returned.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Specifies the set to be searched.
|
|
|
|
DevInst - Specifies the device instance handle to search for.
|
|
|
|
PrevDevInfoElem - Optionaly, supplies the address of the variable that
|
|
receives a pointer to the device information element immediately
|
|
preceding the matching element. If the element was found at the
|
|
front of the list, then this variable will be set to NULL.
|
|
|
|
Return Value:
|
|
|
|
If a device information element is found, the return value is a
|
|
pointer to that element, otherwise, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDEVINFO_ELEM cur, prev;
|
|
|
|
for(cur = DeviceInfoSet->DeviceInfoHead, prev = NULL;
|
|
cur;
|
|
prev = cur, cur = cur->Next)
|
|
{
|
|
if((cur->DiElemFlags & DIE_IS_REGISTERED) && (cur->DevInst == DevInst)) {
|
|
|
|
if(PrevDevInfoElem) {
|
|
*PrevDevInfoElem = prev;
|
|
}
|
|
return cur;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DevInfoDataFromDeviceInfoElement(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDEVINFO_ELEM DevInfoElem,
|
|
OUT PSP_DEVINFO_DATA DeviceInfoData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a SP_DEVINFO_DATA structure based on the
|
|
information in the supplied DEVINFO_ELEM structure.
|
|
|
|
Note: The supplied DeviceInfoData structure must have its cbSize
|
|
field filled in correctly, or the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies a pointer to the device information set
|
|
containing the specified element.
|
|
|
|
DevInfoElem - Supplies a pointer to the DEVINFO_ELEM structure
|
|
containing information to be used in filling in the
|
|
SP_DEVINFO_DATA buffer.
|
|
|
|
DeviceInfoData - Supplies a pointer to the buffer that will
|
|
receive the filled-in SP_DEVINFO_DATA structure
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE, otherwise, it
|
|
is FALSE.
|
|
|
|
--*/
|
|
{
|
|
if(DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)) {
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(DeviceInfoData, sizeof(SP_DEVINFO_DATA));
|
|
DeviceInfoData->cbSize = sizeof(SP_DEVINFO_DATA);
|
|
|
|
CopyMemory(&(DeviceInfoData->ClassGuid),
|
|
&(DevInfoElem->ClassGuid),
|
|
sizeof(GUID)
|
|
);
|
|
|
|
DeviceInfoData->DevInst = DevInfoElem->DevInst;
|
|
|
|
//
|
|
// The 'Reserved' field actually contains a pointer to the
|
|
// corresponding device information element.
|
|
//
|
|
DeviceInfoData->Reserved = (DWORD)DevInfoElem;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PDEVINFO_ELEM
|
|
FindAssociatedDevInfoElem(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PSP_DEVINFO_DATA DeviceInfoData,
|
|
OUT PDEVINFO_ELEM *PreviousElement OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches through all elements of a device information
|
|
set, looking for one that corresponds to the specified device
|
|
information data structure. If a match is found, a pointer to the
|
|
device information element is returned.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Specifies the set to be searched.
|
|
|
|
DeviceInfoData - Supplies a pointer to a device information data
|
|
buffer specifying the device information element to retrieve.
|
|
|
|
PreviousElement - Optionally, supplies the address of a
|
|
DEVINFO_ELEM pointer that receives the element that precedes
|
|
the specified element in the linked list. If the returned
|
|
element is located at the front of the list, then this value
|
|
will be set to NULL.
|
|
|
|
Return Value:
|
|
|
|
If a device information element is found, the return value is a
|
|
pointer to that element, otherwise, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDEVINFO_ELEM DevInfoElem, CurElem, PrevElem;
|
|
|
|
if((DeviceInfoData->cbSize != sizeof(SP_DEVINFO_DATA)) ||
|
|
!(DevInfoElem = (PDEVINFO_ELEM)DeviceInfoData->Reserved)) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
for(CurElem = DeviceInfoSet->DeviceInfoHead, PrevElem = NULL;
|
|
CurElem;
|
|
PrevElem = CurElem, CurElem = CurElem->Next) {
|
|
|
|
if(CurElem == DevInfoElem) {
|
|
//
|
|
// We found the element in our set.
|
|
//
|
|
if(PreviousElement) {
|
|
*PreviousElement = PrevElem;
|
|
}
|
|
return CurElem;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
BOOL
|
|
DrvInfoDataFromDriverNode(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDRIVER_NODE DriverNode,
|
|
IN DWORD DriverType,
|
|
OUT PSP_DRVINFO_DATA DriverInfoData
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a SP_DRVINFO_DATA structure based on the
|
|
information in the supplied DRIVER_NODE structure.
|
|
|
|
Note: The supplied DriverInfoData structure must have its cbSize
|
|
field filled in correctly, or the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies a pointer to the device information set
|
|
in which the driver node is located.
|
|
|
|
DriverNode - Supplies a pointer to the DRIVER_NODE structure
|
|
containing information to be used in filling in the
|
|
SP_DRVNFO_DATA buffer.
|
|
|
|
DriverType - Specifies what type of driver this is. This value
|
|
may be either SPDIT_CLASSDRIVER or SPDIT_COMPATDRIVER.
|
|
|
|
DriverInfoData - Supplies a pointer to the buffer that will
|
|
receive the filled-in SP_DRVINFO_DATA structure
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE, otherwise, it
|
|
is FALSE.
|
|
|
|
--*/
|
|
{
|
|
PTSTR StringPtr;
|
|
|
|
if(DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA)) {
|
|
return FALSE;
|
|
}
|
|
|
|
ZeroMemory(DriverInfoData, sizeof(SP_DRVINFO_DATA));
|
|
DriverInfoData->cbSize = sizeof(SP_DRVINFO_DATA);
|
|
|
|
DriverInfoData->DriverType = DriverType;
|
|
|
|
MYASSERT(DriverNode->DevDescriptionDisplayName != -1);
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->DevDescriptionDisplayName
|
|
);
|
|
lstrcpy(DriverInfoData->Description,
|
|
StringPtr
|
|
);
|
|
|
|
MYASSERT(DriverNode->MfgDisplayName != -1);
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->MfgDisplayName
|
|
);
|
|
lstrcpy(DriverInfoData->MfgName,
|
|
StringPtr
|
|
);
|
|
|
|
if(DriverNode->ProviderDisplayName != -1) {
|
|
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->ProviderDisplayName
|
|
);
|
|
lstrcpy(DriverInfoData->ProviderName,
|
|
StringPtr
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// The 'Reserved' field actually contains a pointer to the
|
|
// corresponding driver node.
|
|
//
|
|
DriverInfoData->Reserved = (DWORD)DriverNode;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
PDRIVER_NODE
|
|
FindAssociatedDriverNode(
|
|
IN PDRIVER_NODE DriverListHead,
|
|
IN PSP_DRVINFO_DATA DriverInfoData,
|
|
OUT PDRIVER_NODE *PreviousNode OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches through all driver nodes in a driver node
|
|
list, looking for one that corresponds to the specified driver
|
|
information structure. If a match is found, a pointer to the
|
|
driver node is returned.
|
|
|
|
Arguments:
|
|
|
|
DriverListHead - Supplies a pointer to the head of linked list
|
|
of driver nodes to be searched.
|
|
|
|
DriverInfoData - Supplies a pointer to a driver information buffer
|
|
specifying the driver node to retrieve.
|
|
|
|
PreviousNode - Optionally, supplies the address of a DRIVER_NODE
|
|
pointer that receives the node that precedes the specified
|
|
node in the linked list. If the returned node is located at
|
|
the front of the list, then this value will be set to NULL.
|
|
|
|
Return Value:
|
|
|
|
If a driver node is found, the return value is a pointer to that
|
|
node, otherwise, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_NODE DriverNode, CurNode, PrevNode;
|
|
|
|
if((DriverInfoData->cbSize != sizeof(SP_DRVINFO_DATA)) ||
|
|
!(DriverNode = (PDRIVER_NODE)DriverInfoData->Reserved)) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
for(CurNode = DriverListHead, PrevNode = NULL;
|
|
CurNode;
|
|
PrevNode = CurNode, CurNode = CurNode->Next) {
|
|
|
|
if(CurNode == DriverNode) {
|
|
//
|
|
// We found the driver node in our list.
|
|
//
|
|
if(PreviousNode) {
|
|
*PreviousNode = PrevNode;
|
|
}
|
|
return CurNode;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
PDRIVER_NODE
|
|
SearchForDriverNode(
|
|
IN PVOID StringTable,
|
|
IN PDRIVER_NODE DriverListHead,
|
|
IN PSP_DRVINFO_DATA DriverInfoData,
|
|
OUT PDRIVER_NODE *PreviousNode OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches through all driver nodes in a driver node
|
|
list, looking for one that matches the fields in the specified
|
|
driver information structure (the 'Reserved' field is ignored).
|
|
If a match is found, a pointer to the driver node is returned.
|
|
|
|
Arguments:
|
|
|
|
StringTable - Supplies the string table that should be used in
|
|
retrieving string IDs for driver look-up.
|
|
|
|
DriverListHead - Supplies a pointer to the head of linked list
|
|
of driver nodes to be searched.
|
|
|
|
DriverInfoData - Supplies a pointer to a driver information buffer
|
|
specifying the driver parameters we're looking for.
|
|
|
|
PreviousNode - Optionally, supplies the address of a DRIVER_NODE
|
|
pointer that receives the node that precedes the specified
|
|
node in the linked list. If the returned node is located at
|
|
the front of the list, then this value will be set to NULL.
|
|
|
|
Return Value:
|
|
|
|
If a driver node is found, the return value is a pointer to that
|
|
node, otherwise, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_NODE CurNode, PrevNode;
|
|
LONG DevDescription, MfgName, ProviderName;
|
|
TCHAR TempString[LINE_LEN];
|
|
DWORD TempStringLength;
|
|
|
|
MYASSERT(DriverInfoData->cbSize == sizeof(SP_DRVINFO_DATA));
|
|
|
|
//
|
|
// Retrieve the string IDs for the 3 driver parameters we'll be
|
|
// matching against.
|
|
//
|
|
lstrcpyn(TempString, DriverInfoData->Description, SIZECHARS(TempString));
|
|
if((DevDescription = pStringTableLookUpString(
|
|
StringTable,
|
|
TempString,
|
|
&TempStringLength,
|
|
NULL,
|
|
NULL,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)) == -1) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
lstrcpyn(TempString, DriverInfoData->MfgName, SIZECHARS(TempString));
|
|
if((MfgName = pStringTableLookUpString(
|
|
StringTable,
|
|
TempString,
|
|
&TempStringLength,
|
|
NULL,
|
|
NULL,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)) == -1) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// ProviderName may be empty...
|
|
//
|
|
if(*(DriverInfoData->ProviderName)) {
|
|
|
|
lstrcpyn(TempString, DriverInfoData->ProviderName, SIZECHARS(TempString));
|
|
if((ProviderName = pStringTableLookUpString(
|
|
StringTable,
|
|
TempString,
|
|
&TempStringLength,
|
|
NULL,
|
|
NULL,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)) == -1) {
|
|
|
|
return NULL;
|
|
}
|
|
|
|
} else {
|
|
ProviderName = -1;
|
|
}
|
|
|
|
for(CurNode = DriverListHead, PrevNode = NULL;
|
|
CurNode;
|
|
PrevNode = CurNode, CurNode = CurNode->Next)
|
|
{
|
|
//
|
|
// Check first on DevDescription (least likely to match), then on MfgName, and finally
|
|
// on ProviderName.
|
|
//
|
|
if(CurNode->DevDescription == DevDescription) {
|
|
|
|
if(CurNode->MfgName == MfgName) {
|
|
|
|
if(CurNode->ProviderName == ProviderName) {
|
|
//
|
|
// We found the driver node in our list.
|
|
//
|
|
if(PreviousNode) {
|
|
*PreviousNode = PrevNode;
|
|
}
|
|
return CurNode;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
DWORD
|
|
DrvInfoDetailsFromDriverNode(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDRIVER_NODE DriverNode,
|
|
OUT PSP_DRVINFO_DETAIL_DATA DriverInfoDetailData, OPTIONAL
|
|
IN DWORD BufferSize,
|
|
OUT PDWORD RequiredSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a SP_DRVINFO_DETAIL_DATA structure based on the
|
|
information in the supplied DRIVER_NODE structure.
|
|
|
|
If the buffer is supplied, and is valid, this routine is guaranteed to
|
|
fill in all statically-sized fields, and as many IDs as will fit in the
|
|
variable-length multi-sz buffer.
|
|
|
|
Note: If supplied, the DriverInfoDetailData structure must have its
|
|
cbSize field filled in correctly, or the call will fail. Here correctly
|
|
means sizeof(SP_DRVINFO_DETAIL_DATA), which we use as a signature.
|
|
This is entirely separate from BufferSize. See below.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies a pointer to the device information set
|
|
in which the driver node is located.
|
|
|
|
DriverNode - Supplies a pointer to the DRIVER_NODE structure
|
|
containing information to be used in filling in the
|
|
SP_DRVNFO_DETAIL_DATA buffer.
|
|
|
|
DriverInfoDetailData - Optionally, supplies a pointer to the buffer
|
|
that will receive the filled-in SP_DRVINFO_DETAIL_DATA structure.
|
|
If this buffer is not supplied, then the caller is only interested
|
|
in what the RequiredSize for the buffer is.
|
|
|
|
BufferSize - Supplies size of the DriverInfoDetailData buffer, in
|
|
bytes. If DriverInfoDetailData is not specified, then this
|
|
value must be zero. This value must be at least the size
|
|
of the fixed part of the structure (ie,
|
|
offsetof(SP_DRVINFO_DETAIL_DATA,HardwareID)) plus sizeof(TCHAR),
|
|
which gives us enough room to store the fixed part plus
|
|
a terminating nul to guarantee we return at least a valid
|
|
empty multi_sz.
|
|
|
|
RequiredSize - Optionally, supplies the address of a variable that
|
|
receives the number of bytes required to store the data. Note that
|
|
depending on structure alignment and the data itself, this may
|
|
actually be *smaller* than sizeof(SP_DRVINFO_DETAIL_DATA).
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
PTSTR StringPtr, BufferPtr;
|
|
DWORD IdListLen, CompatIdListLen, StringLen, TotalLen, i;
|
|
DWORD Err = ERROR_INSUFFICIENT_BUFFER;
|
|
|
|
#define FIXEDPARTLEN offsetof(SP_DRVINFO_DETAIL_DATA,HardwareID)
|
|
|
|
if(DriverInfoDetailData) {
|
|
//
|
|
// Check validity of the DriverInfoDetailData buffer on the way in,
|
|
// and make sure we have enough room for the fixed part
|
|
// of the structure plus the extra nul that will terminate the
|
|
// multi_sz.
|
|
//
|
|
if((DriverInfoDetailData->cbSize != sizeof(SP_DRVINFO_DETAIL_DATA))
|
|
|| (BufferSize < (FIXEDPARTLEN+sizeof(TCHAR)))) {
|
|
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
//
|
|
// The buffer is large enough to contain at least the fixed-length part
|
|
// of the structure.
|
|
//
|
|
Err = NO_ERROR;
|
|
|
|
} else if(BufferSize) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
if(DriverInfoDetailData) {
|
|
|
|
ZeroMemory(DriverInfoDetailData,FIXEDPARTLEN);
|
|
DriverInfoDetailData->cbSize = FIXEDPARTLEN + sizeof(TCHAR);
|
|
|
|
DriverInfoDetailData->InfDate = DriverNode->InfDate;
|
|
|
|
MYASSERT(DriverNode->InfSectionName != -1);
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->InfSectionName
|
|
);
|
|
lstrcpy(DriverInfoDetailData->SectionName, StringPtr);
|
|
|
|
MYASSERT(DriverNode->InfFileName != -1);
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->InfFileName
|
|
);
|
|
lstrcpy(DriverInfoDetailData->InfFileName, StringPtr);
|
|
|
|
MYASSERT(DriverNode->DrvDescription != -1);
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->DrvDescription
|
|
);
|
|
lstrcpy(DriverInfoDetailData->DrvDescription, StringPtr);
|
|
|
|
//
|
|
// Initialize the multi_sz to be empty.
|
|
//
|
|
DriverInfoDetailData->HardwareID[0] = 0;
|
|
|
|
//
|
|
// The 'Reserved' field actually contains a pointer to the
|
|
// corresponding driver node.
|
|
//
|
|
DriverInfoDetailData->Reserved = (DWORD)DriverNode;
|
|
}
|
|
|
|
//
|
|
// Now, build the multi-sz buffer containing the hardware and compatible IDs.
|
|
//
|
|
if(DriverNode->HardwareId == -1) {
|
|
//
|
|
// If there's no HardwareId, then we know there are no compatible IDs, so
|
|
// we can return right now.
|
|
//
|
|
if(RequiredSize) {
|
|
*RequiredSize = FIXEDPARTLEN + sizeof(TCHAR);
|
|
}
|
|
return Err;
|
|
}
|
|
|
|
if(DriverInfoDetailData) {
|
|
BufferPtr = DriverInfoDetailData->HardwareID;
|
|
IdListLen = (BufferSize - FIXEDPARTLEN) / sizeof(TCHAR);
|
|
} else {
|
|
IdListLen = 0;
|
|
}
|
|
|
|
//
|
|
// Retrieve the HardwareId.
|
|
//
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->HardwareId
|
|
);
|
|
|
|
TotalLen = StringLen = lstrlen(StringPtr) + 1; // include nul terminator
|
|
|
|
if(StringLen < IdListLen) {
|
|
MYASSERT(Err == NO_ERROR);
|
|
CopyMemory(BufferPtr,
|
|
StringPtr,
|
|
StringLen * sizeof(TCHAR)
|
|
);
|
|
BufferPtr += StringLen;
|
|
IdListLen -= StringLen;
|
|
DriverInfoDetailData->CompatIDsOffset = StringLen;
|
|
} else {
|
|
if(RequiredSize) {
|
|
//
|
|
// Since the caller requested the required size, we can't just return
|
|
// here. Set the error, so we'll know not to bother trying to fill
|
|
// the buffer.
|
|
//
|
|
Err = ERROR_INSUFFICIENT_BUFFER;
|
|
} else {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Remember the size of the buffer left over for CompatibleIDs.
|
|
//
|
|
CompatIdListLen = IdListLen;
|
|
|
|
//
|
|
// Now retrieve the CompatibleIDs.
|
|
//
|
|
for(i = 0; i < DriverNode->NumCompatIds; i++) {
|
|
|
|
MYASSERT(DriverNode->CompatIdList[i] != -1);
|
|
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DriverNode->CompatIdList[i]
|
|
);
|
|
StringLen = lstrlen(StringPtr) + 1;
|
|
|
|
if(Err == NO_ERROR) {
|
|
|
|
if(StringLen < IdListLen) {
|
|
CopyMemory(BufferPtr,
|
|
StringPtr,
|
|
StringLen * sizeof(TCHAR)
|
|
);
|
|
BufferPtr += StringLen;
|
|
IdListLen -= StringLen;
|
|
|
|
} else {
|
|
|
|
Err = ERROR_INSUFFICIENT_BUFFER;
|
|
if(!RequiredSize) {
|
|
//
|
|
// We've run out of buffer, and the caller doesn't care what
|
|
// the total required size is, so bail now.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
TotalLen += StringLen;
|
|
}
|
|
|
|
if(DriverInfoDetailData) {
|
|
//
|
|
// Append the additional terminating nul. Note that we've been saving the
|
|
// last character position in the buffer all along, so we're guaranteed to
|
|
// be inside the buffer.
|
|
//
|
|
MYASSERT(BufferPtr < (PTSTR)((PBYTE)DriverInfoDetailData + BufferSize));
|
|
*BufferPtr = 0;
|
|
|
|
//
|
|
// Store the length of the CompatibleIDs list. Note that this is the length
|
|
// of the list actually returned, which may be less than the length of the
|
|
// entire list (if the caller-supplied buffer wasn't large enough).
|
|
//
|
|
if(CompatIdListLen -= IdListLen) {
|
|
//
|
|
// If this list is non-empty, then add a character for the extra nul
|
|
// terminating the multi-sz list.
|
|
//
|
|
CompatIdListLen++;
|
|
}
|
|
DriverInfoDetailData->CompatIDsLength = CompatIdListLen;
|
|
}
|
|
|
|
if(RequiredSize) {
|
|
*RequiredSize = FIXEDPARTLEN + ((TotalLen + 1) * sizeof(TCHAR));
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
PDRIVER_LIST_OBJECT
|
|
GetAssociatedDriverListObject(
|
|
IN PDRIVER_LIST_OBJECT ObjectListHead,
|
|
IN PDRIVER_NODE DriverListHead,
|
|
OUT PDRIVER_LIST_OBJECT *PrevDriverListObject OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine searches through a driver list object list, and returns a
|
|
pointer to the driver list object containing the list specified by
|
|
DrvListHead. It also optionally returns the preceding object in the list
|
|
(used when extracting the driver list object from the linked list).
|
|
|
|
Arguments:
|
|
|
|
ObjectListHead - Specifies the linked list of driver list objects to be
|
|
searched.
|
|
|
|
DriverListHead - Specifies the driver list to be searched for.
|
|
|
|
PrevDriverListObject - Optionaly, supplies the address of the variable that
|
|
receives a pointer to the driver list object immediately preceding the
|
|
matching object. If the object was found at the front of the list, then
|
|
this variable will be set to NULL.
|
|
|
|
Return Value:
|
|
|
|
If the matching driver list object is found, the return value is a pointer
|
|
to that element, otherwise, the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_LIST_OBJECT prev = NULL;
|
|
|
|
while(ObjectListHead) {
|
|
|
|
if(ObjectListHead->DriverListHead == DriverListHead) {
|
|
|
|
if(PrevDriverListObject) {
|
|
*PrevDriverListObject = prev;
|
|
}
|
|
|
|
return ObjectListHead;
|
|
}
|
|
|
|
prev = ObjectListHead;
|
|
ObjectListHead = ObjectListHead->Next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
DereferenceClassDriverList(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDRIVER_NODE DriverListHead OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine dereferences the class driver list object associated with the
|
|
supplied DriverListHead. If the refcount goes to zero, the object is destroyed,
|
|
and all associated memory is freed.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the address of the device information set containing the
|
|
linked list of class driver list objects.
|
|
|
|
DriverListHead - Optionally, supplies a pointer to the header of the driver list
|
|
to be dereferenced. If this parameter is not supplied, the routine does nothing.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_LIST_OBJECT DrvListObject, PrevDrvListObject;
|
|
|
|
if(DriverListHead) {
|
|
|
|
DrvListObject = GetAssociatedDriverListObject(DeviceInfoSet->ClassDrvListObjectList,
|
|
DriverListHead,
|
|
&PrevDrvListObject
|
|
);
|
|
MYASSERT(DrvListObject && DrvListObject->RefCount);
|
|
|
|
if(!(--DrvListObject->RefCount)) {
|
|
|
|
if(PrevDrvListObject) {
|
|
PrevDrvListObject->Next = DrvListObject->Next;
|
|
} else {
|
|
DeviceInfoSet->ClassDrvListObjectList = DrvListObject->Next;
|
|
}
|
|
MyFree(DrvListObject);
|
|
|
|
DestroyDriverNodes(DriverListHead);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetDevInstallParams(
|
|
IN PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PDEVINSTALL_PARAM_BLOCK DevInstParamBlock,
|
|
OUT PSP_DEVINSTALL_PARAMS DeviceInstallParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a SP_DEVINSTALL_PARAMS structure based on the
|
|
installation parameter block supplied.
|
|
|
|
Note: The DeviceInstallParams structure must have its cbSize field
|
|
filled in correctly, or the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the address of the device information set
|
|
containing the parameters to be retrieved. (This parameter is
|
|
used to gain access to the string table for some of the string
|
|
parameters).
|
|
|
|
DevInstParamBlock - Supplies the address of an installation parameter
|
|
block containing the parameters to be used in filling out the
|
|
return buffer.
|
|
|
|
DeviceInstallParams - Supplies the address of a buffer that will
|
|
receive the filled-in SP_DEVINSTALL_PARAMS structure.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
PTSTR StringPtr;
|
|
|
|
if(DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS)) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
//
|
|
// Fill in parameters.
|
|
//
|
|
ZeroMemory(DeviceInstallParams, sizeof(SP_DEVINSTALL_PARAMS));
|
|
DeviceInstallParams->cbSize = sizeof(SP_DEVINSTALL_PARAMS);
|
|
|
|
DeviceInstallParams->Flags = DevInstParamBlock->Flags;
|
|
DeviceInstallParams->FlagsEx = DevInstParamBlock->FlagsEx;
|
|
DeviceInstallParams->hwndParent = DevInstParamBlock->hwndParent;
|
|
DeviceInstallParams->InstallMsgHandler = DevInstParamBlock->InstallMsgHandler;
|
|
DeviceInstallParams->InstallMsgHandlerContext = DevInstParamBlock->InstallMsgHandlerContext;
|
|
DeviceInstallParams->FileQueue = DevInstParamBlock->UserFileQ;
|
|
DeviceInstallParams->ClassInstallReserved = DevInstParamBlock->ClassInstallReserved;
|
|
//
|
|
// The Reserved field is currently unused.
|
|
//
|
|
|
|
if(DevInstParamBlock->DriverPath != -1) {
|
|
StringPtr = pStringTableStringFromId(DeviceInfoSet->StringTable,
|
|
DevInstParamBlock->DriverPath
|
|
);
|
|
lstrcpy(DeviceInstallParams->DriverPath, StringPtr);
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetClassInstallParams(
|
|
IN PDEVINSTALL_PARAM_BLOCK DevInstParamBlock,
|
|
OUT PSP_CLASSINSTALL_HEADER ClassInstallParams, OPTIONAL
|
|
IN DWORD BufferSize,
|
|
OUT PDWORD RequiredSize OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a buffer with the class installer parameters (if any)
|
|
contained in the installation parameter block supplied.
|
|
|
|
Note: If supplied, the ClassInstallParams structure must have the cbSize
|
|
field of the embedded SP_CLASSINSTALL_HEADER structure set to the size, in bytes,
|
|
of the header. If this is not set correctly, the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DevInstParamBlock - Supplies the address of an installation parameter block
|
|
containing the class installer parameters to be used in filling out the
|
|
return buffer.
|
|
|
|
DeviceInstallParams - Optionally, supplies the address of a buffer
|
|
that will receive the class installer parameters structure currently
|
|
stored in the installation parameters block. If this parameter is not
|
|
supplied, then BufferSize must be zero.
|
|
|
|
BufferSize - Supplies the size, in bytes, of the DeviceInstallParams
|
|
buffer, or zero if DeviceInstallParams is not supplied.
|
|
|
|
RequiredSize - Optionally, supplies the address of a variable that
|
|
receives the number of bytes required to store the data.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// First, see whether we have any class install params, and if not, return
|
|
// ERROR_NO_CLASSINSTALL_PARAMS.
|
|
//
|
|
if(!DevInstParamBlock->ClassInstallHeader) {
|
|
return ERROR_NO_CLASSINSTALL_PARAMS;
|
|
}
|
|
|
|
if(ClassInstallParams) {
|
|
|
|
if((BufferSize < sizeof(SP_CLASSINSTALL_HEADER)) ||
|
|
(ClassInstallParams->cbSize != sizeof(SP_CLASSINSTALL_HEADER))) {
|
|
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
} else if(BufferSize) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
//
|
|
// Store required size in output parameter (if requested).
|
|
//
|
|
if(RequiredSize) {
|
|
*RequiredSize = DevInstParamBlock->ClassInstallParamsSize;
|
|
}
|
|
|
|
//
|
|
// See if supplied buffer is large enough.
|
|
//
|
|
if(BufferSize < DevInstParamBlock->ClassInstallParamsSize) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
CopyMemory((PVOID)ClassInstallParams,
|
|
(PVOID)DevInstParamBlock->ClassInstallHeader,
|
|
DevInstParamBlock->ClassInstallParamsSize
|
|
);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetDevInstallParams(
|
|
IN OUT PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PSP_DEVINSTALL_PARAMS DeviceInstallParams,
|
|
OUT PDEVINSTALL_PARAM_BLOCK DevInstParamBlock,
|
|
IN BOOL MsgHandlerIsNativeCharWidth
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates an internal parameter block based on the parameters
|
|
supplied in a SP_DEVINSTALL_PARAMS structure.
|
|
|
|
Note: The supplied DeviceInstallParams structure must have its cbSize
|
|
field filled in correctly, or the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the address of the device information set
|
|
containing the parameters to be set.
|
|
|
|
DeviceInstallParams - Supplies the address of a buffer containing the new
|
|
installation parameters.
|
|
|
|
DevInstParamBlock - Supplies the address of an installation parameter
|
|
block to be updated.
|
|
|
|
MsgHandlerIsNativeCharWidth - supplies a flag indicating whether the
|
|
InstallMsgHandler in the DeviceInstallParams structure points to
|
|
a callback routine that is expecting arguments in the 'native'
|
|
character format. A value of FALSE is meaningful only in the
|
|
Unicode build and specifies that the callback routine wants
|
|
ANSI parameters.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
DWORD DriverPathLen;
|
|
LONG StringId;
|
|
TCHAR TempString[MAX_PATH];
|
|
HSPFILEQ OldQueueHandle = NULL;
|
|
BOOL bRestoreQueue = FALSE;
|
|
|
|
if(DeviceInstallParams->cbSize != sizeof(SP_DEVINSTALL_PARAMS)) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
//
|
|
// No validation is currently required for the hwndParent, InstallMsgHandler,
|
|
// InstallMsgHandlerContext, or ClassInstallReserved fields.
|
|
//
|
|
|
|
//
|
|
// Validate Flags(Ex)
|
|
//
|
|
if((DeviceInstallParams->Flags & DI_FLAGS_ILLEGAL) ||
|
|
(DeviceInstallParams->FlagsEx & DI_FLAGSEX_ILLEGAL)) {
|
|
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Make sure that if DI_CLASSINSTALLPARAMS is being set, that we really do have
|
|
// class install parameters.
|
|
//
|
|
if((DeviceInstallParams->Flags & DI_CLASSINSTALLPARAMS) &&
|
|
!(DevInstParamBlock->ClassInstallHeader)) {
|
|
|
|
return ERROR_NO_CLASSINSTALL_PARAMS;
|
|
}
|
|
|
|
//
|
|
// Make sure that if DI_NOVCP is being set, that we have a caller-supplied file queue.
|
|
//
|
|
if((DeviceInstallParams->Flags & DI_NOVCP) &&
|
|
((DeviceInstallParams->FileQueue == NULL) || (DeviceInstallParams->FileQueue == INVALID_HANDLE_VALUE))) {
|
|
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// Validate that the DriverPath string is properly NULL-terminated.
|
|
//
|
|
if((DriverPathLen = lstrlen(DeviceInstallParams->DriverPath)) >= MAX_PATH) {
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Validate the caller-supplied file queue.
|
|
//
|
|
if((DeviceInstallParams->FileQueue == NULL) || (DeviceInstallParams->FileQueue == INVALID_HANDLE_VALUE)) {
|
|
//
|
|
// Store the current file queue handle (if any) to be released later.
|
|
//
|
|
OldQueueHandle = DevInstParamBlock->UserFileQ;
|
|
DevInstParamBlock->UserFileQ = NULL;
|
|
bRestoreQueue = TRUE;
|
|
|
|
} else {
|
|
//
|
|
// The caller supplied a file queue handle. There's presently not much validation we can do
|
|
// on it, so assume it's valid.
|
|
//
|
|
if(DeviceInstallParams->FileQueue != DevInstParamBlock->UserFileQ) {
|
|
//
|
|
// The caller has supplied a file queue handle that's different from the one we currently
|
|
// have stored. Remember the old handle (in case we need to restore), and store the new
|
|
// handle. Also, increment the lock refcount on the new handle (enclose in try/except in
|
|
// case it's a bogus one).
|
|
//
|
|
OldQueueHandle = DevInstParamBlock->UserFileQ;
|
|
bRestoreQueue = TRUE;
|
|
|
|
try {
|
|
((PSP_FILE_QUEUE)(DeviceInstallParams->FileQueue))->LockRefCount++;
|
|
DevInstParamBlock->UserFileQ = DeviceInstallParams->FileQueue;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
DevInstParamBlock->UserFileQ = OldQueueHandle;
|
|
bRestoreQueue = FALSE;
|
|
}
|
|
|
|
if(!bRestoreQueue) {
|
|
//
|
|
// The file queue handle we were given was invalid.
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Store the specified driver path.
|
|
//
|
|
if(DriverPathLen) {
|
|
lstrcpy(TempString, DeviceInstallParams->DriverPath);
|
|
if((StringId = pStringTableAddString(DeviceInfoSet->StringTable,
|
|
TempString,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
|
|
)) == -1) {
|
|
//
|
|
// We couldn't add the new driver path string to the string table. Restore the old
|
|
// file queue (if necessary) and return an out-of-memory error.
|
|
//
|
|
if(bRestoreQueue) {
|
|
|
|
if(DevInstParamBlock->UserFileQ) {
|
|
try {
|
|
((PSP_FILE_QUEUE)(DevInstParamBlock->UserFileQ))->LockRefCount--;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
}
|
|
DevInstParamBlock->UserFileQ = OldQueueHandle;
|
|
}
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
DevInstParamBlock->DriverPath = StringId;
|
|
} else {
|
|
DevInstParamBlock->DriverPath = -1;
|
|
}
|
|
|
|
//
|
|
// Should be smooth sailing from here on out. Decrement the refcount on the old queue handle,
|
|
// if there was one.
|
|
//
|
|
if(OldQueueHandle) {
|
|
try {
|
|
MYASSERT(((PSP_FILE_QUEUE)OldQueueHandle)->LockRefCount);
|
|
((PSP_FILE_QUEUE)OldQueueHandle)->LockRefCount--;
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
; // nothing to do
|
|
}
|
|
}
|
|
|
|
//
|
|
// Ignore attempts at modifying read-only flags.
|
|
//
|
|
DevInstParamBlock->Flags = (DeviceInstallParams->Flags & ~DI_FLAGS_READONLY) |
|
|
(DevInstParamBlock->Flags & DI_FLAGS_READONLY);
|
|
|
|
DevInstParamBlock->FlagsEx = (DeviceInstallParams->FlagsEx & ~DI_FLAGSEX_READONLY) |
|
|
(DevInstParamBlock->FlagsEx & DI_FLAGSEX_READONLY);
|
|
|
|
//
|
|
// Store the rest of the parameters.
|
|
//
|
|
DevInstParamBlock->hwndParent = DeviceInstallParams->hwndParent;
|
|
DevInstParamBlock->InstallMsgHandler = DeviceInstallParams->InstallMsgHandler;
|
|
DevInstParamBlock->InstallMsgHandlerContext = DeviceInstallParams->InstallMsgHandlerContext;
|
|
DevInstParamBlock->ClassInstallReserved = DeviceInstallParams->ClassInstallReserved;
|
|
|
|
DevInstParamBlock->InstallMsgHandlerIsNativeCharWidth = MsgHandlerIsNativeCharWidth;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetClassInstallParams(
|
|
IN OUT PDEVICE_INFO_SET DeviceInfoSet,
|
|
IN PSP_CLASSINSTALL_HEADER ClassInstallParams, OPTIONAL
|
|
IN DWORD ClassInstallParamsSize,
|
|
OUT PDEVINSTALL_PARAM_BLOCK DevInstParamBlock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine updates an internal class installer parameter block based on
|
|
the parameters supplied in a class installer parameter buffer. If this
|
|
buffer is not supplied, then the existing class installer parameters (if
|
|
any) are cleared.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the address of the device information set
|
|
for which class installer parameters are to be set.
|
|
|
|
ClassInstallParams - Optionally, supplies the address of a buffer containing
|
|
the class installer parameters to be used. The SP_CLASSINSTALL_HEADER
|
|
structure at the beginning of the buffer must have its cbSize field set to
|
|
be sizeof(SP_CLASSINSTALL_HEADER), and the InstallFunction field must be
|
|
set to the DI_FUNCTION code reflecting the type of parameters supplied in
|
|
the rest of the buffer.
|
|
|
|
If this parameter is not supplied, then the current class installer parameters
|
|
(if any) will be cleared for the specified device information set or element.
|
|
|
|
ClassInstallParamsSize - Supplies the size, in bytes, of the ClassInstallParams
|
|
buffer. If the buffer is not supplied (i.e., the class installer parameters
|
|
are to be cleared), then this value must be zero.
|
|
|
|
DevInstParamBlock - Supplies the address of an installation parameter
|
|
block to be updated.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
PBYTE NewParamBuffer;
|
|
|
|
if(ClassInstallParams) {
|
|
|
|
if((ClassInstallParamsSize < sizeof(SP_CLASSINSTALL_HEADER)) ||
|
|
(ClassInstallParams->cbSize != sizeof(SP_CLASSINSTALL_HEADER))) {
|
|
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
} else {
|
|
//
|
|
// We are to clear any existing class installer parameters.
|
|
//
|
|
if(ClassInstallParamsSize) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
if(DevInstParamBlock->ClassInstallHeader) {
|
|
MyFree(DevInstParamBlock->ClassInstallHeader);
|
|
DevInstParamBlock->ClassInstallHeader = NULL;
|
|
DevInstParamBlock->ClassInstallParamsSize = 0;
|
|
DevInstParamBlock->Flags &= ~DI_CLASSINSTALLPARAMS;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
//
|
|
// Validate the new class install parameters w.r.t. the value of the specified
|
|
// InstallFunction code.
|
|
//
|
|
switch(ClassInstallParams->InstallFunction) {
|
|
|
|
case DIF_ENABLECLASS :
|
|
//
|
|
// We should have a SP_ENABLECLASS_PARAMS structure.
|
|
//
|
|
if(ClassInstallParamsSize == sizeof(SP_ENABLECLASS_PARAMS)) {
|
|
|
|
PSP_ENABLECLASS_PARAMS EnableClassParams;
|
|
|
|
EnableClassParams = (PSP_ENABLECLASS_PARAMS)ClassInstallParams;
|
|
//
|
|
// Don't bother validating GUID--just validate EnableMessage field.
|
|
//
|
|
if((EnableClassParams->EnableMessage >= ENABLECLASS_QUERY) &&
|
|
(EnableClassParams->EnableMessage <= ENABLECLASS_FAILURE)) {
|
|
//
|
|
// parameter set validated.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
case DIF_MOVEDEVICE :
|
|
//
|
|
// We should have a SP_MOVEDEV_PARAMS structure.
|
|
//
|
|
if(ClassInstallParamsSize == sizeof(SP_MOVEDEV_PARAMS)) {
|
|
|
|
PSP_MOVEDEV_PARAMS MoveDevParams;
|
|
|
|
MoveDevParams = (PSP_MOVEDEV_PARAMS)ClassInstallParams;
|
|
if(FindAssociatedDevInfoElem(DeviceInfoSet,
|
|
&(MoveDevParams->SourceDeviceInfoData),
|
|
NULL)) {
|
|
//
|
|
// parameter set validated.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
case DIF_PROPERTYCHANGE :
|
|
//
|
|
// We should have a SP_PROPCHANGE_PARAMS structure.
|
|
//
|
|
if(ClassInstallParamsSize == sizeof(SP_PROPCHANGE_PARAMS)) {
|
|
|
|
PSP_PROPCHANGE_PARAMS PropChangeParams;
|
|
|
|
PropChangeParams = (PSP_PROPCHANGE_PARAMS)ClassInstallParams;
|
|
if((PropChangeParams->StateChange >= DICS_ENABLE) &&
|
|
(PropChangeParams->StateChange <= DICS_STOP)) {
|
|
|
|
//
|
|
// Validate Scope specifier--even though these values are defined like
|
|
// flags, they are mutually exclusive, so treat them like ordinals.
|
|
//
|
|
if((PropChangeParams->Scope == DICS_FLAG_GLOBAL) ||
|
|
(PropChangeParams->Scope == DICS_FLAG_CONFIGSPECIFIC) ||
|
|
(PropChangeParams->Scope == DICS_FLAG_CONFIGGENERAL)) {
|
|
|
|
//
|
|
// DICS_START and DICS_STOP are always config specific.
|
|
//
|
|
if(((PropChangeParams->StateChange == DICS_START) || (PropChangeParams->StateChange == DICS_STOP)) &&
|
|
(PropChangeParams->Scope != DICS_FLAG_CONFIGSPECIFIC)) {
|
|
|
|
goto BadPropChangeParams;
|
|
}
|
|
|
|
//
|
|
// parameter set validated
|
|
//
|
|
// NOTE: Even though DICS_FLAG_CONFIGSPECIFIC indicates
|
|
// that the HwProfile field specifies a hardware profile,
|
|
// there's no need to do validation on that.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
BadPropChangeParams:
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
case DIF_REMOVE :
|
|
//
|
|
// We should have a SP_REMOVEDEVICE_PARAMS structure.
|
|
//
|
|
if(ClassInstallParamsSize == sizeof(SP_REMOVEDEVICE_PARAMS)) {
|
|
|
|
PSP_REMOVEDEVICE_PARAMS RemoveDevParams;
|
|
|
|
RemoveDevParams = (PSP_REMOVEDEVICE_PARAMS)ClassInstallParams;
|
|
if((RemoveDevParams->Scope == DI_REMOVEDEVICE_GLOBAL) ||
|
|
(RemoveDevParams->Scope == DI_REMOVEDEVICE_CONFIGSPECIFIC)) {
|
|
//
|
|
// parameter set validated
|
|
//
|
|
// NOTE: Even though DI_REMOVEDEVICE_CONFIGSPECIFIC indicates
|
|
// that the HwProfile field specifies a hardware profile,
|
|
// there's no need to do validation on that.
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
case DIF_SELECTDEVICE :
|
|
//
|
|
// We should have a SP_SELECTDEVICE_PARAMS structure.
|
|
//
|
|
if(ClassInstallParamsSize == sizeof(SP_SELECTDEVICE_PARAMS)) {
|
|
|
|
PSP_SELECTDEVICE_PARAMS SelectDevParams;
|
|
|
|
SelectDevParams = (PSP_SELECTDEVICE_PARAMS)ClassInstallParams;
|
|
//
|
|
// Validate that the string fields are properly NULL-terminated.
|
|
//
|
|
if((lstrlen(SelectDevParams->Title) < (MAX_TITLE_LEN - 1)) &&
|
|
(lstrlen(SelectDevParams->Instructions) < (MAX_INSTRUCTION_LEN - 1)) &&
|
|
(lstrlen(SelectDevParams->ListLabel) < (MAX_LABEL_LEN - 1))) {
|
|
//
|
|
// parameter set validated
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
case DIF_INSTALLWIZARD :
|
|
//
|
|
// We should have a SP_INSTALLWIZARD_DATA structure.
|
|
//
|
|
if(ClassInstallParamsSize == sizeof(SP_INSTALLWIZARD_DATA)) {
|
|
|
|
PSP_INSTALLWIZARD_DATA InstallWizData;
|
|
DWORD i;
|
|
|
|
InstallWizData = (PSP_INSTALLWIZARD_DATA)ClassInstallParams;
|
|
//
|
|
// Validate that the propsheet handle list.
|
|
//
|
|
if(InstallWizData->NumDynamicPages < MAX_INSTALLWIZARD_DYNAPAGES) {
|
|
|
|
for(i = 0; i < InstallWizData->NumDynamicPages; i++) {
|
|
//
|
|
// For now, just verify that all handles are non-NULL.
|
|
//
|
|
if(!(InstallWizData->DynamicPages[i])) {
|
|
//
|
|
// Invalid property sheet page handle
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Handles are verified, now verify Flags.
|
|
//
|
|
if(!(InstallWizData->Flags & NDW_INSTALLFLAG_ILLEGAL)) {
|
|
|
|
if(!(InstallWizData->DynamicPageFlags & DYNAWIZ_FLAG_ILLEGAL)) {
|
|
//
|
|
// parameter set validated
|
|
//
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return ERROR_INVALID_PARAMETER;
|
|
|
|
default :
|
|
//
|
|
// Some generic buffer. No validation to be done.
|
|
//
|
|
break;
|
|
}
|
|
|
|
//
|
|
// The class install parameters have been validated. Allocate a buffer for the
|
|
// new parameter structure.
|
|
//
|
|
if(!(NewParamBuffer = MyMalloc(ClassInstallParamsSize))) {
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
}
|
|
|
|
try {
|
|
|
|
CopyMemory(NewParamBuffer,
|
|
ClassInstallParams,
|
|
ClassInstallParamsSize
|
|
);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
MyFree(NewParamBuffer);
|
|
NewParamBuffer = NULL;
|
|
}
|
|
|
|
if(!NewParamBuffer) {
|
|
//
|
|
// Then an error occurred and we couldn't store the new parameters.
|
|
//
|
|
return ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
if(DevInstParamBlock->ClassInstallHeader) {
|
|
MyFree(DevInstParamBlock->ClassInstallHeader);
|
|
}
|
|
DevInstParamBlock->ClassInstallHeader = (PSP_CLASSINSTALL_HEADER)NewParamBuffer;
|
|
DevInstParamBlock->ClassInstallParamsSize = ClassInstallParamsSize;
|
|
DevInstParamBlock->Flags |= DI_CLASSINSTALLPARAMS;
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetDrvInstallParams(
|
|
IN PDRIVER_NODE DriverNode,
|
|
OUT PSP_DRVINSTALL_PARAMS DriverInstallParams
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine fills in a SP_DRVINSTALL_PARAMS structure based on the
|
|
driver node supplied
|
|
|
|
Note: The supplied DriverInstallParams structure must have its cbSize
|
|
field filled in correctly, or the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DriverNode - Supplies the address of the driver node containing the
|
|
installation parameters to be retrieved.
|
|
|
|
DriverInstallParams - Supplies the address of a SP_DRVINSTALL_PARAMS
|
|
structure that will receive the installation parameters.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
if(DriverInstallParams->cbSize != sizeof(SP_DRVINSTALL_PARAMS)) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
//
|
|
// Copy the parameters.
|
|
//
|
|
DriverInstallParams->Rank = DriverNode->Rank;
|
|
DriverInstallParams->Flags = DriverNode->Flags;
|
|
DriverInstallParams->PrivateData = DriverNode->PrivateData;
|
|
|
|
//
|
|
// The 'Reserved' field of the SP_DRVINSTALL_PARAMS structure isn't currently
|
|
// used.
|
|
//
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
SetDrvInstallParams(
|
|
IN PSP_DRVINSTALL_PARAMS DriverInstallParams,
|
|
OUT PDRIVER_NODE DriverNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the driver installation parameters for the specified
|
|
driver node based on the caller-supplied SP_DRVINSTALL_PARAMS structure.
|
|
|
|
Note: The supplied DriverInstallParams structure must have its cbSize
|
|
field filled in correctly, or the call will fail.
|
|
|
|
Arguments:
|
|
|
|
DriverInstallParams - Supplies the address of a SP_DRVINSTALL_PARAMS
|
|
structure containing the installation parameters to be used.
|
|
|
|
DriverNode - Supplies the address of the driver node whose installation
|
|
parameters are to be set.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, an ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
if(DriverInstallParams->cbSize != sizeof(SP_DRVINSTALL_PARAMS)) {
|
|
return ERROR_INVALID_USER_BUFFER;
|
|
}
|
|
|
|
//
|
|
// Validate the flags.
|
|
//
|
|
if(DriverInstallParams->Flags & DNF_FLAGS_ILLEGAL) {
|
|
return ERROR_INVALID_FLAGS;
|
|
}
|
|
|
|
//
|
|
// No validation currently being done on Rank and PrivateData fields.
|
|
//
|
|
// We're ready to copy the parameters.
|
|
//
|
|
DriverNode->Rank = DriverInstallParams->Rank;
|
|
DriverNode->PrivateData = DriverInstallParams->PrivateData;
|
|
//
|
|
// Ignore attempts at modifying read-only flags.
|
|
//
|
|
DriverNode->Flags = (DriverInstallParams->Flags & ~DNF_FLAGS_READONLY) |
|
|
(DriverNode->Flags & DNF_FLAGS_READONLY);
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
LONG
|
|
AddMultiSzToStringTable(
|
|
IN PVOID StringTable,
|
|
IN PTCHAR MultiSzBuffer,
|
|
OUT PLONG StringIdList,
|
|
IN DWORD StringIdListSize,
|
|
IN BOOL CaseSensitive,
|
|
OUT PTCHAR *UnprocessedBuffer OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine adds every string in the MultiSzBuffer to the specified
|
|
string table, and stores the resulting IDs in the supplied output buffer.
|
|
|
|
Arguments:
|
|
|
|
StringTable - Supplies the handle of the string table to add the strings to.
|
|
|
|
MultiSzBuffer - Supplies the address of the REG_MULTI_SZ buffer containing
|
|
the strings to be added.
|
|
|
|
StringIdList - Supplies the address of an array of LONGs that receives the
|
|
list of IDs for the added strings (the ordering of the IDs in this
|
|
list will be the same as the ordering of the strings in the MultiSzBuffer.
|
|
|
|
StringIdListSize - Supplies the size, in LONGs, of the StringIdList. If the
|
|
number of strings in MultiSzBuffer exceeds this amount, then only the
|
|
first StringIdListSize strings will be added, and the position in the
|
|
buffer where processing was halted will be stored in UnprocessedBuffer.
|
|
|
|
CaseSensitive - Specifies whether the string should be added case-sensitively.
|
|
|
|
UnprocessedBuffer - Optionally, supplies the address of a character pointer
|
|
that receives the position where processing was aborted because the
|
|
StringIdList buffer was filled. If all strings in the MultiSzBuffer were
|
|
processed, then this pointer will be set to NULL.
|
|
|
|
Return Value:
|
|
|
|
If successful, the return value is the number of strings added.
|
|
If failure, the return value is -1 (this happens if a string cannot be
|
|
added because of an out-of-memory condition).
|
|
|
|
--*/
|
|
{
|
|
PTSTR CurString;
|
|
LONG StringCount = 0;
|
|
|
|
for(CurString = MultiSzBuffer;
|
|
(*CurString && (StringCount < (LONG)StringIdListSize));
|
|
CurString += (lstrlen(CurString)+1)) {
|
|
|
|
if((StringIdList[StringCount] = pStringTableAddString(
|
|
StringTable,
|
|
CurString,
|
|
CaseSensitive
|
|
? STRTAB_CASE_SENSITIVE
|
|
: STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE
|
|
)) == -1)
|
|
{
|
|
StringCount = -1;
|
|
break;
|
|
}
|
|
|
|
StringCount++;
|
|
}
|
|
|
|
if(UnprocessedBuffer) {
|
|
*UnprocessedBuffer = (*CurString ? CurString : NULL);
|
|
}
|
|
|
|
return StringCount;
|
|
}
|
|
|
|
|
|
LONG
|
|
LookUpStringInDevInfoSet(
|
|
IN HDEVINFO DeviceInfoSet,
|
|
IN PTSTR String,
|
|
IN BOOL CaseSensitive
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine looks up the specified string in the string table associated with
|
|
the specified device information set.
|
|
|
|
Arguments:
|
|
|
|
DeviceInfoSet - Supplies the pointer to the device information set containing
|
|
the string table to look the string up in.
|
|
|
|
String - Specifies the string to be looked up. This string is not specified as
|
|
const, so that the lookup routine may modify it (i.e., lower-case it) without
|
|
having to allocate a temporary buffer.
|
|
|
|
CaseSensitive - If TRUE, then a case-sensitive lookup is performed, otherwise, the
|
|
lookup is case-insensitive.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is the string's ID in the string table.
|
|
device information set.
|
|
|
|
If the function fails, the return value is -1.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_INFO_SET pDeviceInfoSet;
|
|
LONG StringId;
|
|
DWORD StringLen;
|
|
|
|
if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) {
|
|
return -1;
|
|
}
|
|
|
|
try {
|
|
|
|
StringId = pStringTableLookUpString(pDeviceInfoSet->StringTable,
|
|
String,
|
|
&StringLen,
|
|
NULL,
|
|
NULL,
|
|
STRTAB_BUFFER_WRITEABLE |
|
|
(CaseSensitive ? STRTAB_CASE_SENSITIVE
|
|
: STRTAB_CASE_INSENSITIVE)
|
|
);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
StringId = -1;
|
|
}
|
|
|
|
UnlockDeviceInfoSet(pDeviceInfoSet);
|
|
|
|
return StringId;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ShouldClassBeExcluded(
|
|
IN LPGUID ClassGuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines whether a class should be excluded from
|
|
some operation, based on whether it has a NoInstallClass or
|
|
NoUseClass value entry in its registry key.
|
|
|
|
Arguments:
|
|
|
|
ClassGuidString - Supplies the address of the class GUID to be
|
|
filtered.
|
|
|
|
Return Value:
|
|
|
|
If the class should be excluded, the return value is TRUE, otherwise
|
|
it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
HKEY hk;
|
|
BOOL ExcludeClass = FALSE;
|
|
|
|
if((hk = SetupDiOpenClassRegKey(ClassGuid, KEY_READ)) != INVALID_HANDLE_VALUE) {
|
|
|
|
try {
|
|
|
|
if(RegQueryValueEx(hk,
|
|
pszNoInstallClass,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL) == ERROR_SUCCESS) {
|
|
|
|
ExcludeClass = TRUE;
|
|
|
|
} else if(RegQueryValueEx(hk,
|
|
pszNoUseClass,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL) == ERROR_SUCCESS) {
|
|
|
|
ExcludeClass = TRUE;
|
|
}
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
//
|
|
// Nothing to do.
|
|
//
|
|
;
|
|
}
|
|
|
|
RegCloseKey(hk);
|
|
}
|
|
|
|
return ExcludeClass;
|
|
}
|
|
|
|
|
|
BOOL
|
|
ClassGuidFromInfVersionNode(
|
|
IN PINF_VERSION_NODE VersionNode,
|
|
OUT LPGUID ClassGuid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine retrieves the class GUID for the INF whose version node
|
|
is specified. If the version node doesn't have a ClassGUID value,
|
|
then the Class value is retrieved, and all class GUIDs matching this
|
|
class name are retrieved. If there is exactly 1 match found, then
|
|
this GUID is returned, otherwise, the routine fails.
|
|
|
|
Arguments:
|
|
|
|
VersionNode - Supplies the address of an INF version node that
|
|
must contain either a ClassGUID or Class entry.
|
|
|
|
ClassGuid - Supplies the address of the variable that receives the
|
|
class GUID.
|
|
|
|
Return Value:
|
|
|
|
If a class GUID was retrieved, the return value is TRUE, otherwise,
|
|
it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
PCTSTR GuidString, NameString;
|
|
DWORD NumGuids;
|
|
|
|
if(GuidString = pSetupGetVersionDatum(VersionNode, pszClassGuid)) {
|
|
|
|
if(pSetupGuidFromString(GuidString, ClassGuid) == NO_ERROR) {
|
|
return TRUE;
|
|
}
|
|
|
|
} else {
|
|
|
|
NameString = pSetupGetVersionDatum(VersionNode, pszClass);
|
|
if(NameString &&
|
|
SetupDiClassGuidsFromName(NameString,
|
|
ClassGuid,
|
|
1,
|
|
&NumGuids) && NumGuids) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
EnumSingleInf(
|
|
IN PCTSTR InfName,
|
|
IN OUT LPWIN32_FIND_DATA InfFileData,
|
|
IN DWORD SearchControl,
|
|
IN PSP_ENUMINF_CALLBACK_ROUTINE EnumInfCallback,
|
|
IN OUT PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds and opens the specified INF, and calls the
|
|
supplied callback routine for it.
|
|
|
|
Arguments:
|
|
|
|
InfName - Supplies the name of the INF to call the callback for.
|
|
|
|
InfFileData - Supplies data returned from FindFirstFile/FindNextFile
|
|
for this INF. This parameter is used as input if the
|
|
INFINFO_INF_NAME_IS_ABSOLUTE SearchControl value is specified.
|
|
If any other SearchControl value is specified, then this buffer
|
|
is used to retrieve the Win32 Find Data for the specified INF.
|
|
|
|
SearchControl - Specifies where the INF should be searched for. May
|
|
be one of the following values:
|
|
|
|
INFINFO_INF_NAME_IS_ABSOLUTE - Open the specified INF name as-is.
|
|
INFINFO_DEFAULT_SEARCH - Look in INF dir, then System32
|
|
INFINFO_REVERSE_DEFAULT_SEARCH - reverse of the above
|
|
INFINFO_INF_PATH_LIST_SEARCH - search each dir in 'DevicePath' list
|
|
(stored in registry).
|
|
|
|
EnumInfCallback - Supplies the address of the callback routine
|
|
to use. The prototype for this callback is as follows:
|
|
|
|
typedef BOOL (*PSP_ENUMINF_CALLBACK_ROUTINE) (
|
|
IN PCTSTR InfFullPath,
|
|
IN LPWIN32_FIND_DATA InfFileData,
|
|
IN OUT PVOID Context
|
|
);
|
|
|
|
The callback routine returns TRUE to continue enumeration,
|
|
or FALSE to abort it.
|
|
|
|
Context - Supplies the address of a buffer that the callback may
|
|
use to retrieve/return data.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, and the enumeration callback returned
|
|
TRUE (continue enumeration), the return value is NO_ERROR.
|
|
|
|
If the function succeeds, and the enumeration callback returned
|
|
FALSE (abort enumeration), the return value is ERROR_CANCELLED.
|
|
|
|
If the function fails, the return value is an ERROR_* status code.
|
|
|
|
--*/
|
|
{
|
|
TCHAR PathBuffer[MAX_PATH];
|
|
PCTSTR InfFullPath;
|
|
DWORD Err;
|
|
|
|
if(SearchControl == INFINFO_INF_NAME_IS_ABSOLUTE) {
|
|
InfFullPath = InfName;
|
|
} else {
|
|
//
|
|
// The specified INF name should be searched for based
|
|
// on the SearchControl type.
|
|
//
|
|
if(Err = SearchForInfFile(InfName,
|
|
InfFileData,
|
|
SearchControl,
|
|
PathBuffer,
|
|
SIZECHARS(PathBuffer),
|
|
NULL) != NO_ERROR) {
|
|
return Err;
|
|
} else {
|
|
InfFullPath = PathBuffer;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Call the supplied callback routine.
|
|
//
|
|
Err = EnumInfCallback(InfFullPath, InfFileData, Context) ? NO_ERROR : ERROR_CANCELLED;
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
DWORD
|
|
EnumInfsInDirPathList(
|
|
IN PCTSTR DirPathList, OPTIONAL
|
|
IN DWORD SearchControl,
|
|
IN PSP_ENUMINF_CALLBACK_ROUTINE EnumInfCallback,
|
|
IN BOOL IgnoreNonCriticalErrors,
|
|
IN OUT PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates all INFs present in the search list specified
|
|
by SearchControl, and calls the supplied callback routine for each.
|
|
|
|
Arguments:
|
|
|
|
DirPathList - Optionally, specifies the search path listing all
|
|
directories to be enumerated. This string may contain multiple
|
|
paths, separated by semicolons (;). If this parameter is not
|
|
specified, then the SearchControl value will determine the
|
|
search path to be used.
|
|
|
|
SearchControl - Specifies the set of directories to be enumerated.
|
|
If SearchPath is specified, this parameter is ignored. May be
|
|
one of the following values:
|
|
|
|
INFINFO_DEFAULT_SEARCH : enumerate %windir%\inf, then
|
|
%windir%\system32
|
|
|
|
INFINFO_REVERSE_DEFAULT_SEARCH : reverse of the above
|
|
|
|
INFINFO_INF_PATH_LIST_SEARCH : enumerate INFs in each of the
|
|
directories listed in the DevicePath value entry under:
|
|
|
|
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion.
|
|
|
|
EnumInfCallback - Supplies the address of the callback routine
|
|
to use. The prototype for this callback is as follows:
|
|
|
|
typedef BOOL (*PSP_ENUMINF_CALLBACK_ROUTINE) (
|
|
IN PCTSTR InfFullPath,
|
|
IN LPWIN32_FIND_DATA InfFileData,
|
|
IN OUT PVOID Context
|
|
);
|
|
|
|
The callback must return TRUE to continue enumeration, or
|
|
FALSE to abort.
|
|
|
|
IgnoreNonCriticalErrors - If TRUE, then all errors are ignored
|
|
except those that prevent enumeration from continuing.
|
|
|
|
Context - Supplies the address of a buffer that the callback may
|
|
use to retrieve/return data.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, and enumeration has not been aborted,
|
|
then the return value is NO_ERROR.
|
|
|
|
If the function succeeds, and enumeration has been aborted,
|
|
then the return value is ERROR_CANCELLED.
|
|
|
|
If the function fails, the return value is an ERROR_* status code.
|
|
|
|
--*/
|
|
{
|
|
DWORD Err = NO_ERROR;
|
|
PCTSTR PathList, CurPath;
|
|
BOOL FreePathList = TRUE;
|
|
|
|
if(DirPathList) {
|
|
//
|
|
// Use the specified search path(s).
|
|
//
|
|
PathList = GetFullyQualifiedMultiSzPathList(DirPathList);
|
|
|
|
} else if(SearchControl == INFINFO_INF_PATH_LIST_SEARCH) {
|
|
//
|
|
// Use our global list of INF search paths.
|
|
//
|
|
PathList = InfSearchPaths;
|
|
FreePathList = FALSE;
|
|
|
|
} else {
|
|
//
|
|
// Retrieve the path list.
|
|
//
|
|
PathList = AllocAndReturnDriverSearchList(SearchControl);
|
|
}
|
|
|
|
if(!PathList) {
|
|
Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Now enumerate the INFs in each path in our MultiSz list.
|
|
//
|
|
for(CurPath = PathList;
|
|
*CurPath;
|
|
CurPath += lstrlen(CurPath) + 1) {
|
|
|
|
if((Err = EnumInfsInDirectory(CurPath,
|
|
EnumInfCallback,
|
|
IgnoreNonCriticalErrors,
|
|
Context)) != NO_ERROR) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(FreePathList) {
|
|
MyFree(PathList);
|
|
}
|
|
|
|
clean0:
|
|
|
|
if((Err == ERROR_CANCELLED) || !IgnoreNonCriticalErrors) {
|
|
return Err;
|
|
} else {
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
EnumInfsInDirectory(
|
|
IN PCTSTR DirPath,
|
|
IN PSP_ENUMINF_CALLBACK_ROUTINE EnumInfCallback,
|
|
IN BOOL IgnoreNonCriticalErrors,
|
|
IN OUT PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine enumerates all of the INF files in the specified directory,
|
|
and calls the supplied callback routine for each one.
|
|
|
|
Arguments:
|
|
|
|
DirPath - Supplies the (fully-qualified) path of the directory to be enumerated.
|
|
|
|
EnumInfCallback - Supplies the address of the callback routine
|
|
to use. The prototype for this callback is as follows:
|
|
|
|
typedef BOOL (*PSP_ENUMINF_CALLBACK_ROUTINE) (
|
|
IN PCTSTR InfFullPath,
|
|
IN LPWIN32_FIND_DATA InfFileData,
|
|
IN OUT PVOID Context
|
|
);
|
|
|
|
The callback must return TRUE to continue enumeration, or
|
|
FALSE to abort.
|
|
|
|
IgnoreNonCriticalErrors - If TRUE, then all errors are ignored
|
|
except those that prevent enumeration from continuing.
|
|
|
|
Context - Supplies the address of a buffer that the callback may
|
|
use to retrieve/return data.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, and enumeration has not been aborted,
|
|
then the return value is NO_ERROR.
|
|
|
|
If the function succeeds, and enumeration has been aborted,
|
|
then the return value is ERROR_CANCELLED.
|
|
|
|
If the function fails, the return value is an ERROR_* status code.
|
|
|
|
--*/
|
|
{
|
|
TCHAR PathBuffer[MAX_PATH];
|
|
HANDLE FindHandle;
|
|
WIN32_FIND_DATA FindData;
|
|
DWORD Err;
|
|
PTSTR CurrentInfFile;
|
|
|
|
//
|
|
// Build a file spec to find all INFs in specified directory
|
|
// (i.e., <DirPath>\*.INF)
|
|
//
|
|
lstrcpy(PathBuffer, DirPath);
|
|
ConcatenatePaths(PathBuffer,
|
|
pszInfWildcard,
|
|
SIZECHARS(PathBuffer),
|
|
NULL
|
|
);
|
|
|
|
FindHandle = FindFirstFile(PathBuffer, &FindData);
|
|
if(FindHandle == INVALID_HANDLE_VALUE) {
|
|
Err = GetLastError();
|
|
goto clean0;
|
|
} else {
|
|
//
|
|
// Get a pointer to the end of the path part of the string
|
|
// (minus the wildcard filename), so that we can append
|
|
// each filename to it.
|
|
//
|
|
CurrentInfFile = _tcsrchr(PathBuffer, TEXT('\\')) + 1;
|
|
}
|
|
|
|
try {
|
|
|
|
do {
|
|
//
|
|
// Build the full pathname.
|
|
//
|
|
lstrcpy(CurrentInfFile, FindData.cFileName);
|
|
|
|
//
|
|
// Now, enumerate this INF.
|
|
//
|
|
if((Err = EnumSingleInf(PathBuffer,
|
|
&FindData,
|
|
INFINFO_INF_NAME_IS_ABSOLUTE,
|
|
EnumInfCallback,
|
|
Context)) != NO_ERROR) {
|
|
|
|
if((Err == ERROR_CANCELLED) || !IgnoreNonCriticalErrors) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
if(FindNextFile(FindHandle, &FindData)) {
|
|
Err = NO_ERROR;
|
|
} else {
|
|
Err = GetLastError();
|
|
}
|
|
|
|
} while(Err == NO_ERROR);
|
|
|
|
} except(EXCEPTION_EXECUTE_HANDLER) {
|
|
Err = ERROR_INVALID_DATA;
|
|
}
|
|
|
|
FindClose(FindHandle);
|
|
|
|
clean0:
|
|
|
|
if((Err == ERROR_CANCELLED) || !IgnoreNonCriticalErrors) {
|
|
return Err;
|
|
} else {
|
|
return NO_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
DWORD
|
|
CreateDriverNode(
|
|
IN UINT Rank,
|
|
IN PCTSTR DevDescription,
|
|
IN PCTSTR DrvDescription,
|
|
IN PCTSTR ProviderName, OPTIONAL
|
|
IN PCTSTR MfgName,
|
|
IN PFILETIME InfDate,
|
|
IN PCTSTR InfFileName,
|
|
IN PCTSTR InfSectionName,
|
|
IN PVOID StringTable,
|
|
OUT PDRIVER_NODE *DriverNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a new driver node, and initializes it with
|
|
the supplied information.
|
|
|
|
Arguments:
|
|
|
|
Rank - The rank match of the driver node being created. This is a
|
|
value in [0..n], where a lower number indicates a higher level of
|
|
compatibility between the driver represented by the node, and the
|
|
device being installed.
|
|
|
|
DevDescription - Supplies the description of the device that will be
|
|
supported by this driver.
|
|
|
|
DrvDescription - Supplies the description of this driver.
|
|
|
|
ProviderName - Supplies the name of the provider of this INF.
|
|
|
|
MfgName - Supplies the name of the manufacturer of this device.
|
|
|
|
InfDate - Supplies the address of the variable containing the date
|
|
when the INF was last written to.
|
|
|
|
InfFileName - Supplies the full name of the INF file for this driver.
|
|
|
|
InfSectionName - Supplies the name of the install section in the INF
|
|
that would be used to install this driver.
|
|
|
|
DriverNode - Supplies the address of a DRIVER_NODE pointer that will
|
|
receive a pointer to the newly-allocated node.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR, otherwise the
|
|
ERROR_* code is returned.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_NODE pDriverNode;
|
|
DWORD Err = ERROR_NOT_ENOUGH_MEMORY;
|
|
TCHAR TempString[MAX_PATH]; // an INF path is the longest string we'll store in here.
|
|
|
|
if(!(pDriverNode = MyMalloc(sizeof(DRIVER_NODE)))) {
|
|
return Err;
|
|
}
|
|
|
|
//
|
|
// Initialize the various fields in the driver node structure.
|
|
//
|
|
ZeroMemory(pDriverNode, sizeof(DRIVER_NODE));
|
|
|
|
pDriverNode->Rank = Rank;
|
|
pDriverNode->InfDate = *InfDate;
|
|
pDriverNode->HardwareId = -1;
|
|
|
|
//
|
|
// Now, add the strings to the associated string table, and store the string IDs.
|
|
//
|
|
// Cast the DrvDescription string being added case-sensitively as PTSTR instead of PCTSTR.
|
|
// Case sensitive string additions don't modify the buffer passed in, so we're safe in
|
|
// doing so.
|
|
//
|
|
if((pDriverNode->DrvDescription = pStringTableAddString(StringTable,
|
|
(PTSTR)DrvDescription,
|
|
STRTAB_CASE_SENSITIVE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// For DevDescription, ProviderName, and MfgName, we use the string table IDs to do fast
|
|
// comparisons for driver nodes. Thus, we need to store case-insensitive IDs. However,
|
|
// these strings are also used for display, so we have to store them in their case-sensitive
|
|
// form as well.
|
|
//
|
|
// We must first copy the strings into a modifiable buffer, since we're going to need to add
|
|
// them case-insensitively.
|
|
//
|
|
lstrcpyn(TempString, DevDescription, SIZECHARS(TempString));
|
|
if((pDriverNode->DevDescriptionDisplayName = pStringTableAddString(StringTable,
|
|
TempString,
|
|
STRTAB_CASE_SENSITIVE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
if((pDriverNode->DevDescription = pStringTableAddString(
|
|
StringTable,
|
|
TempString,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
if(ProviderName) {
|
|
lstrcpyn(TempString, ProviderName, SIZECHARS(TempString));
|
|
if((pDriverNode->ProviderDisplayName = pStringTableAddString(
|
|
StringTable,
|
|
TempString,
|
|
STRTAB_CASE_SENSITIVE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
if((pDriverNode->ProviderName = pStringTableAddString(
|
|
StringTable,
|
|
TempString,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)
|
|
) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
} else {
|
|
pDriverNode->ProviderName = pDriverNode->ProviderDisplayName = -1;
|
|
}
|
|
|
|
lstrcpyn(TempString, MfgName, SIZECHARS(TempString));
|
|
if((pDriverNode->MfgDisplayName = pStringTableAddString(StringTable,
|
|
TempString,
|
|
STRTAB_CASE_SENSITIVE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
if((pDriverNode->MfgName = pStringTableAddString(
|
|
StringTable,
|
|
TempString,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
lstrcpyn(TempString, InfFileName, SIZECHARS(TempString));
|
|
if((pDriverNode->InfFileName = pStringTableAddString(
|
|
StringTable,
|
|
TempString,
|
|
STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// Add INF section name case-sensitively, since we may have a legacy driver node, which requires
|
|
// that the original case be maintained.
|
|
//
|
|
if((pDriverNode->InfSectionName = pStringTableAddString(StringTable,
|
|
(PTSTR)InfSectionName,
|
|
STRTAB_CASE_SENSITIVE)) == -1) {
|
|
goto clean0;
|
|
}
|
|
|
|
//
|
|
// If we get to here, then we've successfully stored all strings.
|
|
//
|
|
Err = NO_ERROR;
|
|
|
|
clean0:
|
|
|
|
if(Err == NO_ERROR) {
|
|
*DriverNode = pDriverNode;
|
|
} else {
|
|
DestroyDriverNodes(pDriverNode);
|
|
}
|
|
|
|
return Err;
|
|
}
|
|
|
|
|
|
VOID
|
|
DestroyDriverNodes(
|
|
IN PDRIVER_NODE DriverNode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys the specified driver node linked list, freeing
|
|
all resources associated with it.
|
|
|
|
Arguments:
|
|
|
|
DriverNode - Supplies a pointer to the head of the driver node linked
|
|
list to be destroyed.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDRIVER_NODE NextNode;
|
|
|
|
while(DriverNode) {
|
|
|
|
NextNode = DriverNode->Next;
|
|
|
|
if(DriverNode->CompatIdList) {
|
|
MyFree(DriverNode->CompatIdList);
|
|
}
|
|
|
|
MyFree(DriverNode);
|
|
|
|
DriverNode = NextNode;
|
|
}
|
|
}
|
|
|
|
|
|
PTSTR
|
|
GetFullyQualifiedMultiSzPathList(
|
|
IN PCTSTR PathList
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine takes a list of semicolon-delimited directory paths, and returns a
|
|
newly-allocated buffer containing a multi-sz list of those paths, fully qualified.
|
|
The buffer returned from this routine must be freed with MyFree().
|
|
|
|
Arguments:
|
|
|
|
PathList - list of directories to be converted.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is a pointer to the allocated buffer
|
|
containing the multi-sz list.
|
|
|
|
If failure (due to out-of-memory), the return value is NULL.
|
|
|
|
--*/
|
|
{
|
|
TCHAR PathListBuffer[MAX_PATH + 1]; // extra char 'cause this is a multi-sz list
|
|
PTSTR CurPath, CharPos, NewBuffer, TempPtr;
|
|
DWORD RequiredSize;
|
|
|
|
//
|
|
// First, convert this semicolon-delimited list into a multi-sz list.
|
|
//
|
|
lstrcpy(PathListBuffer, PathList);
|
|
RequiredSize = DelimStringToMultiSz(PathListBuffer,
|
|
SIZECHARS(PathListBuffer),
|
|
TEXT(';')
|
|
);
|
|
|
|
RequiredSize = (RequiredSize * MAX_PATH * sizeof(TCHAR)) + sizeof(TCHAR);
|
|
|
|
if(!(NewBuffer = MyMalloc(RequiredSize * sizeof(TCHAR)))) {
|
|
return NULL;
|
|
}
|
|
|
|
//
|
|
// Now fill in the buffer with the fully-qualified directory paths.
|
|
//
|
|
CharPos = NewBuffer;
|
|
|
|
for(CurPath = PathListBuffer; *CurPath; CurPath += (lstrlen(CurPath) + 1)) {
|
|
CharPos += GetFullPathName(CurPath, MAX_PATH, CharPos, &TempPtr) + 1;
|
|
}
|
|
|
|
*(CharPos++) = TEXT('\0'); // add extra NULL to terminate the multi-sz list.
|
|
|
|
//
|
|
// Trim this buffer down to just the size required (this should never fail, but
|
|
// it's no big deal if it does).
|
|
//
|
|
if(TempPtr = MyRealloc(NewBuffer, (PBYTE)CharPos - (PBYTE)NewBuffer)) {
|
|
return TempPtr;
|
|
}
|
|
|
|
return NewBuffer;
|
|
}
|
|
|
|
|
|
BOOL
|
|
InitMiniIconList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine initializes the global mini-icon list, including setting up
|
|
the synchronization lock. When this global structure is no longer needed,
|
|
DestroyMiniIconList must be called.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE, otherwise it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
ZeroMemory(&GlobalMiniIconList, sizeof(MINI_ICON_LIST));
|
|
return InitializeSynchronizedAccess(&GlobalMiniIconList.Lock);
|
|
}
|
|
|
|
|
|
BOOL
|
|
DestroyMiniIconList(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine destroys the global mini-icon list created by a call to
|
|
InitMiniIconList.
|
|
|
|
Arguments:
|
|
|
|
None.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is TRUE, otherwise it is FALSE.
|
|
|
|
--*/
|
|
{
|
|
|
|
if(LockMiniIconList(&GlobalMiniIconList)) {
|
|
DestroyMiniIcons();
|
|
DestroySynchronizedAccess(&GlobalMiniIconList.Lock);
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DWORD
|
|
GetModuleEntryPoint(
|
|
IN HKEY hk,
|
|
IN LPCTSTR RegistryValue,
|
|
IN LPCTSTR DefaultProcName,
|
|
OUT HINSTANCE *phinst,
|
|
OUT CLASS_INSTALL_PROC *pEntryPoint
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to retrieve the procedure address of a specified
|
|
function in a specified module.
|
|
|
|
Arguments:
|
|
|
|
hk - Supplies an open registry key that contains a value entry specifying
|
|
the module (and optionally, the entry point) to be retrieved.
|
|
|
|
RegistryValue - Supplies the name of the registry value that contains the
|
|
module and entry point information.
|
|
|
|
DefaultProcName - Supplies the name of a default procedure to use if one
|
|
is not specified in the registry value.
|
|
|
|
phinst - Supplies the address of a variable that receives a handle to the
|
|
specified module, if it is successfully loaded and the entry point found.
|
|
|
|
pEntryPoint - Supplies the address of a function pointer that receives the
|
|
specified entry point in the loaded module.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the specified value entry could not be found, the return value is
|
|
ERROR_DI_DO_DEFAULT.
|
|
If any other error is encountered, an ERROR_* code is returned.
|
|
|
|
Remarks:
|
|
|
|
This function is useful for loading a class installer or property provider,
|
|
and receiving the procedure address specified. The syntax of the registry
|
|
entry is: value=dll[,proc name] where dll is the name of the module to load,
|
|
and proc name is an optional procedure to search for. If proc name is not
|
|
specified, the procedure specified by DefaultProcName will be used.
|
|
|
|
--*/
|
|
{
|
|
DWORD Err;
|
|
DWORD RegDataType, BufferSize;
|
|
TCHAR TempBuffer[MAX_PATH];
|
|
TCHAR ModulePath[MAX_PATH];
|
|
PTSTR StringPtr;
|
|
PSTR ProcName; // ANSI-only, because it's used for GetProcAddress.
|
|
|
|
*phinst = NULL;
|
|
*pEntryPoint = NULL;
|
|
|
|
//
|
|
// See if the specified value entry is present (and of the right data type).
|
|
//
|
|
BufferSize = sizeof(TempBuffer);
|
|
if((RegQueryValueEx(hk,
|
|
RegistryValue,
|
|
NULL,
|
|
&RegDataType,
|
|
(PBYTE)TempBuffer,
|
|
&BufferSize) != ERROR_SUCCESS) ||
|
|
(RegDataType != REG_SZ)) {
|
|
|
|
return ERROR_DI_DO_DEFAULT;
|
|
}
|
|
|
|
lstrcpyn(ModulePath, SystemDirectory, MAX_PATH);
|
|
|
|
//
|
|
// Find the beginning of the entry point name, if present.
|
|
//
|
|
for(StringPtr = TempBuffer + ((BufferSize / sizeof(TCHAR)) - 2);
|
|
StringPtr >= TempBuffer;
|
|
StringPtr--) {
|
|
|
|
if(*StringPtr == TEXT(',')) {
|
|
*(StringPtr++) = TEXT('\0');
|
|
break;
|
|
}
|
|
//
|
|
// If we hit a double-quote mark, then set the character pointer
|
|
// to the beginning of the string so we'll terminate the search.
|
|
//
|
|
if(*StringPtr == TEXT('\"')) {
|
|
StringPtr = TempBuffer;
|
|
}
|
|
}
|
|
|
|
if(StringPtr > TempBuffer) {
|
|
//
|
|
// We encountered a comma in the string. Scan forward from that point to
|
|
// ensure that there aren't any leading spaces in the entry point name.
|
|
//
|
|
for(; (*StringPtr && IsWhitespace(StringPtr)); StringPtr++);
|
|
|
|
if(!(*StringPtr)) {
|
|
//
|
|
// Then there was no entry point given after all.
|
|
//
|
|
StringPtr = TempBuffer;
|
|
}
|
|
}
|
|
|
|
ConcatenatePaths(ModulePath, TempBuffer, MAX_PATH, NULL);
|
|
|
|
if(!(*phinst = LoadLibrary(ModulePath))) {
|
|
return GetLastError();
|
|
}
|
|
|
|
//
|
|
// We've successfully loaded the module, now get the entry point.
|
|
// (GetProcAddress is an ANSI-only API, so if we're compiled UNICODE,
|
|
// we have to convert the proc name to ANSI here. We'll re-use the
|
|
// ModulePath buffer as an ANSI buffer to store the ANSI version of
|
|
// the entry point name.)
|
|
//
|
|
#ifdef UNICODE
|
|
ProcName = (PSTR)ModulePath; // Re-use ModulePath as an ANSI buffer.
|
|
#endif
|
|
|
|
if(StringPtr > TempBuffer) {
|
|
//
|
|
// An entry point was specified in the value entry--use it instead
|
|
// of the default provided.
|
|
//
|
|
#ifdef UNICODE
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
StringPtr,
|
|
-1,
|
|
ProcName,
|
|
sizeof(ModulePath),
|
|
NULL,
|
|
NULL
|
|
);
|
|
#else // !UNICODE
|
|
ProcName = StringPtr;
|
|
#endif // !UNICODE
|
|
|
|
} else {
|
|
//
|
|
// No entry point was specified--use default.
|
|
//
|
|
#ifdef UNICODE
|
|
WideCharToMultiByte(CP_ACP,
|
|
0,
|
|
DefaultProcName,
|
|
-1,
|
|
ProcName,
|
|
sizeof(ModulePath),
|
|
NULL,
|
|
NULL
|
|
);
|
|
#else // !UNICODE
|
|
ProcName = (PSTR)DefaultProcName;
|
|
#endif // !UNICODE
|
|
|
|
}
|
|
|
|
if(!(*pEntryPoint = (CLASS_INSTALL_PROC)GetProcAddress(*phinst, ProcName))) {
|
|
Err = GetLastError();
|
|
FreeLibrary(*phinst);
|
|
*phinst = NULL;
|
|
return Err;
|
|
}
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupGuidFromString(
|
|
IN PCTSTR GuidString,
|
|
OUT LPGUID Guid
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts the character representation of a GUID into its binary
|
|
form (a GUID struct). The GUID is in the following form:
|
|
|
|
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
|
|
|
where 'x' is a hexadecimal digit.
|
|
|
|
Arguments:
|
|
|
|
GuidString - Supplies a pointer to the null-terminated GUID string. The
|
|
|
|
Guid - Supplies a pointer to the variable that receives the GUID structure.
|
|
|
|
Return Value:
|
|
|
|
If the function succeeds, the return value is NO_ERROR.
|
|
If the function fails, the return value is RPC_S_INVALID_STRING_UUID.
|
|
|
|
--*/
|
|
{
|
|
TCHAR UuidBuffer[GUID_STRING_LEN - 1];
|
|
|
|
//
|
|
// Since we're using a RPC UUID routine, we need to strip off the surrounding
|
|
// curly braces first.
|
|
//
|
|
if(*GuidString++ != TEXT('{')) {
|
|
return RPC_S_INVALID_STRING_UUID;
|
|
}
|
|
|
|
lstrcpyn(UuidBuffer, GuidString, SIZECHARS(UuidBuffer));
|
|
|
|
if((lstrlen(UuidBuffer) != GUID_STRING_LEN - 2) ||
|
|
(UuidBuffer[GUID_STRING_LEN - 3] != TEXT('}'))) {
|
|
|
|
return RPC_S_INVALID_STRING_UUID;
|
|
}
|
|
|
|
UuidBuffer[GUID_STRING_LEN - 3] = TEXT('\0');
|
|
|
|
return ((UuidFromString(UuidBuffer, Guid) == RPC_S_OK) ? NO_ERROR : RPC_S_INVALID_STRING_UUID);
|
|
}
|
|
|
|
|
|
DWORD
|
|
pSetupStringFromGuid(
|
|
IN CONST GUID *Guid,
|
|
OUT PTSTR GuidString,
|
|
IN DWORD GuidStringSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine converts a GUID into a null-terminated string which represents
|
|
it. This string is of the form:
|
|
|
|
{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}
|
|
|
|
where x represents a hexadecimal digit.
|
|
|
|
This routine comes from ole32\common\ccompapi.cxx. It is included here to avoid linking
|
|
to ole32.dll. (The RPC version allocates memory, so it was avoided as well.)
|
|
|
|
Arguments:
|
|
|
|
Guid - Supplies a pointer to the GUID whose string representation is
|
|
to be retrieved.
|
|
|
|
GuidString - Supplies a pointer to character buffer that receives the
|
|
string. This buffer must be _at least_ 39 (GUID_STRING_LEN) characters
|
|
long.
|
|
|
|
Return Value:
|
|
|
|
If success, the return value is NO_ERROR.
|
|
if failure, the return value is
|
|
|
|
--*/
|
|
{
|
|
CONST BYTE *GuidBytes;
|
|
INT i;
|
|
|
|
if(GuidStringSize < GUID_STRING_LEN) {
|
|
return ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
GuidBytes = (CONST BYTE *)Guid;
|
|
|
|
*GuidString++ = TEXT('{');
|
|
|
|
for(i = 0; i < sizeof(GuidMap); i++) {
|
|
|
|
if(GuidMap[i] == '-') {
|
|
*GuidString++ = TEXT('-');
|
|
} else {
|
|
*GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0xF0) >> 4 ];
|
|
*GuidString++ = szDigits[ (GuidBytes[GuidMap[i]] & 0x0F) ];
|
|
}
|
|
}
|
|
|
|
*GuidString++ = TEXT('}');
|
|
*GuidString = TEXT('\0');
|
|
|
|
return NO_ERROR;
|
|
}
|
|
|
|
|
|
BOOL
|
|
pSetupIsGuidNull(
|
|
IN CONST GUID *Guid
|
|
)
|
|
{
|
|
return IsEqualGUID(Guid, &GUID_NULL);
|
|
}
|
|
|