6

I need help in associating PHYSICAL_MONITOR which i get from GetPhysicalMonitorsFromHMONITOR with monitors DISPLAY_DEVICE.DeviceID (e.g. "\?\DISPLAY#GSM59AB#5&932a802&1&UID261#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}"), which is from EnumDisplayDevices used with flag EDD_GET_DEVICE_INTERFACE_NAME, or somehow get PHYSICAL_MONITOR from DISPLAY_DEVICE.DeviceID or vice versa.

I need both of them associated because:

  1. HANDLE PHYSICAL_MONITOR.hPhysicalMonitor will be used for lowlevelmonitorconfigurationapi because i need to send commands to monitors.

  2. DISPLAY_DEVICE.DeviceID is used to get EDID structure from registry (first 128 bytes are enough for me , need only manufacturer code and model) using SetupAPI

1 and 2 are done, the problem is associating id with physical monitor. Also it is possible to get all monitors EDIDs from registry using only SetupAPI, but in this case it is impossible to get physical monitors HANDLEs.

Same question on MSDN, not solved((

Also i noticed one thing: This code enumerates all monitors:

DWORD DispNum = 0;
DISPLAY_DEVICE DisplayDevice;
// Initialize DisplayDevice.
ZeroMemory(&DisplayDevice, sizeof(DisplayDevice));
DisplayDevice.cb = sizeof(DisplayDevice);

while ((EnumDisplayDevices(NULL, DispNum, &DisplayDevice, 0)))
{
    std::wstring deviceName = DisplayDevice.DeviceName;
    DISPLAY_DEVICE DisplayDeviceM;
    ZeroMemory(&DisplayDeviceM, sizeof(DisplayDeviceM));
    DisplayDeviceM.cb = sizeof(DisplayDeviceM);
    int monitorIndex = 0;
    while (EnumDisplayDevices(deviceName.c_str(), monitorIndex, &DisplayDeviceM, EDD_GET_DEVICE_INTERFACE_NAME))
    {
        std::wstring monitorID = DisplayDeviceM.DeviceID;
        ++monitorIndex;
    }
    DispNum++;
}

In the same order as this one:

BOOL CALLBACK EnumProc2(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, LPARAM dwData)
{
LPPHYSICAL_MONITOR pMons = NULL;
DWORD i, mcnt;

MONITORINFOEX mi;
ZeroMemory(&mi, sizeof(mi));
mi.cbSize = sizeof(mi);

GetMonitorInfo(hMonitor, &mi);

DISPLAY_DEVICE dd;
ZeroMemory(&dd, sizeof(dd));
dd.cb = sizeof(dd);
EnumDisplayDevices(mi.szDevice, 0, &dd, EDD_GET_DEVICE_INTERFACE_NAME);

monitorModelMnufac MdlManuf = findMonitorModelManufactFromEDID(dd.DeviceID);

if (!GetNumberOfPhysicalMonitorsFromHMONITOR(hMonitor, &mcnt)) return TRUE;
pMons = (LPPHYSICAL_MONITOR)malloc(mcnt * sizeof(PHYSICAL_MONITOR));
if (GetPhysicalMonitorsFromHMONITOR(hMonitor, mcnt, pMons))
    for (i = 0; i < mcnt; i++)
    {
        AddToMonHandles(pMons[i].hPhysicalMonitor, MdlManuf);
    }
free(pMons);
return TRUE;
}

And physical monitors HANDLEs are 0,1,2 and so on, so i can make HANDLEs from "monitorIndex" but i'm not sure if doing like that is secure.

I also looked in registry to find physical monitors HANDLEs, but nothing there.

Also found function which is helpful VideoPortDDCMonitorHelper but as i googled, it need to be used in driver/filter and can't be used from a simple executable.

Also tried to reverse windows dlls, all calls seems to be made from WIN32U.dll and Ghidra doesn't want to decompile it, or i'm just noob at that.

Please help me guys :)

PuFF1k
  • 303
  • 4
  • 13
  • `EnumDisplayDevices` shows the adapters and `EnumDisplayMonitors` shows the monitors. I have checked their parameters and found no key similarities between them, except for the display name, but this does not guarantee uniqueness. And I used them to enumerate the monitors separately, and the result shows that their order is the same. This can only be used as a reference. – Strive Sun Jul 30 '20 at 07:16
  • @StriveSun-MSFT Thank you a lot, this seems to be the only solution, as i also found nothing. – PuFF1k Jul 31 '20 at 02:11
  • I will consult with relevant internal engineers to find out if there is any way to achieve this, and update here. – Strive Sun Jul 31 '20 at 02:18
  • hi, `EnumDisplayDevices` and `EnumDisplayMonitors` are two unrelated APIs. As I said in the first comment, one is the corresponding adapter and the other is the corresponding monitor. Simply, the `DeviceId` is allocated by the graphics card, and `PHYSICAL_MONITOR` is allocated by the monitor. Can you tell me what you want to do with SetupAPI and what kind of work you want to accomplish in the end? – Strive Sun Aug 03 '20 at 07:43
  • In addition, please review [About Multiple Display Monitors](https://learn.microsoft.com/en-us/windows/win32/gdi/about-multiple-display-monitors). – Strive Sun Aug 03 '20 at 07:50
  • 1
    @StriveSun-MSFT So i need to read EDID data of each physical monitor but i can't do it owning only physical monitors HANDLEs. SetupAPI allows me to get path in registry to get EDID (it is there because windows pulled it out of monitor when it was connected), but to get path to registry i need deviceID which i can't get from physical monitors HANDLE. In the end what i want to accomplish is get from EDID monitor name and manufacturer and based on that information, use SetVCPFeature function for each monitor separately. – PuFF1k Aug 03 '20 at 14:27
  • 1
    It seems people in Monitorian (you can find it on github) projet have a solution, I'm not sure since it's complicated. – Enzojz Jun 22 '22 at 19:08

3 Answers3

1

I've looked for a connection between EnumDisplayDevices and GetPhysicalMonitorsFromHMONITOR for somewhat similar purposes; I need a unique ID for each monitor, plus the ability to set a VCP value.

Some related posts don't give me confidence there's a simple answer.

I'm not sure if it's of any help, but I found ControlMyMonitor to be useful.

Running:

controlmymonitor.exe /smonitors

Produces info for each monitor:

Monitor Device Name: "\\.\DISPLAY1\Monitor0"
Monitor Name: "VX4380 SERIES"
Serial Number: ""
Adapter Name: "NVIDIA GeForce GTX 760"
Monitor ID: "MONITOR\VSC5B34\{4d36e96e-e325-11ce-bfc1-08002be10318}\0004"

And you can set VCP values easily. For example, tell the monitor to change to HDMI 1:

controlmymonitor.exe /SetValue "\\.\DISPLAY1\Monitor0" 60 17

Also, if you call EnumDisplayMonitors in order to get PHYSICAL_MONITOR.hPhysicalMonitor, then you pass that handle to CapabilitiesRequestAndCapabilitiesReply, it produces this output:

prot(monitor)
type(LCD)
model(VX4380)
cmds(01 02 03 07 0C E3 F3)
vcp(02 04 05 08 0B 0C 10 12 14(01 08 06 05 04 0B) 16 18 1A 52 60(0F 10 11 12) 62 87 8D(01 02) A5 AC AE B2 B6 C6 C8 CA CC(01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 12 14 16 17 1A 1E 24) D6(01 04 05) DC(00 01 02 03 05 08 1F) DF E0(00 01 02 03 14) EC(01 02 03) F6 F7(42 FF) FA(00 01 02) FB FC FD FE(00 01 02 04) FF)
mswhql(1)
asset_eep(40)
mccs_ver(2.2)

Which happens to contain the model number.

Hopefully some information produced by the above commands helps you toward your goal.

Good luck!

Fidel
  • 7,027
  • 11
  • 57
  • 81
0

I found some useful information, I hope it will help you.

...
while ((EnumDisplayDevices(NULL, DispNum, &DisplayDevice, 0)))
    {
        std::wstring deviceName = DisplayDevice.DeviceName;
        DISPLAY_DEVICE DisplayDeviceM;
        ZeroMemory(&DisplayDeviceM, sizeof(DisplayDeviceM));
        DisplayDeviceM.cb = sizeof(DisplayDeviceM);
        int monitorIndex = 0;
        while (EnumDisplayDevices(deviceName.c_str(), monitorIndex, &DisplayDeviceM, EDD_GET_DEVICE_INTERFACE_NAME))
        {
            wcout << "deviceName:" << deviceName << endl;
            std::wstring monitorID = DisplayDeviceM.DeviceID;
            wcout <<"monitorID :"<< monitorID<< endl;
            ++monitorIndex;            
        }
        DispNum++;
    }
...

Output:

deviceName: \\.\DISPLAY1

Then use EnumDisplayMonitors to get HMONITOR and use it as a parameter of GetMonitorInfo.

static BOOL CALLBACK MonitorEnum(HMONITOR hMon, HDC hdc, LPRECT lprcMonitor, LPARAM pData)
{
    cout << "hmonitor:" << hMon << endl;

    MONITORINFOEX mi;
    mi.cbSize = sizeof(mi);

    GetMonitorInfo(hMon, (LPMONITORINFO)&mi);

    wcout << "deviceName: "<<mi.szDevice << endl;

    DWORD cPhysicalMonitors;
    BOOL bSuccess = GetNumberOfPhysicalMonitorsFromHMONITOR(hMon, &cPhysicalMonitors);
    cout << "GetNumber: " << bSuccess << ", number of physical monitors: " << cPhysicalMonitors << endl;

    LPPHYSICAL_MONITOR pPhysicalMonitors = (LPPHYSICAL_MONITOR)malloc(cPhysicalMonitors * sizeof(PHYSICAL_MONITOR));
    bSuccess = GetPhysicalMonitorsFromHMONITOR(hMon, cPhysicalMonitors, pPhysicalMonitors);
    cout << "GetPhysicalMonitor: " << bSuccess << endl
        << "Handle: " << pPhysicalMonitors->hPhysicalMonitor << endl
        << "Description: ";
    wcout << (WCHAR*)(pPhysicalMonitors->szPhysicalMonitorDescription) << endl;;

    D(pPhysicalMonitors->hPhysicalMonitor);

    DestroyPhysicalMonitors(cPhysicalMonitors, pPhysicalMonitors);
    free(pPhysicalMonitors);

    cout << "---------------------------------------" << endl;

    return TRUE;
}

...

EnumDisplayMonitors(0, 0, MonitorEnum, NULL);

Output:

deviceName: \\.\DISPLAY1

If the two outputs are the same, they correspond to the same monitor. Finally, we can use the obtained HMONITOR as the parameter of GetPhysicalMonitorsFromHMONITOR, so that we can get the hPhysicalMonitor we need.

Strive Sun
  • 5,988
  • 1
  • 9
  • 26
  • Thank you for that, but an HMONITOR or an \\.\DISPLAYx can contain more than one physical monitor(when cloned), i can`t just pick first of them i need to distinguish them and get path (e.g. "\?\DISPLAY#GSM59AB#5&932a802&1&UID261#{e6f07b5f-ee97-4a90-b076-33f57bf4eaa7}") for each of them – PuFF1k Aug 04 '20 at 13:16
  • @PuFF1k If an HMONITOR has multiple physical monitors, will their deviceid be different? – Strive Sun Aug 05 '20 at 05:53
  • 1
    if you are talking about "\\.\DISPLAYx" than no it will be the same for both. This is why "EnumDisplayDevices(deviceName.c_str(), monitorIndex, &DisplayDeviceM, EDD_GET_DEVICE_INTERFACE_NAME)" for one "\\.\DISPLAYx" can repeat multiple times. – PuFF1k Aug 05 '20 at 06:00
0

I have a hard time with this stuff too, at the moment.

I try to find out, which is the PHYSICAL_MONITOR of the primary display device.

I want to change the input source of the primary display, or better said the actual „GetDesktopWindow()“ monitor. But therefore i need to know which one it is, since the same HMONITOR is used for multiple PHYSICAL_MONITORs.

I slowly come to the conclusion this is simply not possible at all. :(

MBODM
  • 141
  • 1
  • 1
  • 11