#include "types.h" #if defined(DSI_TYPES_WINDOWS) #include "WinDevice.h" #include "macros.h" #include #include #include #include #include // Prototypes for functions found in the SetupAPI dll. typedef BOOL (WINAPI* SetupAPI_CallClassInstaller_t)(DI_FUNCTION,HDEVINFO,PSP_DEVINFO_DATA); typedef BOOL (WINAPI* SetupAPI_EnumDeviceInfo_t)(HDEVINFO,DWORD,PSP_DEVINFO_DATA); typedef BOOL (WINAPI* SetupAPI_DestroyDeviceInfoList_t)(HDEVINFO); typedef BOOL (WINAPI* SetupAPI_GetDeviceRegistryProperty_t)(HDEVINFO,PSP_DEVINFO_DATA,DWORD,PDWORD,PBYTE,DWORD,PDWORD); typedef BOOL (WINAPI* SetupAPI_SetClassInstallParams_t)(HDEVINFO,PSP_DEVINFO_DATA,PSP_CLASSINSTALL_HEADER,DWORD); #ifdef UNICODE typedef BOOL (WINAPI* SetupAPI_GetDeviceInstallParams_t)(HDEVINFO,PSP_DEVINFO_DATA,PSP_DEVINSTALL_PARAMS_W); typedef BOOL (WINAPI* SetupAPI_ClassGuidsFromNameEx_t)(PCWSTR,LPGUID,DWORD,PDWORD,PCWSTR,PVOID); typedef HDEVINFO (WINAPI* SetupAPI_GetClassDevsEx_t)(CONST GUID*,PCWSTR,HWND,DWORD,HDEVINFO,PCWSTR,PVOID); typedef HDEVINFO (WINAPI* SetupAPI_CreateDeviceInfoListEx_t)(CONST GUID*,HWND,PCWSTR,PVOID); typedef BOOL (WINAPI* SetupAPI_OpenDeviceInfo_t)(HDEVINFO,PCWSTR,HWND,DWORD,PSP_DEVINFO_DATA); typedef BOOL (WINAPI* SetupAPI_GetDeviceInfoListDetail_t)(HDEVINFO,PSP_DEVINFO_LIST_DETAIL_DATA_W); #else typedef BOOL (WINAPI* SetupAPI_GetDeviceInstallParams_t)(HDEVINFO,PSP_DEVINFO_DATA,PSP_DEVINSTALL_PARAMS_A); typedef BOOL (WINAPI* SetupAPI_ClassGuidsFromNameEx_t)(PCSTR,LPGUID,DWORD,PDWORD,PCSTR,PVOID); typedef HDEVINFO (WINAPI* SetupAPI_GetClassDevsEx_t)(CONST GUID*,PCSTR,HWND,DWORD,HDEVINFO,PCSTR,PVOID); typedef HDEVINFO (WINAPI* SetupAPI_CreateDeviceInfoListEx_t)(CONST GUID*,HWND,PCSTR,PVOID); typedef BOOL (WINAPI* SetupAPI_OpenDeviceInfo_t)(HDEVINFO,PCSTR,HWND,DWORD,PSP_DEVINFO_DATA); typedef BOOL (WINAPI* SetupAPI_GetDeviceInfoListDetail_t)(HDEVINFO,PSP_DEVINFO_LIST_DETAIL_DATA_A); #endif // Function pointers for functions found in the SetupAPI dll. SetupAPI_SetClassInstallParams_t SetupAPI_SetClassInstallParams; SetupAPI_CallClassInstaller_t SetupAPI_CallClassInstaller; SetupAPI_GetDeviceInstallParams_t SetupAPI_GetDeviceInstallParams; SetupAPI_GetDeviceRegistryProperty_t SetupAPI_GetDeviceRegistryProperty; SetupAPI_ClassGuidsFromNameEx_t SetupAPI_ClassGuidsFromNameEx; SetupAPI_GetClassDevsEx_t SetupAPI_GetClassDevsEx; SetupAPI_CreateDeviceInfoListEx_t SetupAPI_CreateDeviceInfoListEx; SetupAPI_OpenDeviceInfo_t SetupAPI_OpenDeviceInfo; SetupAPI_GetDeviceInfoListDetail_t SetupAPI_GetDeviceInfoListDetail; SetupAPI_EnumDeviceInfo_t SetupAPI_EnumDeviceInfo; SetupAPI_DestroyDeviceInfoList_t SetupAPI_DestroyDeviceInfoList; // Prototypes for functions found in the cfgmgr32 dll. #ifdef UNICODE typedef CONFIGRET (WINAPI* CfgMgr32_Get_Device_ID_Ex_t)(DEVINST dnDevInst, PWCHAR Buffer, ULONG BufferLen, ULONG ulFlags, HMACHINE hMachine); #else typedef CONFIGRET (WINAPI* CfgMgr32_Get_Device_ID_Ex_t)(DEVINST dnDevInst, PCHAR Buffer, ULONG BufferLen, ULONG ulFlags, HMACHINE hMachine); #endif // Function pointers for functions found in the cfgmgr32 dll. CfgMgr32_Get_Device_ID_Ex_t CfgMgr32_Get_Device_ID_Ex; typedef int (*CallbackFunc)(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,LPVOID Context); int ControlCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,LPVOID Context); //DLL Definitions BOOL LoadSetupDLLFunctions(); void FreeSetupDLLFunctions(); HMODULE hSetupDll; HMODULE hCfgMgrDll; int EnumerateDevices(LPCTSTR Machine,DWORD Flags,int argc,LPTSTR argv[],CallbackFunc Callback,LPVOID Context); LPTSTR GetDeviceStringProperty(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop); LPTSTR GetDeviceDescription(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo); LPTSTR * GetDevMultiSz(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop); LPTSTR * GetRegMultiSz(HKEY hKey,LPCTSTR Val); LPTSTR * GetMultiSzIndexArray(LPTSTR MultiSz); void DelMultiSz(LPTSTR * Array); LPTSTR * CopyMultiSz(LPTSTR * Array); typedef struct { LPCTSTR String; // string looking for LPCTSTR Wild; // first wild character if any BOOL InstanceId; } IdEntry; typedef struct { DWORD count; DWORD control; BOOL reboot; } GenericContext; typedef struct { int argc_right; LPTSTR * argv_right; DWORD prop; int skipped; int modified; } SetHwidContext; int WinDevice_Enable(int argc,TCHAR* argv[]) /*++ Routine Description: ENABLE ... use EnumerateDevices to do hardwareID matching for each match, attempt to enable global, and if needed, config specific Arguments: argc/argv - remaining parameters - passed into EnumerateDevices Return Value: EXIT_xxxx (EXIT_REBOOT if reboot is required) --*/ { GenericContext context; int failcode = EXIT_FAIL; if(!LoadSetupDLLFunctions()) return EXIT_FAIL; if(!argc) { // // arguments required // FreeSetupDLLFunctions(); return EXIT_USAGE; } context.control = DICS_ENABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE context.reboot = FALSE; context.count = 0; failcode = EnumerateDevices(NULL,DIGCF_PRESENT,argc,argv,ControlCallback,&context); if(failcode == EXIT_OK) { if((context.count) && (context.reboot)) failcode = EXIT_REBOOT; } FreeSetupDLLFunctions(); return failcode; } int WinDevice_Disable(int argc,TCHAR* argv[]) /*++ Routine Description: DISABLE ... use EnumerateDevices to do hardwareID matching for each match, attempt to disable global Arguments: argc/argv - remaining parameters - passed into EnumerateDevices Return Value: EXIT_xxxx (EXIT_REBOOT if reboot is required) --*/ { GenericContext context; int failcode = EXIT_FAIL; if(!LoadSetupDLLFunctions()) return EXIT_FAIL; if(!argc) { // // arguments required // FreeSetupDLLFunctions(); return EXIT_USAGE; } context.control = DICS_DISABLE; // DICS_PROPCHANGE DICS_ENABLE DICS_DISABLE context.reboot = FALSE; context.count = 0; failcode = EnumerateDevices(NULL,DIGCF_PRESENT,argc,argv,ControlCallback,&context); if(failcode == EXIT_OK) { if((context.count) && (context.reboot)) failcode = EXIT_REBOOT; } FreeSetupDLLFunctions(); return failcode; } int WinDevice_Restart(int argc,TCHAR* argv[]) /*++ Routine Description: RESTART ... use EnumerateDevices to do hardwareID matching for each match, attempt to restart by issueing a PROPCHANGE Arguments: argc/argv - remaining parameters - passed into EnumerateDevices Return Value: EXIT_xxxx (EXIT_REBOOT if reboot is required) --*/ { GenericContext context; int failcode = EXIT_FAIL; if(!LoadSetupDLLFunctions()) return EXIT_FAIL; if(!argc) { // // arguments required // FreeSetupDLLFunctions(); return EXIT_USAGE; } context.control = DICS_PROPCHANGE; context.reboot = FALSE; context.count = 0; failcode = EnumerateDevices(NULL,DIGCF_PRESENT,argc,argv,ControlCallback,&context); if(failcode == EXIT_OK) { if((context.count) && (context.reboot)) failcode = EXIT_REBOOT; } FreeSetupDLLFunctions(); return failcode; } int ControlCallback(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,LPVOID Context) /*++ Routine Description: Callback for use by Enable/Disable/Restart Invokes DIF_PROPERTYCHANGE with correct parameters uses SetupDiCallClassInstaller so cannot be done for remote devices Don't use CM_xxx API's, they bypass class/co-installers and this is bad. In Enable case, we try global first, and if still disabled, enable local Arguments: Devs )_ uniquely identify the device DevInfo ) Context - GenericContext Return Value: EXIT_xxxx --*/ { SP_PROPCHANGE_PARAMS pcp; GenericContext *pControlContext = (GenericContext*)Context; SP_DEVINSTALL_PARAMS devParams; switch(pControlContext->control) { case DICS_ENABLE: // // enable both on global and config-specific profile // do global first and see if that succeeded in enabling the device // (global enable doesn't mark reboot required if device is still // disabled on current config whereas vice-versa isn't true) // pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; pcp.StateChange = pControlContext->control; pcp.Scope = DICS_FLAG_GLOBAL; pcp.HwProfile = 0; // // don't worry if this fails, we'll get an error when we try config- // specific. if(SetupAPI_SetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp))) { SetupAPI_CallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo); } // // now enable on config-specific // pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; pcp.StateChange = pControlContext->control; pcp.Scope = DICS_FLAG_CONFIGSPECIFIC; pcp.HwProfile = 0; break; default: // // operate on config-specific profile // pcp.ClassInstallHeader.cbSize = sizeof(SP_CLASSINSTALL_HEADER); pcp.ClassInstallHeader.InstallFunction = DIF_PROPERTYCHANGE; pcp.StateChange = pControlContext->control; pcp.Scope = DICS_FLAG_CONFIGSPECIFIC; pcp.HwProfile = 0; break; } if(!SetupAPI_SetClassInstallParams(Devs,DevInfo,&pcp.ClassInstallHeader,sizeof(pcp)) || !SetupAPI_CallClassInstaller(DIF_PROPERTYCHANGE,Devs,DevInfo)) { // // failed to invoke DIF_PROPERTYCHANGE // } else { // // see if device needs reboot // devParams.cbSize = sizeof(devParams); if(SetupAPI_GetDeviceInstallParams(Devs,DevInfo,&devParams) && (devParams.Flags & (DI_NEEDRESTART|DI_NEEDREBOOT))) { pControlContext->reboot = TRUE; } else { // // appears to have succeeded // } pControlContext->count++; } return EXIT_OK; } /////////////////////////////////////////////////////////////////////// // Loads Setup API functions from the DLL. /////////////////////////////////////////////////////////////////////// BOOL LoadSetupDLLFunctions(void) { BOOL bStatus = TRUE; FreeSetupDLLFunctions(); // Guarantee the library functions are freed. char acSystemDirectory[MAX_PATH]; char asSetupAPI[255]; char asConfig[255]; if(GetSystemDirectory(acSystemDirectory, MAX_PATH) == 0) return FALSE; if(acSystemDirectory == NULL) return FALSE; SNPRINTF(asSetupAPI, 255, "%s/setupapi.dll", acSystemDirectory); SNPRINTF(asConfig, 255, "%s/cfgmgr32.dll", acSystemDirectory); hSetupDll = LoadLibrary(asSetupAPI); hCfgMgrDll = LoadLibrary(asConfig); if(hSetupDll == NULL || hCfgMgrDll == NULL) { bStatus = FALSE; } else { //Functions from SetupAPI DLL. SetupAPI_CallClassInstaller = (SetupAPI_CallClassInstaller_t) GetProcAddress(hSetupDll, "SetupDiCallClassInstaller"); if(SetupAPI_CallClassInstaller == NULL) bStatus = FALSE; SetupAPI_EnumDeviceInfo = (SetupAPI_EnumDeviceInfo_t) GetProcAddress(hSetupDll, "SetupDiEnumDeviceInfo"); if(SetupAPI_EnumDeviceInfo == NULL) bStatus = FALSE; SetupAPI_DestroyDeviceInfoList = (SetupAPI_DestroyDeviceInfoList_t) GetProcAddress(hSetupDll, "SetupDiDestroyDeviceInfoList"); if(SetupAPI_DestroyDeviceInfoList == NULL) bStatus = FALSE; #ifdef UNICODE SetupAPI_SetClassInstallParams = (SetupAPI_SetClassInstallParams_t) GetProcAddress(hSetupDll, "SetupDiSetClassInstallParamsW"); if(SetupAPI_SetClassInstallParams == NULL) bStatus = FALSE; SetupAPI_GetDeviceInstallParams = (SetupAPI_GetDeviceInstallParams_t) GetProcAddress(hSetupDll, "SetupDiGetDeviceInstallParamsW"); if(SetupAPI_GetDeviceInstallParams == NULL) bStatus = FALSE; SetupAPI_GetDeviceRegistryProperty = (SetupAPI_GetDeviceRegistryProperty_t) GetProcAddress(hSetupDll, "SetupDiGetDeviceRegistryPropertyW"); if(SetupAPI_GetDeviceRegistryProperty == NULL) bStatus = FALSE; SetupAPI_ClassGuidsFromNameEx = (SetupAPI_ClassGuidsFromNameEx_t) GetProcAddress(hSetupDll, "SetupDiClassGuidsFromNameExW"); if(SetupAPI_ClassGuidsFromNameEx == NULL) bStatus = FALSE; SetupAPI_GetClassDevsEx = (SetupAPI_GetClassDevsEx_t) GetProcAddress(hSetupDll, "SetupDiGetClassDevsExW"); if(SetupAPI_GetClassDevsEx == NULL) bStatus = FALSE; SetupAPI_CreateDeviceInfoListEx = (SetupAPI_CreateDeviceInfoListEx_t) GetProcAddress(hSetupDll, "SetupDiCreateDeviceInfoListExW"); if(SetupAPI_CreateDeviceInfoListEx == NULL) bStatus = FALSE; SetupAPI_OpenDeviceInfo = (SetupAPI_OpenDeviceInfo_t) GetProcAddress(hSetupDll, "SetupDiOpenDeviceInfoW"); if(SetupAPI_OpenDeviceInfo == NULL) bStatus = FALSE; SetupAPI_GetDeviceInfoListDetail = (SetupAPI_GetDeviceInfoListDetail_t) GetProcAddress(hSetupDll, "SetupDiGetDeviceInfoListDetailW"); if(SetupAPI_GetDeviceInfoListDetail == NULL) bStatus = FALSE; #else SetupAPI_SetClassInstallParams = (SetupAPI_SetClassInstallParams_t) GetProcAddress(hSetupDll, "SetupDiSetClassInstallParamsA"); if(SetupAPI_SetClassInstallParams == NULL) bStatus = FALSE; SetupAPI_GetDeviceInstallParams = (SetupAPI_GetDeviceInstallParams_t) GetProcAddress(hSetupDll, "SetupDiGetDeviceInstallParamsA"); if(SetupAPI_GetDeviceInstallParams == NULL) bStatus = FALSE; SetupAPI_GetDeviceRegistryProperty = (SetupAPI_GetDeviceRegistryProperty_t) GetProcAddress(hSetupDll, "SetupDiGetDeviceRegistryPropertyA"); if(SetupAPI_GetDeviceRegistryProperty == NULL) bStatus = FALSE; SetupAPI_ClassGuidsFromNameEx = (SetupAPI_ClassGuidsFromNameEx_t) GetProcAddress(hSetupDll, "SetupDiClassGuidsFromNameExA"); if(SetupAPI_ClassGuidsFromNameEx == NULL) bStatus = FALSE; SetupAPI_GetClassDevsEx = (SetupAPI_GetClassDevsEx_t) GetProcAddress(hSetupDll, "SetupDiGetClassDevsExA"); if(SetupAPI_GetClassDevsEx == NULL) bStatus = FALSE; SetupAPI_CreateDeviceInfoListEx = (SetupAPI_CreateDeviceInfoListEx_t) GetProcAddress(hSetupDll, "SetupDiCreateDeviceInfoListExA"); if(SetupAPI_CreateDeviceInfoListEx == NULL) bStatus = FALSE; SetupAPI_OpenDeviceInfo = (SetupAPI_OpenDeviceInfo_t) GetProcAddress(hSetupDll, "SetupDiOpenDeviceInfoA"); if(SetupAPI_OpenDeviceInfo == NULL) bStatus = FALSE; SetupAPI_GetDeviceInfoListDetail = (SetupAPI_GetDeviceInfoListDetail_t) GetProcAddress(hSetupDll, "SetupDiGetDeviceInfoListDetailA"); if(SetupAPI_GetDeviceInfoListDetail == NULL) bStatus = FALSE; #endif //Functions from CfgMgr32 DLL. #ifdef UNICODE SetupAPI_Get_Device_ID_Ex = (SetupAPI_Get_Device_ID_Ex_t) GetProcAddress(hSetupDll, "CM_Get_Device_ID_ExW"); if(SetupAPI_Get_Device_ID_Ex == NULL) bStatus = FALSE; #else CfgMgr32_Get_Device_ID_Ex = (CfgMgr32_Get_Device_ID_Ex_t) GetProcAddress(hCfgMgrDll, "CM_Get_Device_ID_ExA"); if(CfgMgr32_Get_Device_ID_Ex == NULL) bStatus = FALSE; #endif } if (!bStatus) FreeSetupDLLFunctions(); return bStatus; } /////////////////////////////////////////////////////////////////////// // Unloads Setup API DLL. /////////////////////////////////////////////////////////////////////// void FreeSetupDLLFunctions(void) { if (hSetupDll != NULL) { FreeLibrary(hSetupDll); hSetupDll = NULL; } if(hCfgMgrDll != NULL) { FreeLibrary(hCfgMgrDll); hCfgMgrDll = NULL; } } LPTSTR GetDeviceStringProperty(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop) /*++ Routine Description: Return a string property for a device, otherwise NULL Arguments: Devs )_ uniquely identify device DevInfo ) Prop - string property to obtain Return Value: string containing description --*/ { LPTSTR buffer; DWORD size; DWORD reqSize; DWORD dataType; DWORD szChars; size = 1024; // initial guess buffer = new TCHAR[(size/sizeof(TCHAR))+1]; if(!buffer) { return NULL; } while(!SetupAPI_GetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) { if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto failed; } if(dataType != REG_SZ) { goto failed; } size = reqSize; delete [] buffer; buffer = new TCHAR[(size/sizeof(TCHAR))+1]; if(!buffer) { goto failed; } } szChars = reqSize/sizeof(TCHAR); buffer[szChars] = TEXT('\0'); return buffer; failed: if(buffer) { delete [] buffer; } return NULL; } LPTSTR GetDeviceDescription(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo) /*++ Routine Description: Return a string containing a description of the device, otherwise NULL Always try friendly name first Arguments: Devs )_ uniquely identify device DevInfo ) Return Value: string containing description --*/ { LPTSTR desc; desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_FRIENDLYNAME); if(!desc) { desc = GetDeviceStringProperty(Devs,DevInfo,SPDRP_DEVICEDESC); } return desc; } IdEntry GetIdType(LPCTSTR Id) /*++ Routine Description: Determine if this is instance id or hardware id and if there's any wildcards instance ID is prefixed by '@' wildcards are '*' Arguments: Id - ptr to string to check Return Value: IdEntry --*/ { IdEntry Entry; Entry.InstanceId = FALSE; Entry.Wild = NULL; Entry.String = Id; if(Entry.String[0] == INSTANCEID_PREFIX_CHAR) { Entry.InstanceId = TRUE; Entry.String = CharNext(Entry.String); } if(Entry.String[0] == QUOTE_PREFIX_CHAR) { // // prefix to treat rest of string literally // Entry.String = CharNext(Entry.String); } else { // // see if any wild characters exist // Entry.Wild = _tcschr(Entry.String,WILD_CHAR); } return Entry; } LPTSTR * GetMultiSzIndexArray(LPTSTR MultiSz) /*++ Routine Description: Get an index array pointing to the MultiSz passed in Arguments: MultiSz - well formed multi-sz string Return Value: array of strings. last entry+1 of array contains NULL returns NULL on failure --*/ { LPTSTR scan; LPTSTR * array; int elements; for(scan = MultiSz, elements = 0; scan[0] ;elements++) { scan += lstrlen(scan)+1; } array = new LPTSTR[elements+2]; if(!array) { return NULL; } array[0] = MultiSz; array++; if(elements) { for(scan = MultiSz, elements = 0; scan[0]; elements++) { array[elements] = scan; scan += lstrlen(scan)+1; } } array[elements] = NULL; return array; } LPTSTR * CopyMultiSz(LPTSTR * Array) /*++ Routine Description: Creates a new array from old old array need not have been allocated by GetMultiSzIndexArray Arguments: Array - array of strings, last entry is NULL Return Value: MultiSz array allocated by GetMultiSzIndexArray --*/ { LPTSTR multiSz = NULL; int len = 0; int c; if(Array) { for(c=0;Array[c];c++) { len+=lstrlen(Array[c])+1; } } len+=1; // final Null multiSz = new TCHAR[len]; if(!multiSz) { return NULL; } len = 0; if(Array) { for(c=0;Array[c];c++) { lstrcpy(multiSz+len,Array[c]); len+=lstrlen(multiSz+len)+1; } } multiSz[len] = TEXT('\0'); LPTSTR * pRes = GetMultiSzIndexArray(multiSz); if(pRes) { return pRes; } delete [] multiSz; return NULL; } void DelMultiSz(LPTSTR * Array) /*++ Routine Description: Deletes the string array allocated by GetDevMultiSz/GetRegMultiSz/GetMultiSzIndexArray Arguments: Array - pointer returned by GetMultiSzIndexArray Return Value: None --*/ { if(Array) { Array--; if(Array[0]) { delete [] Array[0]; } delete [] Array; } } LPTSTR * GetDevMultiSz(HDEVINFO Devs,PSP_DEVINFO_DATA DevInfo,DWORD Prop) /*++ Routine Description: Get a multi-sz device property and return as an array of strings Arguments: Devs - HDEVINFO containing DevInfo DevInfo - Specific device Prop - SPDRP_HARDWAREID or SPDRP_COMPATIBLEIDS Return Value: array of strings. last entry+1 of array contains NULL returns NULL on failure --*/ { LPTSTR buffer; DWORD size; DWORD reqSize; DWORD dataType; LPTSTR * array; DWORD szChars; size = 8192; // initial guess, nothing magic about this buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { return NULL; } while(!SetupAPI_GetDeviceRegistryProperty(Devs,DevInfo,Prop,&dataType,(LPBYTE)buffer,size,&reqSize)) { if(GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto failed; } if(dataType != REG_MULTI_SZ) { goto failed; } size = reqSize; delete [] buffer; buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { goto failed; } } szChars = reqSize/sizeof(TCHAR); buffer[szChars] = TEXT('\0'); buffer[szChars+1] = TEXT('\0'); array = GetMultiSzIndexArray(buffer); if(array) { return array; } failed: if(buffer) { delete [] buffer; } return NULL; } LPTSTR * GetRegMultiSz(HKEY hKey,LPCTSTR Val) /*++ Routine Description: Get a multi-sz from registry and return as an array of strings Arguments: hKey - Registry Key Val - Value to query Return Value: array of strings. last entry+1 of array contains NULL returns NULL on failure --*/ { LPTSTR buffer; DWORD size; DWORD reqSize; DWORD dataType; LPTSTR * array; DWORD szChars; size = 8192; // initial guess, nothing magic about this buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { return NULL; } reqSize = size; while(RegQueryValueEx(hKey,Val,NULL,&dataType,(PBYTE)buffer,&reqSize) != NO_ERROR) { if(GetLastError() != ERROR_MORE_DATA) { goto failed; } if(dataType != REG_MULTI_SZ) { goto failed; } size = reqSize; delete [] buffer; buffer = new TCHAR[(size/sizeof(TCHAR))+2]; if(!buffer) { goto failed; } } szChars = reqSize/sizeof(TCHAR); buffer[szChars] = TEXT('\0'); buffer[szChars+1] = TEXT('\0'); array = GetMultiSzIndexArray(buffer); if(array) { return array; } failed: if(buffer) { delete [] buffer; } return NULL; } BOOL WildCardMatch(LPCTSTR Item,const IdEntry & MatchEntry) /*++ Routine Description: Compare a single item against wildcard I'm sure there's better ways of implementing this Other than a command-line management tools it's a bad idea to use wildcards as it implies assumptions about the hardware/instance ID eg, it might be tempting to enumerate root\* to find all root devices, however there is a CfgMgr API to query status and determine if a device is root enumerated, which doesn't rely on implementation details. Arguments: Item - item to find match for eg a\abcd\c MatchEntry - eg *\*bc*\* Return Value: TRUE if any match, otherwise FALSE --*/ { LPCTSTR scanItem; LPCTSTR wildMark; LPCTSTR nextWild; size_t matchlen; // // before attempting anything else // try and compare everything up to first wild // if(!MatchEntry.Wild) { return _tcsicmp(Item,MatchEntry.String) ? FALSE : TRUE; } if(_tcsnicmp(Item,MatchEntry.String,MatchEntry.Wild-MatchEntry.String) != 0) { return FALSE; } wildMark = MatchEntry.Wild; scanItem = Item + (MatchEntry.Wild-MatchEntry.String); for(;wildMark[0];) { // // if we get here, we're either at or past a wildcard // if(wildMark[0] == WILD_CHAR) { // // so skip wild chars // wildMark = CharNext(wildMark); continue; } // // find next wild-card // nextWild = _tcschr(wildMark,WILD_CHAR); if(nextWild) { // // substring // matchlen = nextWild-wildMark; } else { // // last portion of match // size_t scanlen = lstrlen(scanItem); matchlen = lstrlen(wildMark); if(scanlen < matchlen) { return FALSE; } return _tcsicmp(scanItem+scanlen-matchlen,wildMark) ? FALSE : TRUE; } if(_istalpha(wildMark[0])) { // // scan for either lower or uppercase version of first character // TCHAR u = (TCHAR)_totupper(wildMark[0]); TCHAR l = (TCHAR)_totlower(wildMark[0]); while(scanItem[0] && scanItem[0]!=u && scanItem[0]!=l) { scanItem = CharNext(scanItem); } if(!scanItem[0]) { // // ran out of string // return FALSE; } } else { // // scan for first character (no case) // scanItem = _tcschr(scanItem,wildMark[0]); if(!scanItem) { // // ran out of string // return FALSE; } } // // try and match the sub-string at wildMark against scanItem // if(_tcsnicmp(scanItem,wildMark,matchlen)!=0) { // // nope, try again // scanItem = CharNext(scanItem); continue; } // // substring matched // scanItem += matchlen; wildMark += matchlen; } return (wildMark[0] ? FALSE : TRUE); } BOOL WildCompareHwIds(LPTSTR * Array,const IdEntry & MatchEntry) /*++ Routine Description: Compares all strings in Array against Id Use WildCardMatch to do real compare Arguments: Array - pointer returned by GetDevMultiSz MatchEntry - string to compare against Return Value: TRUE if any match, otherwise FALSE --*/ { if(Array) { while(Array[0]) { if(WildCardMatch(Array[0],MatchEntry)) { return TRUE; } Array++; } } return FALSE; } int EnumerateDevices(LPCTSTR Machine,DWORD Flags,int argc,LPTSTR argv[],CallbackFunc Callback,LPVOID Context) /*++ Routine Description: Generic enumerator for devices that will be passed the following arguments: [...] = [...] where can either be @instance-id, or hardware-id and may contain wildcards is a class name Arguments: Machine - name of machine to enumerate Flags - extra enumeration flags (eg DIGCF_PRESENT) argc/argv - remaining arguments on command line Callback - function to call for each hit Context - data to pass function for each hit Return Value: EXIT_xxxx --*/ { HDEVINFO devs = INVALID_HANDLE_VALUE; IdEntry * templ = NULL; int failcode = EXIT_FAIL; int retcode; int argIndex; DWORD devIndex; SP_DEVINFO_DATA devInfo; SP_DEVINFO_LIST_DETAIL_DATA devInfoListDetail; BOOL doSearch = FALSE; BOOL match; BOOL all = FALSE; GUID cls; DWORD numClass = 0; int skip = 0; if(!argc) { return EXIT_USAGE; } templ = new IdEntry[argc]; if(!templ) { goto final; } // // determine if a class is specified // if(argc>skip && argv[skip][0]==CLASS_PREFIX_CHAR && argv[skip][1]) { if(!SetupAPI_ClassGuidsFromNameEx(argv[skip]+1,&cls,1,&numClass,Machine,NULL) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { goto final; } if(!numClass) { failcode = EXIT_OK; goto final; } skip++; } if(argc>skip && argv[skip][0]==WILD_CHAR && !argv[skip][1]) { // // catch convinient case of specifying a single argument '*' // all = TRUE; skip++; } else if(argc<=skip) { // // at least one parameter, but no 's // all = TRUE; } // // determine if any instance id's were specified // // note, if = was specified with no id's // we'll mark it as not doSearch // but will go ahead and add them all // for(argIndex=skip;argIndex