How to associate PHYSICAL_MONITOR with monitor DeviceID

1.6k views Asked by At

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 :)

3

There are 3 answers

0
Fidel On

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!

3
Strive Sun On

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.

0
MBODM On

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. :(