I am interested to find out if I am doing this right:
//DeviceManager.h
#include <windows.h>
//#include <hidsdi.h>
#include <setupapi.h>
#include <iostream>
#include <cfgmgr32.h>
#include <tchar.h>
#include <devpkey.h>
extern "C"{
#include <hidsdi.h>
}
//#pragma comment (lib, "setupapi.lib")
class DeviceManager
{
public:
DeviceManager();
~DeviceManager();
void ListAllDevices();
void GetDevice(std::string vid, std::string pid);
HANDLE PSMove;
byte reportBuffer;
private:
HDEVINFO deviceInfoSet; //A list of all the devices
SP_DEVINFO_DATA deviceInfoData; //A device from deviceInfoSet
SP_DEVICE_INTERFACE_DATA deviceInterfaceData;
SP_DEVICE_INTERFACE_DETAIL_DATA deviceInterfaceDetailedData;
};
//DeviceManager.cpp
#include"DeviceManager.h"
DeviceManager::DeviceManager()
{
deviceInfoSet = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); //Gets all Devices
}
DeviceManager::~DeviceManager()
{
}
void DeviceManager::ListAllDevices()
{
DWORD deviceIndex = 0;
deviceInfoData.cbSize = sizeof(deviceInfoData);
while(SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, &deviceInfoData))
{
deviceInfoData.cbSize = sizeof(deviceInfoData);
ULONG tcharSize;
CM_Get_Device_ID_Size(&tcharSize, deviceInfoData.DevInst, 0);
TCHAR* deviceIDBuffer = new TCHAR[tcharSize]; //the device ID will be stored in this array, so the tcharSize needs to be big enough to hold all the info.
//Or we can use MAX_DEVICE_ID_LEN, which is 200
CM_Get_Device_ID(deviceInfoData.DevInst, deviceIDBuffer, MAX_PATH, 0); //gets the devices ID - a long string that looks like a file path.
/*
//SetupDiGetDevicePropertyKeys(deviceInfoSet, &deviceInfoData, &devicePropertyKey, NULL, 0, 0);
if( deviceIDBuffer[8]=='8' && deviceIDBuffer[9]=='8' && deviceIDBuffer[10]=='8' && deviceIDBuffer[11]=='8' && //VID
deviceIDBuffer[17]=='0' && deviceIDBuffer[18]=='3' && deviceIDBuffer[19]=='0' && deviceIDBuffer[20]=='8') //PID
{
std::cout << deviceIDBuffer << "\t<-- Playstation Move" << std::endl;
}
else
{
std::cout << deviceIDBuffer << std::endl;
}*/
std::cout << deviceIDBuffer << std::endl;
deviceIndex++;
}
}
void DeviceManager::GetDevice(std::string vid, std::string pid)
{
DWORD deviceIndex = 0;
deviceInfoData.cbSize = sizeof(deviceInfoData);
while(SetupDiEnumDeviceInfo(deviceInfoSet, deviceIndex, &deviceInfoData))
{
deviceInfoData.cbSize = sizeof(deviceInfoData);
ULONG IDSize;
CM_Get_Device_ID_Size(&IDSize, deviceInfoData.DevInst, 0);
TCHAR* deviceID = new TCHAR[IDSize];
CM_Get_Device_ID(deviceInfoData.DevInst, deviceID, MAX_PATH, 0);
if( deviceID[8]==vid.at(0) && deviceID[9]==vid.at(1) && deviceID[10]==vid.at(2) && deviceID[11]==vid.at(3) && //VID
deviceID[17]==pid.at(0) && deviceID[18]==pid.at(1) && deviceID[19]==pid.at(2) && deviceID[20]==pid.at(3)) //PID
{
//DWORD requiredBufferSize;
//SetupDiGetDeviceInterfaceDetail(deviceInfoSet, &deviceInterfaceData, NULL, 0, &requiredBufferSize,
HDEVINFO deviceInterfaceSet = SetupDiGetClassDevs(&deviceInfoData.ClassGuid, NULL, NULL, DIGCF_ALLCLASSES);
DWORD deviceInterfaceIndex = 0;
deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
while(SetupDiEnumDeviceInterfaces(deviceInterfaceSet, NULL, &deviceInterfaceData.InterfaceClassGuid, deviceInterfaceIndex, &deviceInterfaceData))
{
deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
std::cout << deviceInterfaceIndex << std::endl;
deviceInterfaceIndex++;
}
//std::cout << deviceInterfaceData.cbSize << std::endl;
break;
}
deviceIndex++;
}
}
My SetupDiEnumDeviceInterfaces (in the GetDevice() function) is not doing anything. It is not even entering the while loop. What is it that I'm doing wrong?
Edit
I've just called the GetLastError() function and it has returned a 259 - ERROR_NO_MORE_ITEMS. Is it even possible for a device to contain no interfaces?
Have a go with this.
I have tried to hack your original code about as little as possible; the following codes (for me at least) get through to the inner while(SetupDiEnumDeviceInterfaces..):
void DeviceManager::GetDeviceUSB(std::string vid, std::string pid)
{
DWORD deviceIndex = 0;
deviceInfoData.cbSize = sizeof(deviceInfoData);
//buried somewhere deep in the ddk
static GUID GUID_DEVINTERFACE_USB_HUB={ 0xf18a0e88, 0xc30c, 0x11d0, {0x88, 0x15, 0x00, 0xa0, 0xc9, 0x06, 0xbe, 0xd8} };
static GUID GUID_DEVINTERFACE_USB_DEVICE ={ 0xA5DCBF10L, 0x6530, 0x11D2, { 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
static GUID GUID_DEVINTERFACE_USB_HOST_CONTROLLER={ 0x3abf6f2d, 0x71c4, 0x462a, {0x8a, 0x92, 0x1e, 0x68, 0x61, 0xe6, 0xaf, 0x27}};
//get usb device interfaces
HDEVINFO deviceInterfaceSet=SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_DEVICEINTERFACE);
while(SetupDiEnumDeviceInfo(deviceInterfaceSet, deviceIndex, &deviceInfoData))
{
deviceInfoData.cbSize = sizeof(deviceInfoData);
ULONG IDSize;
CM_Get_Device_ID_Size(&IDSize, deviceInfoData.DevInst, 0);
TCHAR* deviceID = new TCHAR[IDSize];
CM_Get_Device_ID(deviceInfoData.DevInst, deviceID, MAX_PATH, 0);
if( deviceID[8]==vid.at(0) && deviceID[9]==vid.at(1) && deviceID[10]==vid.at(2) && deviceID[11]==vid.at(3) && //VID
deviceID[17]==pid.at(0) && deviceID[18]==pid.at(1) && deviceID[19]==pid.at(2) && deviceID[20]==pid.at(3)) //PID
{
DWORD deviceInterfaceIndex = 0;
deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
while(SetupDiEnumDeviceInterfaces(deviceInterfaceSet, &deviceInfoData, &GUID_DEVINTERFACE_USB_DEVICE, deviceInterfaceIndex, &deviceInterfaceData))
{
deviceInterfaceData.cbSize = sizeof(deviceInterfaceData);
std::cout << deviceInterfaceIndex << std::endl;
//get some more details etc
//DWORD requiredBufferSize;
//SetupDiGetDeviceInterfaceDetail(deviceInterfaceSet, &deviceInterfaceData, NULL, 0, &requiredBufferSize,
deviceInterfaceIndex++;
}
}
deviceIndex++;
}
}
As far as I know, this method picks up the same devices as with your OP constructor call (NB: I included some other useful interface guids):
deviceInfoSet = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES); //Gets all Devices
But I am doing this to get the device interfaces:
// /coughs/ you might want to put back the DIGCF_PRESENT flag I removed for testing
HDEVINFO deviceInterfaceSet=SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE, NULL, NULL, DIGCF_DEVICEINTERFACE);
I also pass the deviceInfoData to SetupDiEnumDeviceInterfaces since according to the documentation:
A pointer to an SP_DEVINFO_DATA structure that specifies a device
information element in DeviceInfoSet. This parameter is optional and
can be NULL. If this parameter is specified,
SetupDiEnumDeviceInterfaces constrains the enumeration to the
interfaces that are supported by the specified device. If this
parameter is NULL, repeated calls to SetupDiEnumDeviceInterfaces
return information about the interfaces that are associated with all
the device information elements in DeviceInfoSet. This pointer is
typically returned by SetupDiEnumDeviceInfo.
Edit
Additional note as requested. Your USB device has an associated setup class and interface class(es):
From the device setup classes documentation:
The device setup class defines the class installer and class co-installers that are involved in installing the device
From the device interface classes documentation:
A device interface class is a way of exporting device and driver functionality to other system components, including other drivers, as
well as user-mode applications
Also, see this handy comparison
Also, this related doc is useful
So:
deviceInfoSet = SetupDiGetClassDevs(NULL, TEXT("USB"), NULL,DIGCF_PRESENT|DIGCF_ALLCLASSES);
This is retrieving all setup class information sets and filtering on "USB"
You could do this:
deviceInfoSet = SetupDiGetClassDevs(NULL, NULL, NULL, DIGCF_PRESENT|DIGCF_ALLCLASSES|DIGCF_DEVICEINTERFACE);`
This retrieves class information sets for devices that support a device interface of any class. (Applying an emumerator ID s/a "USB" seems to have no affect).
Crucially however: The function adds to the device information set a device information element that represents such a device and then adds to the device information element a device interface list that contains all the device interfaces that the device supports.
(And note: SP_DEVINFO_DATA.ClassGuid is always the GUID of the device's setup class)
As far as I know, you then still need to provide an InterfaceClassGuid when invoking SetupDiEnumDeviceInterfaces(). To be honest, I don't really understand why this would be necessary if the caller is providing the optional DeviceInfoData but since it's all closed source, how would I know? :)
And here is info regarding GUID_DEVINTERFACE_USB_DEVICE
Disclaimer: I do not work for M$; do treat the above information with suspicion, and of course do your own research.
The problem starts with how SetupDiGetClassDevs is called.
If you are looking to get a device path, use
SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE ,,,)
SetupDiEnumDeviceInterfaces fails with error 259 if SetupDiGetClassDevs is given the wrong GUID in
ClassGuid which MS Help says is
A pointer to the GUID for a device setup class or a device interface class.
Include file devguid.h contains a set of GUID_DEVCLASS values. These are NOT the same as GUID_DEVINTERFACE_* values which are the one you need.
Use #include <uuids.h> which includes ksuuids.h where you'll find GUID_DEVINTERFACE_* values.
There's a more detailed explanation on my website, with some source code that should help in correctly enumerating USB devices.
See http://pixcl.com/SetupDiEnumInterfaces_Fail.htm
Related
i'm developing on a Bluetooth Low Energy Device and i need to see in code if the device is connected or not.
First thing i noticed was that there is in the Devicemanager a Attribute "Verbunden"-> English: Connected and it says true or false if my device is connected or not. So i need to read that Attribute in my program.
What i have tried till now:
Getting all Devices with SetupDiGetClassDevs
Getting the FriendlyName with SetupDiGetDeviceRegistryProperty
Searching for my Device with the name.
That works.
Now i wanted to get that Connected-Attribute but i didn't find out what i have to use at SetupDiGetDeviceRegistryProperty.
SetupDiGetDeviceRegistryProperty is described here https://msdn.microsoft.com/en-us/library/windows/hardware/ff551967(v=vs.85).aspx
Maybe someone knows what is the right value for Property.
My Code:
int get_device_info( void )
{
HDEVINFO hDevInfo;
SP_DEVINFO_DATA DeviceInfoData;
DWORD i;
FILE * devices = fopen("devices.txt", "a+");
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
//
// Call function with null to begin with,
// then use the returned buffer size (doubled)
// to Alloc the buffer. Keep calling until
// success or an unknown failure.
//
// Double the returned buffersize to correct
// for underlying legacy CM functions that
// return an incorrect buffersize value on
// DBCS/MBCS systems.
//
while (!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
//SPDRP_DEVICEDESC,
//SPDRP_CAPABILITIES,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
if(buffer)
{
if( strcmp("Name of Device",AnsiString(buffer).c_str())==0)
{
fprintf(devices,"Result:[%s]",AnsiString(buffer).c_str());
if (buffer) LocalFree(buffer);
}
}
}
if ( GetLastError()!=NO_ERROR &&
GetLastError()!=ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
return 1;
}
// Cleanup
SetupDiDestroyDeviceInfoList(hDevInfo);
fclose(devices);
return 0;
}
Instead of using SetupDiEnumDeviceInfo, you would try:
1. using SetupDiEnumDeviceInterfaces
2. using SetupDiGetDeviceInterfaceProperty
3. using SetupDiGetDeviceInterfacePropertyKeys to get a list of all Property Keys available for the interface
4. using SetupDiGetDeviceProperty and/or SetupDiGetDeviceRegistryProperty
Instead of using SPDRP_XXX constants, you would use DEVPROP, as defined in 'devpkey.h' ...
Below are a few examples taken from the log of a test prog I wrote to discover the whole thing:
DEVPROPNAME: DEVPKEY_DeviceInterface_Bluetooth_DeviceAddress
DEVPROPGUID: {2BD67D8B-8BEB-48D5-87E0-6CDA3428040A}
DEVPROPPID: 1
DEVPROPTYPE: DEVPROP_TYPE_STRING
Value: c026df001017
DEVPROPNAME: DEVPKEY_Device_Children
DEVPROPGUID: {4340A6C5-93FA-4706-972C-7B648008A5A7}
DEVPROPPID: 9
DEVPROPTYPE: DEVPROP_TYPE_STRING_LIST
Value:
BTHLEDevice\{00001800-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0001
BTHLEDevice\{00001801-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0008
BTHLEDevice\{00001809-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&000c
BTHLEDevice\{0000180f-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0010
BTHLEDevice\{0000180a-0000-1000-8000-00805f9b34fb}_c026df001017\8&2fd07168&1&0014
BTHLEDevice\{00001523-1212-efde-1523-785feabcd123}_c026df001017\8&2fd07168&1&0019
On a second subject, you are 'working' on the 'device' itself ( SetupDiGetClassDevs(&BluetoothInterfaceGUID...) [and then working on the \BTHLE\ tree in Registry].
After listing all GattServices of this device and getting their uuids, you could restart that iteration on the device_guid itself SetupDiGetClassDevs(&GattServiceGUID...) [and then working on the \BTHLEDevice\ tree in Registry].
Now, to answer your question, I'm still searching myself :) But I'm not really sure:
1) that it is a working (dynamic) information to know the connection state
2) that it is a 'Property' you can access by the above methods
I have found out a solution.
GUID AGuid;
//GUID can be constructed from "{xxx....}" string using CLSID
CLSIDFromString(TEXT(TO_SEARCH_DEVICE_UUID), &AGuid);
GUID BluetoothInterfaceGUID = AGuid;
// Create a HDEVINFO with all present devices.
hDevInfo = SetupDiGetClassDevs(&BluetoothInterfaceGUID,
0, // Enumerator
0,
DIGCF_ALLCLASSES | DIGCF_PRESENT);//DIGCF_DEVICEINTERFACE | DIGCF_PRESENT);//DIGCF_ALLCLASSES | DIGCF_PRESENT);
if (hDevInfo == INVALID_HANDLE_VALUE)
{
// Insert error handling here.
return 1;
}
// Enumerate through all devices in Set.
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
for (i=0;SetupDiEnumDeviceInfo(hDevInfo,i,
&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
LPTSTR buffer1 = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty( // Get Name
hDevInfo,
&DeviceInfoData,
SPDRP_FRIENDLYNAME,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() ==
ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
// Double the size to avoid problems on
// W2k MBCS systems per KB 888609.
buffer = (wchar_t *)LocalAlloc(LPTR,buffersize * 2);
}
else
{
// Insert error handling here.
break;
}
}
{
if(strcmp("Your Device",AnsiString(buffer).c_str())==0) //Found your device
{
//########
DEVPROPTYPE ulPropertyType;
DWORD dwSize;
ULONG devst;
// memset(devst,0,sizeof(devst));
bool err = SetupDiGetDeviceProperty( //Checking Connection State
hDevInfo,
&DeviceInfoData,
&DEVPKEY_Device_DevNodeStatus, //Connected(0x02000000)
&ulPropertyType,
(BYTE *) &devst,
sizeof(devst),
&dwSize,
0);
DWORD error;
error = GetLastError();
if (devst &0x02000000) {
//"Status: Getrennt "
}
else
{
//"Status: Verbunden"
}
Hope this snippet helps.
I want to read the hardware configuration to check if a license for my software is valid. Currently, I tried using WMI. This works fine on many machines for several weeks but sometimes, without an obvious reason, WMI returns the hardware configuration in a different format. For example, the serial number of the primary hard disc is converted from characters to a hex string, with all character hex values being swapped pair wise. I figured out that different Windows user types (admin/normal) influence the format but it also changes in other situations and in different ways, for which I am unable to figure out a pattern.
Does anybody know how to reliably check the hardware configuration using WMI? Or would it be possible to avoid the above problem using MFC?
WMI is indeed unreliable. You should avoid using it when you don't need it.
Here's one way without WMI:
#include <string>
#include <Dbt.h>
#include <winioctl.h>
#include <SetupAPI.h>
#pragma comment(lib, "SetupAPI.lib")
#include <initguid.h>
DWORD getDeviceNumber(HANDLE hDeviceHandle)
{
STORAGE_DEVICE_NUMBER sdn = { 0 };
sdn.DeviceNumber = -1;
DWORD dwBytesReturned = 0;
if (!DeviceIoControl(hDeviceHandle, IOCTL_STORAGE_GET_DEVICE_NUMBER, nullptr, 0, &sdn, sizeof(sdn), &dwBytesReturned, nullptr))
{
return -1; //Error
}
return sdn.DeviceNumber;
}
bool GetDeviceString(std::wstring &out)
{
wchar_t wDevicePath[] = L"\\\\.\\#:";
wDevicePath[4] = L'C'; //Replace this with your drive-letter & adjust code (C: / D: whatever)
HANDLE deviceHandle = CreateFileW(wDevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (deviceHandle == INVALID_HANDLE_VALUE)
return false;
DWORD dwVolumeDeviceNumber = getDeviceNumber(deviceHandle);
CloseHandle(deviceHandle);
HDEVINFO hDevInfo = SetupDiGetClassDevsW(&GUID_DEVINTERFACE_DISK, nullptr, nullptr, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE)
return false; //Error
std::vector<BYTE> buf(1024);
PSP_DEVICE_INTERFACE_DETAIL_DATA_W psp = reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buf.data());
SP_DEVICE_INTERFACE_DATA spInt;
SP_DEVINFO_DATA spDev;
spInt.cbSize = sizeof(spInt);
DWORD dwIndex = 0;
while (true)
{
if (!SetupDiEnumDeviceInterfaces(hDevInfo, nullptr, &GUID_DEVINTERFACE_DISK, dwIndex, &spInt))
break;
DWORD dwSize = 0;
SetupDiGetDeviceInterfaceDetailW(hDevInfo, &spInt, nullptr, 0, &dwSize, nullptr);
if (dwSize && dwSize <= buf.size())
{
psp->cbSize = sizeof(*psp);
memset(&spDev, sizeof(spDev), 0);
spDev.cbSize = sizeof(spDev);
long res = SetupDiGetDeviceInterfaceDetailW(hDevInfo, &spInt, psp, dwSize, &dwSize, &spDev);
if (res)
{
HANDLE hDrive = CreateFileW(psp->DevicePath, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (hDrive != INVALID_HANDLE_VALUE)
{
DWORD dwUsbDeviceNumber = getDeviceNumber(hDrive);
if (dwUsbDeviceNumber == dwVolumeDeviceNumber)
{
//Found
out = psp->DevicePath;
break;
}
}
CloseHandle(hDrive);
}
}
++dwIndex;
}
SetupDiDestroyDeviceInfoList(hDevInfo);
if (out.empty()) //Was not found
return false;
return true;
}
After that, you will get a large device string. You might want to read the needed information out of it.
Check the following regular expressions to retrieve these:
(Note that the string CAN change, depending on the device-type, so test it and add/adjust the regular expressions - these are from an USB-stick test)
ven_([^&#]+) //Vendor String/ID
prod_([^&#]+) //Product String/ID
rev_([^&#]+) //Revision String/ID
&[^#]*#([^&#]+) //Serial String/Number
Regular expressions ? Another example:
std::wregex (see std::basic_regex...).
std::wsmatch (see std::match_results...)
std::wstring wsDeviceString;
if (GetDeviceString(wsDeviceString))
{
std::wregex regexSerialNumber(L"&[^#]*#([^&#]+)");
std::wsmatch match;
if (std::regex_search(wsDeviceString, match, regexSerialNumber))
std::wcout << L"Serial Number of device is: " << match[1].str() << std::endl;
}
One license for your product please =)
I am attempting to list the device information for all the monitors currently connected to the computer. I have a function that can do this and its 90% done except when I go to call the function SetupDiGetClassDevs() with the 2nd parameter set(not NULL) then the function always fails(returns INVALID_HANDLE_VALUE).
When I call GetLastError() I get the error 13(decimal), ie, "The data is invalid" which I am not sure what that means?
What is going wrong? Can you provide any advice on whats happening and how I can fix it?
Function Information:
HDEVINFO SetupDiGetClassDevs(
_In_opt_ const GUID *ClassGuid,
_In_opt_ PCTSTR Enumerator, // According to MSDN this param MUST be set if I want Device Information for a specific class(Monitors)
_In_opt_ HWND hwndParent,
_In_ DWORD Flags
);
My function that attempts to get a Device Information Set for Monitors only and output each monitors details(the error line is commented):
void printDeviceData(GUID guID)
{
// Device Classes: http://msdn.microsoft.com/en-us/library/windows/hardware/ff553426
// System Device Classes: http://msdn.microsoft.com/en-us/library/windows/hardware/ff553428
// Monitor Class GUI: {4d36e96e-e325-11ce-bfc1-08002be10318}
DWORD dataT = 0;
PCTSTR monitorGuID = _T("");
SP_DEVINFO_DATA deviceInfoData = {0};
deviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
deviceInfoData.ClassGuid = guID;
// Step 1: Get Device Information Set for Monitors only
// ERROR OCCURS HERE: SetupDiGetClassDevs() always fails
// Also tried these values for param 2: "Monitor" "PCI" but all cause the function to return INVALID_HANDLE_VALUE
HDEVINFO hDevInfo = SetupDiGetClassDevs(&guID, _T("{4d36e96e-e325-11ce-bfc1-08002be10318}"), NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) {
//outputLastError(_T("Fail 1"));
printf("hDevInfo == INVALID_HANDLE_VALUE\n");
return;
}
else printf("SUCCESS 1\n");
if (SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE) {
//outputLastError(_T("SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE"));
printf("SetupDiGetSelectedDevice(hDevInfo, &deviceInfoData) == FALSE, %d, %x\n", GetLastError(), GetLastError());
return;
}
else printf("SUCCESS 2\n");
// Step 2: For each Monitor: Output Device information
const unsigned int FLAG_NUM = 30;
DWORD flags[] = {SPDRP_FRIENDLYNAME, SPDRP_ENUMERATOR_NAME, SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, SPDRP_DEVICEDESC,
SPDRP_ADDRESS, SPDRP_BUSNUMBER, SPDRP_BUSTYPEGUID, SPDRP_CHARACTERISTICS, SPDRP_CLASS, SPDRP_CLASSGUID,
SPDRP_COMPATIBLEIDS, SPDRP_CONFIGFLAGS, SPDRP_DEVICE_POWER_DATA, SPDRP_DEVTYPE, SPDRP_DRIVER,
SPDRP_ENUMERATOR_NAME, SPDRP_EXCLUSIVE, SPDRP_HARDWAREID, SPDRP_INSTALL_STATE, SPDRP_LEGACYBUSTYPE,
SPDRP_LOCATION_INFORMATION, SPDRP_LOCATION_PATHS, SPDRP_LOWERFILTERS, SPDRP_MFG,
SPDRP_PHYSICAL_DEVICE_OBJECT_NAME, SPDRP_UI_NUMBER, SPDRP_UI_NUMBER_DESC_FORMAT, SPDRP_UPPERFILTERS,
SPDRP_SECURITY_SDS, SPDRP_SECURITY, SPDRP_SERVICE };
for (int i=0; i<=FLAG_NUM; i++) {
DWORD buffersize = 0;
LPTSTR buffer = NULL;
while (!SetupDiGetDeviceRegistryProperty(hDevInfo, &deviceInfoData, flags[i], &dataT,
(PBYTE)buffer, buffersize, &buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// Change the buffer size.
if (buffer)
LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR, buffersize);
}
else {
// Insert error handling here.
break;
}
}
printf("Data: %d: %s\n", i, buffer);
if (buffer)
LocalFree(buffer);
}
SetupDiDestroyDeviceInfoList(hDevInfo);
}
According to the documentation, Enumerator must be set to a valid device Instance ID which according to http://msdn.microsoft.com/en-us/library/windows/hardware/ff541327 has to be specified like this
"PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02\1&08"
I haven't tested it, but I'd assume that's where the invalid data come from.
I am using Qt on windows platform.
i want to get and display vendor id and product id of a plugged usb device from my local system.
Below is my full source code to get the vendor id and product id from the usb device.
when i run the my qt application it does not throw me any errors .
so i plug the usb device into the system.
but my print statement displays the result as below
qDebug ()<<pDetData->DevicePath;
i get the result as 0x4
Whether i have any implementation mistakes in my source code ?
if so please guide me what i am doing wrong..
Have i missed out any other functions ?
Is it possible to get the vendor id and product id from the usb device based on my source code .( my implementation of the code ) ?
kindly find my source code below
static GUID GUID_DEVINTERFACE_USB_DEVICE = { 0xA5DCBF10L, 0x6530, 0x11D2,
{ 0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED } };
HANDLE hInfo = SetupDiGetClassDevs(&GUID_DEVINTERFACE_USB_DEVICE,NULL,NULL,
DIGCF_PRESENT | DIGCF_INTERFACEDEVICE);
if ( hInfo == INVALID_HANDLE_VALUE )
{
qDebug ()<<"invalid";
}
else
{
qDebug ()<<"valid handle";
SP_DEVINFO_DATA DeviceInfoData;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
SP_INTERFACE_DEVICE_DATA Interface_Info;
Interface_Info.cbSize = sizeof(Interface_Info);
BYTE Buf[1024];
DWORD i;
DWORD InterfaceNumber= 0;
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd =
(PSP_DEVICE_INTERFACE_DETAIL_DATA) Buf;
for (i=0;SetupDiEnumDeviceInfo(hInfo,i,&DeviceInfoData);i++)
{
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
while (!SetupDiGetDeviceRegistryProperty( hInfo,
&DeviceInfoData,
SPDRP_DEVICEDESC,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
// Change the buffer size.
if (buffer) LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR,buffersize);
}
else
{
// Insert error handling here.
break;
}
qDebug ()<<(TEXT("Device Number %i is: %s\n"),i, buffer);
if (buffer) LocalFree(buffer);
if ( GetLastError() != NO_ERROR
&& GetLastError() != ERROR_NO_MORE_ITEMS )
{
// Insert error handling here.
qDebug ()<<"return false";
}
InterfaceNumber = 0; // this just returns the first one, you can iterate on this
if (SetupDiEnumDeviceInterfaces(hInfo,
NULL,
&GUID_DEVINTERFACE_USB_DEVICE,
InterfaceNumber,
&Interface_Info))
{
printf("Got interface");
DWORD needed;
pspdidd->cbSize = sizeof(*pspdidd);
SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
DWORD dwDetDataSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA)
+ 256;
SetupDiGetDeviceInterfaceDetail(hInfo,
&Interface_Info, pDetData,dwDetDataSize, NULL,
&DeviceInfoData);
qDebug ()<<pDetData->DevicePath;
//qDebug ()<<QString::fromWCharArray(pDetData->DevicePath);
}
else
{
printf("\nNo interface");
//ErrorExit((LPTSTR) "SetupDiEnumDeviceInterfaces");
if ( GetLastError() == ERROR_NO_MORE_ITEMS)
printf(", since there are no more items found.");
else
printf(", unknown reason.");
}
// Cleanup
SetupDiDestroyDeviceInfoList(hInfo);
qDebug ()<<"return true";
}
}
}
--------------- Edited to add: -----------------
Hi... the application comes and prints this
\?\usb#vid_04f2&pid_0111#5&1ba5a77f&0&2#{a5dcbf1 0-6530-11d2-901f-00c04fb951ed}
again it goes to while loop .... here it gets breaked in the else statement...
Qt Code:
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
// Change the buffer size.
if (buffer) LocalFree(buffer);
buffer = (LPTSTR)LocalAlloc(LPTR,buffersize);
} else {
qDebug ()<<"Here it quits the application";
// Insert error handling here. break;
}
Any ideas in this....
After this line:
SP_DEVICE_INTERFACE_DETAIL_DATA *pDetData = NULL;
Add this:
DWORD dwDetDataSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
pDetData = (_SP_DEVICE_INTERFACE_DETAIL_DATA_A*) malloc (dwDetDataSize);
pDetData->cbSize = sizeof (SP_DEVICE_INTERFACE_DETAIL_DATA);
After this line:
qDebug ()<<pDetData->DevicePath;
Add this:
free(pDetData);
But eventually you're going to have to read the docs for SetupDiGetDeviceInterfaceDetail(). Do it, there are lots of functions that work like this, with pointers to variable-size structs.
-------- Edited to add: --------
You're really going about this the wrong way. I see you're following the advice you got here, and it's taken you down the wrong path. idVendor and idProduct can only be found in the USB_DEVICE_DESCRIPTOR (MSDN).
It looks like you already know how to get the device handle (using CreateFile()). After that, you call WinUsb_Initialize() (MSDN). That gets you a WINUSB_INTERFACE_HANDLE.
Once you have that handle, you want to call WinUsb_GetDescriptor() (MSDN), with the DescriptorType set to URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE. I can't test code now, but it will look something like this:
USB_DEVICE_DESCRIPTOR udd;
memset(&udd, 0, sizeof(udd));
ULONG LengthTransferred = 0;
WinUsb_GetDescriptor(
winusb_interface_handle, // returned by WinUsbInitialize
URB_FUNCTION_GET_DESCRIPTOR_FROM_DEVICE,
0, // not sure if we need this
0x409, // not sure if we need this
&udd,
sizeof(udd),
&LengthTransferred);
After that, udd->idVendor and udd->idProduct should have what you want.
Microsoft used to supply sample code for all this in the DDK, and probably still does, but I don't have access to one.
---------- Edited to add: ----------
Daniel K writes that the code should really be:
USB_DEVICE_DESCRIPTOR udd;
memset(&udd, 0, sizeof(udd));
ULONG LengthTransferred = 0;
WinUsb_GetDescriptor(
winusb_interface_handle, // returned by WinUsbInitialize
USB_DEVICE_DESCRIPTOR_TYPE, // Daniel K's suggestion
0,
0x409, // asks for English
&udd,
sizeof(udd),
&LengthTransferred);
See the comments for further details.
An alternative is to obtain the hardwareID which includes the VID and PID.
Call SetupDiGetDeviceRegistryProperty with SPDRP_HARDWAREID like so:
wchar_t *hardwareID;
// First get requiredLength
SetupDiGetDeviceRegistryProperty(deviceInfoList, &deviceInfoData, SPDRP_HARDWAREID, NULL, NULL, 0, &requiredLength);
hardwareID = (wchar_t*)(new char[requiredLength]());
// Second call to populate hardwareID
SetupDiGetDeviceRegistryProperty(deviceInfoList, &deviceInfoData, SPDRP_HARDWAREID, NULL, (PBYTE)hardwareID, requiredLength, NULL);
// Display the string
qDebug() << "hardwareID =" << QString::fromWCharArray(hardwareID);
This will give you a string like USB\ROOT_HUB20&VID1002&PID4396&REV0000 which you can parse.
*Note: not all devices will have a VID and PID, such as non-USB devices.
You are enumerating the device "interface". Interfaces do not have a VID or PID - device instances do. I am not sure whether you are enumerating the interfaces to narrow down the devices you are interested in, for because it's an error.
If you just enumerate the device instances, then you can call SetupDiGetDeviceProperty with DEVPKEY_Device_HardwareIds and then grep the resulting hardware id for the VID and PID.
If you are using the device interfaces on purpose, then you need to call SetupDiGetDeviceInterfaceDetail once with a NULL PSP_DEVICE_INTERFACE_DETAIL parameter and a valid requiredSize pointer to get the required size of memory to allocate, allocate that memory and then call the function again. In that call, the last parameter is a SP_DEVINFO_DATA structure, which once retrieved, you can use in the call to SetupDiGetDeviceProperty as I mentioned above.
I am maintaining an application that uses SetupDiGetDeviceInterfaceDetail() to find out information on the installed serial ports on the computer. I have noticed while testing this that there are some devices, such as my Lucent WinModem, that do not show up in that enumeration. It turns out that I am having a similar issue with a set of devices manufactured by my company that implement the serial port interface. My assumption is that there is something that is missing from the INF file for the device. Does anyone know what kinds of conditions can result in this kind of omission?
Edit: Here is a sample of the code that I am using to enumerate the serial ports. I have tried various combinations of flags but have not seen any significant difference in behaviour.
DEFINE_GUID(GUID_CLASS_COMPORT, 0x4d36e978, 0xe325, 0x11ce, 0xbf, 0xc1, \
0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18);
GUID *serial_port_guid = const_cast<GUID *>(&GUID_CLASS_COMPORT);
HDEVINFO device_info = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DETAIL_DATA *detail_data = 0;
device_info = SetupDiGetClassDevs(
serial_port_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(device_info != INVALID_HANDLE_VALUE)
{
uint4 const detail_data_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA) + 256;
detail_data = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA *>(new char[detail_data_size]);
SP_DEVICE_INTERFACE_DATA ifc_data;
bool more_interfaces = true;
int rcd;
memset(&ifc_data, 0, sizeof(ifc_data));
memset(detail_data, 0, detail_data_size);
ifc_data.cbSize = sizeof(ifc_data);
detail_data->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
for(uint4 index = 0; more_interfaces; ++index)
{
rcd = SetupDiEnumDeviceInterfaces(device_info, 0, serial_port_guid, index, &ifc_data);
if(rcd)
{
// we need to get the details of this device
SP_DEVINFO_DATA device_data = { sizeof(SP_DEVINFO_DATA) };
rcd = SetupDiGetDeviceInterfaceDetail(
device_info, &ifc_data, detail_data, detail_data_size, 0, &device_data);
if(rcd)
{
StrAsc device_path(detail_data->DevicePath);
byte friendly_name[256];
rcd = SetupDiGetDeviceRegistryProperty(
device_info, &device_data, SPDRP_FRIENDLYNAME, 0, friendly_name, sizeof(friendly_name), 0);
if(rcd)
{
std::for_each(
port_names.begin(),
port_names.end(),
update_friendly_name(
reinterpret_cast<char const *>(friendly_name)));
}
}
else
more_interfaces = false;
}
}
}
This is more of a question about the issue. When you call the function the first arg you pass in should be DeviceInfoSet which you likely got from the SetupDiGetClassDevs function. When you called the SetupDiGetClassDevs function what did you specify for the flags (Last Argument) Quoting Microsoft's Page on the function:
DIGCF_ALLCLASSES
Return a list of installed devices for all device setup classes or all
device interface classes.
DIGCF_DEVICEINTERFACE
Return devices that support device interfaces for the specified device
interface classes. This flag must be
set in the Flags parameter if the
Enumerator parameter specifies a
device instance ID.
DIGCF_DEFAULT
Return only the device that is associated with the system default
device interface, if one is set, for
the specified device interface
classes.
DIGCF_PRESENT
Return only devices that are currently present in a system.
DIGCF_PROFILE
Return only devices that are a part of the current hardware profile.
Depending on your choice the list of devices changes. For example The Present flag will only show devices plugged in actively.
UPDATE: Thanks for the sample code.
My question now, is if you want to know the friendly name of the modem why not use the same call but specify the Modem Guid instead of the COM Port? I have the Modem GUID being 4D36E96D-E325-11CE-BFC1-08002BE10318
In the registry I can see a value called 'AttachedTo' which specifies a COM Port. I'll have to research which property thats is tied to in the API. The registry key is at
HKLM\SYSTEM\CurrentControlSet\Control\Class{4D36E96D-E325-11CE-BFC1-08002BE10318}\
ANOTHER UPDATE:
Looking closer at the sample code. Based on this, if you are trying to get the device interface class that should return a SP_DEVICE_INTERFACE_DETAIL_DATA Structure. That wouldn't provide a way of getting the friendly name of the device. I believe instead you would want the device instance.
From what I've read, the Device Interface is used to as a way to get the device path which can be used to write to it.
One thing I did to test your code was try it again the Disk Device Interface. I made a few changes to get it to work on my system and it still isn't quite done. I think the one problem (probably more) is that I need to resize the DevicePath variable inbetween the SetupDiGetDeviceInterfaceDetail calls.
void Test()
{
GUID *serial_port_guid = const_cast<GUID *>(&GUID_DEVINTERFACE_DISK);
HDEVINFO device_info = INVALID_HANDLE_VALUE;
SP_DEVICE_INTERFACE_DETAIL_DATA detail_data;
device_info = SetupDiGetClassDevs(
serial_port_guid, 0, 0, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if(device_info != INVALID_HANDLE_VALUE)
{
//uint4 const detail_data_size = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);// + 256;
//detail_data = reinterpret_cast<SP_DEVICE_INTERFACE_DETAIL_DATA *>(new char[detail_data_size]);
SP_DEVICE_INTERFACE_DATA ifc_data;
bool more_interfaces = true;
int rcd;
memset(&ifc_data, 0, sizeof(ifc_data));
//memset(detail_data, 0, detail_data_size);
ifc_data.cbSize = sizeof(ifc_data);
detail_data.cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
for(uint4 index = 0; more_interfaces; ++index)
{
rcd = SetupDiEnumDeviceInterfaces(device_info, 0, serial_port_guid, index, &ifc_data);
if(rcd)
{
// we need to get the details of this device
SP_DEVINFO_DATA device_data;
device_data.cbSize = sizeof(SP_DEVINFO_DATA);
DWORD intReqSize;
rcd = SetupDiGetDeviceInterfaceDetail(device_info, &ifc_data, 0, 0, &intReqSize, &device_data);
rcd = SetupDiGetDeviceInterfaceDetail(device_info, &ifc_data, &detail_data,intReqSize,&intReqSize,&device_data);
if(rcd)
{
//StrAsc device_path(detail_data->DevicePath);
byte friendly_name[256];
rcd = SetupDiGetDeviceRegistryProperty(
device_info, &device_data, SPDRP_FRIENDLYNAME, 0, friendly_name, sizeof(friendly_name), reinterpret_cast<DWORD *>(sizeof(friendly_name)));
if(rcd)
{
cout<<reinterpret_cast<char const *>(friendly_name);
}
else
{ int num = GetLastError();
}
}
else
{
int num = GetLastError();
}
}
else
more_interfaces = false;
}
}
SetupDiDestroyDeviceInfoList(device_info);
}
Also, in the INF, you may have to add the AddInterface directive to associate your driver with the correct interface.
I am not sure whether following hotfix will solve your problem as mentioned in
http://support.microsoft.com/kb/327868
One more intersting point: GUID_CLASS_COMPORT is obsolete from Win2000 onwards..
http://msdn.microsoft.com/en-us/library/bb663140.aspx
http://msdn.microsoft.com/en-us/library/bb663174.aspx
Another site I find having 9 different ways of enumeration. Best of luck.
http://www.naughter.com/enumser.html
You say your device is present and accessible, but are you accessing your device directly or are you accessing a port by name and number COMn:
I have a WinModem that is connected to my audio driver. I have no serial port, not even a simulated one.
I decided to punt on this and to do away with the dependency on the SetupDi() functions. Instead, I have written code that traverses the subkeys in HKEY_LOCAL_MACHINE\System\CurrentControlSet\Enum to find any drivers that support the serial port GUID. I have the feeling that this is what the device manager does. In case anyone is interested, my code fragment can be seen below:
typedef std::string StrAsc;
typedef std::pair<StrAsc, StrAsc> port_name_type;
typedef std::list<port_name_type> friendly_names_type;
void SerialPortBase::list_ports_friendly(friendly_names_type &port_names)
{
// we will first get the list of names. This will ensure that, at the very least, we get
// the same list of names as we would have otherwise obtained.
port_names_type simple_list;
list_ports(simple_list);
port_names.clear();
for(port_names_type::iterator pi = simple_list.begin(); pi != simple_list.end(); ++pi)
port_names.push_back(friendly_name_type(*pi, *pi));
// we will now need to enumerate the subkeys of the Enum registry key. We will need to
// consider many levels of the registry key structure in doing this so we will use a list
// of key handles as a stack.
HKEY enum_key ;
char const enum_key_name[] = "SYSTEM\\CurrentControlSet\\Enum";
StrAsc const com_port_guid("{4d36e978-e325-11ce-bfc1-08002be10318}");
char const class_guid_name[] = "ClassGUID";
char const friendly_name_name[] = "FriendlyName";
char const device_parameters_name[] = "Device Parameters";
char const port_name_name[] = "PortName";
long rcd = ::RegOpenKeyEx(
HKEY_LOCAL_MACHINE, enum_key_name, 0, KEY_READ, &enum_key);
char value_buff[MAX_PATH];
StrAsc port_name, friendly_name;
if(!port_names.empty() && rcd == ERROR_SUCCESS)
{
std::list<HKEY> key_stack;
key_stack.push_back(enum_key);
while(!key_stack.empty())
{
// we need to determine whether this key has a "ClassGUID" value
HKEY current = key_stack.front();
uint4 value_buff_len = sizeof(value_buff);
key_stack.pop_front();
rcd = ::RegQueryValueEx(
current,
class_guid_name,
0,
0,
reinterpret_cast<byte *>(value_buff),
&value_buff_len);
if(rcd == ERROR_SUCCESS)
{
// we will only consider devices that match the com port GUID
if(com_port_guid == value_buff)
{
// this key appears to identify a com port. We will need to get the friendly name
// and try to get the 'PortName' from the 'Device Parameters' subkey. Once we
// have those things, we can update the friendly name in our original list
value_buff_len = sizeof(value_buff);
rcd = ::RegQueryValueEx(
current,
friendly_name_name,
0,
0,
reinterpret_cast<byte *>(value_buff),
&value_buff_len);
if(rcd == ERROR_SUCCESS)
{
HKEY device_parameters_key;
rcd = ::RegOpenKeyEx(
current,
device_parameters_name,
0,
KEY_READ,
&device_parameters_key);
if(rcd == ERROR_SUCCESS)
{
friendly_name = value_buff;
value_buff_len = sizeof(value_buff);
rcd = ::RegQueryValueEx(
device_parameters_key,
port_name_name,
0,
0,
reinterpret_cast<byte *>(value_buff),
&value_buff_len);
if(rcd == ERROR_SUCCESS)
{
friendly_names_type::iterator fi;
port_name = value_buff;
fi = std::find_if(
port_names.begin(), port_names.end(), port_has_name(port_name));
if(fi != port_names.end())
fi->second = friendly_name;
}
::RegCloseKey(device_parameters_key);
}
}
}
}
else
{
// since this key did not have what we expected, we will need to check its
// children
uint4 index = 0;
rcd = ERROR_SUCCESS;
while(rcd == ERROR_SUCCESS)
{
value_buff_len = sizeof(value_buff);
rcd = ::RegEnumKeyEx(
current, index, value_buff, &value_buff_len, 0, 0, 0, 0);
if(rcd == ERROR_SUCCESS)
{
HKEY child;
rcd = ::RegOpenKeyEx(current, value_buff, 0, KEY_READ, &child);
if(rcd == ERROR_SUCCESS)
key_stack.push_back(child);
}
++index;
}
}
::RegCloseKey(current);
}
}
} // list_ports_friendly