Windows Multi-monitor: how to determine if an object is physically connected to the source when the target is available but not active?

I want to turn on a separate disabled monitor based on information obtained from DISPLAYCONFIG_TARGET_DEVICE_NAME and / or DISPLAYCONFIG_PATH_TARGET_INFO . To really turn this monitor on, all I have to do is successfully match this with the map name with the devicename name to enable, for example. \\.\DISPLAY1 . But I cannot find any general way to make this definition without prior specialized knowledge. If I could relate it to the current match DISPLAYCONFIG_PATH_SOURCE_INFO .

QueryDisplayConfig returns all possible combination of source and target on my machine, even pairing monitors with sources with which they are not actually connected. I have 4 ports and 3 monitors, so I get 12 combinations with targetAvailable in the target, because it repeats each target with corresponding and irrelevant sources. Since I get source + target combinations that are not real, I can’t determine which source is really physically connected to which target if the source + target pair is no longer active, for example. DISPLAYCONFIG_PATH_INFO::flags has DISPLAYCONFIG_PATH_ACTIVE . Then I can easily tell what is happening.

In principle, as long as the target is used / attached to the desktop, there is no problem; There are many ways to associate the source with which it is associated. But in this case, the target is disabled, but connected (which means on the control panel, the monitor is available, but excluded from the setting of multiple monitors). The API shows the disconnected device without problems, but I can’t determine which port it is connected to or which devicename to include. Since the monitor is turned off, EnumDisplayMonitors useless.

Obviously, EnumDisplayDevices will provide me with IDevNum and deviceName all possible options to enable, but nothing in this API will connect me to DISPLAYCONFIG_TARGET_DEVICE_NAME , since I cannot associate sources with their related goals as described above. Therefore, my only choice is to turn on the monitor blindly, not being able to guarantee that I will resolve the correct one that matches my target structures.

Does anyone know these APIs well enough to help? My hunch is that I would need to use something other than the API that I was trying to use, since I looked at all my potential outputs in the debugger with a thin comb, but I could miss something. Maybe something that is stored in the registry, I can use to connect the points? I would be willing to consider using an undocumented api or framework if necessary.

thanks

+7
c ++ windows winapi multiple-monitors nvidia
source share
1 answer

I figured this out and hopefully this answer helps someone. Ironically, in my question, I kind of guessed what the answer would be without realizing it! I said

My only choice is to blindly turn on the monitor.

Which turns out to be not bad at all, because SetDisplayConfig has a flag called SDC_VALIDATE , which simply checks if the configuration is normal and does not affect the user if I name him. Therefore, in order to find out which source is connected to which target, all I need to do is try to turn on each source-pair pair that contains my target until you work. A real pair + source-source will succeed, while fake ones will be returned ERROR_GEN_FAILURE . This is a pretty dumb and long method, and as far as I know, this scenario is completely undocumented, but it makes some intuitive sense in some way: just identify the source + target pair that you can include and the source you want.

Here is a sample code for it:

 LUID& targetAdapter; // the LUID of the target we want to find the source for ULONG targetId; // the id of the target we want to find the source for DISPLAYCONFIG_PATH_SOURCE_INFO* pSource = NULL; // will contain the answer DISPLAYCONFIG_PATH_INFO* pPathInfoArray = NULL; DISPLAYCONFIG_MODE_INFO* pModeInfoArray = NULL; UINT32 numPathArrayElements; UINT32 numModeInfoArrayElements; // First, grab the system current configuration for (UINT32 tryBufferSize = 32;; tryBufferSize <<= 1) { pPathInfoArray = new DISPLAYCONFIG_PATH_INFO[tryBufferSize]; pModeInfoArray = new DISPLAYCONFIG_MODE_INFO[tryBufferSize]; numPathArrayElements = numModeInfoArrayElements = tryBufferSize; ULONG rc = QueryDisplayConfig( QDC_ALL_PATHS, &numPathArrayElements, pPathInfoArray, &numModeInfoArrayElements, pModeInfoArray, NULL); if (rc == ERROR_SUCCESS) break; if (rc != ERROR_INSUFFICIENT_BUFFER || tryBufferSize > 1024) return; // failure } // Narrow down the source that truly connected to our target. // Try "test" enabling one <source>+<ourtarget> pair at a time until we have the right one for (int tryEnable = 0;; ++tryEnable) { DISPLAYCONFIG_PATH_INFO* pCurrentPath = NULL; for (UINT32 i = 0, j = 0; i < numPathArrayElements; ++i) { if (pPathInfoArray[i].targetInfo.targetAvailable && !memcmp(&pPathInfoArray[i].targetInfo.adapterId, &targetAdapter, sizeof (LUID)) && pPathInfoArray[i].targetInfo.id == targetId) { pPathInfoArray[i].targetInfo.statusFlags |= DISPLAYCONFIG_TARGET_IN_USE; if (j++ == tryEnable) { pCurrentPath = &pPathInfoArray[i]; if (pCurrentPath->flags & DISPLAYCONFIG_PATH_ACTIVE) { // trivial early out... user already had this enabled, therefore we know this is the right source. pSource = &pCurrentPath->sourceInfo; break; } // try to activate this particular source pCurrentPath->flags |= DISPLAYCONFIG_PATH_ACTIVE; pCurrentPath->sourceInfo.statusFlags |= DISPLAYCONFIG_SOURCE_IN_USE; } } } if (!pCurrentPath) return; // failure. tried everything, apparently no source is connected to our target LONG rc = SetDisplayConfig( numPathArrayElements, pPathInfoArray, numModeInfoArrayElements, pModeInfoArray, SDC_VALIDATE | SDC_USE_SUPPLIED_DISPLAY_CONFIG | SDC_ALLOW_CHANGES); if (rc != ERROR_SUCCESS) { // it didn't work, undo trying to activate this source pCurrentPath->flags &= ~DISPLAYCONFIG_PATH_ACTIVE; pCurrentPath->sourceInfo.statusFlags &= DISPLAYCONFIG_SOURCE_IN_USE; } else { pSource = &pCurrentPath->sourceInfo; break; // success! } } //Note: pSource is pointing to the source relevant to the relevant source now! //You just need to copy off whatever you need. 

This is the answer to this question, but I decided to publish some other related discoveries. So what can you do when you know the source of the goal you are interested in?

One thing you can do is find the Gdi device name for the source, for example. \\.\DISPLAY1 using DisplayConfigGetDeviceInfo .

 DISPLAYCONFIG_SOURCE_DEVICE_NAME sourceInfo; ZeroMemory(&sourceInfo, sizeof(sourceInfo)); sourceInfo.header.size = sizeof(queryInfo.source); sourceInfo.header.adapterId = pSource->adapterId; sourceInfo.header.id = pSource->id; sourceInfo.header.type = DISPLAYCONFIG_DEVICE_INFO_GET_SOURCE_NAME; ULONG rc = DisplayConfigGetDeviceInfo(&sourceInfo.header); if (rc == ERROR_SUCCESS) cout << queryInfo.source.viewGdiDeviceName; // eg \\.\DISPLAY1 

Note that DisplayConfigGetDeviceInfo can provide you with a friendly name for the purpose. If you scan all targets for one that matches your connected display, for example. "PanasonicTV0" or "SyncMaster" or something else, you can use this target as an input for the above method. This gives you enough code to combine for the entire end-to-end implementation for EnableDisplay("SyncMaster") or somesuch.

Since now you can get GdiDeviceName , you can ChangeDisplaySettingsEx to make it the main monitor. One of the secrets to using CDS_SET_PRIMARY is that the primary monitor must have DM_POSITION 0,0, and you need to update ALL monitors so that they are adjacent to the new adjusted position. I have a sample code for this:

 HRESULT ChangePrimaryMonitor(wstring gdiDeviceName) { HRESULT hr; wstring lastPrimaryDisplay = L""; bool shouldRefresh = false; DEVMODE newPrimaryDeviceMode; newPrimaryDeviceMode.dmSize = sizeof(newPrimaryDeviceMode); if (!EnumDisplaySettings(gdiDeviceName.c_str(), ENUM_CURRENT_SETTINGS, &newPrimaryDeviceMode)) { hr = E_FAIL; goto Out; } for (int i = 0;; ++i) { ULONG flags = CDS_UPDATEREGISTRY | CDS_NORESET; DISPLAY_DEVICE device; device.cb = sizeof(device); if (!EnumDisplayDevices(NULL, i, &device, EDD_GET_DEVICE_INTERFACE_NAME)) break; if ((device.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP) == 0) continue; if (!wcscmp(device.DeviceName, gdiDeviceName.c_str())) flags |= CDS_SET_PRIMARY; DEVMODE deviceMode; newPrimaryDeviceMode.dmSize = sizeof(deviceMode); if (!EnumDisplaySettings(device.DeviceName, ENUM_CURRENT_SETTINGS, &deviceMode)) { hr = E_FAIL; goto Out; } deviceMode.dmPosition.x -= newPrimaryDeviceMode.dmPosition.x; deviceMode.dmPosition.y -= newPrimaryDeviceMode.dmPosition.y; deviceMode.dmFields |= DM_POSITION; LONG rc = ChangeDisplaySettingsEx(device.DeviceName, &deviceMode, NULL, flags, NULL); if (rc != DISP_CHANGE_SUCCESSFUL) { hr = E_FAIL; goto Out; } shouldRefresh = true; } hr = S_OK; Out: if (shouldRefresh) ChangeDisplaySettingsEx(NULL, NULL, NULL, CDS_RESET, NULL); return hr; } 
+10
source share

All Articles