I have a setup with two regular displays and three projectors connected to a windows pc. In my win32 program I need to uniquely identify each monitor and store information for each such that I can retrieve the stored information even after computer restart.
The EnumDisplayDevices seems to return different device orders after restarting the computer. There is also GetPhysicalMonitorsFromHMONITOR which at least gives me the display's name. However, I need something like a serial number for my projectors, since they are the same model. How can I get such a unique identifier?
EDIT: This is the solution I came up with after reading the answer from user Anders (thanks!):
DISPLAY_DEVICEA dispDevice;
ZeroMemory(&dispDevice, sizeof(dispDevice));
dispDevice.cb = sizeof(dispDevice);
DWORD screenID;
while (EnumDisplayDevicesA(NULL, screenID, &dispDevice, 0))
{
// important: make copy of DeviceName
char name[sizeof(dispDevice.DeviceName)];
strcpy(name, dispDevice.DeviceName);
if (EnumDisplayDevicesA(name, 0, &dispDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
// at this point dispDevice.DeviceID contains a unique identifier for the monitor
}
++screenID;
}
EnumDisplayDevices with the EDD_GET_DEVICE_INTERFACE_NAME flag should give you a usable string. And if not, you can use this string with the SetupAPI to get the hardware id or driver key or whatever is unique enough for your purpose.
Set this flag to EDD_GET_DEVICE_INTERFACE_NAME (0x00000001) to retrieve the device interface name for GUID_DEVINTERFACE_MONITOR, which is registered by the operating system on a per monitor basis. The value is placed in the DeviceID member of the DISPLAY_DEVICE structure returned in lpDisplayDevice. The resulting device interface name can be used with SetupAPI functions and serves as a link between GDI monitor devices and SetupAPI monitor devices.
Related
I'm trying to identify when a specific USB microphone is plugged into windows. The device is using a generic VID/PID. Our application is asking the user to identify the device as part of setup. We record the UUID of the device and connect to it later. Occasionally windows seem to report that the device with that UUID isn't present. Does the UUID reported by mmdeviceenumerator ever change? We are fairly certain the device isn't changing ports for getting unplugged. The machine may be getting rebooted though.
This will work fine for days on most machines, but then suddenly it's not found. Our logs show the device isn't available and it appears the UUID is different.
Here's some simplified code on how I'm reading the ids and names:
com_pointer<IMMDeviceCollection> devices;
enumerator->EnumAudioEndpoints(flow, DEVICE_STATE_ACTIVE | DEVICE_STATE_UNPLUGGED, &devices);
UINT count;
devices->GetCount(&count);
for (UINT i = 0; i < count; ++i)
{
com_pointer<IMMDevice> comDevice;
devices->Item(i, &comDevice)
AudioDevice audioDevice = createAudioDevice(comDevice);
if (audioDevice.isFound()) {
list.append(audioDevice);
}
PROPVARIANT name;
PropVariantInit(&name);
properties->GetValue(PKEY_Device_FriendlyName, &name);
PROPVARIANT hardwareId;
PropVariantInit(&hardwareId);
properties->GetValue(PKEY_Device_HardwareIds, &hardwareId);
LPWSTR id;
device->GetId(&id);
}
The PKEY_Device_HardwareIds always return an empty string. Right now I'm storing and comparing the GUID from GetId to see if they are the same device. Sometimes that GUID changes. After some research, I suspect it occasionally gets changed due to a windows patch.
Is there a better id to use to keep track of the same device?
I have a setup with two regular displays and three projectors connected to a windows pc. In my win32 program I need to uniquely identify each monitor and store information for each such that I can retrieve the stored information even after computer restart.
The EnumDisplayDevices seems to return different device orders after restarting the computer. There is also GetPhysicalMonitorsFromHMONITOR which at least gives me the display's name. However, I need something like a serial number for my projectors, since they are the same model. How can I get such a unique identifier?
EDIT: This is the solution I came up with after reading the answer from user Anders (thanks!):
DISPLAY_DEVICEA dispDevice;
ZeroMemory(&dispDevice, sizeof(dispDevice));
dispDevice.cb = sizeof(dispDevice);
DWORD screenID;
while (EnumDisplayDevicesA(NULL, screenID, &dispDevice, 0))
{
// important: make copy of DeviceName
char name[sizeof(dispDevice.DeviceName)];
strcpy(name, dispDevice.DeviceName);
if (EnumDisplayDevicesA(name, 0, &dispDevice, EDD_GET_DEVICE_INTERFACE_NAME))
{
// at this point dispDevice.DeviceID contains a unique identifier for the monitor
}
++screenID;
}
EnumDisplayDevices with the EDD_GET_DEVICE_INTERFACE_NAME flag should give you a usable string. And if not, you can use this string with the SetupAPI to get the hardware id or driver key or whatever is unique enough for your purpose.
Set this flag to EDD_GET_DEVICE_INTERFACE_NAME (0x00000001) to retrieve the device interface name for GUID_DEVINTERFACE_MONITOR, which is registered by the operating system on a per monitor basis. The value is placed in the DeviceID member of the DISPLAY_DEVICE structure returned in lpDisplayDevice. The resulting device interface name can be used with SetupAPI functions and serves as a link between GDI monitor devices and SetupAPI monitor devices.
I am posting here for the first time, I hope this will be clear and readable.
I am currently trying to test the presence of usb devices on an embedded system using a specific HCD and port path programmatically using C++ and Visual Studio 2008.
The idea is to pass in the port number and the hcd value as the parameters of the function, and the function will return a true or false that indicates the connection status.
I have written some code to populate the root hub and prove that the device attached to port 1 of the root hub is a hub using bool DeviceIsHub from usbioctl.h.
However, when I attempt to enumerate the usb hub attached to port 1 of root so that I may test for the connection status of the downstream ports for the presence of device(ports 1 and 2 of this usb hub). It does not seem to know how many downstream ports the usb hub has.
I checked USBVIEW/TreeView, both application tells me that devices are there
but I am not sure what ioctl command code to use such that I can enumerate the downstream ports so I can check the connection status.
The structure of the device based on USB view and USB tree provides the following.
Root hub - it has 7 ports, only the first port is being used.
A USB hub (it has four available ports) is attached to the first port of the root hub.
Two USB devices (USB mouse and USB keyboard) are attached to port 1 and port 2 of the USB hub.
I have tried IOCTL_USB_GET_CONNECTION_INFORMATION, IOCTL_USB_GET_CONNECTION_NAME,
IOCTL_USB_GET_CONNECTION_INFORMATION_EX, IOCTL_USB_PORT_CONNECTOR_PROPERTIES
(which is not supported, it can only be used in windows 8, this is the exact ioctl call they used to enumerate the ports).
Please ignore the MessageBoxes, those are for me to check the control path status and determine which route it was following.
Please find the code that I wrote as I attempt to enumerate/populate the usb hub. I did not include the Root hub code because it would make this snippet too big.
My questions mainly resides in the enumeration process of the secondary USB hub I believe.
I just checked the registry key of the device. it appears that the USB hub is enumerated and present on the device since the information is shown under regedit HKLM->System->CurrentControlSet->Enum->USB. I believe I am not enumerating it correctly within my test application.
Any help would be greatly appreciated.
Update
The part that I am most concerned about is the DeviceIoControl Calls that attempts to get the size and the actual name of that USB hub.
It currently only takes in USB_GET_NODE_INFORMATION. Any other ioctl calls that are intended to retrieve the name of the hub will fail at the first DeviceIoControl where it attempts to get the size of the hub name so that it know how much memory to allocate
for it.
Update Part 2
From observing the usbview open source code, I believe I need to enumerate the host controller and all the devices first before checking for the presence of device. I drew a conclusion such that without doing the enumeration of controller, it only goes so far down the tree (at best second layer, which is where the external hub is attached to in my case).
I am currently attempting to enumerate the other devices and controllers in hope that I can get to the third layer of device. I will keep on updating this thread until either I figure out the problem myself or someone is capable of answering my questions.
//we are connected to the external hub, now we can begin the configuration and enumeration of the external hub.
ULONG kBytes = 0;
USB_HUB_NAME SubHubName;
//Create a Handle for the external hub driver
char Name[16];
wsprintf(Name, "\\\\.\\HCD%d", HcdSub);
HANDLE SubHub = CreateFile(Name,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
//Check to see if the handle was created successfully
if (SubHub == INVALID_HANDLE_VALUE)
{
MessageBox(NULL,"SubHandle Fail ","TEST",MB_OK);
return false;
}
//Query the SUBHUB/External Hub for the structure, this will tell us the number of down stream ports we need to enumerate
ioctlSuccess = DeviceIoControl(SubHub,
IOCTL_USB_GET_NODE_INFORMATION,
0,
0,
&SubHubName,
sizeof(SubHubName),
&kBytes,
NULL);
//If the command failed, close the handle and return false.
if(!ioctlSuccess)
{
CloseHandle(SubHub);
MessageBox(NULL," sub hub size fail ","TEST",MB_OK);
return false;
}
//Prepare to receive the SubHubName
kBytes = SubHubName.ActualLength;
USB_HUB_NAME *subHubNameW = (USB_HUB_NAME *) malloc(sizeof(USB_HUB_NAME) * kBytes);
//Check if the allocation failed, if it did, free the memory allocated and return false.
if (subHubNameW == NULL)
{
free(subHubNameW);
CloseHandle(SubHub);
MessageBox(NULL,"SUBHubNameW=NULL ","TEST",MB_OK);
return false;
}
//Send the command to retrieve the name
ioctlSuccess = DeviceIoControl(SubHub,
IOCTL_USB_GET_NODE_INFORMATION,
NULL,
0,
subHubNameW,
kBytes,
&kBytes,
NULL);
//We no longer need this handle.
CloseHandle(SubHub);
if(!ioctlSuccess)
{
if(subHubNameW !=NULL)
{
free(subHubNameW);
}
MessageBox(NULL,"GET NODE INFO FAIL ","TEST",MB_OK);
return false;
}
//Converts the SubHubNAme from widechar to a cahr.
MessageBox(NULL,"BEGIN CONVERTION","TEST",MB_OK);
kBytes = wcslen(subHubNameW->HubName) + 1;
char *subhubname = (char *) malloc(sizeof(char)*kBytes);
wcstombs(subhubname,subHubNameW->HubName, kBytes);
//we no longer need subHubNameW the information is now in subhubname.
if(subHubNameW !=NULL)
{
free(subHubNameW);
}
//Attempt to open a handle to driver for sub hub.
int SubhdnSize = strlen(subhubname) + sizeof("\\\\.\\");
char *subhubnamelength = (char *) malloc(sizeof(char) * SubhdnSize);
sprintf(subhubnamelength, "\\\\.\\%s", subhubname);
//We no longer need subhubname, so free it.
if(subhubname !=NULL) free(subhubname);
//Attempt to open a handle for enumerating ports on this hub.
HANDLE ExternalHub = CreateFile(subhubnamelength,
GENERIC_WRITE,
FILE_SHARE_WRITE,
NULL,
OPEN_EXISTING,
0,
NULL);
//we no longer need subhubnamelength, so free it if it is not NULL
if(subhubnamelength != NULL) free(subhubnamelength);
//Check and see if the handle was created successfully, if not, return false.
if(ExternalHub == INVALID_HANDLE_VALUE)
{
CloseHandle(ExternalHub);
MessageBox(NULL,"EXT handle fail ","TEST",MB_OK);
return false;
}
}
USB_NODE_CONNECTION_ATTRIBUTES *PortConnection =
(USB_NODE_CONNECTION_ATTRIBUTES *) malloc(sizeof(USB_NODE_CONNECTION_ATTRIBUTES));
PortConnection ->ConnectionIndex = Port;
ioctlSuccess = DeviceIoControl(ExternalHub,
IOCTL_USB_GET_NODE_CONNECTION_ATTRIBUTES,
PortConnection,
sizeof(USB_NODE_CONNECTION_ATTRIBUTES),
PortConnection,
sizeof(USB_NODE_CONNECTION_ATTRIBUTES),
&kBytes,
NULL);
if(!ioctlSuccess)
{
MessageBox(NULL,"DEVICE CONNECTION FAIL ","TEST",MB_OK);
return false;
}
if(PortConnection->ConnectionStatus !=DeviceConnected)
{
printf("The Connection Status Returns: %d",PortConnection->ConnectionStatus);
printf("\n");
return false;
}
Enumerating USB on Windows 7 will fail if UAC is enabled and application doesn't have enough privilege. Windows Embedded 7 may fail also on similar task.
There is also a possibility to enumerate through registry.
You can not enumerate anything possibly. Normally the usb device is either a bus device or a child of a bus device. Sense the bus device is not removable, it is automatically enumerated by acpi.sys the root device during boot, or if a bus device for some other device attached to the root node, then that device has to undergo some event that means it detects the device and then enumerates it, plug and play will automatically look for it's device type's driver and if installed will load it. If the usb say has 4 actual usb connectors, that are controlled from a central device, there could be a usb driver/mini driver pair. In that case the miniport driver is a child of the usb driver and is enumerated by it and attached to it. The miniport's job is to know the actual hardware, and irp's and all other io will come from it's parent. If there are several physical connections there may be additional child drivers. They all operate the hardware and there is a hot plug. When the firmware detects the installation of a usb device it communicates this to the miniport driver and on down the stack to the bus and ultimately it is processed by plug and play. Plug and play will then have the miniport driver enumerate it's device, but it needs to get a hardware id. Then it can find it's driver and load that, then the device is installed and ready for operation.
With out knowing the device stack it is not clear what device it is referring to. Keep in mind the driver stack may not reflect the actual hardware topology. There are also things called logical devices which do not represent any piece of hardware. Any of these would have to be taken into account and you need to know which device corresponds to the end of the node.
Also one little detail. I'm not as familiar with user mode api's and drivers as I am kernel, but it seems wsprintf second argument controls format of the output buffer from the input. It should be %[]%[] were [] is a symbol that represents the format. It would format the first character according to the first symbol then the second character with teh second symbol. Seems you are doing something different.
A final note, it appears the use of wsprintf() is now deprecated instead for other api's.
My main goal at the moment is to get detailed information about all of the local machine's Audio Endpoint Devices. That is the objects representing the audio peripherals. I want to be able to choose which device to record from based on some logic (or eventually allow the user to manually do so).
Here's what I've got so far. I'm pretty new to c++ so dealing with all of these abstract classes is getting a bit tricky so feel free to comment on code quality as well.
//Create vector of IMMDevices
UINT endpointCount = NULL;
(*pCollection).GetCount(&endpointCount);
std::vector<IMMDevice**> IMMDevicePP; //IMMDevice seems to contain all endpoint devices, so why have a collection here?
for (UINT i = 0; i < (endpointCount); i++)
{
IMMDevice* pp = NULL;
(*pCollection).Item(i, &pp);
IMMDevicePP.assign(1, &pp);
}
My more technical goal at present is to get objects that implement this interface: http://msdn.microsoft.com/en-us/library/windows/desktop/dd371414(v=vs.85).aspx
This is a type that is supposed to represent a single Audio Endpoint device whereas the IMMDevice seems to contain a collection of devices. However IMMEndpoint only contains a method called GetDataFlow so I'm unsure if that will help me. Again the goal is to easily select which endpoint device to record and stream audio from.
Any suggestions? Am I using the wrong API? This API definitely has good commands for the actual streaming and sampling of the audio but I'm a bit lost as to how to make sure I'm using the desired device.
WASAPI will allow you to do what you need so you're using the right API. You're mistaken about IMMDevice representing a collection of audio devices though, that is IMMDeviceCollection. IMMDevice represents a single audio device. By "device", WASAPI does't mean audio card as you might expect, rather it means a single input/output on such card. For example an audio card with analog in/out + digital out will show up as 3 IMMDevices each with it's own IMMEndpoint. I'm not sure what detailed info you're after but it seems to me IMMDevice will provide you with everything you need. Basically, you'll want to do something like this:
Create an IMMDeviceEnumerator
Call EnumAudioEndpoints specifying render, capture or both, to enumerate into an IMMDeviceCollection
Obtain individual IMMDevice instances from IMMDeviceCollection
Device name and description can be queried from IMMDevice using OpenPropertyStore (http://msdn.microsoft.com/en-us/library/windows/desktop/dd370812%28v=vs.85%29.aspx). Additional supported device details can be found here: http://msdn.microsoft.com/en-us/library/windows/desktop/dd370794%28v=vs.85%29.aspx.
IMMDevice instances obtained from IMMDeviceCollection will also be instances of IMMEndpoint, use QueryInterface to switch between the two. However, as you noted, this will only tell you if you've got your hands on a render or capture device. Much easier to only ask for what you want directly on EnumAudioEndpoints.
About code quality: use x->f() instead if (*x).f(), although it's technically the same thing the -> operator is the common way to call a function through an object pointer
Don't use vector::assign, apparently that replaces the contents of the entire vector on each call so you'll end up with a collection of size 1 regardless of the number of available devices. Use push_back instead.
After enumerating your IMMDevices as Sjoerd stated it is a must to retrieve the IPropertyStore
information for the device. From there you have to extract the PROPVARIANT object as such:
PROPERTYKEY key;
HRESULT keyResult = (*IMMDeviceProperties[i]).GetAt(p, &key);
then
PROPVARIANT propVari;
HRESULT propVariResult = (*IMMDeviceProperties[i]).GetValue(key, &propVari);
according to these documents:
http://msdn.microsoft.com/en-us/library/windows/desktop/bb761471(v=vs.85).aspx
http://msdn.microsoft.com/en-us/library/windows/desktop/aa380072(v=vs.85).aspx
And finally to navigate the large PROPVARIANT structure in order to get the friendly name of the audio endpoint device simply access the pwszVal member of the PROPVARIANT structure as illustrated here:
http://msdn.microsoft.com/en-us/library/windows/desktop/dd316594(v=vs.85).aspx
All about finding the right documentation!
in windows if you go
DeviceManager -> Select Device(Like a a keyboard) -> Go to details -> List of properties:
Device description
Hardware Ids
Compatible Ids
Device class
Device class guid
Driver key
ConfigFlags
... etc
Where can I find what each of these properties mean?
From MSDN documentation, you will find description for Device Instance IDs, and link to other descriptions. This link briefly describes some of them, which are listed as following:
Device IDs
A device ID is a vendor-defined identification string that is the most
specific ID that Setup uses to match a device to an INF file. A device
has only one device ID. A device ID has the same format as a hardware
ID. If an enumerator reports a list of hardware IDs for a device, the
device ID should be the first hardware ID in the list.
The PnP Manager uses the device ID to create a subkey for a device
under the registry key for the device's enumerator.
To obtain a device ID, use an IRP_MN_QUERY_ID request and set the
Parameters.QueryId.IdType field to BusQueryDeviceID.
Hardware IDs
A hardware ID is a vendor-defined identification string that Setup
uses to match a device to an INF file. In most cases, a device has
associated with it a list of hardware IDs. (However, there are
exceptions – see Identifiers for 1394 Devices). The first hardware ID
in the list should be the device ID, and the remaining IDs should be
listed in order of decreasing suitability.
A hardware ID has one of the following generic formats:
<enumerator>\<enumerator-specific-device-ID> This is the most common
format for individual PnP devices reported to the PnP Manager by a
single enumerator. New enumerators should use this format or the
following format.
*<enumerator-specific-ID> The asterisk indicates that the device is supported by more than one enumerator, such as ISAPNP and the BIOS.
<device-class-specific-ID> An existing device class that has
established its own naming convention might use a custom format. For
information on their hardware ID formats, see the hardware
specification for such buses. New enumerators should not use this
format. The number of characters of a hardware ID, excluding a
NULL-terminator, must be less than MAX_LENGTH_LEN. This constraint
applies to the sum of the lengths of all the fields and any “\” field
separators in a hardware ID. In addition, when an instance ID is
concatenated to a hardware ID to create a device instance ID, the
lengths of the hardware ID and the instance ID are further constrained
by the maximum possible length of a device instance ID.
To obtain the list of hardware IDs for a device, call
IoGetDeviceProperty with the DeviceProperty parameter set to
DevicePropertyHardwareID. The list of hardware IDs that this routine
retrieves is a REG_MULTI_SZ value. The maximum number of characters in
a hardware list, including a NULL terminator after each hardware ID
and a final NULL terminator, is REGSTR_VAL_MAX_HCID_LEN. The maximum
possible number of IDs in a list of hardware IDs is MAX_HCID_COUNT.
Examples of Hardware IDs
In the following, the first example is a generic identifier for a PnP
device, and the second example is an identifier for a PCI device:
root*PNP0F08
PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02
Compatible IDs
A compatible ID is a vendor-defined identification string that Setup
uses to match a device to an INF file. A device can have associated
with it a list of compatible IDs. The compatible IDs should be listed
in order of decreasing suitability. If Setup cannot locate an INF file
that matches one of a device's hardware IDs, it uses compatible IDs to
locate an INF file. Compatible IDs have the same format as hardware
IDs; however, compatible IDs are typically more generic than hardware
IDs.
If a vendor ships an INF file that specifies a compatible ID for a
driver node, the vendor should ensure that their INF file can support
all the hardware that matches the compatible ID. Because a match with
a compatible ID is not as strong as a match to a hardware ID, the PnP
Manager prompts the user for confirmation before processing the INF
file.
To obtain a list of compatible IDs for a device, call
IoGetDeviceProperty with the DeviceProperty parameter set to
DevicePropertyCompatibleID. The list of compatible IDs that this
routine retrieves is a REG_MULTI_SZ value. The maximum number of
characters in a compatible ID list, including a NULL terminator after
each compatible ID and a final NULL terminator, is
REGSTR_VAL_MAX_HCID_LEN. The maximum possible number of IDs in a list
of compatible IDs is MAX_HCID_COUNT.
Instance IDs
An instance ID is a device identification string that distinguishes a
device from other devices of the same type on a machine. An instance
ID contains serial number information, if supported by the underlying
bus, or some kind of location information. The string cannot contain
any "\" characters; otherwise, the generic format of the string is
bus-specific.
The number of characters of an instance ID, excluding a
NULL-terminator, must be less than MAX_LENGTH_LEN. In addition, when
an instance ID is concatenated to a device ID to create a device
instance ID, the lengths of the device ID and the instance ID are
further constrained by the maximum possible length of a device
instance ID.
The UniqueID member of the DEVICE_CAPABILITIES structure for a device
indicates if a bus-supplied instance ID is unique across the system,
as follows:
If UniqueID is FALSE, the bus-supplied instance ID for a device is
unique only to the device's bus. The PnP Manager modifies the
bus-supplied instance ID, and combines it with the corresponding
device ID, to create a device instance ID that is unique in the
system. If UniqueID is TRUE, the device instance ID, formed from the
bus-supplied device ID and instance ID, uniquely identifies a device
in the system. An instance ID is persistent across system boots.
To obtain the bus-supplied instance ID for a device, use an
IRP_MN_QUERY_ID request and set the Parameters.QueryId.IdType member
to BusQueryInstanceID.
Device Instance IDs
A device instance ID is a system-supplied device identification string
that uniquely identifies a device in the system. The PnP Manager
assigns a device instance ID to each device node in a system's device
tree.
The format of this string consists of an instance ID concatenated to a
device ID, as follows:
\ The number of characters of a
device instance ID, excluding a NULL-terminator, must be less than
MAX_LENGTH_LEN. This constraint applies to the sum of the lengths of
all the fields and “\” field separator between the device ID and
instance-specific-ID fields. A device instance ID is persistent across
system boots.
The following is an example of an instance ID ("1&08") concatenated to
a device ID for a PCI device:
PCI\VEN_1000&DEV_0001&SUBSYS_00000000&REV_02\1&08
Most of it is exposed via the "Setup API". Some data may be driver-specific, in which case you'd have to ask the driver writer. The ones you've listed are all standard Microsoft properties. E.g. the "device class" describes what type of device it is, eg. mouse/keyboard/storage/videocard/audio/...