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
Related
I am making a simple Application to collect and represent data over bluetooth serial COM port and I don't want to hard-code the COM ports. So I want to enumerate the COM ports by looking up HKEY_LOCAL_MACHINE\HARDWARE\DEVICEMAP\SERIALCOMM. However I'm fairly new to both wxWidgets and win32 registry terminology(I still don't understand what's supposed to be the "name" and "value" in wxWidgets documentation, they don't work as expected). What I want is to iterate through all the COMM ports and add them to a drop down list. This doesn't work:
wxRegKey regKey(wxRegKey::HKLM,"HARDWARE\\DEVICEMAP\\SERIALCOMM");
size_t subkeys;
long k = 1;
regKey.Open(wxRegKey::Read);
regKey.GetKeyInfo(&subkeys, NULL, NULL, NULL);
wxString key_name;
regKey.GetFirstValue(key_name, k);
m_drop_down->Append(key_name);
for (int i = 0; i < subkeys; i++) {
regKey.GetNextValue(key_name, k);
m_drop_down->Append(key_name);
}
regKey.Close();
It only appends one \Device\BthModem2 to the dropdown list. I would be grateful if someone cleared the terminologies up and tell me how I should go about making it work. For reference, here's what that registry entry looks to me, I want COM3, COM4, COM5, COM6 appended to the drop down list:
If you want to get the "value" (the thing in the right column), you can declare a function like this:
wxString GetStringData(const wxRegKey& regKey, const wxString& valueName)
{
wxRegKey::ValueType vtype = regKey.GetValueType(valueName);
wxString data;
if ( vtype == wxRegKey::Type_String )
{
regKey.QueryValue(valueName, data, true);
}
return data;
}
To get the complete list of them from the registry entry, you can iterate over the values in the registry entry like this:
while(regKey.GetNextValue(key_name, k)) {
wxString data = GetStringData(regKey,valueName);
if ( !data.IsEmpty() )
{
m_drop_down->Append(key_name);
}
}
GetNextValue will return false when there are no more values to be fetched breaking out of the loop.
This isn't really complete since this won't distinguish between a registry value whose type isn't string and a registy value whose type is a string but with empty data, but it should be clear how to modify the function and/or the iteration if it's necessary to make that distinction.
According to MSDN:Enumerating Registry Subkeys and RegGetValue, RegGetValue retrieves the type and data for the specified registry value.
Code:
int main()
{
long ret;
unsigned long reg_index = 0;
DWORD dwValSize = MAX_VALUE_NAME;
DWORD dwDataSize = MAX_VALUE_NAME;
LPTSTR lpValname = new TCHAR[MAX_VALUE_NAME]{};
LPTSTR lpDataname = new TCHAR[MAX_VALUE_NAME]{};
HKEY hkey;
int result = RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("SOFTWARE\\Microsoft"), 0, KEY_READ, &hkey);
if (result == ERROR_SUCCESS) {
while ((ret = RegEnumValue(hkey, reg_index++, lpValname, &dwValSize, NULL, NULL, NULL, NULL)) != ERROR_NO_MORE_ITEMS)
{
if ((ret == ERROR_SUCCESS) || (ret == ERROR_MORE_DATA)) {
_tprintf(TEXT("(%d) %s"), reg_index, lpValname);
DWORD type = 0;
if (RegGetValue(hkey, 0,lpValname, RRF_RT_ANY, &type, lpDataname, &dwDataSize) == ERROR_SUCCESS)
{
_tprintf(TEXT("(Value data %s)\n"), lpDataname);
}
}
dwValSize = MAX_VALUE_NAME;
dwDataSize = MAX_VALUE_NAME;
ZeroMemory(lpDataname,sizeof(TCHAR)* dwDataSize);
ZeroMemory(lpValname,sizeof(TCHAR)* dwValSize);
}
RegCloseKey(hkey);
}
return TRUE;
}
I'm trying to get list of supported page sizes of network printer using DeviceCapabilities and completely confused with sPort parameter.
DWORD nPapersCount = ::DeviceCapabilities(sPrinter, sPort, DC_PAPERS, nullptr, nullptr);
Could anyone suggest me, what should I provide in sPort for network printer and how could I reliably obtain that port?
As experiment, I have tried PC name in format "\\Share", its port "\\Share\LPT1", just "LPT1" and have no luck.
Also, I've found EnumPorts function, so I could obtain a list of printer ports on remote server, but I have no idea how to deal with list of ports i n case of multiple printers on server.
typedef struct _PORT_INFO_2 {
LPTSTR pPortName;
LPTSTR pMonitorName;
LPTSTR pDescription;
DWORD fPortType;
DWORD Reserved;
} PORT_INFO_2, *PPORT_INFO_2;
You can't guess the portname, specially in case of network printers. Use PrintDlg to get information from currently selected printer (this can be done without showing the print dialog).
Portname is available through hDevNames member of PRINTDLG structure.
DEVNAMES structure:
typedef struct tagDEVNAMES {
WORD wDriverOffset;
WORD wDeviceOffset;
WORD wOutputOffset; //<= port name
WORD wDefault;
} DEVNAMES, *LPDEVNAMES;
Unicode example:
PRINTDLG pdlg = { sizeof PRINTDLG };
pdlg.Flags = PD_RETURNDEFAULT;
PrintDlg(&pdlg);
LPDEVNAMES lpDev = (LPDEVNAMES)GlobalLock(pdlg.hDevNames);
std::wstring device = (LPCTSTR)lpDev + lpDev->wDeviceOffset;
std::wstring port = (LPCTSTR)lpDev + lpDev->wOutputOffset;
::GlobalUnlock(pdlg.hDevNames);
//clean up after PrintDlg, as pointed out by #RemyLebeau
GlobalFree(pdlg.hDevMode);
GlobalFree(pdlg.hDevNames);
int nPapersCount;
nPapersCount = ::DeviceCapabilities(device.c_str(), port.c_str(), DC_PAPERS, NULL, NULL);
if (nPapersCount > 0)
{
WORD* sizeBuf = new WORD[nPapersCount];
DeviceCapabilities(device.c_str(), port.c_str(), DC_PAPERS, (LPTSTR)sizeBuf, NULL);
for (int i = 0; i < nPapersCount; i++)
std::wcout << sizeBuf[i] << "\n";
delete[] sizeBuf;
}
Using PrintDlg is a little unwieldy, and has the disadvantage that it only lets you query the default printer (AFAIK). The better bet is to use GetPrinter, with a requested output level of 5:
HANDLE h = NULL;
OpenPrinter(printerName, &h, NULL); // printerName is a LPWSTR
if (!h)
return;
DWORD cbBuf=0;
GetPrinter(h, 5, NULL, 0, &cbBuf);
// First call to GetPrinter determines an output buffer size
BYTE *buf = new BYTE[cbBuf];
if (!GetPrinter(h, 5, buf, cbBuf, &cbBuf))
{ ClosePrinter(h); return; }
PRINTER_INFO_5 *ppi = (PRINTER_INFO_5*) buf;
LPWSTR port = ppi->pPortName;
ClosePrinter(h);
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 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
I would like to know whether i can get the drive information using the
SP_DEVICE_INTERFACE_DETAIL_DATA's DevicePath
my device path looks like below
"\?\usb#vid_04f2&pid_0111#5&39fe81e&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}"
also please tell me in the winapi they say "To determine whether a drive is a USB-type drive, call SetupDiGetDeviceRegistryProperty and specify the SPDRP_REMOVAL_POLICY property."
i too use SetupDiGetDeviceRegistryProperty like below
while ( !SetupDiGetDeviceRegistryProperty( hDevInfo,&DeviceInfoData,
SPDRP_REMOVAL_POLICY,&DataT,( PBYTE )buffer,buffersize,&buffersize ))
but i dont know how can i get the drive type using the above..
Please help me up
I've created a GetMountedVolumes method with optional mask to specify what type of volumes should be included in the search (readable, writeable, removeable, hotplugable, eraseable).
I abstained from Setup API methods at all and only used null access volume handle for DeviceIoControl calls (no administrative privileges are required). I will share my code, may be this will help others to implement methods to determine drive (volume) type without using Setup API.
enum VolumesFlags {
VolumeReadable = 1,
VolumeWriteable = 2,
VolumeEraseable = 4,
VolumeRemoveable = 8,
VolumeHotplugable = 16,
VolumeMounted = 128
};
bool GetMountedVolumes(std::vector<std::wstring> &Volumes,
unsigned int Flags = VolumeReadable | VolumeWriteable,
unsigned int Mask = VolumeReadable | VolumeWriteable) {
wchar_t Volume[MAX_PATH] = {0};
wchar_t* VolumeEndPtr = Volume;
Flags |= VolumeMounted;
Mask |= VolumeMounted;
Flags &= Mask;
HANDLE hFind = FindFirstVolume(Volume, sizeof(Volume) / sizeof(wchar_t));
if (hFind != INVALID_HANDLE_VALUE) {
do {
bool IsMatching = false;
VolumeEndPtr = &Volume[wcslen(Volume) - 1];
*VolumeEndPtr = L'\0';
HANDLE hDevice = CreateFile(Volume, 0, 0, 0, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
if (hDevice != INVALID_HANDLE_VALUE) {
unsigned int CurrentFlags = 0;
DWORD ReturnedSize;
STORAGE_HOTPLUG_INFO Info = {0};
if (DeviceIoControl(hDevice, IOCTL_STORAGE_GET_HOTPLUG_INFO, 0, 0, &Info, sizeof(Info), &ReturnedSize, NULL)) {
if (Info.MediaRemovable) {
CurrentFlags |= VolumeRemoveable;
}
if (Info.DeviceHotplug) {
CurrentFlags |= VolumeHotplugable;
}
}
DWORD MediaTypeSize = sizeof(GET_MEDIA_TYPES);
GET_MEDIA_TYPES* MediaType = (GET_MEDIA_TYPES*) new char[MediaTypeSize];
while (DeviceIoControl(hDevice, IOCTL_STORAGE_GET_MEDIA_TYPES_EX, 0, 0, MediaType, MediaTypeSize, &ReturnedSize, NULL) == FALSE && GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
delete [] (char*) MediaType;
MediaTypeSize *= 2;
MediaType = (GET_MEDIA_TYPES*) new char[MediaTypeSize];
}
if (MediaType->MediaInfoCount > 0) {
DWORD Characteristics = 0;
// Supports: Disk, CD, DVD
if (MediaType->DeviceType == FILE_DEVICE_DISK || MediaType->DeviceType == FILE_DEVICE_CD_ROM || MediaType->DeviceType == FILE_DEVICE_DVD) {
if (Info.MediaRemovable) {
Characteristics = MediaType->MediaInfo[0].DeviceSpecific.RemovableDiskInfo.MediaCharacteristics;
} else {
Characteristics = MediaType->MediaInfo[0].DeviceSpecific.DiskInfo.MediaCharacteristics;
}
if (Characteristics & MEDIA_CURRENTLY_MOUNTED) {
CurrentFlags |= VolumeMounted;
}
if (Characteristics & (MEDIA_READ_ONLY | MEDIA_READ_WRITE)) {
CurrentFlags |= VolumeReadable;
}
if (((Characteristics & MEDIA_READ_WRITE) != 0 || (Characteristics & MEDIA_WRITE_ONCE) != 0) && (Characteristics & MEDIA_WRITE_PROTECTED) == 0 && (Characteristics & MEDIA_READ_ONLY) == 0) {
CurrentFlags |= VolumeWriteable;
}
if (Characteristics & MEDIA_ERASEABLE) {
CurrentFlags |= VolumeEraseable;
}
}
}
delete [] (char*) MediaType;
CloseHandle(hDevice);
CurrentFlags &= Mask;
if (CurrentFlags == Flags) {
*VolumeEndPtr = L'\\';
wchar_t VolumePaths[MAX_PATH] = {0};
if (GetVolumePathNamesForVolumeName(Volume, VolumePaths, MAX_PATH, &ReturnedSize)) {
if (*VolumePaths) {
Volumes.push_back(VolumePaths);
}
}
}
}
} while (FindNextVolume(hFind, Volume, sizeof(Volume) / sizeof(wchar_t)));
FindVolumeClose(hFind);
}
return Volumes.size() > 0;
}
Probably what you are looking for you will be find here http://support.microsoft.com/kb/264203/en. Another link http://support.microsoft.com/kb/305184/en can be also interesting for you.
UPDATED: Example from http://support.microsoft.com/kb/264203/en shows you how to use to determine whether USB-Drive is removable. You can also use SetupDiGetDeviceRegistryProperty with SPDRP_REMOVAL_POLICY on the device instance (use SetupDiEnumDeviceInfo, SetupDiGetDeviceInstanceId and then SetupDiGetDeviceRegistryProperty). If returned DWORD has CM_REMOVAL_POLICY_EXPECT_SURPRISE_REMOVAL or CM_REMOVAL_POLICY_EXPECT_ORDERLY_REMOVAL as value, the drive is removable.
Moreover the code example show how to open device handle which you can use with DeviceIoControl function to retrieve a lot of useful information which you can need. IOCTL_STORAGE_QUERY_PROPERTY (see http://msdn.microsoft.com/en-us/library/ff566997%28v=VS.85%29.aspx) with different QueryType and PropertyId only one example. You can use IOCTL_STORAGE_GET_DEVICE_NUMBER for example to receive storage volumes and their disk number.
If you will have full STORAGE_DEVICE_NUMBER information about your USB device we will be able to find all other information about it with different ways. One of the easiest is: just enumerate all drive letters with QueryDosDevice and query STORAGE_DEVICE_NUMBER for every drive. If you will find full match in STORAGE_DEVICE_NUMBER you will find the drive letter.
Given your Storage Device Path:
Open the device using CreateFile
use DeviceIOControl to issue IOCTL_STORAGE_QUERY_PROPERTY
this populates STORAGE_DEVICE_DESCRIPTOR structure
which has a STORAGE_BUS_TYPE enumeration:
STORAGE_DEVICE_DESCRIPTOR {
DWORD Version;
DWORD Size;
BYTE DeviceType;
BYTE DeviceTypeModifier;
BOOLEAN RemovableMedia;
BOOLEAN CommandQueueing;
DWORD VendorIdOffset;
DWORD ProductIdOffset;
DWORD ProductRevisionOffset;
DWORD SerialNumberOffset;
STORAGE_BUS_TYPE BusType; //<---------------
DWORD RawPropertiesLength;
BYTE RawDeviceProperties[1];
}
The different storage bus types are
BusTypeScsi: SCSI
BusTypeAtapi: ATAPI
BusTypeAta: ATA
BusType1394: IEEE-1394
BusTypeSsa: SSA
BusTypeFibre: Fiber Channel
BusTypeUsb: USB
BusTypeRAID: RAID
BusTypeiSCSI: iSCSI
BusTypeSas: Serial Attached SCSI (SAS)
BusTypeSata: SATA
So example psueudo-code
STORAGE_BUS_TYPE GetStorageDeviceBusType(String StorageDevicePath)
{
/*
Given a storage device path of
\?\usb#vid_04f2&pid_0111#5&39fe81e&0&2#{a5dcbf10-6530-11d2-901f-00c04fb951ed}
return its StorageBusType, e.g.:
BusTypeUsb
*/
//Open the disk for reading (must be an administrator)
HANDLE diskHandle = CreateFile(StorageDevicePath,
GENERIC_READ, //desired access
FILE_SHARE_READ | FILE_SHARE_WRITE, //share mode
null, //security attributes
OPEN_EXISTING, //creation disposition
FILE_ATTRIBUTE_NORMAL, //flags and attributes
0);
if (diskHandle == INVALID_HANDLE_VALUE)
RaiseLastWin32Error();
try
{
BOOL res;
DWORD bytesReturned;
//Set up what we want to query from the drive
STORAGE_PROPERTY_QUERY query= {};
query.QueryType = PropertyStandardQuery;
query.PropertyID = StorageDeviceProperty;
DWORD bufferSize;
// Query for the header to get the required buffer size
STORAGE_DESCRIPTOR_HEADER header;
res = DeviceIoControl(diskHandle, IOCTL_STORAGE_QUERY_PROPERTY,
ref query, sizeof(STORAGE_PROPERTY_QUERY),
ref header, sizeof(STORAGE_DESCRIPTOR_HEADER),
out bytesReturned, null);
if (!res) RaiseLastWin32Error();
bufferSize = header.Size;
//Allocate the buffer and query for the full property
STORAGE_DEVICE_DESCRIPTOR *deviceDescriptor = GetMem(bufferSize);
try
{
//Issue IOCTL_STORAGE_QUERY_PROPERTY to get STORAGE_DEVICE_DESCRIPTOR
res = DeviceIoControl(diskHandle, IOCTL_STORAGE_QUERY_PROPERTY,
#query, sizeof(STORAGE_PROPERTY_QUERY),
deviceDescriptor, bufferSize,
out bytesReturned, null));
if (!res)
RaiseLastWin32Error();
return deviceDescriptor.BusType;
}
finally
{
FreeMem(deviceDescriptor);
}
}
finally
{
CloseHandle(diskHandle);
}
}