/*++ Copyright (c) 1993-2000 Microsoft Corporation Module Name: devinfo.c Abstract: Device Installer routines dealing with device information sets Author: Lonny McMichael (lonnym) 10-May-1995 Revision History: --*/ #include "precomp.h" #pragma hdrstop // // Define the context structure used by the default device comparison // callback (used by SetupDiRegisterDeviceInfo). // typedef struct _DEFAULT_DEVCMP_CONTEXT { PCS_RESOURCE NewDevCsResource; PCS_RESOURCE CurDevCsResource; ULONG CsResourceSize; // applies to both buffers. } DEFAULT_DEVCMP_CONTEXT, *PDEFAULT_DEVCMP_CONTEXT; // // Private routine prototypes. // DWORD pSetupOpenAndAddNewDevInfoElem( IN PDEVICE_INFO_SET DeviceInfoSet, IN PCTSTR DeviceInstanceId, IN BOOL AllowPhantom, IN CONST GUID *ClassGuid, OPTIONAL IN HWND hwndParent, OPTIONAL OUT PDEVINFO_ELEM *DevInfoElem, IN BOOL CheckIfAlreadyThere, OUT PBOOL AlreadyPresent, OPTIONAL IN BOOL OpenExistingOnly, IN ULONG CmLocateFlags, IN PDEVICE_INFO_SET ContainingDeviceInfoSet ); DWORD pSetupAddNewDeviceInfoElement( IN PDEVICE_INFO_SET DeviceInfoSet, IN DEVINST DevInst, IN CONST GUID *ClassGuid, IN PCTSTR Description, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD DiElemFlags, IN PDEVICE_INFO_SET ContainingDeviceInfoSet, OUT PDEVINFO_ELEM *DeviceInfoElement ); DWORD pSetupClassGuidFromDevInst( IN DEVINST DevInst, IN HMACHINE hMachine, OUT LPGUID ClassGuid ); DWORD pSetupDupDevCompare( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA NewDeviceData, IN PSP_DEVINFO_DATA ExistingDeviceData, IN PVOID CompareContext ); DWORD pSetupAddInterfaceDeviceToDevInfoElem( IN PDEVICE_INFO_SET DeviceInfoSet, IN PDEVINFO_ELEM DevInfoElem, IN CONST GUID *ClassGuid, IN PTSTR InterfaceDeviceName, IN BOOL IsActive, IN BOOL IsDefault, IN BOOL StoreTruncateNode, IN BOOL OpenExistingOnly, OUT PINTERFACE_DEVICE_NODE *InterfaceDeviceNode OPTIONAL ); DWORD _SetupDiOpenInterfaceDevice( IN HDEVINFO DeviceInfoSet, IN PTSTR DevicePath, IN DWORD OpenFlags, OUT PSP_DEVICE_INTERFACE_DATA InterfaceDeviceData OPTIONAL ); DWORD pSetupGetDevInstNameAndStatusForInterfaceDevice( IN HKEY hKeyInterfaceClass, IN PCTSTR InterfaceDeviceName, OUT PTSTR OwningDevInstName, OPTIONAL IN DWORD OwningDevInstNameSize, OUT PBOOL IsActive, OPTIONAL OUT PBOOL IsDefault OPTIONAL ); BOOL pSetupDiGetOrSetDeviceInfoContext( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD InContext, OUT PDWORD OutContext OPTIONAL ); HDEVINFO WINAPI SetupDiCreateDeviceInfoList( IN CONST GUID *ClassGuid, OPTIONAL IN HWND hwndParent OPTIONAL ) /*++ Routine Description: This API creates an empty device information set that will contain device device information member elements. This set may be associated with an optionally-specified class GUID. Arguments: ClassGuid - Optionally, supplies a pointer to the class GUID that is to be associated with this set. hwndParent - Optionally, supplies the window handle of the top-level window to use for any UI related to installation of a class driver contained in this set's global class driver list (if it has one). Return Value: If the function succeeds, the return value is a handle to an empty device information set. If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError. --*/ { return SetupDiCreateDeviceInfoListEx(ClassGuid, hwndParent, NULL, NULL); } #ifdef UNICODE // // ANSI version // HDEVINFO WINAPI SetupDiCreateDeviceInfoListExA( IN CONST GUID *ClassGuid, OPTIONAL IN HWND hwndParent, OPTIONAL IN PCSTR MachineName, OPTIONAL IN PVOID Reserved ) { PCWSTR UnicodeMachineName; DWORD rc; HDEVINFO hDevInfo; hDevInfo = INVALID_HANDLE_VALUE; if(MachineName) { rc = pSetupCaptureAndConvertAnsiArg(MachineName, &UnicodeMachineName); } else { UnicodeMachineName = NULL; rc = NO_ERROR; } if(rc == NO_ERROR) { hDevInfo = SetupDiCreateDeviceInfoListExW(ClassGuid, hwndParent, UnicodeMachineName, Reserved ); rc = GetLastError(); if(UnicodeMachineName) { MyFree(UnicodeMachineName); } } SetLastError(rc); return hDevInfo; } #else // // Unicode version // HDEVINFO WINAPI SetupDiCreateDeviceInfoListExW( IN CONST GUID *ClassGuid, OPTIONAL IN HWND hwndParent, OPTIONAL IN PCWSTR MachineName, OPTIONAL IN PVOID Reserved ) { UNREFERENCED_PARAMETER(ClassGuid); UNREFERENCED_PARAMETER(hwndParent); UNREFERENCED_PARAMETER(MachineName); UNREFERENCED_PARAMETER(Reserved); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif HDEVINFO WINAPI SetupDiCreateDeviceInfoListEx( IN CONST GUID *ClassGuid, OPTIONAL IN HWND hwndParent, OPTIONAL IN PCTSTR MachineName, OPTIONAL IN PVOID Reserved ) /*++ Routine Description: This API creates an empty device information set that will contain device device information member elements. This set may be associated with an optionally-specified class GUID. Arguments: ClassGuid - Optionally, supplies a pointer to the class GUID that is to be associated with this set. hwndParent - Optionally, supplies the window handle of the top-level window to use for any UI related to installation of a class driver contained in this set's global class driver list (if it has one). MachineName - Optionally, supplies the name of the machine for which this device information set is to be related. Only devices on that machine may be opened/created. If this parameter is NULL, then the local machine is used. Reserved - Reserved for future use--must be NULL. Return Value: If the function succeeds, the return value is a handle to an empty device information set. If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError. --*/ { PDEVICE_INFO_SET DeviceInfoSet; DWORD Err = NO_ERROR; CONFIGRET cr; // // Make sure the user didn't pass us anything in the Reserved parameter. // if(Reserved) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } if(DeviceInfoSet = AllocateDeviceInfoSet()) { try { // // If the user specified the name of a remote machine, connect to // that machine now. // if(MachineName) { if(CR_SUCCESS != (cr = CM_Connect_Machine(MachineName, &(DeviceInfoSet->hMachine)))) { // // Make sure hMachine is still NULL, so we won't try to disconnect later. // DeviceInfoSet->hMachine = NULL; Err = MapCrToSpError(cr, ERROR_INVALID_DATA); goto clean0; } // // Store the machine name in the string table, so it can be // retrieved later via SetupDiGetDeviceInfoListDetail. // if(-1 == (DeviceInfoSet->MachineName = pStringTableAddString(DeviceInfoSet->StringTable, (PTSTR)MachineName, STRTAB_CASE_SENSITIVE, NULL, 0))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } } if(ClassGuid) { // // If a class GUID was specified, then store it away in // the device information set. // CopyMemory(&(DeviceInfoSet->ClassGuid), ClassGuid, sizeof(GUID) ); DeviceInfoSet->HasClassGuid = TRUE; } DeviceInfoSet->InstallParamBlock.hwndParent = hwndParent; clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Reference the following variable so the compiler will respect statement ordering // w.r.t. assignment. // DeviceInfoSet->hMachine = DeviceInfoSet->hMachine; } if(Err != NO_ERROR) { DestroyDeviceInfoSet(NULL, DeviceInfoSet); } } else { Err = ERROR_NOT_ENOUGH_MEMORY; } SetLastError(Err); return (Err == NO_ERROR) ? (HDEVINFO)DeviceInfoSet : (HDEVINFO)INVALID_HANDLE_VALUE; } BOOL WINAPI SetupDiGetDeviceInfoListClass( IN HDEVINFO DeviceInfoSet, OUT LPGUID ClassGuid ) /*++ Routine Description: This API retrieves the class GUID associated with a device information set (if it has an associated class). Arguments: DeviceInfoSet - Supplies a handle to a device information set whose associated class is being queried. ClassGuid - Supplies a pointer to a variable that receives the GUID for the associated class. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. If the set has no associated class, then GetLastError will return ERROR_NO_ASSOCIATED_CLASS. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { if(pDeviceInfoSet->HasClassGuid) { // // Copy the GUID to the user-supplied buffer. // CopyMemory(ClassGuid, &(pDeviceInfoSet->ClassGuid), sizeof(GUID) ); } else { Err = ERROR_NO_ASSOCIATED_CLASS; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return (Err == NO_ERROR); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiGetDeviceInfoListDetailA( IN HDEVINFO DeviceInfoSet, OUT PSP_DEVINFO_LIST_DETAIL_DATA_A DeviceInfoSetDetailData ) { DWORD rc; BOOL b; SP_DEVINFO_LIST_DETAIL_DATA_W UnicodeDevInfoSetDetails; UnicodeDevInfoSetDetails.cbSize = sizeof(SP_DEVINFO_LIST_DETAIL_DATA_W); b = SetupDiGetDeviceInfoListDetailW(DeviceInfoSet, &UnicodeDevInfoSetDetails); rc = GetLastError(); if(b) { rc = pSetupDiDevInfoSetDetailDataUnicodeToAnsi(&UnicodeDevInfoSetDetails, DeviceInfoSetDetailData); if(rc != NO_ERROR) { b = FALSE; } } SetLastError(rc); return(b); } #else // // Unicode stub // BOOL WINAPI SetupDiGetDeviceInfoListDetailW( IN HDEVINFO DeviceInfoSet, OUT PSP_DEVINFO_LIST_DETAIL_DATA_W DeviceInfoSetDetailData ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DeviceInfoSetDetailData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiGetDeviceInfoListDetail( IN HDEVINFO DeviceInfoSet, OUT PSP_DEVINFO_LIST_DETAIL_DATA DeviceInfoSetDetailData ) /*++ Routine Description: This routine retrieves information about the specified device information set, such as its associated class (if any), and the remote machine it was opened for (if this is a remoted HDEVINFO). This API supercedes SetupDiGetDeviceInfoListClass. Arguments: DeviceInfoSet - Supplies a handle to the device information set to retrieve detailed information for. DeviceInfoSetDetailData - Supplies the address of a structure that receives information about the specified device information set. This structure is defined as follows: typedef struct _SP_DEVINFO_LIST_DETAIL_DATA { DWORD cbSize; GUID ClassGuid; HANDLE RemoteMachineHandle; TCHAR RemoteMachineName[SP_MAX_MACHINENAME_LENGTH]; } SP_DEVINFO_LIST_DETAIL_DATA, *PSP_DEVINFO_LIST_DETAIL_DATA; where: ClassGuid specifies the class associated with the device information set, or GUID_NULL if there is no associated class. RemoteMachineHandle is the ConfigMgr32 machine handle used to access the remote machine, if this is a remoted HDEVINFO (i.e., a MachineName was specified when the set was created via SetupDiCreateDeviceInfoListEx or SetupDiGetClassDevsEx). All DevInst handles stored in SP_DEVINFO_DATA structures for elements of this set are relative to this handle, and must be used in combination with this handle when calling any CM_*_Ex APIs. If this is not a device information set for a remote machine, this field will be NULL. NOTE: DO NOT destroy this handle via CM_Disconnect_Machine. This handle will be cleaned up when the device information set is destroyed via SetupDiDestroyDeviceInfoList. RemoteMachineName specifies the name used to connect to the remote machine whose handle is stored in RemoteMachineHandle. If this is not a device information set for a remote machine, this will be an empty string. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: The cbSize field of the output structure must be set to sizeof(SP_DEVINFO_LIST_DETAIL_DATA) or the call will fail with ERROR_INVALID_USER_BUFFER. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PCTSTR MachineName; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { if(DeviceInfoSetDetailData->cbSize != sizeof(SP_DEVINFO_LIST_DETAIL_DATA)) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } // // Store the set's associated class GUID, or GUID_NULL if there isn't one. // if(pDeviceInfoSet->HasClassGuid) { CopyMemory(&(DeviceInfoSetDetailData->ClassGuid), &(pDeviceInfoSet->ClassGuid), sizeof(GUID) ); } else { CopyMemory(&(DeviceInfoSetDetailData->ClassGuid), &GUID_NULL, sizeof(GUID)); } DeviceInfoSetDetailData->RemoteMachineHandle = pDeviceInfoSet->hMachine; // // If this is a remoted HDEVINFO, store the machine name in the caller's buffer, // otherwise store an empty string. // if(pDeviceInfoSet->hMachine) { MYASSERT(pDeviceInfoSet->MachineName != -1); MachineName = pStringTableStringFromId(pDeviceInfoSet->StringTable, pDeviceInfoSet->MachineName); lstrcpyn(DeviceInfoSetDetailData->RemoteMachineName, MachineName, SIZECHARS(DeviceInfoSetDetailData->RemoteMachineName) ); } else { MYASSERT(pDeviceInfoSet->MachineName == -1); *(DeviceInfoSetDetailData->RemoteMachineName) = TEXT('\0'); } clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return (Err == NO_ERROR); } BOOL WINAPI SetupDiDestroyDeviceInfoList( IN HDEVINFO DeviceInfoSet ) /*++ Routine Description: This API destroys a device information set, freeing all associated memory. Arguments: DeviceInfoSet - Supplies a handle to a device information set to be destroyed. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { DWORD Err; PDEVICE_INFO_SET pDeviceInfoSet; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } try { Err = DestroyDeviceInfoSet(DeviceInfoSet, pDeviceInfoSet); } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_HANDLE; } SetLastError(Err); return (Err == NO_ERROR); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiCreateDeviceInfoA( IN HDEVINFO DeviceInfoSet, IN PCSTR DeviceName, IN CONST GUID *ClassGuid, IN PCSTR DeviceDescription, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD CreationFlags, OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) { PCWSTR deviceName,deviceDescription; DWORD rc; BOOL b; b = FALSE; rc = pSetupCaptureAndConvertAnsiArg(DeviceName,&deviceName); if(rc == NO_ERROR) { if(DeviceDescription) { rc = pSetupCaptureAndConvertAnsiArg(DeviceDescription,&deviceDescription); } else { deviceDescription = NULL; } if(rc == NO_ERROR) { b = SetupDiCreateDeviceInfoW( DeviceInfoSet, deviceName, ClassGuid, deviceDescription, hwndParent, CreationFlags, DeviceInfoData ); rc = GetLastError(); if(deviceDescription) { MyFree(deviceDescription); } } MyFree(deviceName); } else { // // The DeviceName parameter was bad--return the same error the unicode API does. // rc = ERROR_INVALID_DEVINST_NAME; } SetLastError(rc); return(b); } #else // // Unicode version // BOOL WINAPI SetupDiCreateDeviceInfoW( IN HDEVINFO DeviceInfoSet, IN PCWSTR DeviceName, IN CONST GUID *ClassGuid, IN PCWSTR DeviceDescription, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD CreationFlags, OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DeviceName); UNREFERENCED_PARAMETER(ClassGuid); UNREFERENCED_PARAMETER(DeviceDescription); UNREFERENCED_PARAMETER(hwndParent); UNREFERENCED_PARAMETER(CreationFlags); UNREFERENCED_PARAMETER(DeviceInfoData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiCreateDeviceInfo( IN HDEVINFO DeviceInfoSet, IN PCTSTR DeviceName, IN CONST GUID *ClassGuid, IN PCTSTR DeviceDescription, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD CreationFlags, OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) /*++ Routine Description: This API creates a new device information element, and adds it as a new member to the specified set. Arguments: DeviceInfoSet - Supplies a handle to a device information set to which this new device information element is to be added. DeviceName - Supplies either a full device instance ID (e.g., Root\*PNP0500\0000) or a Root-enumerated device ID, minus enumerator branch prefix and instance ID suffix (e.g., *PNP0500). The latter may only be specified if the DICD_GENERATE_ID flag is specified in the CreationFlags parameter. ClassGuid - Supplies a pointer to the GUID for this device's class. If the class is not yet known, this value should be GUID_NULL. DeviceDescription - Optionally, supplies a textual description of the device. hwndParent - Optionally, supplies the window handle of the top-level window to use for any UI related to installing the device. CreationFlags - Supplies flags controlling how the device information element is to be created. May be a combination of the following values: DICD_GENERATE_ID - If this flag is specified, then DeviceName contains only a Root-enumerated device ID, and needs to have a unique device instance key created for it. This unique device instance key will be generated as: Enum\Root\\ where is a 4-digit, base-10 number that is unique among all subkeys under Enum\Root\. The API, SetupDiGetDeviceInstanceId, may be called to find out what ID was generated for this device information element. DICD_INHERIT_CLASSDRVS - If this flag is specified, then the resulting device information element will inherit the class driver list (if any) associated with the device information set itself. In addition, if there is a selected driver for the device information set, that same driver will be selected for the new device information element. DeviceInfoData - Optionaly, supplies a pointer to the variable that receives a context structure initialized for this new device information element. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: If this device instance is being added to a set that has an associated class, then the device class must be the same, or the call will fail, and GetLastError will return ERROR_CLASS_MISMATCH. If the specified device instance is the same as an existing device instance key in the registry, the call will fail with ERROR_DEVINST_ALREADY_EXISTS. (This only applies if DICD_GENERATE_ID is not specified.) The specified class GUID will be written out to the ClassGUID device instance value entry. If the class name can be retrieved (via SetupDiClassNameFromGuid), then it will be written to the Class value entry as well. If the new device information element was successfully created, but the user-supplied DeviceInfoData buffer is invalid, this API will return FALSE, with GetLastError returning ERROR_INVALID_USER_BUFFER. The device information element _will_ have been added as a new member of the set, however. Note that since new device information elements are always added at the end of the existing list, the enumeration ordering is preserved, thus we don't need to invalidate our enumeration hint. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err, StringLen; PDEVINFO_ELEM DevInfoElem, PrevTailDevInfoElem; DEVINST DevInst, RootDevInst; CONFIGRET cr; ULONG CmFlags; TCHAR TempString[GUID_STRING_LEN]; PDRIVER_LIST_OBJECT CurDrvListObject; // // We use the TempString buffer both for the string representation of // a Class GUID, and for the Class name. The following assert ensures // that our assumptions about the relative lengths of these two strings // continues to be valid. // MYASSERT(GUID_STRING_LEN >= MAX_CLASS_NAME_LEN); if(CreationFlags & ~(DICD_GENERATE_ID | DICD_INHERIT_CLASSDRVS)) { SetLastError(ERROR_INVALID_FLAGS); return FALSE; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; DevInst = 0; DevInfoElem = NULL; try { // // Get a pointer to the current tail of the devinfo element list for this // set, so that we can easily lop off the new node if we encounter an error // after insertion. // PrevTailDevInfoElem = pDeviceInfoSet->DeviceInfoTail; // // Get a handle to the root device instance, to be used as the parent // for the phantom device instance we're about to create. // if(CM_Locate_DevInst_Ex(&RootDevInst, NULL, CM_LOCATE_DEVINST_NORMAL, pDeviceInfoSet->hMachine) != CR_SUCCESS) { // // We're really hosed if we can't get a handle to the root device // instance! // Err = ERROR_INVALID_DATA; goto clean0; } // // Create a handle to a phantom device instance. // CmFlags = CM_CREATE_DEVINST_PHANTOM; if(CreationFlags & DICD_GENERATE_ID) { CmFlags |= CM_CREATE_DEVINST_GENERATE_ID; } if((cr = CM_Create_DevInst_Ex(&DevInst, (DEVINSTID)DeviceName, RootDevInst, CmFlags, pDeviceInfoSet->hMachine)) != CR_SUCCESS) { // // Make sure DevInst handle is still invalid, so we won't try to // delete it later. // DevInst = 0; Err = MapCrToSpError(cr, ERROR_INVALID_DATA); goto clean0; } if(NO_ERROR != (Err = pSetupAddNewDeviceInfoElement(pDeviceInfoSet, DevInst, ClassGuid, DeviceDescription, hwndParent, DIE_IS_PHANTOM, pDeviceInfoSet, &DevInfoElem))) { // // Make sure DevInfoElem is still NULL, so we won't try to free it later. // DevInfoElem = NULL; goto clean0; } // // Now, set the Class and ClassGUID properties for the new device instance. // pSetupStringFromGuid(ClassGuid, TempString, SIZECHARS(TempString)); CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst, CM_DRP_CLASSGUID, (PVOID)TempString, GUID_STRING_LEN * sizeof(TCHAR), 0,pDeviceInfoSet->hMachine); if(!IsEqualGUID(ClassGuid, &GUID_NULL) && SetupDiClassNameFromGuid(ClassGuid, TempString, SIZECHARS(TempString), &StringLen)) { CM_Set_DevInst_Registry_Property_Ex(DevInfoElem->DevInst, CM_DRP_CLASS, (PVOID)TempString, StringLen * sizeof(TCHAR), 0,pDeviceInfoSet->hMachine); } // // If the caller wants the newly-created devinfo element to inherit the global // class driver list, do that now. // if((CreationFlags & DICD_INHERIT_CLASSDRVS) && (pDeviceInfoSet->ClassDriverHead)) { // // Find the global class driver list in the devinfo set's list of driver lists. // CurDrvListObject = GetAssociatedDriverListObject(pDeviceInfoSet->ClassDrvListObjectList, pDeviceInfoSet->ClassDriverHead, NULL ); MYASSERT(CurDrvListObject && (CurDrvListObject->RefCount > 0)); // // We found the driver list object, now do the inheritance, and increment the refcount. // DevInfoElem->ClassDriverCount = pDeviceInfoSet->ClassDriverCount; DevInfoElem->ClassDriverHead = pDeviceInfoSet->ClassDriverHead; DevInfoElem->ClassDriverTail = pDeviceInfoSet->ClassDriverTail; if(DevInfoElem->SelectedDriver = pDeviceInfoSet->SelectedClassDriver) { DevInfoElem->SelectedDriverType = SPDIT_CLASSDRIVER; } DevInfoElem->InstallParamBlock.Flags |= CurDrvListObject->ListCreationFlags; DevInfoElem->InstallParamBlock.FlagsEx |= CurDrvListObject->ListCreationFlagsEx; DevInfoElem->InstallParamBlock.DriverPath = CurDrvListObject->ListCreationDriverPath; CurDrvListObject->RefCount++; } clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Reference the following variables so the compiler will respect our statement ordering // w.r.t. assignment. // DevInst = DevInst; DevInfoElem = DevInfoElem; PrevTailDevInfoElem = PrevTailDevInfoElem; } if(Err == NO_ERROR) { if(DeviceInfoData) { // // The user supplied a buffer to receive a SP_DEVINFO_DATA // structure, so fill that in now. // try { if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, DevInfoElem, DeviceInfoData))) { Err = ERROR_INVALID_USER_BUFFER; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } } } else if(DevInst) { // // This should never fail. // cr = CM_Uninstall_DevInst(DevInst, 0); MYASSERT(cr == CR_SUCCESS); if(DevInfoElem) { // // An error occurred after we created the device information element--clean it up now. // try { MYASSERT(!DevInfoElem->Next); if(PrevTailDevInfoElem) { MYASSERT(PrevTailDevInfoElem->Next == DevInfoElem); PrevTailDevInfoElem->Next = NULL; pDeviceInfoSet->DeviceInfoTail = PrevTailDevInfoElem; } else { pDeviceInfoSet->DeviceInfoHead = pDeviceInfoSet->DeviceInfoTail = NULL; } MYASSERT(pDeviceInfoSet->DeviceInfoCount > 0); pDeviceInfoSet->DeviceInfoCount--; MyFree(DevInfoElem); } except(EXCEPTION_EXECUTE_HANDLER) { ; // nothing to do. } } } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiOpenDeviceInfoA( IN HDEVINFO DeviceInfoSet, IN PCSTR DeviceInstanceId, IN HWND hwndParent, OPTIONAL IN DWORD OpenFlags, OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) { PCWSTR deviceInstanceId; DWORD rc; BOOL b; rc = pSetupCaptureAndConvertAnsiArg(DeviceInstanceId,&deviceInstanceId); if(rc == NO_ERROR) { b = SetupDiOpenDeviceInfoW( DeviceInfoSet, deviceInstanceId, hwndParent, OpenFlags, DeviceInfoData ); rc = GetLastError(); MyFree(deviceInstanceId); } else { b = FALSE; } SetLastError(rc); return(b); } #else // // Unicode version // BOOL WINAPI SetupDiOpenDeviceInfoW( IN HDEVINFO DeviceInfoSet, IN PCWSTR DeviceInstanceId, IN HWND hwndParent, OPTIONAL IN DWORD OpenFlags, OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DeviceInstanceId); UNREFERENCED_PARAMETER(hwndParent); UNREFERENCED_PARAMETER(OpenFlags); UNREFERENCED_PARAMETER(DeviceInfoData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiOpenDeviceInfo( IN HDEVINFO DeviceInfoSet, IN PCTSTR DeviceInstanceId, IN HWND hwndParent, OPTIONAL IN DWORD OpenFlags, OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) /*++ Routine Description: This API retrieves information about an existing device instance, and adds it to the specified device information set. If a device information element already exists for this device instance, the existing element is returned. Arguments: DeviceInfoSet - Supplies a handle to a device information set to which the opened device information element is to be added. DeviceInstanceId - Supplies the ID of the device instance. This is the registry path (relative to the Enum branch) of the device instance key. (E.g., Root\*PNP0500\0000) hwndParent - Optionally, supplies the window handle of the top-level window to use for any UI related to installing the device. OpenFlags - Supplies flags controlling how the device information element is to be opened. May be a combination of the following values: DIOD_INHERIT_CLASSDRVS - If this flag is specified, then the resulting device information element will inherit the class driver list (if any) associated with the device information set itself. In addition, if there is a selected driver for the device information set, that same driver will be selected for the new device information element. If the device information element was already present, its class driver list (if any) will be replaced with this new, inherited, list. DIOD_CANCEL_REMOVE - If this flag is set, a device that was marked for removal will be have its pending removal cancelled. DeviceInfoData - Optionally, supplies a pointer to the variable that receives a context structure initialized for the opened device information element. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: If this device instance is being added to a set that has an associated class, then the device class must be the same, or the call will fail, and GetLastError will return ERROR_CLASS_MISMATCH. If the new device information element was successfully opened, but the user-supplied DeviceInfoData buffer is invalid, this API will return FALSE, with GetLastError returning ERROR_INVALID_USER_BUFFER. The device information element _will_ have been added as a new member of the set, however. Note that since new device information elements are always added at the end of the existing list, the enumeration ordering is preserved, thus we don't need to invalidate our enumeration hint. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem; PDRIVER_LIST_OBJECT CurDrvListObject; BOOL AlreadyPresent; if(OpenFlags & ~(DIOD_INHERIT_CLASSDRVS | DIOD_CANCEL_REMOVE)) { SetLastError(ERROR_INVALID_FLAGS); return FALSE; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { Err = pSetupOpenAndAddNewDevInfoElem(pDeviceInfoSet, DeviceInstanceId, TRUE, NULL, hwndParent, &DevInfoElem, TRUE, &AlreadyPresent, FALSE, ((OpenFlags & DIOD_CANCEL_REMOVE) ? CM_LOCATE_DEVNODE_CANCELREMOVE : 0), pDeviceInfoSet ); if(Err != NO_ERROR) { goto clean0; } // // If the caller wants the newly-opened devinfo element to inherit the global // class driver list, do that now. // if(OpenFlags & DIOD_INHERIT_CLASSDRVS) { // // If this devinfo element already existed, then it may already have a class // driver list. Destroy that list before inheriting from the global class // driver list. // if(AlreadyPresent) { // // If the selected driver is a class driver, then reset the selection. // if(DevInfoElem->SelectedDriverType == SPDIT_CLASSDRIVER) { DevInfoElem->SelectedDriverType = SPDIT_NODRIVER; DevInfoElem->SelectedDriver = NULL; } // // Destroy the existing class driver list for this device. // DereferenceClassDriverList(pDeviceInfoSet, DevInfoElem->ClassDriverHead); DevInfoElem->ClassDriverCount = 0; DevInfoElem->ClassDriverHead = DevInfoElem->ClassDriverTail = NULL; DevInfoElem->InstallParamBlock.Flags &= ~(DI_DIDCLASS | DI_MULTMFGS); DevInfoElem->InstallParamBlock.FlagsEx &= ~DI_FLAGSEX_DIDINFOLIST; } if(pDeviceInfoSet->ClassDriverHead) { // // Find the global class driver list in the devinfo set's list of driver lists. // CurDrvListObject = GetAssociatedDriverListObject(pDeviceInfoSet->ClassDrvListObjectList, pDeviceInfoSet->ClassDriverHead, NULL ); MYASSERT(CurDrvListObject && (CurDrvListObject->RefCount > 0)); // // We found the driver list object, now increment its refcount, and do the // inheritance. // CurDrvListObject->RefCount++; DevInfoElem->ClassDriverCount = pDeviceInfoSet->ClassDriverCount; DevInfoElem->ClassDriverHead = pDeviceInfoSet->ClassDriverHead; DevInfoElem->ClassDriverTail = pDeviceInfoSet->ClassDriverTail; if(pDeviceInfoSet->SelectedClassDriver) { DevInfoElem->SelectedDriver = pDeviceInfoSet->SelectedClassDriver; DevInfoElem->SelectedDriverType = SPDIT_CLASSDRIVER; } DevInfoElem->InstallParamBlock.Flags |= CurDrvListObject->ListCreationFlags; DevInfoElem->InstallParamBlock.FlagsEx |= CurDrvListObject->ListCreationFlagsEx; DevInfoElem->InstallParamBlock.DriverPath = CurDrvListObject->ListCreationDriverPath; } } clean0: ; // nothing to do } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } if((Err == NO_ERROR) && DeviceInfoData) { // // The user supplied a buffer to receive a SP_DEVINFO_DATA // structure, so fill that in now. // try { if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, DevInfoElem, DeviceInfoData))) { Err = ERROR_INVALID_USER_BUFFER; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } #ifdef UNICODE // // ANSI version // HDEVINFO WINAPI SetupDiGetClassDevsA( IN CONST GUID *ClassGuid, OPTIONAL IN PCSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags ) { PCWSTR enumerator; DWORD rc; HDEVINFO h; if(Enumerator) { rc = pSetupCaptureAndConvertAnsiArg(Enumerator,&enumerator); if(rc != NO_ERROR) { SetLastError(rc); return INVALID_HANDLE_VALUE; } } else { enumerator = NULL; } h = SetupDiGetClassDevsExW(ClassGuid, enumerator, hwndParent, Flags, NULL, NULL, NULL ); rc = GetLastError(); if(enumerator) { MyFree(enumerator); } SetLastError(rc); return h; } #else // // Unicode version // HDEVINFO WINAPI SetupDiGetClassDevsW( IN CONST GUID *ClassGuid, OPTIONAL IN PCWSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags ) { UNREFERENCED_PARAMETER(ClassGuid); UNREFERENCED_PARAMETER(Enumerator); UNREFERENCED_PARAMETER(hwndParent); UNREFERENCED_PARAMETER(Flags); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(INVALID_HANDLE_VALUE); } #endif HDEVINFO WINAPI SetupDiGetClassDevs( IN CONST GUID *ClassGuid, OPTIONAL IN PCTSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags ) /*++ Routine Description: See SetupDiGetClassDevsEx for details. --*/ { return SetupDiGetClassDevsEx(ClassGuid, Enumerator, hwndParent, Flags, NULL, NULL, NULL ); } #ifdef UNICODE // // ANSI version // HDEVINFO WINAPI SetupDiGetClassDevsExA( IN CONST GUID *ClassGuid, OPTIONAL IN PCSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags, IN HDEVINFO DeviceInfoSet, OPTIONAL IN PCSTR MachineName, OPTIONAL IN PVOID Reserved ) { PCWSTR UnicodeEnumerator, UnicodeMachineName; DWORD rc; HDEVINFO h; h = INVALID_HANDLE_VALUE; if(Enumerator) { rc = pSetupCaptureAndConvertAnsiArg(Enumerator, &UnicodeEnumerator); if(rc != NO_ERROR) { goto clean0; } } else { UnicodeEnumerator = NULL; } if(MachineName) { rc = pSetupCaptureAndConvertAnsiArg(MachineName,&UnicodeMachineName); if(rc != NO_ERROR) { goto clean1; } } else { UnicodeMachineName = NULL; } h = SetupDiGetClassDevsExW(ClassGuid, UnicodeEnumerator, hwndParent, Flags, DeviceInfoSet, UnicodeMachineName, Reserved ); rc = GetLastError(); if(UnicodeMachineName) { MyFree(UnicodeMachineName); } clean1: if(UnicodeEnumerator) { MyFree(UnicodeEnumerator); } clean0: SetLastError(rc); return h; } #else // // Unicode version // HDEVINFO WINAPI SetupDiGetClassDevsExW( IN CONST GUID *ClassGuid, OPTIONAL IN PCWSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags, IN HDEVINFO DeviceInfoSet, OPTIONAL IN PCWSTR MachineName, OPTIONAL IN PVOID Reserved ) { UNREFERENCED_PARAMETER(ClassGuid); UNREFERENCED_PARAMETER(Enumerator); UNREFERENCED_PARAMETER(hwndParent); UNREFERENCED_PARAMETER(Flags); UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(MachineName); UNREFERENCED_PARAMETER(Reserved); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(INVALID_HANDLE_VALUE); } #endif HDEVINFO WINAPI SetupDiGetClassDevsEx( IN CONST GUID *ClassGuid, OPTIONAL IN PCTSTR Enumerator, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD Flags, IN HDEVINFO DeviceInfoSet, OPTIONAL IN PCTSTR MachineName, OPTIONAL IN PVOID Reserved ) /*++ Routine Description: This routine returns a device information set containing all installed devices of the specified class. Arguments: ClassGuid - Optionally, supplies the address of the class GUID to use when creating the list of devices. If the DIGCF_ALLCLASSES flag is set, then this parameter is ignored, and the resulting list will contain all classes of devices (i.e., every installed device). If the DIGCF_DEVICEINTERFACE flag _is not_ set, then this class GUID represents a setup class. If the DIGCF_DEVICEINTERFACE flag _is_ set, then this class GUID represents an interface class. Enumerator - Optional parameter that filters the members of the returned device information set based on their enumerator (i.e., provider). If the DIGCF_DEVICEINTERFACE flag _is not_ set in the Flags parameter, then this string represents the name of the key under the Enum branch containing devices instances for which information is to be retrieved. If this parameter is not specified, then device information will be retrieved for all device instances in the entire Enum tree. If the DIGCF_DEVICEINTERFACE flag _is_ set, then this string represents the PnP name of a particular device for which interfaces are to be retrieved. In this case, the resulting device information set will consist of a single device information element--the device whose name was specified as the enumerator. The interface devices provided by this PnP device can then be enumerated via SetupDiEnumInterfaceDevice. hwndParent - Optionally, supplies the handle of the top-level window to be used for any UI relating to the members of this set. Flags - Supplies control options used in building the device information set. May be a combination of the following values: DIGCF_PRESENT - Return only devices that are currently present. DIGCF_ALLCLASSES - Return a list of installed devices for all classes. If set, this flag will cause ClassGuid to be ignored. DIGCF_PROFILE - Return only devices that are a part of the current hardware profile. DIGCF_DEVICEINTERFACE - Return a list of all devices that expose interfaces of the class specified by ClassGUID (NOTE: in this context, ClassGuid is an interface class, _not_ a setup class). The interface devices exposed by the members of the resulting set may be enumerated via SetupDiEnumInterfaceDevice. DIGCF_DEFAULT - When used with DIGCF_DEVICEINTERFACE, this flag results in a list that contains only one device information element. Enumerating that device will return exactly one interface device--the one that has been marked as the system default interface device for that particular interface class. If there is no default interface device for the specified class, the API will fail, and GetLastError will return ERROR_NO_DEFAULT_DEVICE_INTERFACE. DeviceInfoSet - Optionally, supplies the handle of an existing device information set into which these new device information elements (and, if DIGCF_DEVICEINTERFACE is specified, device interfaces) will be added. If this parameter is specified, then this same HDEVINFO will be returned upon success, with the retrieved device information/device interface elements added. If this parameter is not specified, then a new device information set will be created, and its handle returned. NOTE: if this parameter is specified, then the associated class of this device information set (if any) must match the ClassGuid specified, if that class GUID is a setup class (i.e., the DIGCF_DEVICEINTERFACE flag isn't set). If the DIGCF_DEVICEINTERFACE flag is set, then the device interfaces retrieved will be filtered based on whether or not their corresponding device's setup class matches that of the device information set. This trick can be used, for example, to retrieve a list of device interfaces of a particular interface class, but only if those interfaces are exposed by devices of a particular setup class. E.g., 1. Create a device information set (via SetupDiCreateDeviceInfoList) whose associated setup class is "Volume". 2. Call SetupDiGetClassDevsEx to retrieve a list of all device interfaces of interface class "mounted device", passing in the HDEVINFO retrieved in step 1. The result of the above steps would be a device information set containing all device interfaces of (interface) class "mounted device" that are exposed by devnodes of (setup) class "Volume". Note that retrieval of new device information elements into an existing HDEVINFO set doesn't invalidate our devinfo enumeration hint, since new devinfo elements are always added onto the end of the list. MachineName - Optionally, supplies the name of a remote machine for which a device information set is to be retrieved. If this parameter is NULL, then the local machine is used. Reserved - Reserved for future use--must be NULL. Return Value: If the function succeeds, the return value is a handle to a device information set containing all installed devices matching the specified parameters. If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError. --*/ { HDEVINFO hDevInfo; PDEVICE_INFO_SET pDeviceInfoSet; PDEVINFO_ELEM DevInfoElem; DWORD Err; CONFIGRET cr; PTCHAR DevIdBuffer; ULONG DevIdBufferLen, CSConfigFlags; PTSTR CurDevId, DeviceInstanceToOpen; HKEY hKeyDevClassRoot, hKeyCurDevClass; TCHAR InterfaceGuidString[GUID_STRING_LEN]; BOOL GetInterfaceList, GetNextInterfaceClass; DWORD InterfaceClassKeyIndex; FILETIME LastWriteTime; GUID GuidBuffer; TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN]; DWORD RegDataType, DataBufferSize; BOOL DevInfoAlreadyPresent, IsActive, IsDefault; SP_DEVINFO_DATA DeviceInfoData; CONST GUID * ExistingClassGuid; // // Make sure the user didn't pass us anything in the Reserved parameter. // if(Reserved) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } // // Unless the caller wants a list of all classes, they'd better supply a class GUID. // if(!(Flags & DIGCF_ALLCLASSES) && !ClassGuid) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } // // DIGCF_DEFAULT can only be used in conjunction with DIGCF_DEVICEINTERFACE. // if((Flags & (DIGCF_DEFAULT | DIGCF_DEVICEINTERFACE)) == DIGCF_DEFAULT) { SetLastError(ERROR_INVALID_FLAGS); return INVALID_HANDLE_VALUE; } if(!DeviceInfoSet || (DeviceInfoSet == INVALID_HANDLE_VALUE)) { // // The caller didn't supply us with a device information set in which // to add our newly-retrieved elements, so we need to create our own. // if((hDevInfo = SetupDiCreateDeviceInfoListEx((Flags & (DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE)) ? NULL : ClassGuid, hwndParent, MachineName, NULL)) == INVALID_HANDLE_VALUE) { // // Last error already set. // MYASSERT(GetLastError()); return INVALID_HANDLE_VALUE; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(hDevInfo))) { // // this should not happen // MYASSERT(pDeviceInfoSet); SetLastError(ERROR_INVALID_HANDLE); return INVALID_HANDLE_VALUE; } } else { // // The caller wants us to use an existing device information set. Make // a copy of it, and work with that one, so that if something fails, we // haven't messed up the original one. // // NOTE: DO NOT do anything with the DeviceInfoSet after this point, // as doing so will get the original out-of-sync with the current copy // we're working with. // hDevInfo = NULL; pDeviceInfoSet = CloneDeviceInfoSet(DeviceInfoSet); if(!pDeviceInfoSet) { SetLastError(ERROR_INVALID_PARAMETER); return INVALID_HANDLE_VALUE; } } Err = NO_ERROR; DevIdBuffer = NULL; hKeyDevClassRoot = hKeyCurDevClass = INVALID_HANDLE_VALUE; try { // // If the caller supplied us with a previously-existing devinfo set in // which to add new elements, we need to make sure that the setup class // GUID associated with this devinfo set (if any) matches the setup // class GUID that the caller supplied. // if(hDevInfo) { // // We always want the ExistingClassGuid pointer to be NULL when we // haven't been passed in a previously-existing device information // set. // ExistingClassGuid = NULL; } else { if(pDeviceInfoSet->HasClassGuid) { // // Remember the devinfo set's associated setup class GUID, to // be used later in filtering device interfaces based on the // setup class of their underlying devnode. // ExistingClassGuid = &(pDeviceInfoSet->ClassGuid); if(ClassGuid && !(Flags & (DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE))) { if(!IsEqualGUID(ExistingClassGuid, ClassGuid)) { Err = ERROR_CLASS_MISMATCH; goto clean0; } } } else { // // The caller-supplied devinfo set had no associated setup // class. Remember that fact, so that we won't try to filter // device interfaces based on the underlying devices' setup // class. // ExistingClassGuid = NULL; } } if(GetInterfaceList = (Flags & DIGCF_DEVICEINTERFACE)) { // yes, we want an assignment here. // // Open the root of the DeviceClasses registry branch // hKeyDevClassRoot = SetupDiOpenClassRegKeyEx(NULL, KEY_READ, DIOCR_INTERFACE, MachineName, NULL ); if(hKeyDevClassRoot == INVALID_HANDLE_VALUE) { Err = GetLastError(); goto clean0; } if(Flags & DIGCF_ALLCLASSES) { InterfaceClassKeyIndex = 0; ClassGuid = &GuidBuffer; } if(Flags & DIGCF_PRESENT) { // // Since we're only going to be retrieving a list of device // interfaces that are currently 'active', we can set the // 'IsActive' flag to always be TRUE. // IsActive = TRUE; } } // // As an optimization, start out with a 16K (character) buffer, in the hopes of avoiding // two scans through the hardware tree (once to get the size, and again to get the data). // DevIdBufferLen = 16384; do { if(GetInterfaceList) { if(Flags & DIGCF_ALLCLASSES) { // // We have to enumerate through all interface device classes, and retrieve // a list of device interfaces for each one. // DataBufferSize = SIZECHARS(InterfaceGuidString); switch(RegEnumKeyEx(hKeyDevClassRoot, InterfaceClassKeyIndex, InterfaceGuidString, &DataBufferSize, NULL, NULL, NULL, &LastWriteTime)) { case ERROR_SUCCESS : GetNextInterfaceClass = TRUE; InterfaceClassKeyIndex++; break; case ERROR_NO_MORE_ITEMS : // // We've processed all of the interface class GUIDs--we're done. // GetNextInterfaceClass = FALSE; continue; default : // // Some other error occurred. Skip this subkey, and continue // with the next one. // GetNextInterfaceClass = TRUE; InterfaceClassKeyIndex++; continue; } // // Convert the GUID string retrieved above into its binary form, for use // below. // if(pSetupGuidFromString(InterfaceGuidString, &GuidBuffer) != NO_ERROR) { // // The subkey we enumerated is not a valid GUID string--skip this // subkey, and continue on with the next one. // continue; } } else { // // We're just retrieving devices for a single interface class (which the // caller specified). All we need to do is initialize the GUID string // buffer with the textual form of the GUID. // pSetupStringFromGuid(ClassGuid, InterfaceGuidString, SIZECHARS(InterfaceGuidString) ); // // We only need to go through this list once. // GetNextInterfaceClass = FALSE; } // // We'll be using the same character buffer to store each device instance ID // we're opening below. // DeviceInstanceToOpen = DeviceInstanceId; } else { // // We're not retrieving a list of interface devices, so we'll never go through // this loop more than once. // GetNextInterfaceClass = FALSE; } // // Retrieve a list of device names. // while(TRUE) { if(!DevIdBuffer) { if(!(DevIdBuffer = MyMalloc(DevIdBufferLen * sizeof(TCHAR)))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } } if(GetInterfaceList) { cr = CM_Get_Device_Interface_List_Ex((LPGUID)ClassGuid, (DEVINSTID)Enumerator, DevIdBuffer, DevIdBufferLen, (Flags & DIGCF_PRESENT) ? CM_GET_DEVICE_INTERFACE_LIST_PRESENT : CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES, pDeviceInfoSet->hMachine ); } else { cr = CM_Get_Device_ID_List_Ex(Enumerator, DevIdBuffer, DevIdBufferLen, Enumerator ? CM_GETIDLIST_FILTER_ENUMERATOR : CM_GETIDLIST_FILTER_NONE, pDeviceInfoSet->hMachine ); } if(cr == CR_SUCCESS) { // // Device list successfully retrieved! // break; } else { // // Free the current buffer before determining what error occurred. // MyFree(DevIdBuffer); DevIdBuffer = NULL; if(cr == CR_BUFFER_SMALL) { // // OK, so our buffer wasn't big enough--just how big // does it need to be? // if(GetInterfaceList) { if(CM_Get_Device_Interface_List_Size_Ex(&DevIdBufferLen, (LPGUID)ClassGuid, (DEVINSTID)Enumerator, (Flags & DIGCF_PRESENT) ? CM_GET_DEVICE_INTERFACE_LIST_PRESENT : CM_GET_DEVICE_INTERFACE_LIST_ALL_DEVICES, pDeviceInfoSet->hMachine) != CR_SUCCESS) { // // Couldn't retrieve the list size--this should // never happen. // Err = ERROR_INVALID_DATA; goto clean0; } } else { if(CM_Get_Device_ID_List_Size_Ex(&DevIdBufferLen, Enumerator, Enumerator ? CM_GETIDLIST_FILTER_ENUMERATOR : CM_GETIDLIST_FILTER_NONE, pDeviceInfoSet->hMachine) != CR_SUCCESS) { // // Couldn't retrieve the list size--this should // never happen. // Err = ERROR_INVALID_DATA; goto clean0; } } } else { // // An error occurred, and it wasn't because we supplied // too small a buffer. // Err = ERROR_INVALID_DATA; goto clean0; } } } // // We have now retrieved a list of all the specified devices. If // these are device interfaces, we need to open the key for this // interface class underneath the DeviceClasses key. // if(GetInterfaceList) { if(RegOpenKeyEx(hKeyDevClassRoot, InterfaceGuidString, 0, KEY_READ, &hKeyCurDevClass) != ERROR_SUCCESS) { // // Make sure hKeyCurDevClass is still set to // INVALID_HANDLE_VALUE, so that we'll know not to close it. // hKeyCurDevClass = INVALID_HANDLE_VALUE; // // Skip this interface class. // continue; } } // // Now create device information elements from the members of this // list. // for(CurDevId = DevIdBuffer; *CurDevId; CurDevId += lstrlen(CurDevId) + 1) { // // If this is a device interface, we must retrieve the // associated device instance name. // if(GetInterfaceList) { if(NO_ERROR != pSetupGetDevInstNameAndStatusForInterfaceDevice( hKeyCurDevClass, CurDevId, DeviceInstanceId, SIZECHARS(DeviceInstanceId), (Flags & DIGCF_PRESENT) ? NULL : &IsActive, &IsDefault)) { // // Couldn't retrieve the name of the owning device // instance--skip this device interface. // continue; } if ((Flags & DIGCF_DEFAULT) && !IsDefault) { // // The caller only wants the default device interface. // Since CM_Get_Device_Interface_List places the default // device interface first in the list (if there is one), // and we stop searching the list when we find it, we // know that if we get here, there is no default device // interface for this interface class. // Err = ERROR_NO_DEFAULT_DEVICE_INTERFACE; goto clean0; } } else { DeviceInstanceToOpen = CurDevId; } if(Flags & DIGCF_PROFILE) { // // Verify that this device instance is part of the current // hardware profile. // if(CM_Get_HW_Prof_Flags_Ex(DeviceInstanceToOpen, 0, &CSConfigFlags, 0, pDeviceInfoSet->hMachine) == CR_SUCCESS) { if(CSConfigFlags & CSCONFIGFLAG_DO_NOT_CREATE) { continue; } } } // // Note the last parameter in the following call to // pSetupOpenAndAddNewDevInfoElem--in the case where we're // adding to an existing caller-supplied HDEVINFO set (i.e., // hDevInfo is NULL), we cast the HDEVINFO to a DEVICE_INFO_SET // pointer, since that's what ends up getting stored in the // ContainingDeviceInfoSet field of a devinfo element structure. // That field is used for quick validation that a caller- // supplied device information element is valid. // // If we ever decide to change the internal implementation of // how an HDEVINFO translates into its underlying // DEVICE_INFO_SET, then we'll need to update the code below // accordingly. (See also the comments under // AccessDeviceInfoSet, CloneDeviceInfoSet, and // RollbackDeviceInfoSet.) // Err = pSetupOpenAndAddNewDevInfoElem(pDeviceInfoSet, DeviceInstanceToOpen, !(Flags & DIGCF_PRESENT), ((Flags & (DIGCF_ALLCLASSES | DIGCF_DEVICEINTERFACE)) ? ExistingClassGuid : ClassGuid), hwndParent, &DevInfoElem, (GetInterfaceList || !hDevInfo), &DevInfoAlreadyPresent, FALSE, 0, (hDevInfo ? pDeviceInfoSet : (PDEVICE_INFO_SET)DeviceInfoSet) ); if(Err != NO_ERROR) { if(Err == ERROR_NOT_ENOUGH_MEMORY) { goto clean0; } Err = NO_ERROR; continue; } if(GetInterfaceList) { // // Now that we've successfully opened up the device instance that 'owns' // this device interface, add a new interface device node onto this // devinfo element's list. // if(NO_ERROR != (Err = pSetupAddInterfaceDeviceToDevInfoElem(pDeviceInfoSet, DevInfoElem, ClassGuid, CurDevId, IsActive, IsDefault, !hDevInfo, FALSE, NULL))) { // // The only error we should be getting back from this routine is // out-of-memory, which is always a fatal error. // goto clean0; } if ((Flags & DIGCF_DEFAULT) && IsDefault) { // // The caller only wants the default device interface, // and this is it. // if ((Flags & DIGCF_PRESENT) && !IsActive) { // // The caller doesn't want to know about a // non-present default device interface. // Err = ERROR_NO_DEFAULT_DEVICE_INTERFACE; } RegCloseKey(hKeyCurDevClass); hKeyCurDevClass = INVALID_HANDLE_VALUE; goto clean0; } } } // // If we're working with interface devices, we need to close the interface // class key we opened above. // if(GetInterfaceList) { RegCloseKey(hKeyCurDevClass); hKeyCurDevClass = INVALID_HANDLE_VALUE; } } while(GetNextInterfaceClass); clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; if(hKeyCurDevClass != INVALID_HANDLE_VALUE) { RegCloseKey(hKeyCurDevClass); } // // Access the following variables, so the compiler will respect // the statement ordering in the try clause. // DevIdBuffer = DevIdBuffer; hKeyDevClassRoot = hKeyDevClassRoot; } if(DevIdBuffer) { MyFree(DevIdBuffer); } if(hKeyDevClassRoot != INVALID_HANDLE_VALUE) { RegCloseKey(hKeyDevClassRoot); } if(Err != NO_ERROR) { if(hDevInfo) { DestroyDeviceInfoSet(hDevInfo, pDeviceInfoSet); } else { if(!(pDeviceInfoSet = RollbackDeviceInfoSet(DeviceInfoSet, pDeviceInfoSet))) { MYASSERT(pDeviceInfoSet); } else { UnlockDeviceInfoSet(pDeviceInfoSet); } } SetLastError(Err); hDevInfo = INVALID_HANDLE_VALUE; } else { if(!hDevInfo) { // // We retrieved additional elements into an existing device // information set. Replace the existing device information set // with the new one (i.e., into the same handle), and return the // same HDEVINFO handle that the caller passed in as the // DeviceInfoSet parameter. // pDeviceInfoSet = CommitDeviceInfoSet(DeviceInfoSet, pDeviceInfoSet); MYASSERT(pDeviceInfoSet); // // Set hDevInfo to be the same as the DeviceInfoSet handle we were // passed in, so that we can return it to the caller. // hDevInfo = DeviceInfoSet; MYASSERT(hDevInfo); } if (pDeviceInfoSet) { UnlockDeviceInfoSet(pDeviceInfoSet); } MYASSERT(hDevInfo != INVALID_HANDLE_VALUE); } return hDevInfo; } BOOL WINAPI SetupDiSetDeviceInterfaceDefault( IN HDEVINFO DeviceInfoSet, IN OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IN DWORD Flags, IN PVOID Reserved ) /*++ Routine Description: This routine sets the specified device interface as the default device interface for its class. Arguments: DeviceInfoSet - Points to the device information set containing the device interface for which to set as the default device interface. This handle is typically returned by SetupDiGetClassDevs. DeviceInterfaceData - Points to a structure that identifies the device interface within the device information set. This pointer is typically returned by SetupDiEnumDeviceInterfaces. If successful, this routine will update the information contained in this structure. Flags - Not used, must be zero. Reserved - Reserved for future use, must be NULL. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: The caller must have the appropriate permission to set the default device interface. --*/ { DWORD Err; PDEVICE_INFO_SET pDeviceInfoSet; PDEVINFO_ELEM DevInfoElem; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; PCTSTR MachineName, InterfaceDeviceName; HKEY hKeyInterfaceClass = INVALID_HANDLE_VALUE; BOOL IsActive, IsDefault; // // Make sure the user didn't pass us anything in the Reserved parameter. // if(Reserved) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } if(Flags & ~(0x0)) { SetLastError(ERROR_INVALID_FLAGS); return FALSE; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // First, find the devinfo element that owns this interface device (for validation). // if(!(DevInfoElem = FindDevInfoElemForInterfaceDevice(pDeviceInfoSet, DeviceInterfaceData))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // Retrieve the name of the machine associated with this DeviceInfoSet. // if(pDeviceInfoSet->hMachine) { MYASSERT(pDeviceInfoSet->MachineName != -1); MachineName = pStringTableStringFromId(pDeviceInfoSet->StringTable, pDeviceInfoSet->MachineName); } else { MachineName = NULL; } // // The Reserved field contains a pointer to the underlying interface device node. // InterfaceDeviceNode = (PINTERFACE_DEVICE_NODE)(DeviceInterfaceData->Reserved); // // Retrieve the device path (symbolic link name) for this interface device. // InterfaceDeviceName = pStringTableStringFromId(pDeviceInfoSet->StringTable, InterfaceDeviceNode->SymLinkName); // // Open this interface class key under the DeviceClasses registry branch. // hKeyInterfaceClass = SetupDiOpenClassRegKeyEx(&DeviceInterfaceData->InterfaceClassGuid, KEY_READ | KEY_WRITE, DIOCR_INTERFACE, MachineName, NULL); if(hKeyInterfaceClass == INVALID_HANDLE_VALUE) { Err = GetLastError(); goto clean0; } // // Get the current status of this device interface. // Err = pSetupGetDevInstNameAndStatusForInterfaceDevice(hKeyInterfaceClass, InterfaceDeviceName, NULL, 0, &IsActive, &IsDefault); if (Err != NO_ERROR) { goto clean0; } // // If this interface is already the default, then we're done. // if (IsDefault) { goto clean1; } // // Set the "Default" value under this interface class key to this device // interface. // Err = RegSetValueEx(hKeyInterfaceClass, pszDefault, 0, REG_SZ, (PBYTE)InterfaceDeviceName, (lstrlen(InterfaceDeviceName) + 1) * sizeof(TCHAR)); if (Err != NO_ERROR) { goto clean0; } // // This interface was successfully set as the default device interface // for this interface class. // IsDefault = TRUE; clean1: // // Update the flags for this interface. // InterfaceDeviceNode->Flags = (InterfaceDeviceNode->Flags & ~SPINT_ACTIVE) | (IsActive ? SPINT_ACTIVE : 0); InterfaceDeviceNode->Flags = (InterfaceDeviceNode->Flags & ~SPINT_DEFAULT) | (IsDefault ? SPINT_DEFAULT : 0); // // Finally, update the flags in the caller-supplied buffer to indicate the new status // of this interface device. // DeviceInterfaceData->Flags = InterfaceDeviceNode->Flags; clean0: if(hKeyInterfaceClass != INVALID_HANDLE_VALUE) { RegCloseKey(hKeyInterfaceClass); } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return (Err == NO_ERROR); } DWORD pSetupAddNewDeviceInfoElement( IN PDEVICE_INFO_SET pDeviceInfoSet, IN DEVINST DevInst, IN CONST GUID *ClassGuid, IN PCTSTR Description, OPTIONAL IN HWND hwndParent, OPTIONAL IN DWORD DiElemFlags, IN PDEVICE_INFO_SET ContainingDeviceInfoSet, OUT PDEVINFO_ELEM *DeviceInfoElement ) /*++ Routine Description: This routine creates a new device information element based on the supplied information, and adds it to the specified device information set. ASSUMES THAT THE CALLING ROUTINE HAS ALREADY ACQUIRED THE LOCK! Arguments: pDeviceInfoSet - Device information set to add this new element to. DevInst - Supplies the device instance handle of the element to be added. ClassGuid - Class GUID of the element to be added. Description - Optionally, supplies the description of the element to be added. hwndParent - Optionally, supplies the handle to the top level window for UI relating to this element. DiElemFlags - Specifies flags pertaining to the device information element being created. ContainingDeviceInfoSet - Supplies a pointer to the device information set structure with which this element is to be associated. This may be different from the pDeviceInfoSet parameter if we're working against a cloned devinfo set (i.e., to facilitate rollback). DeviceInfoElement - Supplies the address of the variable that receives a pointer to the newly-allocated device information element. Return Value: If the function succeeds, the return value is NO_ERROR, otherwise the ERROR_* code is returned. Remarks: Since the new element is added onto the end of the existing list, our enumeration hint isn't invalidated. --*/ { DWORD Err = NO_ERROR; TCHAR TempString[LINE_LEN]; *DeviceInfoElement = NULL; try { // // If there is a class associated with this device information set, // verify that it is the same as that of the new element. // if(pDeviceInfoSet->HasClassGuid && !IsEqualGUID(&(pDeviceInfoSet->ClassGuid), ClassGuid)) { Err = ERROR_CLASS_MISMATCH; goto clean0; } // // Allocate storage for the element. // if(!(*DeviceInfoElement = MyMalloc(sizeof(DEVINFO_ELEM)))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } ZeroMemory(*DeviceInfoElement, sizeof(DEVINFO_ELEM)); // // Store the address of the containing devinfo set in the structure // for this element. This is used for efficient validation of a // caller-supplied SP_DEVINFO_DATA. // (*DeviceInfoElement)->ContainingDeviceInfoSet = ContainingDeviceInfoSet; // // Initialize the element with the specified information // CopyMemory(&((*DeviceInfoElement)->ClassGuid), ClassGuid, sizeof(GUID) ); (*DeviceInfoElement)->InstallParamBlock.hwndParent = hwndParent; if(Description) { // // Set the device instance's DeviceDesc property to the specified // description. // CM_Set_DevInst_Registry_Property_Ex(DevInst, CM_DRP_DEVICEDESC, Description, (lstrlen(Description) + 1) * sizeof(TCHAR), 0, pDeviceInfoSet->hMachine); // // Store two versions of the description--one case-sensitive (for display) // and the other case-insensitive (for fast lookup). // lstrcpyn(TempString, Description, SIZECHARS(TempString)); if((((*DeviceInfoElement)->DeviceDescriptionDisplayName = pStringTableAddString(pDeviceInfoSet->StringTable, TempString, STRTAB_CASE_SENSITIVE, NULL,0)) == -1) || (((*DeviceInfoElement)->DeviceDescription = pStringTableAddString(pDeviceInfoSet->StringTable, TempString, STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE, NULL,0)) == -1)) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } } else { (*DeviceInfoElement)->DeviceDescription = (*DeviceInfoElement)->DeviceDescriptionDisplayName = -1; } (*DeviceInfoElement)->DevInst = DevInst; (*DeviceInfoElement)->DiElemFlags = DiElemFlags; (*DeviceInfoElement)->InstallParamBlock.DriverPath = -1; (*DeviceInfoElement)->InstallParamBlock.CoInstallerCount = -1; // // If we're in GUI-mode setup on Windows NT, we'll automatically set // the DI_FLAGSEX_IN_SYSTEM_SETUP flag in the devinstall parameter // block for this devinfo element. // if(GuiSetupInProgress) { (*DeviceInfoElement)->InstallParamBlock.FlagsEx |= DI_FLAGSEX_IN_SYSTEM_SETUP; } // // If we're in non-interactive mode, set the "be quiet" bits. // if(GlobalSetupFlags & (PSPGF_NONINTERACTIVE|PSPGF_UNATTENDED_SETUP)) { (*DeviceInfoElement)->InstallParamBlock.Flags |= DI_QUIETINSTALL; (*DeviceInfoElement)->InstallParamBlock.FlagsEx |= DI_FLAGSEX_NOUIONQUERYREMOVE; } // // Initialize our enumeration 'hints' // (*DeviceInfoElement)->ClassDriverEnumHintIndex = INVALID_ENUM_INDEX; (*DeviceInfoElement)->CompatDriverEnumHintIndex = INVALID_ENUM_INDEX; // // Create a log context separate from the parent. // if(CreateLogContext(NULL, FALSE, &(*DeviceInfoElement)->InstallParamBlock.LogContext) != NO_ERROR) { // // if it failed, we will inheret the log context, since it's better than nothing // in theory, this should never happen, or if it does, other things will fail too // (*DeviceInfoElement)->InstallParamBlock.LogContext = NULL; Err = InheritLogContext(pDeviceInfoSet->InstallParamBlock.LogContext, &(*DeviceInfoElement)->InstallParamBlock.LogContext); if (Err != NO_ERROR) { goto clean0; } } // // Now, insert the new element at the end of the device // information set's list of elements. // if(pDeviceInfoSet->DeviceInfoHead) { pDeviceInfoSet->DeviceInfoTail->Next = *DeviceInfoElement; pDeviceInfoSet->DeviceInfoTail = *DeviceInfoElement; } else { pDeviceInfoSet->DeviceInfoHead = pDeviceInfoSet->DeviceInfoTail = *DeviceInfoElement; } pDeviceInfoSet->DeviceInfoCount++; clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } if((Err != NO_ERROR) && *DeviceInfoElement) { MyFree(*DeviceInfoElement); *DeviceInfoElement = NULL; } return Err; } DWORD pSetupClassGuidFromDevInst( IN DEVINST DevInst, IN HMACHINE hMachine, OUT LPGUID ClassGuid ) /*++ Routine Description: This routine attempts to retrieve the class GUID for the specified device instance from its device registry key. If it cannot retrieve one, it returns GUID_NULL. Arguments: DevInst - Supplies the handle of the device instance whose class GUID is to be retrieved. hMachine - Machine context to operate in ClassGuid - Supplies the address of the variable that receives the class GUID, or GUID_NULL if no class GUID can be retrieved. Return Value: If the function succeeds, the return value is NO_ERROR. If the function fails, an ERROR_* code is returned. (Presently, the only failure condition returned is ERROR_NOT_ENOUGH_MEMORY.) --*/ { DWORD NumGuids; TCHAR TempString[GUID_STRING_LEN]; DWORD StringSize; StringSize = sizeof(TempString); if(CM_Get_DevInst_Registry_Property_Ex(DevInst, CM_DRP_CLASSGUID, NULL, TempString, &StringSize, 0, hMachine) == CR_SUCCESS) { // // We retrieved the class GUID (in string form) for this device // instance--now, convert it into its binary representation. // return pSetupGuidFromString(TempString, ClassGuid); } // // We couldn't retrieve a ClassGUID--let's see if there's a Class name we can // work with. // StringSize = sizeof(TempString); if(CM_Get_DevInst_Registry_Property_Ex(DevInst, CM_DRP_CLASS, NULL, TempString, &StringSize, 0, hMachine) == CR_SUCCESS) { // // OK, we found out the class name. Now see if we can find a // single class GUID to match it. // if(SetupDiClassGuidsFromName(TempString, ClassGuid, 1, &NumGuids) && NumGuids) { // // We found exactly one, so we're happy. // return NO_ERROR; } } // // We have no idea what class of device this is, so use GUID_NULL. // CopyMemory(ClassGuid, &GUID_NULL, sizeof(GUID)); return NO_ERROR; } BOOL WINAPI SetupDiDeleteDeviceInfo( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData ) /*++ Routine Description: This routine deletes a member from the specified device information set. THIS DOES NOT DELETE ACTUAL DEVICES! Arguments: DeviceInfoSet - Supplies a handle to the device information set containing the device information element to be deleted. DeviceInfoData - Supplies a pointer to the SP_DEVINFO_DATA structure for the device information element to be deleted. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: If the specified device information element is explicitly in use by a wizard page, then the call will fail, and GetLastError will return ERROR_DEVINFO_DATA_LOCKED. This will happen if a handle to a wizard page was retrieved via SetupDiGetWizardPage, and this element was specified, along with the DIWP_FLAG_USE_DEVINFO_DATA flag. In order to be able to delete this element, the wizard HPROPSHEETPAGE handle must be closed (either explicitly, or after a call to PropertySheet() completes). Since we don't track where this devinfo element lives in relation to our current enumeration hint, we just invalidate the hint, so that next enumeration must scan from the beginning of the list. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM ElemToDelete, PrevElem, NextElem; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // Get a pointer to the element we are to delete. // ElemToDelete = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, &PrevElem ); if(ElemToDelete) { // // Make sure that this element isn't currently locked by // a wizard page. // if(ElemToDelete->DiElemFlags & DIE_IS_LOCKED) { Err = ERROR_DEVINFO_DATA_LOCKED; goto clean0; } NextElem = ElemToDelete->Next; // // Destroy the devinfo element. We need to do this before // altering the list, because we will be calling the class // installer with DIF_DESTROYPRIVATEDATA, and it needs to // be able to access this element (obviously). // DestroyDeviceInfoElement(DeviceInfoSet, pDeviceInfoSet, ElemToDelete); // // Now remove the element from the list. // if(PrevElem) { PrevElem->Next = NextElem; } else { pDeviceInfoSet->DeviceInfoHead = NextElem; } if(!NextElem) { pDeviceInfoSet->DeviceInfoTail = PrevElem; } 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 == ElemToDelete) { pDeviceInfoSet->SelectedDevInfoElem = NULL; } // // Invalidate our enumeration hint for this devinfo element list. // pDeviceInfoSet->DeviceInfoEnumHint = NULL; pDeviceInfoSet->DeviceInfoEnumHintIndex = INVALID_ENUM_INDEX; } else { Err = ERROR_INVALID_PARAMETER; } clean0: ; // nothing to do } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } BOOL WINAPI SetupDiEnumDeviceInfo( IN HDEVINFO DeviceInfoSet, IN DWORD MemberIndex, OUT PSP_DEVINFO_DATA DeviceInfoData ) /*++ Routine Description: This API enumerates the members of the specified device information set. Arguments: DeviceInfoSet - Supplies a handle to the device information set whose members are to be enumerated. MemberIndex - Supplies the zero-based index of the device information member to be retreived. DeviceInfoData - Supplies a pointer to a SP_DEVINFO_DATA structure that will receive information about this member. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: To enumerate device information members, an application should initially call the SetupDiEnumDeviceInfo function with the MemberIndex parameter set to zero. The application should then increment MemberIndex and call the SetupDiEnumDeviceInfo function until there are no more values (i.e., the function fails, and GetLastError returns ERROR_NO_MORE_ITEMS). --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err, i; PDEVINFO_ELEM DevInfoElem; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { if(MemberIndex >= pDeviceInfoSet->DeviceInfoCount) { Err = ERROR_NO_MORE_ITEMS; goto clean0; } // // Find the element corresponding to the specified index (using our // enumeration hint optimization, if possible) // if(pDeviceInfoSet->DeviceInfoEnumHintIndex <= MemberIndex) { MYASSERT(pDeviceInfoSet->DeviceInfoEnumHint); DevInfoElem = pDeviceInfoSet->DeviceInfoEnumHint; i = pDeviceInfoSet->DeviceInfoEnumHintIndex; } else { DevInfoElem = pDeviceInfoSet->DeviceInfoHead; i = 0; } for(; i < MemberIndex; i++) { DevInfoElem = DevInfoElem->Next; } if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, DevInfoElem, DeviceInfoData))) { Err = ERROR_INVALID_USER_BUFFER; } // // Remember this element as our new enumeration hint. // pDeviceInfoSet->DeviceInfoEnumHintIndex = MemberIndex; pDeviceInfoSet->DeviceInfoEnumHint = DevInfoElem; clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } BOOL WINAPI SetupDiRegisterDeviceInfo( IN HDEVINFO DeviceInfoSet, IN OUT PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Flags, IN PSP_DETSIG_CMPPROC CompareProc, OPTIONAL IN PVOID CompareContext, OPTIONAL OUT PSP_DEVINFO_DATA DupDeviceInfoData OPTIONAL ) /*++ Routine Description: This API registers a device instance with the Plug & Play Manager. Arguments: DeviceInfoSet - Supplies a handle to the device information set that contains the device information element for this device instance. DeviceInfoData - Supplies a pointer to the SP_DEVINFO_DATA structure for the device instance being registered. This is an IN OUT parameter, since the DevInst field of the structure may be updated with a new handle value upon return. Flags - Controls how the device is to be registered. May be a combination of the following values: SPRDI_FIND_DUPS - Search for a previously-existing device instance corresponding to this device information. If this flag is not specified, the device instance will be registered, regardless of whether a device instance already exists for it. CompareProc - Optionally, supplies a comparison callback function to be used in duplicate detection. If specified, the function will be called for each device instance that is of the same class as the device instance being registered. The prototype of the callback function is as follows: typedef DWORD (CALLBACK* PSP_DETSIG_CMPPROC)( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA NewDeviceData, IN PSP_DEVINFO_DATA ExistingDeviceData, IN PVOID CompareContext OPTIONAL ); The compare function must return ERROR_DUPLICATE_FOUND if it finds the two devices to be duplicates of each other, and NO_ERROR otherwise. If some other error (e.g., out-of-memory) is encountered, the callback should return the appropriate ERROR_* code indicating the failure that occurred. If a CompareProc is not supplied, and duplicate detection is requested, then a default comparison behavior will be used. (See pSetupDupDevCompare for details.) CompareContext - Optionally, supplies the address of a caller-supplied context buffer that will be passed into the compare callback routine. This parameter is ignored if CompareProc is not supplied. DupDeviceInfoData - Optionally, supplies a pointer to a device information element that will be initialized for the duplicate device instance, if any, discovered as a result of attempting to register this device. This will be filled in if the function returns FALSE, and GetLastError returns ERROR_DUPLICATE_FOUND. This device information element will be added as a member of the specified DeviceInfoSet (if it wasn't already a member). If DupDeviceInfoData is not supplied, then the duplicate WILL NOT be added to the device information set. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: After registering a device information element, the caller should refresh any stored copies of the devinst handle associated with this device, as the handle value may have changed during registration. The caller need not re-retrieve the SP_DEVINFO_DATA structure, because the devinst field of the DeviceInfoData structure will be updated to reflect the current handle value. This API may invalidate our devinfo element enumeration hint. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem, CurDevInfoElem; CONFIGRET cr; ULONG DevIdBufferLen, ulStatus, ulProblem; PTCHAR DevIdBuffer = NULL; PTSTR CurDevId; DEVINST ParentDevInst; BOOL AlreadyPresent; SP_DEVINFO_DATA CurDevInfoData; TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN]; DEFAULT_DEVCMP_CONTEXT DevCmpContext; LOG_CONF NewDevLogConfig; RES_DES NewDevResDes; if(Flags & ~SPRDI_FIND_DUPS) { SetLastError(ERROR_INVALID_FLAGS); return FALSE; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; // // Initialize the following variables so we'll know whether we need to free any of their // associated resources. // ZeroMemory(&DevCmpContext, sizeof(DevCmpContext)); NewDevLogConfig = (LOG_CONF)NULL; NewDevResDes = (RES_DES)NULL; try { DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, NULL ); if(!DevInfoElem) { Err = ERROR_INVALID_PARAMETER; goto clean0; } else if(DevInfoElem->DiElemFlags & DIE_IS_REGISTERED) { // // Nothing to do--it's already been registered. // goto clean0; } // // If the caller requested duplicate detection then retrieve // all device instances of this class, and compare each one // with the device instance being registered. // if(Flags & SPRDI_FIND_DUPS) { do { if(CM_Get_Device_ID_List_Size_Ex(&DevIdBufferLen, NULL, CM_GETIDLIST_FILTER_NONE, pDeviceInfoSet->hMachine) != CR_SUCCESS) { Err = ERROR_INVALID_DATA; goto clean0; } else if(!DevIdBufferLen) { break; } if(!(DevIdBuffer = MyMalloc(DevIdBufferLen * sizeof(TCHAR)))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } cr = CM_Get_Device_ID_List_Ex(NULL, DevIdBuffer, DevIdBufferLen, CM_GETIDLIST_FILTER_NONE, pDeviceInfoSet->hMachine); if(cr == CR_BUFFER_SMALL) { // // This will only happen if a device instance was added between // the time that we calculated the size, and when we attempted // to retrieve the list. In this case, we'll simply retrieve // the size again, and re-attempt to retrieve the list. // MyFree(DevIdBuffer); DevIdBuffer = NULL; } else if(cr != CR_SUCCESS) { Err = ERROR_INVALID_DATA; goto clean0; } } while(cr == CR_BUFFER_SMALL); if(!DevIdBufferLen) { goto NoDups; } // // Initialize the structure to be used during duplicate comparison callback. // CurDevInfoData.cbSize = sizeof(SP_DEVINFO_DATA); // // We have retrieved a list of every device instance in the system--now // do the comparison for each one that matches the class of the device // being registered. // if(!CompareProc) { // // We are supposed to do the comparisons, so set up to do our default comparison. // if((cr = CM_Get_First_Log_Conf_Ex(&NewDevLogConfig, DevInfoElem->DevInst, BOOT_LOG_CONF, pDeviceInfoSet->hMachine)) != CR_SUCCESS) { // // Ensure that our NewDevLogConfig handle is still NULL, so we won't try // to free it. // NewDevLogConfig = (LOG_CONF)NULL; if(cr == CR_INVALID_DEVINST) { Err = ERROR_INVALID_PARAMETER; goto clean0; } else { // // The only value we should get here is CR_NO_MORE_LOG_CONF. // In this case, there is no comparison data, so we assume there is // no possibility of duplication. // goto NoDups; } } if(CM_Get_Next_Res_Des_Ex(&NewDevResDes, NewDevLogConfig, ResType_ClassSpecific, NULL, 0, pDeviceInfoSet->hMachine) != CR_SUCCESS) { // // Ensure that our NewDevResDes is still NULL, so we won't try to free it. // NewDevResDes = (RES_DES)NULL; // // Since we can't retrieve the ResDes handle, assume there are no duplicates. // goto NoDups; } // // Now retrieve the actual data for the ResDes. // do { if((CM_Get_Res_Des_Data_Size_Ex(&DevCmpContext.CsResourceSize, NewDevResDes, 0, pDeviceInfoSet->hMachine) != CR_SUCCESS) || !DevCmpContext.CsResourceSize) { // // Can't find out the size of the data, or there is none--assume no dups. // goto NoDups; } if(DevCmpContext.NewDevCsResource = MyMalloc(DevCmpContext.CsResourceSize)) { if((cr = CM_Get_Res_Des_Data_Ex(NewDevResDes, DevCmpContext.NewDevCsResource, DevCmpContext.CsResourceSize, 0, pDeviceInfoSet->hMachine)) != CR_SUCCESS) { if(cr == CR_BUFFER_SMALL) { // // Then someone increased the size of the resource data before we // got a chance to read it. Free our buffer and try again. // MyFree(DevCmpContext.NewDevCsResource); DevCmpContext.NewDevCsResource = NULL; } else { // // Some other error occurred (highly unlikely). Assume no dups. // goto NoDups; } } } else { // // not enough memory--this is bad enough for us to abort. // Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } } while(cr != CR_SUCCESS); // // We have successfully retrieved the class-specific resource data for the new // device's boot LogConfig. Now allocate a buffer of the same size to store the // corresponding resource data for each device instance we're comparing against. // We don't have to worry about devices whose resource data is larger, because // CM_Get_Res_Des_Data_Ex will do a partial fill to a buffer that's not large enough // to contain the entire structure. Since our default comparison only compares // the PnP detect signature (i.e., it ignores the legacy data at the very end of // the buffer, we're guaranteed that we have enough data to make the determination. // if(!(DevCmpContext.CurDevCsResource = MyMalloc(DevCmpContext.CsResourceSize))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } CompareProc = pSetupDupDevCompare; CompareContext = &DevCmpContext; } for(CurDevId = DevIdBuffer; *CurDevId; CurDevId += lstrlen(CurDevId) + 1) { Err = pSetupOpenAndAddNewDevInfoElem(pDeviceInfoSet, CurDevId, TRUE, &(DevInfoElem->ClassGuid), pDeviceInfoSet->InstallParamBlock.hwndParent, &CurDevInfoElem, TRUE, &AlreadyPresent, FALSE, 0, pDeviceInfoSet ); if(Err == ERROR_NOT_ENOUGH_MEMORY) { // // Out-of-memory error is the only one bad enough to get us to abort. // goto clean0; } else if(Err != NO_ERROR) { // // Just ignore this device instance, and move on to the next. // Err = NO_ERROR; continue; } DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, CurDevInfoElem, &CurDevInfoData); // // We now have the possible duplicate in our set. Call the comparison callback // routine. // Err = CompareProc(DeviceInfoSet, DeviceInfoData, &CurDevInfoData, CompareContext); // // If the device instance was created temporarily for the comparison, then it // may need to be destroyed. It should be destroyed if it wasn't a duplicate, // or if the duplicate output parameter wasn't supplied. // if(!AlreadyPresent) { if((Err != ERROR_DUPLICATE_FOUND) || !DupDeviceInfoData) { SetupDiDeleteDeviceInfo(DeviceInfoSet, &CurDevInfoData); } } if(Err != NO_ERROR) { goto clean0; } } } NoDups: // // To turn this phantom device instance into a 'live' device instance, we simply call // CM_Create_DevInst_Ex, which does the right thing (without reenumerating the whole // hardware tree!). // if(CM_Get_Device_ID_Ex(DevInfoElem->DevInst, DeviceInstanceId, SIZECHARS(DeviceInstanceId), 0, pDeviceInfoSet->hMachine) != CR_SUCCESS) { // // This should never happen! // Err = ERROR_NO_SUCH_DEVINST; } else if(CM_Get_Parent_Ex(&ParentDevInst, DevInfoElem->DevInst, 0,pDeviceInfoSet->hMachine) != CR_SUCCESS) { // // This should never happen! // Err = ERROR_NO_SUCH_DEVINST; } else if(CM_Create_DevInst_Ex(&(DevInfoElem->DevInst), DeviceInstanceId, ParentDevInst, CM_CREATE_DEVINST_NORMAL | CM_CREATE_DEVINST_DO_NOT_INSTALL, pDeviceInfoSet->hMachine) == CR_SUCCESS) { // // Device is no longer a phantom! // DevInfoElem->DiElemFlags &= ~DIE_IS_PHANTOM; } else { // // This should never happen! // Err = ERROR_NO_SUCH_DEVINST; goto clean0; } DevInfoElem->DiElemFlags |= DIE_IS_REGISTERED; clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Access the following variables so the compiler will respect our statement // ordering in the try clause. // DevIdBuffer = DevIdBuffer; DevCmpContext.NewDevCsResource = DevCmpContext.NewDevCsResource; DevCmpContext.CurDevCsResource = DevCmpContext.CurDevCsResource; NewDevLogConfig = NewDevLogConfig; NewDevResDes = NewDevResDes; } if(DevIdBuffer) { MyFree(DevIdBuffer); } if(DevCmpContext.NewDevCsResource) { MyFree(DevCmpContext.NewDevCsResource); } if(DevCmpContext.CurDevCsResource) { MyFree(DevCmpContext.CurDevCsResource); } if(NewDevResDes) { CM_Free_Res_Des_Handle(NewDevResDes); } if(NewDevLogConfig) { CM_Free_Log_Conf_Handle(NewDevLogConfig); } if((Err == ERROR_DUPLICATE_FOUND) && DupDeviceInfoData) { // // The user supplied a buffer to receive the SP_DEVINFO_DATA // structure for the duplicate. // try { if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, CurDevInfoElem, DupDeviceInfoData))) { Err = ERROR_INVALID_USER_BUFFER; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } DWORD pSetupOpenAndAddNewDevInfoElem( IN PDEVICE_INFO_SET pDeviceInfoSet, IN PCTSTR DeviceInstanceId, IN BOOL AllowPhantom, IN CONST GUID *ClassGuid, OPTIONAL IN HWND hwndParent, OPTIONAL OUT PDEVINFO_ELEM *DevInfoElem, IN BOOL CheckIfAlreadyPresent, OUT PBOOL AlreadyPresent, OPTIONAL IN BOOL OpenExistingOnly, IN ULONG CmLocateFlags, IN PDEVICE_INFO_SET ContainingDeviceInfoSet ) /*++ Routine Description: This routine opens a DEVINST handle to an existing device instance, and creates a new device information element for it. This element is added to the specified device information set. ASSUMES THAT THE CALLING ROUTINE HAS ALREADY ACQUIRED THE LOCK! Arguments: DeviceInfoSet - Device information set to add the new element to. DeviceInstanceId - Supplies the name of the device instance to be opened. AllowPhantom - Specifies whether or not phantom device instances should be allowed. If this flag is not set, and the specified device instance is not currently active, then the routine will fail with ERROR_NO_SUCH_DEVINST. ClassGuid - Optionally, supplies the class that the specified device instance must be in order to be added to the set. If the device instance is found to be of some class other than the one specified, then the call will fail with ERROR_CLASS_MISMATCH. If this parameter is not specified, then the only check that will be done on the device's class is to make sure that it matches the class of the set (if the set has an associated class). hwndParent - Optionally, supplies the handle to the top level window for UI relating to this element. DevInfoElem - Optionally, supplies the address of the variable that receives a pointer to the newly-allocated device information element. CheckIfAlreadyPresent - Specifies whether this routine should check to see whether the device instance is already in the specified devinfo set. AlreadyPresent - Optionally, supplies the address of a boolean variable that is set to indicate whether or not the specified device instance was already in the device information set. If CheckIfAlreadyThere is FALSE, then this parameter is ignored. OpenExistingOnly - If this flag is non-zero, then only succeed if the device information element is already in the set. If this flag is TRUE, then the CheckIfAlreadyPresent flag must also be TRUE. CmLocateFlags - Supplies additional flags to be passed to CM_Locate_DevInst. ContainingDeviceInfoSet - Supplies a pointer to the device information set structure with which this element is to be associated. This may be different from the pDeviceInfoSet parameter if we're working against a cloned devinfo set (i.e., to facilitate rollback). Return Value: If the function succeeds, the return value is NO_ERROR, otherwise the ERROR_* code is returned. Remarks: Note that since new device information elements are always added at the end of the existing list, the enumeration ordering is preserved, thus we don't need to invalidate our enumeration hint. --*/ { CONFIGRET cr; DEVINST DevInst; DWORD Err, DiElemFlags; GUID GuidBuffer; if((cr = CM_Locate_DevInst_Ex(&DevInst, (DEVINSTID)DeviceInstanceId, CM_LOCATE_DEVINST_NORMAL | CmLocateFlags, pDeviceInfoSet->hMachine)) == CR_SUCCESS) { DiElemFlags = DIE_IS_REGISTERED; } else { if(cr == CR_INVALID_DEVICE_ID) { return ERROR_INVALID_DEVINST_NAME; } else if(!AllowPhantom) { return ERROR_NO_SUCH_DEVINST; } // // It could be that the device instance is present in the registry, but // not currently 'live'. If this is the case, we'll be able to get a // handle to it by locating it as a phantom device instance. // if(CM_Locate_DevInst_Ex(&DevInst, (DEVINSTID)DeviceInstanceId, CM_LOCATE_DEVINST_PHANTOM | CmLocateFlags, pDeviceInfoSet->hMachine) != CR_SUCCESS) { return ERROR_NO_SUCH_DEVINST; } DiElemFlags = DIE_IS_REGISTERED | DIE_IS_PHANTOM; } // // If requested, search through the current list of device information elements // to see if this element already exists. // if(CheckIfAlreadyPresent) { if(*DevInfoElem = FindDevInfoByDevInst(pDeviceInfoSet, DevInst, NULL)) { // // Make sure that this device instance is of the proper class, if a class GUID // filter was supplied. // if(ClassGuid && !IsEqualGUID(ClassGuid, &((*DevInfoElem)->ClassGuid))) { return ERROR_CLASS_MISMATCH; } if(AlreadyPresent) { *AlreadyPresent = TRUE; } return NO_ERROR; } else if(AlreadyPresent) { *AlreadyPresent = FALSE; if(OpenExistingOnly) { // // The requested device information element isn't in the set, // so we must fail the call. // return ERROR_NO_SUCH_DEVICE_INTERFACE; } } } // // Retrieve the class GUID for this device instance. // if((Err = pSetupClassGuidFromDevInst(DevInst, pDeviceInfoSet->hMachine,&GuidBuffer)) != NO_ERROR) { return Err; } // // If a class GUID filter was specified, then make sure that it matches the // class GUID for this device instance. // if(ClassGuid && !IsEqualGUID(ClassGuid, &GuidBuffer)) { return ERROR_CLASS_MISMATCH; } return pSetupAddNewDeviceInfoElement(pDeviceInfoSet, DevInst, &GuidBuffer, NULL, hwndParent, DiElemFlags, ContainingDeviceInfoSet, DevInfoElem ); } DWORD pSetupDupDevCompare( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA NewDeviceData, IN PSP_DEVINFO_DATA ExistingDeviceData, IN PVOID CompareContext ) /*++ Routine Description: This routine is the default comparison routine for SetupDiRegisterDeviceInfo. It is used to determine whether the new device (i.e., the one being registered) is a duplicate of an existing device. The current algorithm for duplicate detection is as follows: Compare the BOOT_LOG_CONF logical configurations for the two devices. Two resource types are used in this comparison--ResType_IO and ResType_ClassSpecific. The IO ranges, if any, for the two devices will be compared to see if they're identical. Also, if the devices have a class-specific resource, then the CSD_ClassGuid, and the Plug&Play detect signature in CSD_Signature will be binary-compared. (lonnym): presently, the LogConfig only supports the class-specific resource, so I/O resource comparison is not done. Arguments: DeviceInfoSet - Supplies the handle of the device information set containing both devices being compared. NewDeviceData - Supplies the address of the SP_DEVINFO_DATA for the device being registered. ExistingDeviceData - Supplies the address of the SP_DEVINFO_DATA for the existing device with which the new device is being compared. CompareContext - Supplies the address of a context buffer used during the comparison. This buffer is actually a DEFAULT_DEVCMP_CONTEXT structure, defined as follows: typedef struct _DEFAULT_DEVCMP_CONTEXT { PCS_RESOURCE NewDevCsResource; PCS_RESOURCE CurDevCsResource; ULONG CsResourceSize; } DEFAULT_DEVCMP_CONTEXT, *PDEFAULT_DEVCMP_CONTEXT; NewDevCsResource points to the class-specific resource buffer for the new device. CurDevCsResource points to a working buffer that should be used to retrieve the class-specific resource for the existing device. CsResourceSize supplies the size in bytes of these two buffers (they're both the same size). Return Value: If the two devices are not duplicates of each other, the return value is NO_ERROR. If the two devices are duplicates of each other, the return value is ERROR_DUPLICATE_FOUND. --*/ { LOG_CONF ExistingDeviceLogConfig; RES_DES ExistingDeviceResDes; CONFIGRET cr; PDEFAULT_DEVCMP_CONTEXT DevCmpContext; PCS_DES NewCsDes, ExistingCsDes; PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; HMACHINE hMachine; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } hMachine = pDeviceInfoSet->hMachine; UnlockDeviceInfoSet(pDeviceInfoSet); // // First, retrieve the boot LogConfig for the existing device. // if(CM_Get_First_Log_Conf_Ex(&ExistingDeviceLogConfig, ExistingDeviceData->DevInst, BOOT_LOG_CONF, hMachine) != CR_SUCCESS) { // // Couldn't get the boot LogConfig--assume this device isn't a duplicate. // return NO_ERROR; } // // Assume there are no duplicates. // Err = NO_ERROR; // // Now, retrieve the the ResDes handle for the class-specific resource. // if(CM_Get_Next_Res_Des_Ex(&ExistingDeviceResDes, ExistingDeviceLogConfig, ResType_ClassSpecific, NULL, 0, hMachine) != CR_SUCCESS) { // // Couldn't get the class-specific ResDes handle--assume this device isn't a duplicate // goto clean0; } // // Now, retrieve the actual data associated with this ResDes. Note that we don't care if // we get a CR_BUFFER_SMALL error, because we are guaranteed that we got back at least the // amount of data that we have for the new device. That's all we need to do our comparison. // DevCmpContext = (PDEFAULT_DEVCMP_CONTEXT)CompareContext; cr = CM_Get_Res_Des_Data_Ex(ExistingDeviceResDes, DevCmpContext->CurDevCsResource, DevCmpContext->CsResourceSize, 0, hMachine); if((cr == CR_SUCCESS) || (cr == CR_BUFFER_SMALL)) { // // We got _at least_ enough of the buffer to do the comparison. // NewCsDes = &(DevCmpContext->NewDevCsResource->CS_Header); ExistingCsDes = &(DevCmpContext->CurDevCsResource->CS_Header); // // First, see if the Plug&Play detect signatures are both the same size. // if(NewCsDes->CSD_SignatureLength == ExistingCsDes->CSD_SignatureLength) { // // See if the class GUIDs are the same. // if(IsEqualGUID(&(NewCsDes->CSD_ClassGuid), &(ExistingCsDes->CSD_ClassGuid))) { // // Finally, see if the PnP detect signatures are identical // if(!memcmp(NewCsDes->CSD_Signature, ExistingCsDes->CSD_Signature, NewCsDes->CSD_SignatureLength)) { // // We have ourselves a duplicate! // Err = ERROR_DUPLICATE_FOUND; } } } } CM_Free_Res_Des_Handle(ExistingDeviceResDes); clean0: CM_Free_Log_Conf_Handle(ExistingDeviceLogConfig); return Err; } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiGetDeviceInstanceIdA( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OUT PSTR DeviceInstanceId, IN DWORD DeviceInstanceIdSize, OUT PDWORD RequiredSize OPTIONAL ) { WCHAR deviceInstanceId[MAX_DEVICE_ID_LEN]; PSTR deviceInstanceIdA; DWORD AnsiLength; BOOL b; DWORD rc; DWORD requiredSize; b = SetupDiGetDeviceInstanceIdW( DeviceInfoSet, DeviceInfoData, deviceInstanceId, MAX_DEVICE_ID_LEN, &requiredSize ); if(!b) { return(FALSE); } rc = GetLastError(); if(deviceInstanceIdA = pSetupUnicodeToAnsi(deviceInstanceId)) { AnsiLength = lstrlenA(deviceInstanceIdA) + 1; if(RequiredSize) { try { *RequiredSize = AnsiLength; } except(EXCEPTION_EXECUTE_HANDLER) { rc = ERROR_INVALID_PARAMETER; b = FALSE; } } if(DeviceInstanceIdSize >= AnsiLength) { if(!lstrcpyA(DeviceInstanceId,deviceInstanceIdA)) { // // lstrcpy faulted; assume caller's pointer invalid // rc = ERROR_INVALID_USER_BUFFER; b = FALSE; } } else { rc = ERROR_INSUFFICIENT_BUFFER; b = FALSE; } MyFree(deviceInstanceIdA); } else { rc = ERROR_NOT_ENOUGH_MEMORY; b = FALSE; } SetLastError(rc); return(b); } #else // // Unicode version // BOOL WINAPI SetupDiGetDeviceInstanceIdW( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OUT PWSTR DeviceInstanceId, IN DWORD DeviceInstanceIdSize, OUT PDWORD RequiredSize OPTIONAL ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DeviceInfoData); UNREFERENCED_PARAMETER(DeviceInstanceId); UNREFERENCED_PARAMETER(DeviceInstanceIdSize); UNREFERENCED_PARAMETER(RequiredSize); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiGetDeviceInstanceId( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OUT PTSTR DeviceInstanceId, IN DWORD DeviceInstanceIdSize, OUT PDWORD RequiredSize OPTIONAL ) /*++ Routine Description: This routine retrieves the device instance ID associated with a device information element. Arguments: DeviceInfoSet - Supplies a handle to the device information set containing the device information element whose ID is to be retrieved. DeviceInfoData - Supplies a pointer to the SP_DEVINFO_DATA structure for the device information element whose ID is to be retrieved. DeviceInstanceId - Supplies the address of a character buffer that will receive the ID for the specified device information element. DeviceInstanceIdSize - Supplies the size, in characters, of the DeviceInstanceId buffer. RequiredSize - Optionally, supplies the address of a variable that receives the number of characters required to store the device instance ID. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem; CONFIGRET cr; ULONG ulLen; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // Get a pointer to the element whose ID we are to retrieve. // if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, NULL))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // Find out how large the buffer needs to be. We always have to // make this call first, because CM_Get_Device_ID_Ex doesn't return // a CR_BUFFER_SMALL error if there isn't room for the terminating // NULL. // if((cr = CM_Get_Device_ID_Size_Ex(&ulLen, DevInfoElem->DevInst, 0, pDeviceInfoSet->hMachine)) == CR_SUCCESS) { // // The size returned from CM_Get_Device_ID_Size doesn't include // the terminating NULL. // ulLen++; } else { Err = (cr == CR_INVALID_DEVINST) ? ERROR_NO_SUCH_DEVINST : ERROR_INVALID_PARAMETER; goto clean0; } if(RequiredSize) { *RequiredSize = ulLen; } if(DeviceInstanceIdSize < ulLen) { Err = ERROR_INSUFFICIENT_BUFFER; goto clean0; } // // Now retrieve the ID. // if((cr = CM_Get_Device_ID_Ex(DevInfoElem->DevInst, DeviceInstanceId, DeviceInstanceIdSize, 0, pDeviceInfoSet->hMachine)) != CR_SUCCESS) { switch(cr) { case CR_INVALID_POINTER : Err = ERROR_INVALID_USER_BUFFER; break; default : // // Should never hit this! // Err = ERROR_INVALID_DATA; } } clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } DWORD pSetupAddInterfaceDeviceToDevInfoElem( IN PDEVICE_INFO_SET DeviceInfoSet, IN PDEVINFO_ELEM DevInfoElem, IN CONST GUID *InterfaceClassGuid, IN PTSTR InterfaceDeviceName, IN BOOL IsActive, IN BOOL IsDefault, IN BOOL StoreTruncateNode, IN BOOL OpenExistingOnly, OUT PINTERFACE_DEVICE_NODE *InterfaceDeviceNode OPTIONAL ) /*++ Routine Description: This routine adds the specified interface device onto a device information element's list of interface devices. Arguments: DeviceInfoSet - Supplies a pointer to the device information set containing the specified element. DevInfoElem - Supplies a pointer to the DEVINFO_ELEM structure whose interface device list is being added to. InterfaceClassGuid - Supplies a pointer to a GUID representing the class that this interface device is a member of. InterfaceDeviceName - Supplies the symbolic link name of the interface device being added. IsActive - Specifies whether or not the interface device is presently active. IsDefault - Specifies whether or not the interface device is presently the default device interface for this device interface class. StoreTruncateNode - If non-zero, then store the address of this device interface node (if newly-added) when this is the first such node added to the device information elements device interface node list (i.e., the interface class list's InterfaceDeviceTruncateNode field is NULL). OpenExistingOnly - If non-zero, then only succeed if the requested device interface is already in the device information set. InterfaceDeviceNode - Optionally, supplies the address of an interface device node pointer to be filled in with the node created for this interface device. Return Value: If success, the return value is NO_ERROR. If failure, the return value is ERROR_NOT_ENOUGH_MEMORY. --*/ { LONG GuidIndex; PINTERFACE_CLASS_LIST InterfaceClassList; PINTERFACE_DEVICE_NODE NewInterfaceDeviceNode, CurInterfaceDevice, PrevInterfaceDevice; LONG SymLinkNameId; // // First, get a reference (i.e., pointer) to this interface class guid (create one // if it's not already present for this set). // GuidIndex = AddOrGetGuidTableIndex(DeviceInfoSet, InterfaceClassGuid, TRUE); if(GuidIndex == -1) { return ERROR_NOT_ENOUGH_MEMORY; } // // Now, get the interface class list for this class from the relevant // devinfo element (again, we will create a new (empty) list if it doesn't // already exist). // if(!(InterfaceClassList = AddOrGetInterfaceClassList(DeviceInfoSet, DevInfoElem, GuidIndex, TRUE))) { return ERROR_NOT_ENOUGH_MEMORY; } // // Now we will add a new device interface node to this list (making sure // that the node isn't already there). // SymLinkNameId = pStringTableAddString(DeviceInfoSet->StringTable, InterfaceDeviceName, STRTAB_CASE_INSENSITIVE | STRTAB_BUFFER_WRITEABLE, NULL, 0 ); if(SymLinkNameId == -1) { return ERROR_NOT_ENOUGH_MEMORY; } for(CurInterfaceDevice = InterfaceClassList->InterfaceDeviceNode, PrevInterfaceDevice = NULL; CurInterfaceDevice; PrevInterfaceDevice = CurInterfaceDevice, CurInterfaceDevice = CurInterfaceDevice->Next) { if(CurInterfaceDevice->SymLinkName == SymLinkNameId) { // // The node is already in our list, we don't want to add it again. // Update the flags for this interface device to reflect whether // the device is presently active, and whether it is the default // interface for this class. // CurInterfaceDevice->Flags = (CurInterfaceDevice->Flags & ~SPINT_ACTIVE) | (IsActive ? SPINT_ACTIVE : 0); CurInterfaceDevice->Flags = (CurInterfaceDevice->Flags & ~SPINT_DEFAULT) | (IsDefault ? SPINT_DEFAULT : 0); // // Return this node to the caller. // if(InterfaceDeviceNode) { *InterfaceDeviceNode = CurInterfaceDevice; } return NO_ERROR; } } // // The device interface node wasn't already in our list--add it (unless // we've been told not to) // if(OpenExistingOnly) { return ERROR_NO_SUCH_DEVICE_INTERFACE; } if(!(NewInterfaceDeviceNode = MyMalloc(sizeof(INTERFACE_DEVICE_NODE)))) { return ERROR_NOT_ENOUGH_MEMORY; } ZeroMemory(NewInterfaceDeviceNode, sizeof(INTERFACE_DEVICE_NODE)); NewInterfaceDeviceNode->SymLinkName = SymLinkNameId; if(PrevInterfaceDevice) { PrevInterfaceDevice->Next = NewInterfaceDeviceNode; } else { InterfaceClassList->InterfaceDeviceNode = NewInterfaceDeviceNode; } InterfaceClassList->InterfaceDeviceCount++; // // If this is the first device interface node added to this list, then // remember it so we can truncate the list at this point if we later find // that we need to rollback (because we encountered some error). // if(StoreTruncateNode && !InterfaceClassList->InterfaceDeviceTruncateNode) { InterfaceClassList->InterfaceDeviceTruncateNode = NewInterfaceDeviceNode; } // // Store the interface class GUID index in the node, so that we can easily // determine the class of the node later. // NewInterfaceDeviceNode->GuidIndex = GuidIndex; // // Setup the flags for this interface device (these are the same flags that // the caller sees in the SP_INTERFACE_DEVICE_DATA structure). // NewInterfaceDeviceNode->Flags = IsActive ? SPINT_ACTIVE : 0; NewInterfaceDeviceNode->Flags |= IsDefault ? SPINT_DEFAULT : 0; // // Store a back-pointer in the device interface node, so that we can get // back to the devinfo element that owns it (there are circumstances when // we will be given a device interface data buffer outside of the context // of any devinfo element). // NewInterfaceDeviceNode->OwningDevInfoElem = DevInfoElem; if(InterfaceDeviceNode) { *InterfaceDeviceNode = NewInterfaceDeviceNode; } return NO_ERROR; } BOOL WINAPI SetupDiEnumDeviceInterfaces( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OPTIONAL IN CONST GUID *InterfaceClassGuid, IN DWORD MemberIndex, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData ) /*++ Routine Description: This API enumerates device interfaces of the specified class that are contained in the devinfo set (optionally, filtered based on DeviceInfoData). Arguments: DeviceInfoSet - Supplies a handle to the device information set containing device interfaces to be enumerated. DeviceInfoData - Optionally, supplies a pointer to a device information element for whom device interfaces are to be enumerated. InterfaceClassGuid - Supplies a pointer to the interface class GUID whose members are to be enumerated. MemberIndex - Supplies the zero-based index of the device interface to be retrieved. If DeviceInfoData is specified, then this is relative to all device interfaces of the specified class owned by that device information element. If DeviceInfoData is not specified, then this index is relative to all device interfaces contained in the device information set. InterfaceDeviceData - Supplies a pointer to a device interface data buffer that receives information about the specified device interface. The cbSize field of this structure must be filled in with sizeof(SP_DEVICE_INTERFACE_DATA), or the buffer is considered invalid. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: To enumerate device interface members, an application should initially call the SetupDiEnumDeviceInterfaces function with the MemberIndex parameter set to zero. The application should then increment MemberIndex and call the SetupDiEnumDeviceInterfaces function until there are no more values (i.e., the function fails, and GetLastError returns ERROR_NO_MORE_ITEMS). --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err, i; PDEVINFO_ELEM DevInfoElem; LONG InterfaceClassGuidIndex; PINTERFACE_CLASS_LIST InterfaceClassList; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // Retrieve the index of this interface class GUID. // if((InterfaceClassGuidIndex = AddOrGetGuidTableIndex(pDeviceInfoSet, InterfaceClassGuid, FALSE)) == -1) { Err = ERROR_NO_MORE_ITEMS; goto clean0; } // // Find the requested interface device. // if(DeviceInfoData) { // // Then we're enumerating only those interface devices that are owned // by a particular devinfo element. // if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, NULL))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } if(!(InterfaceClassList = AddOrGetInterfaceClassList(pDeviceInfoSet, DevInfoElem, InterfaceClassGuidIndex, FALSE)) || (MemberIndex >= InterfaceClassList->InterfaceDeviceCount)) { Err = ERROR_NO_MORE_ITEMS; goto clean0; } } else { // // We're enumerating across all devinfo elements. Find the appropriate devinfo // element, and adjust the member index accordingly. // for(DevInfoElem = pDeviceInfoSet->DeviceInfoHead; DevInfoElem; DevInfoElem = DevInfoElem->Next) { if(InterfaceClassList = AddOrGetInterfaceClassList(pDeviceInfoSet, DevInfoElem, InterfaceClassGuidIndex, FALSE)) { if(MemberIndex < InterfaceClassList->InterfaceDeviceCount) { // // We've found the devinfo element containing the interface device // we're looking for. // break; } else { // // The interface device we're looking for isn't associated with // this devinfo element. Adjust our index to eliminate the interface // devices for this element, and continue searching. // MemberIndex -= InterfaceClassList->InterfaceDeviceCount; } } } if(!DevInfoElem) { // // Then the specified index was higher than the count of interface devices // in this devinfo set. // Err = ERROR_NO_MORE_ITEMS; goto clean0; } } // // If we reach this point, we've found the devinfo element that contains the requested // interface device, and we have a pointer to the relevant interface class list. Now // all we need to do is retrieve the correct member of this list, and fill in the caller's // interface device data buffer with the appropriate information. // InterfaceDeviceNode = InterfaceClassList->InterfaceDeviceNode; for(i = 0; i < MemberIndex; i++) { InterfaceDeviceNode = InterfaceDeviceNode->Next; } if(!InterfaceDeviceDataFromNode(InterfaceDeviceNode, InterfaceClassGuid, DeviceInterfaceData)) { Err = ERROR_INVALID_USER_BUFFER; } clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiGetDeviceInterfaceDetailA( IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, OUT PSP_DEVICE_INTERFACE_DETAIL_DATA_A DeviceInterfaceDetailData, OPTIONAL IN DWORD DeviceInterfaceDetailDataSize, OUT PDWORD RequiredSize, OPTIONAL OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) { // // Since the maximum length for both the symbolic link and refstring components // of the interface device name is 255 characters (excluding NULL), the maximum // length of the entire interface device name is 512 characters // (255 + 255 + 1 backslash + 1 NULL character). // // Thus, we will retrieve the unicode form of this information using a maximally- // sized buffer, then convert it to ANSI, and store it in the caller's buffer, if // the caller's buffer is large enough. // BYTE UnicodeBuffer[offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath) + (512 * sizeof(WCHAR))]; PCHAR AnsiBuffer; PSP_DEVICE_INTERFACE_DETAIL_DATA_W UnicodeDetailData; DWORD rc, UnicodeRequiredSize, ReturnBufferRequiredSize; int AnsiStringSize; // // Check parameters. // rc = NO_ERROR; try { if(DeviceInterfaceDetailData) { // // Check signature and make sure buffer is large enough // to hold fixed part and at least a valid empty string. // if((DeviceInterfaceDetailData->cbSize != sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_A)) || (DeviceInterfaceDetailDataSize < (offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_A,DevicePath)+sizeof(CHAR)))) { rc = ERROR_INVALID_USER_BUFFER; } } else { // // Doesn't want data, size has to be 0. // if(DeviceInterfaceDetailDataSize) { rc = ERROR_INVALID_USER_BUFFER; } } } except(EXCEPTION_EXECUTE_HANDLER) { rc = ERROR_INVALID_USER_BUFFER; } if(rc != NO_ERROR) { SetLastError(rc); return FALSE; } UnicodeDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA_W)UnicodeBuffer; UnicodeDetailData->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); if(!SetupDiGetDeviceInterfaceDetailW(DeviceInfoSet, DeviceInterfaceData, UnicodeDetailData, sizeof(UnicodeBuffer), &UnicodeRequiredSize, DeviceInfoData)) { return FALSE; } // // We successfully retrieved the (unicode) device interface details. Now convert it // to ANSI, and store it in the caller's buffer. // UnicodeRequiredSize -= offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_W, DevicePath); UnicodeRequiredSize /= sizeof(WCHAR); // // Allocate an ANSI buffer to be used during the conversion. The maximum size the buffer // would need to be would be 2 * NumUnicodeChars. // if(!(AnsiBuffer = MyMalloc(UnicodeRequiredSize * 2))) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return FALSE; } try { AnsiStringSize = WideCharToMultiByte(CP_ACP, 0, UnicodeDetailData->DevicePath, UnicodeRequiredSize, AnsiBuffer, UnicodeRequiredSize * 2, NULL, NULL ); if(!AnsiStringSize) { // // This should never happen! // rc = GetLastError(); goto clean0; } ReturnBufferRequiredSize = AnsiStringSize + offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA_A, DevicePath); if(RequiredSize) { *RequiredSize = ReturnBufferRequiredSize; } if(ReturnBufferRequiredSize > DeviceInterfaceDetailDataSize) { rc = ERROR_INSUFFICIENT_BUFFER; goto clean0; } // // OK, so we've determined that the caller's buffer is big enough. Now, copy the // ANSI data into their buffer. // CopyMemory(DeviceInterfaceDetailData->DevicePath, AnsiBuffer, AnsiStringSize ); clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { rc = ERROR_INVALID_USER_BUFFER; } MyFree(AnsiBuffer); SetLastError(rc); return (rc == NO_ERROR); } #else // // Unicode stub // BOOL WINAPI SetupDiGetDeviceInterfaceDetailW( IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, OUT PSP_DEVICE_INTERFACE_DETAIL_DATA_W DeviceInterfaceDetailData, OPTIONAL IN DWORD DeviceInterfaceDetailDataSize, OUT PDWORD RequiredSize, OPTIONAL OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DeviceInterfaceData); UNREFERENCED_PARAMETER(DeviceInterfaceDetailData); UNREFERENCED_PARAMETER(DeviceInterfaceDetailDataSize); UNREFERENCED_PARAMETER(RequiredSize); UNREFERENCED_PARAMETER(DeviceInfoData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiGetDeviceInterfaceDetail( IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, OUT PSP_DEVICE_INTERFACE_DETAIL_DATA DeviceInterfaceDetailData, OPTIONAL IN DWORD DeviceInterfaceDetailDataSize, OUT PDWORD RequiredSize, OPTIONAL OUT PSP_DEVINFO_DATA DeviceInfoData OPTIONAL ) /*++ Routine Description: This routine retrieves details about a particular device interface (i.e., what it's "name" is that you can do a CreateFile on). Arguments: DeviceInfoSet - Supplies a handle to a device information set containing a device interface to retrieve details about. DeviceInterfaceData - Supplies a device interface information structure for which details are to be retrieved. DeviceInterfaceDetailData - Optionally, supplies the address of a device interface detail data structure that will receive additional information about the specified device interface. If this parameter is not specified, then DeviceInterfaceDetailDataSize must be zero (this would be done if the caller was only interested in finding out how large of a buffer is required). If this parameter is specified, the cbSize field of this structure must be set to the size of the structure before calling this API. NOTE: The 'size of the structure' on input means sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA). Note that this is essentially just a signature and is entirely separate from DeviceInterfaceDetailDataSize. See below. DeviceInterfaceDetailDataSize - Supplies the size, in bytes, of the DeviceInterfaceDetailData buffer. To be valid this buffer must be at least offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + sizeof(TCHAR) bytes, which allows storage of the fixed part of the structure and a single nul to terminate an empty multi_sz. (Depending on structure alignment, character width, and the data to be returned, this may actually be smaller than sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA). RequiredSize - Optionally, supplies the address of a variable that receives the number of bytes required to store the detailed device interface information. This value includes both the size of the structure itself, and the additional number of bytes required for the variable-length character buffer at the end of it that holds the device path. DeviceInfoData - Optionally, supplies a pointer to a SP_DEVINFO_DATA structure that will receive information about the device information element that owns this device interface. Callers that only want to retrieve this parameter may pass NULL for DeviceInterfaceDetailData, and pass 0 for DeviceInterfaceDetailDataSize. Assuming the specified device interface is valid, the API will fail, with GetLastError returning ERROR_INSUFFICIENT_BUFFER. However, DeviceInfoData will have been correctly filled in with the associated device information element. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; PCTSTR DevicePath; DWORD DevicePathLength, BufferLengthNeeded; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // First, find the devinfo element that owns this interface device. This // is used as a form of validation, and also may be needed later on, if the // user supplied us with a DeviceInfoData buffer to be filled in. // if(!(DevInfoElem = FindDevInfoElemForInterfaceDevice(pDeviceInfoSet, DeviceInterfaceData))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // The Reserved field contains a pointer to the underlying interface device node. // InterfaceDeviceNode = (PINTERFACE_DEVICE_NODE)(DeviceInterfaceData->Reserved); DevicePath = pStringTableStringFromId(pDeviceInfoSet->StringTable, InterfaceDeviceNode->SymLinkName ); DevicePathLength = (lstrlen(DevicePath) + 1) * sizeof(TCHAR); // // Before attempting to store the device path in the caller's buffer, check to see // whether they requested that the associated devinfo element be returned. If so, // do that first. // if(DeviceInfoData) { if(!(DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, DevInfoElem, DeviceInfoData))) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } } // // Validate the caller's buffer. // if(DeviceInterfaceDetailData) { if((DeviceInterfaceDetailDataSize < (offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + sizeof(TCHAR))) || (DeviceInterfaceDetailData->cbSize != sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA))) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } } else if(DeviceInterfaceDetailDataSize) { Err = ERROR_INVALID_USER_BUFFER; goto clean0; } // // Compute the buffer size required. // BufferLengthNeeded = offsetof(SP_DEVICE_INTERFACE_DETAIL_DATA, DevicePath) + DevicePathLength; if(RequiredSize) { *RequiredSize = BufferLengthNeeded; } if(BufferLengthNeeded > DeviceInterfaceDetailDataSize) { Err = ERROR_INSUFFICIENT_BUFFER; goto clean0; } CopyMemory(DeviceInterfaceDetailData->DevicePath, DevicePath, DevicePathLength); clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiOpenDeviceInterfaceA( IN HDEVINFO DeviceInfoSet, IN PCSTR DevicePath, IN DWORD OpenFlags, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData OPTIONAL ) { PCWSTR UnicodeDevicePath; DWORD rc; rc = pSetupCaptureAndConvertAnsiArg(DevicePath, &UnicodeDevicePath); if(rc == NO_ERROR) { rc = _SetupDiOpenInterfaceDevice(DeviceInfoSet, (PWSTR)UnicodeDevicePath, OpenFlags, DeviceInterfaceData ); MyFree(UnicodeDevicePath); } SetLastError(rc); return(rc == NO_ERROR); } #else // // Unicode stub // BOOL WINAPI SetupDiOpenDeviceInterfaceW( IN HDEVINFO DeviceInfoSet, IN PCWSTR DevicePath, IN DWORD OpenFlags, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData OPTIONAL ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DevicePath); UNREFERENCED_PARAMETER(OpenFlags); UNREFERENCED_PARAMETER(DeviceInterfaceData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiOpenDeviceInterface( IN HDEVINFO DeviceInfoSet, IN PCTSTR DevicePath, IN DWORD OpenFlags, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData OPTIONAL ) /*++ Routine Description: This routine opens up the device information element that exposes the specified device interface (if it's not already in the device information set), and then adds this device interface to the set. Arguments: DeviceInfoSet - Supplies a handle to a device information set into which this new device interface element is to be opened. NOTE: The class of the underlying device instance must match the class of the set (or the set should have no associated class). If this is not the case, the call will fail, and GetLastError will return ERROR_CLASS_MISMATCH. DevicePath - Supplies the name of the device interface to be opened. This name is a Win32 device path of the form "\\?\[\]", and is returned via a previous enumeration of device interface (i.e., via SetupDiGetClassDevs(...DIGCF_INTERFACEDEVICE) or by notification via RegisterDeviceNotification). OpenFlags - Supplies flags controlling how the device interface element is to be opened. May be a combination of the following values: DIODI_NO_ADD - Only succeed the call (and optionally return the device interface data) if the device interface already exists in the device information set. This flag may be used to get a device interface data context buffer back given a device interface name, without causing that interface to be opened if it's not already in the set. This is useful, for example, when an app receives a device interface removal notification. Such an app will want to remove the corresponding device interface data from the device information they're using as a container, but they wouldn't want to open up a device interface element not already in the set just so they can close it. DeviceInterfaceData - Optionally, supplies a pointer to a device interface data buffer that receives information about the specified device interface. The cbSize field of this structure must be filled in with sizeof(SP_DEVICE_INTERFACE_DATA) or the buffer is considered invalid. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: If the new device interface was successfully opened, but the user-supplied DeviceInterfaceData buffer is invalid, this API will return FALSE, with GetLastError returning ERROR_INVALID_USER_BUFFER. The device interface element _will_ have been added as a new member of the set, however. If the device interface already exists in the set, the flags will be updated to reflect the current state of the device. Thus, for example, if a device was not active when originally opened into the set, but has since become active, this API may be used to 'refresh' the flags on that device interface element, so that the SPINT_ACTIVE bit is once again in sync with reality. Note that since new device information elements are always added at the end of the existing list, the enumeration ordering is preserved, thus we don't need to invalidate our enumeration hint. --*/ { PCTSTR WritableDevicePath; DWORD rc; rc = CaptureStringArg(DevicePath, &WritableDevicePath); if(rc == NO_ERROR) { rc = _SetupDiOpenInterfaceDevice(DeviceInfoSet, (PTSTR)WritableDevicePath, OpenFlags, DeviceInterfaceData ); MyFree(WritableDevicePath); } SetLastError(rc); return(rc == NO_ERROR); } DWORD _SetupDiOpenInterfaceDevice( IN HDEVINFO DeviceInfoSet, IN PTSTR DevicePath, IN DWORD OpenFlags, OUT PSP_DEVICE_INTERFACE_DATA InterfaceDeviceData OPTIONAL ) /*++ Routine Description: Worker routine for SetupDiOpenInterfaceDevice(A|W). This is a separate routine so that both A and W versions can capture their DevicePath argument into a writable buffer, because we need this for adding the case-insensitive form to the string table. Arguments: See SetupDiOpenInterfaceDevice for details. Return Value: If the function succeeds, the return value is NO_ERROR. Otherwise, it is a Win32 error code. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err, DevicePathLen; PCTSTR p; TCHAR InterfaceGuidString[GUID_STRING_LEN]; GUID InterfaceGuid; HKEY hKey; TCHAR DeviceInstanceId[MAX_DEVICE_ID_LEN]; PCTSTR MachineName; BOOL DevInfoAlreadyPresent, IsActive, IsDefault; PDEVINFO_ELEM DevInfoElem; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; if(OpenFlags & ~DIODI_NO_ADD) { return ERROR_INVALID_FLAGS; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { return ERROR_INVALID_HANDLE; } Err = NO_ERROR; hKey = INVALID_HANDLE_VALUE; DevInfoElem = NULL; try { // // Retrieve the interface class of this device. Since the device path is of // the form "\\?\MungedDevInstName#{InterfaceClassGuid}[\RefString]", we can // retrieve the GUID from the name. // // NOTE: The algorithm about how this name is generated must be kept in sync // with the kernel-mode implementation of IoRegisterDeviceClassAssocation, et. al. // DevicePathLen = lstrlen(DevicePath); // // Move past "\\?\" prefix (also allow "\\.\" until Memphis fixes their code) // if((DevicePathLen < 4) || (DevicePath[0] != TEXT('\\')) || (DevicePath[1] != TEXT('\\')) || ((DevicePath[2] != TEXT('?')) && (DevicePath[2] != TEXT('.'))) || (DevicePath[3] != TEXT('\\'))) { Err = ERROR_BAD_PATHNAME; goto clean0; } p = _tcschr(&(DevicePath[4]), TEXT('\\')); if(!p) { // // This name has no refstring--set the pointer to the end of the string // p = DevicePath + DevicePathLen; } // // Make sure there are enough characters preceding the current position for a // GUID to fit. // if(p < (DevicePath + 3 + GUID_STRING_LEN)) { Err = ERROR_BAD_PATHNAME; goto clean0; } lstrcpyn(InterfaceGuidString, p - (GUID_STRING_LEN - 1), SIZECHARS(InterfaceGuidString)); if(pSetupGuidFromString(InterfaceGuidString, &InterfaceGuid) != NO_ERROR) { Err = ERROR_BAD_PATHNAME; goto clean0; } // // Retrieve the name of the machine associated with this DeviceInfoSet. // if(pDeviceInfoSet->hMachine) { MYASSERT(pDeviceInfoSet->MachineName != -1); MachineName = pStringTableStringFromId(pDeviceInfoSet->StringTable, pDeviceInfoSet->MachineName); } else { MachineName = NULL; } // // OK, now we know that we retrieved a valid GUID from an (apparently) valid device path. // Go open up this interface device key under the DeviceClasses registry branch. // hKey = SetupDiOpenClassRegKeyEx(&InterfaceGuid, KEY_READ, DIOCR_INTERFACE, MachineName, NULL ); if(hKey == INVALID_HANDLE_VALUE) { Err = GetLastError(); goto clean0; } if(NO_ERROR != (Err = pSetupGetDevInstNameAndStatusForInterfaceDevice( hKey, DevicePath, DeviceInstanceId, SIZECHARS(DeviceInstanceId), &IsActive, &IsDefault))) { goto clean0; } if(NO_ERROR != (Err = pSetupOpenAndAddNewDevInfoElem(pDeviceInfoSet, DeviceInstanceId, TRUE, NULL, NULL, &DevInfoElem, TRUE, &DevInfoAlreadyPresent, (OpenFlags & DIODI_NO_ADD), 0, pDeviceInfoSet))) { // // Make sure DevInfoElem is still NULL, so we won't try to delete it. // DevInfoElem = NULL; goto clean0; } // // Now that we've successfully opened up the device instance that 'owns' // this interface device, add a new interface device node onto this // devinfo element's list. // if((NO_ERROR == (Err = pSetupAddInterfaceDeviceToDevInfoElem(pDeviceInfoSet, DevInfoElem, &InterfaceGuid, DevicePath, IsActive, IsDefault, FALSE, (OpenFlags & DIODI_NO_ADD), &InterfaceDeviceNode))) || DevInfoAlreadyPresent) { // // Either we successfully added the interface device or the owning devinfo element // was already in the set. In either case, we want to reset the DevInfoElem pointer // to NULL so we won't try to delete it from the set. // DevInfoElem = NULL; } clean0: ; // nothing to do } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Reference the following variables so the compiler will respect statement ordering // w.r.t. assignment. // DevInfoElem = DevInfoElem; hKey = hKey; } if(hKey != INVALID_HANDLE_VALUE) { RegCloseKey(hKey); } if(Err != NO_ERROR) { if(DevInfoElem) { SP_DEVINFO_DATA DeviceInfoData; DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA); DevInfoDataFromDeviceInfoElement(pDeviceInfoSet, DevInfoElem, &DeviceInfoData); SetupDiDeleteDeviceInfo(DeviceInfoSet, &DeviceInfoData); } } else if(InterfaceDeviceData) { try { if(!InterfaceDeviceDataFromNode(InterfaceDeviceNode, &InterfaceGuid, InterfaceDeviceData)) { Err = ERROR_INVALID_USER_BUFFER; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } } UnlockDeviceInfoSet(pDeviceInfoSet); return Err; } BOOL WINAPI SetupDiGetDeviceInterfaceAlias( IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData, IN CONST GUID *AliasInterfaceClassGuid, OUT PSP_DEVICE_INTERFACE_DATA AliasDeviceInterfaceData ) /*++ Routine Description: This routine retrieves the device interface of a particular class that 'aliases' the specified device interface. Two device interfaces are considered aliases of each other if the following to criteria are met: 1. Both device interfaces are exposed by the same device instance. 2. Both device interfaces share the same RefString. Arguments: DeviceInfoSet - Supplies a handle to the device information set containing the device interface for which an alias is to be retrieved. DeviceInterfaceData - Specifies the device interface whose alias is to be retrieved. AliasInterfaceClassGuid - Supplies a pointer to the GUID representing the interface class for which the alias is to be retrieved. AliasDeviceInterfaceData - Supplies a pointer to a device interface data buffer that receives information about the alias device interface. The cbSize field of this structure must be filled in with sizeof(SP_DEVICE_INTERFACE_DATA) or the buffer is considered invalid. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: If the alias device interface was successfully opened, but the user-supplied AliasDeviceInterfaceData buffer is invalid, this API will return FALSE, with GetLastError returning ERROR_INVALID_USER_BUFFER. The alias device interface element _will_ have been added as a new member of the set, however. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem, DevInfoElem2; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; PCTSTR DevicePath; PTSTR AliasPath; ULONG AliasPathLength; CONFIGRET cr; SP_DEVICE_INTERFACE_DATA TempInterfaceDevData; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; AliasPath = NULL; try { // // First, find the devinfo element that owns this interface device (for validation). // if(!(DevInfoElem = FindDevInfoElemForInterfaceDevice(pDeviceInfoSet, DeviceInterfaceData))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // The Reserved field contains a pointer to the underlying interface device node. // InterfaceDeviceNode = (PINTERFACE_DEVICE_NODE)(DeviceInterfaceData->Reserved); // // Get the device path for this interface device. // DevicePath = pStringTableStringFromId(pDeviceInfoSet->StringTable, InterfaceDeviceNode->SymLinkName ); // // Choose a buffer size that should always be large enough (we know this is the // case today, but since there is no defined maximum length on this path, we leave // the capability for it to grow in the future). // AliasPathLength = 512; while(TRUE) { if(!(AliasPath = MyMalloc(AliasPathLength * sizeof(TCHAR)))) { Err = ERROR_NOT_ENOUGH_MEMORY; goto clean0; } // // Now retrieve the name of this interface device's alias in the specified class. // cr = CM_Get_Device_Interface_Alias_Ex(DevicePath, (LPGUID)AliasInterfaceClassGuid, AliasPath, &AliasPathLength, 0, pDeviceInfoSet->hMachine); if(cr == CR_SUCCESS) { break; } else { // // If our buffer was too small, then free it, and try again with a larger buffer. // if(cr == CR_BUFFER_SMALL) { MyFree(AliasPath); AliasPath = NULL; } else { Err = MapCrToSpError(cr, ERROR_NO_SUCH_DEVICE_INTERFACE); goto clean0; } } } // // If we get to here then we've successfully retrieved the alias name. Now open this // interface device in our device information set. // TempInterfaceDevData.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA); if(!SetupDiOpenDeviceInterface(DeviceInfoSet, AliasPath, 0, &TempInterfaceDevData)) { // // This should never happen. // Err = GetLastError(); goto clean0; } // // Retrieve the device information element for this alias interface device (this has to succeed). // DevInfoElem2 = FindDevInfoElemForInterfaceDevice(pDeviceInfoSet, DeviceInterfaceData); // // Since these two interface devices are aliases of each other, they'd better be owned by // the same devinfo element! // MYASSERT(DevInfoElem == DevInfoElem2); InterfaceDeviceNode = (PINTERFACE_DEVICE_NODE)(TempInterfaceDevData.Reserved); clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Reference the following variable so the compiler will respect our statement ordering // w.r.t. assignment. // AliasPath = AliasPath; } UnlockDeviceInfoSet(pDeviceInfoSet); if(AliasPath) { MyFree(AliasPath); } if(Err == NO_ERROR) { try { if(!InterfaceDeviceDataFromNode(InterfaceDeviceNode, AliasInterfaceClassGuid, AliasDeviceInterfaceData)) { Err = ERROR_INVALID_USER_BUFFER; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } } SetLastError(Err); return(Err == NO_ERROR); } DWORD pSetupGetDevInstNameAndStatusForInterfaceDevice( IN HKEY hKeyInterfaceClass, IN PCTSTR InterfaceDeviceName, OUT PTSTR OwningDevInstName, OPTIONAL IN DWORD OwningDevInstNameSize, OUT PBOOL IsActive, OPTIONAL OUT PBOOL IsDefault OPTIONAL ) /*++ Routine Description: This routine retrieves the name of the device instance that exposes the specified interface device and whether or not that interface device is currently active, or is the default interface for the interface class to which it belongs. Arguments: hKeyInterfaceClass - Supplies a handle to the registry key for the interface class of which this interface device is a member. E.g., HKLM\SYSTEM\CurrentControlSet\Control\DeviceClasses\{InterfaceClassGuid} InterfaceDeviceName - Supplies the name of the interface device. OwningDevInstName - Optionally, supplies the address of a character buffer that receives the name of the device instance that exposes this interface device. This buffer should be at least MAX_DEVICE_ID_LEN characters long. OwningDevInstNameSize - Supplies the size, in characters, of the OwningDevInstName buffer. IsActive - Optionally, supplies the address of a boolean variable that is set upon return to indicate whether this interface is presently exposed. IsDefault - Optionally, supplies the address of a boolean variable that is set upon return to indicate whether this interface is presently the default device interface for its device class. Return Value: If the function succeeds, the return value is NO_ERROR. Otherwise, it is a Win32 error code. --*/ { DWORD Err, DataBufferSize, RegDataType; HKEY hKeyInterfaceDevice, hKeyControl; TCHAR InterfaceClassDefault[(2 * MAX_PATH) + 1]; // 2 max-sized regkey names + terminating NULL. hKeyInterfaceDevice = hKeyControl = INVALID_HANDLE_VALUE; try { DataBufferSize = OwningDevInstNameSize * sizeof(TCHAR); Err = OpenDeviceInterfaceSubKey(hKeyInterfaceClass, InterfaceDeviceName, KEY_READ, &hKeyInterfaceDevice, OwningDevInstName, &DataBufferSize ); if(Err != ERROR_SUCCESS) { // // Make sure the key handle is still invalid, so we'll know not to // close it. // hKeyInterfaceDevice = INVALID_HANDLE_VALUE; goto clean0; } if(IsActive) { // // The user wants to find out whether this interface device is currently active. // Check the 'Linked' value entry under the volatile 'Control' subkey to find // this out. // *IsActive = FALSE; if(ERROR_SUCCESS == RegOpenKeyEx(hKeyInterfaceDevice, pszControl, 0, KEY_READ, &hKeyControl)) { DataBufferSize = sizeof(*IsActive); if(ERROR_SUCCESS != RegQueryValueEx(hKeyControl, pszLinked, NULL, NULL, (PBYTE)IsActive, &DataBufferSize)) { *IsActive = FALSE; } } } if(IsDefault) { // // The user wants to find out whether this interface device is the // default device interface for its device class. Check the // 'Default' value entry under the interface class key to find this // out. // *IsDefault = FALSE; DataBufferSize = sizeof(InterfaceClassDefault); if(ERROR_SUCCESS == RegQueryValueEx(hKeyInterfaceClass, pszDefault, NULL, NULL, (PBYTE)InterfaceClassDefault, &DataBufferSize)) { if (lstrcmpi(InterfaceClassDefault, InterfaceDeviceName) == 0) { *IsDefault = TRUE; } } } clean0: ; // nothing to do } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Reference the following variables so the compiler will respect statement // ordering w.r.t. assignment. // hKeyInterfaceDevice = hKeyInterfaceDevice; hKeyControl = hKeyControl; } if(hKeyControl != INVALID_HANDLE_VALUE) { RegCloseKey(hKeyControl); } if(hKeyInterfaceDevice != INVALID_HANDLE_VALUE) { RegCloseKey(hKeyInterfaceDevice); } return Err; } #ifdef UNICODE // // ANSI version // BOOL WINAPI SetupDiCreateDeviceInterfaceA( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN CONST GUID *InterfaceClassGuid, IN PCSTR ReferenceString, OPTIONAL IN DWORD CreationFlags, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData OPTIONAL ) { PCWSTR UnicodeRefString; DWORD rc; BOOL b; b = FALSE; if(ReferenceString) { rc = pSetupCaptureAndConvertAnsiArg(ReferenceString, &UnicodeRefString); } else { UnicodeRefString = NULL; rc = NO_ERROR; } if(rc == NO_ERROR) { b = SetupDiCreateDeviceInterfaceW(DeviceInfoSet, DeviceInfoData, InterfaceClassGuid, UnicodeRefString, CreationFlags, DeviceInterfaceData ); rc = GetLastError(); if(UnicodeRefString) { MyFree(UnicodeRefString); } } SetLastError(rc); return(b); } #else // // Unicode version // BOOL WINAPI SetupDiCreateDeviceInterfaceW( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN CONST GUID *InterfaceClassGuid, IN PCWSTR ReferenceString, OPTIONAL IN DWORD CreationFlags, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData OPTIONAL ) { UNREFERENCED_PARAMETER(DeviceInfoSet); UNREFERENCED_PARAMETER(DeviceInfoData); UNREFERENCED_PARAMETER(InterfaceClassGuid); UNREFERENCED_PARAMETER(ReferenceString); UNREFERENCED_PARAMETER(CreationFlags); UNREFERENCED_PARAMETER(DeviceInterfaceData); SetLastError(ERROR_CALL_NOT_IMPLEMENTED); return(FALSE); } #endif BOOL WINAPI SetupDiCreateDeviceInterface( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN CONST GUID *InterfaceClassGuid, IN PCTSTR ReferenceString, OPTIONAL IN DWORD CreationFlags, OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData OPTIONAL ) /*++ Routine Description: This API creates (registers) a device interface for the specified device information element, and adds this device interface to the device information set. Arguments: DeviceInfoSet - Supplies a handle to a device information set containing the device information element for which a new device interface is being added. DeviceInfoData - Supplies the device information element for whom a device interface is being added. InterfaceClassGuid - Supplies the address of a GUID containing the class for this new device interface. ReferenceString - Optionally, supplies the reference string to be passed to the driver when opening this device interface. This string becomes part of the device interface's name (as an additional path component). CreationFlags - Reserved for future use, must be set to 0. DeviceInterfaceData - Optionally, supplies a pointer to a device interface data buffer that receives information about the newly-created device interface. The cbSize field of this structure must be filled in with sizeof(SP_DEVICE_INTERFACE_DATA) or the buffer is considered invalid. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: If the new device interface was successfully created, but the user-supplied DeviceInterfaceData buffer is invalid, this API will return FALSE, with GetLastError returning ERROR_INVALID_USER_BUFFER. The device interface element _will_ have been added as a new member of the set, however. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem; TCHAR InterfaceDeviceName[(2 * MAX_PATH) + 1]; // 2 max-sized regkey names + terminating NULL. ULONG InterfaceDeviceNameSize; PCTSTR MachineName; CONFIGRET cr; BOOL IsActive, IsDefault; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; HKEY hKey; if(CreationFlags) { SetLastError(ERROR_INVALID_FLAGS); return FALSE; } if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; hKey = INVALID_HANDLE_VALUE; try { // // Get a pointer to the device information element we're registering an // interface device for. // if(!(DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, NULL))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // Register the interface device. // InterfaceDeviceNameSize = SIZECHARS(InterfaceDeviceName); cr = CM_Register_Device_Interface_Ex(DevInfoElem->DevInst, (LPGUID)InterfaceClassGuid, ReferenceString, InterfaceDeviceName, &InterfaceDeviceNameSize, 0, pDeviceInfoSet->hMachine); if(cr != CR_SUCCESS) { Err = MapCrToSpError(cr, ERROR_INVALID_DATA); goto clean0; } // // Retrieve the name of the machine associated with this DeviceInfoSet. // if(pDeviceInfoSet->hMachine) { MYASSERT(pDeviceInfoSet->MachineName != -1); MachineName = pStringTableStringFromId(pDeviceInfoSet->StringTable, pDeviceInfoSet->MachineName); } else { MachineName = NULL; } // // This interface device might have already been registered, in which case it // could already be active. We must check the 'Linked' registry value to see // whether this device is active. // hKey = SetupDiOpenClassRegKeyEx(InterfaceClassGuid, KEY_READ, DIOCR_INTERFACE, MachineName, NULL ); if(hKey != INVALID_HANDLE_VALUE) { if(NO_ERROR != pSetupGetDevInstNameAndStatusForInterfaceDevice( hKey, InterfaceDeviceName, NULL, 0, &IsActive, &IsDefault)) { // // This shouldn't fail, but if it does, then just assume that the // interface device's status is non-active, and it is not the default. // IsActive = FALSE; IsDefault = FALSE; } } else { // // This should never happen--if it does, assume that the interface device // isn't active. // IsActive = FALSE; IsDefault = FALSE; } // // The interface device was successfully registered, now add it to the list of // interface devices associated with this device information element. // Err = pSetupAddInterfaceDeviceToDevInfoElem(pDeviceInfoSet, DevInfoElem, InterfaceClassGuid, InterfaceDeviceName, IsActive, IsDefault, FALSE, FALSE, &InterfaceDeviceNode ); clean0: ; // nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; // // Reference the following variable so the compiler will respect statement // ordering w.r.t. assignment. // hKey = hKey; } if(hKey != INVALID_HANDLE_VALUE) { RegCloseKey(hKey); } if((Err == NO_ERROR) && DeviceInterfaceData) { try { if(!InterfaceDeviceDataFromNode(InterfaceDeviceNode, InterfaceClassGuid, DeviceInterfaceData)) { Err = ERROR_INVALID_USER_BUFFER; } } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_USER_BUFFER; } } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return (Err == NO_ERROR); } BOOL WINAPI SetupDiDeleteDeviceInterfaceData( IN HDEVINFO DeviceInfoSet, IN PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData ) /*++ Routine Description: This API deletes the specified device interface element from the device information set. It _does not_ remove (unregister) the device interface from the system (to do that, use SetupDiRemoveDeviceInterface). Arguments: DeviceInfoSet - Supplies a handle to the device information set containing device interface to be deleted. DeviceInterfaceData - Specifies the device interface to be deleted. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: After a device interface is deleted, the device interface enumeration index is invalid, and enumeration should be re-started at index 0. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem; PINTERFACE_DEVICE_NODE InterfaceDeviceNode, CurInterfaceDeviceNode, PrevInterfaceDeviceNode; PINTERFACE_CLASS_LIST InterfaceClassList; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // First, find the devinfo element that owns this interface device. // if(!(DevInfoElem = FindDevInfoElemForInterfaceDevice(pDeviceInfoSet, DeviceInterfaceData))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // The Reserved field contains a pointer to the underlying interface device node. // InterfaceDeviceNode = (PINTERFACE_DEVICE_NODE)(DeviceInterfaceData->Reserved); // // Find this devinfo element's interface device list for this class. // if(!(InterfaceClassList = AddOrGetInterfaceClassList(pDeviceInfoSet, DevInfoElem, InterfaceDeviceNode->GuidIndex, FALSE))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // Find this interface device node in the list of interface devices for this device // information element. // for(CurInterfaceDeviceNode = InterfaceClassList->InterfaceDeviceNode, PrevInterfaceDeviceNode = NULL; CurInterfaceDeviceNode; PrevInterfaceDeviceNode = CurInterfaceDeviceNode, CurInterfaceDeviceNode = CurInterfaceDeviceNode->Next) { if(CurInterfaceDeviceNode == InterfaceDeviceNode) { break; } } if(!CurInterfaceDeviceNode) { Err = ERROR_INVALID_PARAMETER; goto clean0; } MYASSERT(InterfaceClassList->InterfaceDeviceCount); if(PrevInterfaceDeviceNode) { PrevInterfaceDeviceNode->Next = CurInterfaceDeviceNode->Next; } else { InterfaceClassList->InterfaceDeviceNode = CurInterfaceDeviceNode->Next; } MyFree(InterfaceDeviceNode); InterfaceClassList->InterfaceDeviceCount--; clean0: ; // Nothing to do. } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } BOOL WINAPI SetupDiRemoveDeviceInterface( IN HDEVINFO DeviceInfoSet, IN OUT PSP_DEVICE_INTERFACE_DATA DeviceInterfaceData ) /*++ Routine Description: This API removes (unregisters) the specified device interface. It _does not_ delete the device interface element from the device information set (thus enumeration is not affected). Instead, it marks the device interface element as invalid, so that it cannot be used in any subsequent API calls except SetupDiDeleteDeviceInterfaceData. Arguments: DeviceInfoSet - Supplies a handle to the device information set containing device interface to be removed. DeviceInterfaceData - Specifies the device interface to be removed. All traces of this device will be removed from the registry. Upon return, the Flags field of this structure will be updated to reflect the new state of this device interface. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. Remarks: There is no way to unregister a device interface while it is active. Thus, this API will fail with ERROR_DEVICE_INTERFACE_ACTIVE in this case. If this happens, you can do one of the following things in an attempt to remove the device interface: 1. If there is some defined mechanism of communication to the device interface/underlying device instance (e.g., an IOCTL) that causes the driver to un-expose the device interface, then this method may be used, and _then_ SetupDiRemoveDeviceInterface may be called. 2. If there is no mechanism as described in method (1), then the owning device instance must be stopped (e.g., via SetupDiChangeState), which will cause all device interfaces owned by that device instance to go inactive. After that is done, then SetupDiRemoveDeviceInterface may be called. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; DWORD Err; PDEVINFO_ELEM DevInfoElem; PINTERFACE_DEVICE_NODE InterfaceDeviceNode; PCTSTR DevicePath, MachineName; TCHAR InterfaceClassDefault[(2 * MAX_PATH) + 1]; // 2 max-sized regkey names + terminating NULL. DWORD DataBufferSize; HKEY hKeyInterfaceClass; CONFIGRET cr; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; try { // // Get a pointer to the device information element for the specified // interface device. // if(!(DevInfoElem = FindDevInfoElemForInterfaceDevice(pDeviceInfoSet, DeviceInterfaceData))) { Err = ERROR_INVALID_PARAMETER; goto clean0; } // // The Reserved field contains a pointer to the underlying interface device node. // InterfaceDeviceNode = (PINTERFACE_DEVICE_NODE)(DeviceInterfaceData->Reserved); // // OK, now open the interface device's root storage key. // DevicePath = pStringTableStringFromId(pDeviceInfoSet->StringTable, InterfaceDeviceNode->SymLinkName ); cr = CM_Unregister_Device_Interface_Ex(DevicePath, 0,pDeviceInfoSet->hMachine); if(cr != CR_SUCCESS) { switch(cr) { case CR_NO_SUCH_DEVICE_INTERFACE : // // The device interface was deleted after it was enumerated/opened // by this client. In this case, we'll go ahead and succeed this // call. // break; case CR_DEVICE_INTERFACE_ACTIVE : Err = ERROR_DEVICE_INTERFACE_ACTIVE; // // If our SPINT_ACTIVE flag isn't set, then that means that the device // wasn't active the last time we looked. Update our flag to indicate // the device's new state. // InterfaceDeviceNode->Flags |= SPINT_ACTIVE; goto clean1; default : Err = ERROR_INVALID_DATA; goto clean0; } } // // The interface device was successfully removed. Now, mark the interface device // node to reflect that it's now invalid. // InterfaceDeviceNode->Flags |= SPINT_REMOVED; // // Also, clear the SPINT_ACTIVE flag, in case it's set. It's possible that we thought // the device was active, even though it was deactivated since the last time we looked. // InterfaceDeviceNode->Flags &= ~SPINT_ACTIVE; // // Retrieve the name of the machine associated with this DeviceInfoSet. // if(pDeviceInfoSet->hMachine) { MYASSERT(pDeviceInfoSet->MachineName != -1); MachineName = pStringTableStringFromId(pDeviceInfoSet->StringTable, pDeviceInfoSet->MachineName); } else { MachineName = NULL; } // // Open this interface class key under the DeviceClasses registry // branch. // hKeyInterfaceClass = SetupDiOpenClassRegKeyEx(&DeviceInterfaceData->InterfaceClassGuid, KEY_READ | KEY_WRITE, DIOCR_INTERFACE, MachineName, NULL); if(hKeyInterfaceClass == INVALID_HANDLE_VALUE) { goto clean1; } // // Check if this interface is specified in the registry as the default // device interface. // DataBufferSize = sizeof(InterfaceClassDefault); if(ERROR_SUCCESS == RegQueryValueEx(hKeyInterfaceClass, pszDefault, NULL, NULL, (PBYTE)InterfaceClassDefault, &DataBufferSize)) { if (lstrcmpi(InterfaceClassDefault, DevicePath) == 0) { // // Delete the "Default" value under this interface class key. // if(ERROR_SUCCESS == RegDeleteValue(hKeyInterfaceClass, pszDefault)) { // // This interface has been successfully removed as the // "Default" interface for this class. Clear the // SPINT_DEFAULT flag. // InterfaceDeviceNode->Flags &= ~SPINT_DEFAULT; } } else { // // This interface is not listed in the registry as the // current default device interface for this class, so clear // the SPINT_DEFAULT flag. // InterfaceDeviceNode->Flags &= ~SPINT_DEFAULT; } } else { // // We could not retrieve the "Default" value, but we should still // make sure to clear the SPINT_DEFAULT flag on this interface. // InterfaceDeviceNode->Flags &= ~SPINT_DEFAULT; } RegCloseKey(hKeyInterfaceClass); clean1: // // Finally, updated the flags in the caller-supplied buffer to indicate the new status // of this interface device. // DeviceInterfaceData->Flags = InterfaceDeviceNode->Flags; clean0: ; // nothing to do } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); } BOOL pSetupDiSetDeviceInfoContext( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD Context ) /*++ Routine Description: This API stores a context value into the specified device information element for later retrieval via pSetupDiGetDeviceInfoContext. Arguments: DeviceInfoSet - Supplies a handle to the device information set containing the device information element with which the context data is to be associated. DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure indicating which element the context data should be associated with. Context - Specifies the data value to be stored for this device information element. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { return pSetupDiGetOrSetDeviceInfoContext(DeviceInfoSet, DeviceInfoData, Context, NULL ); } BOOL pSetupDiGetDeviceInfoContext( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, OUT PDWORD Context ) /*++ Routine Description: This API retrieves a context value from the specified device information element (stored there via pSetupDiSetDeviceInfoContext). Arguments: DeviceInfoSet - Supplies a handle to the device information set containing the device information element with which the context data is associated. DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure indicating which element the context data is associated with. Context - Supplies the address of a variable that receives the context value stored for the device information element in a prior call to pSetupDiSetDeviceInfoContext. If no context data has previously been stored for this element, this variable will be filled in with zero upon return. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { // // If we let a NULL context pointer go through to the worker routine, it will // think this is a 'set' instead of a 'get'. Make sure that doesn't happen. // if(!Context) { SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } return pSetupDiGetOrSetDeviceInfoContext(DeviceInfoSet, DeviceInfoData, 0, // ignored Context ); } BOOL pSetupDiGetOrSetDeviceInfoContext( IN HDEVINFO DeviceInfoSet, IN PSP_DEVINFO_DATA DeviceInfoData, IN DWORD InContext, OUT PDWORD OutContext OPTIONAL ) /*++ Routine Description: This API retrieves or sets a context value from the specified device information element (stored there via pSetupDiSetDeviceInfoContext). Arguments: DeviceInfoSet - Supplies a handle to the device information set containing the device information element with which the context data is associated. DeviceInfoData - Supplies the address of a SP_DEVINFO_DATA structure indicating which element the context data is associated with. InContext - Specifies the data value to be stored for this device information element. If OutContext is specified, then this is a 'get' instead of a 'set', and this parameter is ignored. OutContext - Optionally, supplies the address of a variable that receives the context value stored for the device information element in a prior call to pSetupDiSetDeviceInfoContext. If no context data has previously been stored for this element, this variable will be filled in with zero upon return. Return Value: If the function succeeds, the return value is TRUE. If the function fails, the return value is FALSE. To get extended error information, call GetLastError. --*/ { PDEVICE_INFO_SET pDeviceInfoSet; PDEVINFO_ELEM DevInfoElem; DWORD Err; if(!(pDeviceInfoSet = AccessDeviceInfoSet(DeviceInfoSet))) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } Err = NO_ERROR; // assume success. try { DevInfoElem = FindAssociatedDevInfoElem(pDeviceInfoSet, DeviceInfoData, NULL ); if(!DevInfoElem) { Err = ERROR_INVALID_PARAMETER; goto clean0; } if(OutContext) { // // Store the context in the caller-supplied buffer. // *OutContext = DevInfoElem->Context; } else { // // Set the context to the caller-supplied value. // DevInfoElem->Context = InContext; } clean0: ; // nothing to do } except(EXCEPTION_EXECUTE_HANDLER) { Err = ERROR_INVALID_PARAMETER; } UnlockDeviceInfoSet(pDeviceInfoSet); SetLastError(Err); return(Err == NO_ERROR); }