I have a path that looks like this:
\\?\DISPLAY#IVM1A3E#5&1778d8b3&1&UID260#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
From that I would like to get a HMONITOR
handle.
Using WinObj I can see under GLOBAL?? that it's a Symbolic link to some \Device\<number>
.
How would I go about doing something like that?
Edit:
The path can be split in three parts. First the \\?\
prefix, then second DISPLAY#IVM1A3E#5&1778d8b3&1&UID260
if the #
are replaced by \
it is the same as the device instance ID. Thirdly {e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
is the GUID for Monitor devices.
With the device instance ID I can walk the device tree using CM_Get_Child
and CM_Get_Sibling
, checking the device names with CM_Get_Device_ID
. But that only gives me a DEVINST
not a HMONITOR
.
CodePudding user response:
You can use Connecting and configuring displays (CCD) API, especially the The QueryDisplayConfig function which retrieves information about all possible display paths for all display devices, or views, in the current setting.
With the following code, you'll get the correspondance between a Device Path and a Monitor (and its handle).
#include <Windows.h>
#include <stdio.h>
#include <vector>
#include <tuple>
#include <string>
int main()
{
// get all paths
UINT pathCount;
UINT modeCount;
if (GetDisplayConfigBufferSizes(QDC_ONLY_ACTIVE_PATHS, &pathCount, &modeCount))
return 0;
std::vector<DISPLAYCONFIG_PATH_INFO> paths(pathCount);
std::vector<DISPLAYCONFIG_MODE_INFO> modes(modeCount);
if (QueryDisplayConfig(QDC_ONLY_ACTIVE_PATHS, &pathCount, paths.data(), &modeCount, modes.data(), nullptr))
return 0;
// enum all monitors => (handle, device name)>
std::vector<std::tuple<HMONITOR, std::wstring>> monitors;
EnumDisplayMonitors(nullptr, nullptr, [](HMONITOR hmon, HDC hdc, LPRECT rc, LPARAM lp)
{
MONITORINFOEX mi = {};
mi.cbSize = sizeof(MONITORINFOEX);
GetMonitorInfo(hmon, &mi);
auto monitors = (std::vector<std::tuple<HMONITOR, std::wstring>>*)lp;
monitors->push_back({ hmon, mi.szDevice });
return TRUE;
}, (LPARAM)&monitors);
// for each path, get GDI device name and compare with monitor device name
for (UINT i = 0; i < pathCount; i )
{
DISPLAYCONFIG_TARGET_DEVICE_NAME deviceName = {};
deviceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_TARGET_NAME;
deviceName.header.size = sizeof(DISPLAYCONFIG_TARGET_DEVICE_NAME);
deviceName.header.adapterId = paths[i].targetInfo.adapterId;
deviceName.header.id = paths[i].targetInfo.id;
if (DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)&deviceName))
continue;
wprintf(L"Monitor Friendly Name : %s\n", deviceName.monitorFriendlyDeviceName);
wprintf(L"Monitor Device Path : %s\n", deviceName.monitorDevicePath);
DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceName = {};
sourceName.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME;
sourceName.header.size = sizeof(DISPLAYCONFIG_SOURCE_DEVICE_NAME);
sourceName.header.adapterId = paths[i].targetInfo.adapterId;
sourceName.header.id = paths[i].sourceInfo.id;
if (DisplayConfigGetDeviceInfo((DISPLAYCONFIG_DEVICE_INFO_HEADER*)&sourceName))
continue;
wprintf(L"GDI Device Name : %s\n", sourceName.viewGdiDeviceName);
// find the monitor with this device name
auto mon = std::find_if(monitors.begin(), monitors.end(), [&sourceName](std::tuple<HMONITOR, std::wstring> t)
{
return !std::get<1>(t).compare(sourceName.viewGdiDeviceName);
});
wprintf(L"Monitor Handle : %p\n", std::get<0>(*mon));
wprintf(L"\n");
}
return 0;
}
On my PC (with 2 monitors), it will output something like this:
Monitor Friendly Name : C27HG7x
Monitor Device Path : \\?\DISPLAY#SAM0E16#7&307b5912&0&UID1024#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
GDI Device Name : \\.\DISPLAY2
Monitor Handle : 0000000000160045
Monitor Friendly Name : DELL U2715H
Monitor Device Path : \\?\DISPLAY#DELD069#7&307b5912&0&UID1028#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}
GDI Device Name : \\.\DISPLAY1
Monitor Handle : 0000000000020083
Note you can also get the same information using WinRT's DisplayManager class => DisplayView class => Paths property, DisplayPath class, => Target property => TryGetMonitor function