Я хочу включить определенный отключенный монитор, основываясь на информации, полученной из DISPLAYCONFIG_TARGET_DEVICE_NAME
и / или DISPLAYCONFIG_PATH_TARGET_INFO
, Чтобы фактически включить этот монитор, все, что мне нужно сделать, это успешно сопоставить его с согласование имя устройства, которое нужно включить, например, \\.\DISPLAY1
, Но я не могу найти какой-либо общий способ сделать это определение без предварительных специальных знаний. Если бы я только мог связать это с действительно актуально согласование DISPLAYCONFIG_PATH_SOURCE_INFO
,
QueryDisplayConfig
возвращает все возможные комбинации источника и цели на моей машине, даже связывая мониторы с источниками, к которым они фактически не подключены. У меня есть 4 порта и 3 монитора, поэтому я получаю 12 комбинаций, которые имеют targetAvailable
в цели, потому что он повторяет каждую цель с соответствующими и несущественными источниками. Поскольку я получаю исходные + целевые комбинации, которые не являются реальными, я не могу определить, какой источник действительно физически связаны с какой целью, если пара источник + цель уже активен, например DISPLAYCONFIG_PATH_INFO::flags
имеет DISPLAYCONFIG_PATH_ACTIVE
, Тогда я легко могу сказать, что происходит.
По сути, пока цель используется / прикреплена к рабочему столу, никаких проблем не возникает; Есть множество способов определить, с каким источником он связан. Но в этом сценарии цель отключена, но подключена (то есть на панели управления монитор доступен, но исключен из настройки нескольких мониторов). API показывает отключенное устройство без проблем, но я не могу определить к какому порту он подключен или какое имя устройства включить. Поскольку монитор отключен, EnumDisplayMonitors
бесполезно.
очевидно EnumDisplayDevices
даст мне IDevNum
а также deviceName
любой возможной вещи, чтобы включить, но ничто в этом API не соединит меня с DISPLAYCONFIG_TARGET_DEVICE_NAME
, поскольку я не могу связать источники с их подключенными целями, как описано выше. Так что мой единственный выбор — включить монитор вслепую, и я не могу гарантировать, что я включаю правильный монитор, соответствующий моим целевым структурам.
Кто-нибудь знает эти API достаточно хорошо, чтобы оказать помощь? Я догадываюсь, что мне нужно будет использовать что-то большее, чем те API, которые я пытался использовать, так как я просмотрел все их потенциальные результаты в отладчике с помощью зубного гребня, но я мог что-то упустить. Может быть, что-то хранится в реестре, который я могу использовать для подключения точек? Я был бы готов рассмотреть возможность использования недокументированного API или структуры, если это необходимо.
Спасибо
Я понял это, и, надеюсь, этот ответ кому-нибудь поможет. По иронии судьбы, в своем вопросе я как-то догадался, каким будет ответ, даже не осознавая этого! Я сказал
Похоже, мой единственный выбор — включить монитор вслепую.
Который оказывается совсем не так уж плох, потому что SetDisplayConfig имеет флаг под названием SDC_VALIDATE
, который просто проверяет, в порядке ли конфиг, и не влияет на пользователя, если я его называю. Таким образом, чтобы выяснить, какой источник связан с какой целью, все, что мне нужно сделать, это попытаться включить каждую пару источник + цель, которая содержит мою цель, пока одна из них не сработает. реальный пара источник + цель будет успешной, а фальшивые вернутся ERROR_GEN_FAILURE
, Это довольно тупой и длительный метод, и, насколько мне известно, этот сценарий полностью недокументирован, но он в некотором смысле имеет некоторый интуитивный смысл: просто определите пару источник + цель, которую можно включить, и это тот источник, который вам нужен.
Вот пример кода для этого:
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's 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's 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.
Это ответ на этот вопрос, но я решил опубликовать и некоторые другие связанные открытия. Итак, что вы можете сделать, когда узнаете источник интересующей вас цели?
Одна вещь, которую вы можете сделать, это найти имя устройства Gdi для источника, например, \\.\DISPLAY1
, с помощью 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; // e.g. \\.\DISPLAY1
Обратите внимание, что DisplayConfigGetDeviceInfo
может дать вам дружеское имя для цель тоже. Если вы отсканировали все цели на предмет соответствия вашему приложенному дисплею, например, «PanasonicTV0» или «SyncMaster» или что-то еще, вы можете использовать эту цель в качестве входа для вышеуказанного метода. Это дает вам достаточно, чтобы связать код для всей сквозной реализации для EnableDisplay("SyncMaster")
или что-то подобное.
Так как теперь вы можете получить GdiDeviceName
, вы могли бы ChangeDisplaySettingsEx
сделать его основным монитором тоже. Один из секретов применения CDS_SET_PRIMARY
правильно, что основной монитор должен иметь DM_POSITION
0,0, и вы должны обновить ВСЕ мониторы, чтобы они были смежными с новой исправленной позицией. У меня есть пример кода для этого тоже:
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;
}
Других решений пока нет …