/// <summary> /// Remaps an array of DisplayConfigModeInfo entries' adapter IDs from a previous session to /// the current Windows session's adapter IDs for a given display adapter. /// </summary> /// <param name="displayPreset">The Display preset containing old adapter ID information to remap</param> /// <param name="modeInfoArray">The array of mode info entries to modify with new adapter IDs.</param> /// <returns></returns> public bool RemapDisplayConfigModeInfoAdapterIds(DisplayPreset displayPreset, ref CCD.DisplayConfigModeInfo[] modeInfoArray) { bool allAdapterIdsRemappable = true; Dictionary <CCD.LUID, string> displayPresetAdapterIdToAdapterNameMap = new Dictionary <CCD.LUID, string>(); foreach (CCD.DisplayConfigAdapterName adapterName in displayPreset.AdapterNames) { displayPresetAdapterIdToAdapterNameMap.Add(adapterName.header.adapterId, adapterName.adapterDevicePath); } for (int i = 0; i < modeInfoArray.Length; i++) { CCD.LUID currentAdapterId; if (GetCurrentAdapterIdForDevice(displayPresetAdapterIdToAdapterNameMap[modeInfoArray[i].adapterId], out currentAdapterId)) { modeInfoArray[i].adapterId = currentAdapterId; } else { allAdapterIdsRemappable = false; break; } } return(allAdapterIdsRemappable); }
/// <summary> /// Applies the supplied display preset to the system and dynamically switches to it. Returns the /// display configuration in use before the new preset is applied as a DisplayPreset. This allows /// consumers to switch back to that last configuration if needed. Throws exception if failed to /// capture current configuration, or if failed to apply supplied preset configuration. /// </summary> /// <param name="displayPreset">The display configuration to switch to.</param> /// <returns>The display configuration in use before the supplied preset is applied. The name of /// the preset will be a GUID.</returns> static public DisplayPreset ReturnLastConfigAndApplyPreset(DisplayPreset displayPreset) { Guid guid = Guid.NewGuid(); DisplayPreset currentConfig = RecordCurrentConfiguration(guid.ToString()); ApplyPreset(displayPreset); return(currentConfig); }
/// <summary> /// Retrieves the DisplayPreset with a specific name. /// </summary> /// <param name="name">Name of the preset.</param> /// <returns>The DisplayPreset with the supplied name, null otherwise.</returns> public DisplayPreset GetPreset(string name) { DisplayPreset requestedPreset = null; if (mDisplayPresetDictionary.TryGetValue(name, out requestedPreset)) { return(requestedPreset); } else { return(null); } }
/// <summary> /// Adds a new DisplayPreset to the collection of display configuration presets. /// </summary> /// <param name="preset">The new DisplayPreset to add.</param> /// <returns>True if the DisplayPreset.Name is unique in the collection of presets. False otherwise.</returns> public bool TryAddDisplayPreset(DisplayPreset preset) { if (!mDisplayPresetDictionary.ContainsKey(preset.Name)) { mDisplayPresetDictionary.Add(preset.Name, preset); DisplayPresetCollectionChanged?.Invoke(DisplayPresetCollectionChangeType.PresetAdded, preset.Name); return(true); } else { return(false); } }
/// <summary> /// Applies the supplied display preset to the system, and dynamically changes it. /// Throws if the settings failed to be applied. /// </summary> /// <param name="displayPreset">The display preset to apply.</param> static public void ApplyPreset(DisplayPreset displayPreset) { AdapterIdMapper.DisplayPresetAdapterIdValidation validationResult = AdapterIdMapper.GetAdapterIdMapper().ValidateDisplayPresetAdapterIds(displayPreset); // Copy to working arrays so that if we need to remap we don't affect the original // display preset data. CCD.DisplayConfigPathInfo[] pathInfoArrayToApply = new CCD.DisplayConfigPathInfo[displayPreset.PathInfoArray.Length]; displayPreset.PathInfoArray.CopyTo(pathInfoArrayToApply, 0); CCD.DisplayConfigModeInfo[] modeInfoArrayToApply = new CCD.DisplayConfigModeInfo[displayPreset.ModeInfoArray.Length]; displayPreset.ModeInfoArray.CopyTo(modeInfoArrayToApply, 0); if (validationResult == AdapterIdMapper.DisplayPresetAdapterIdValidation.NeedAdapterIdRemap) { // TODO: The remap methods return a boolean, though it sorta doesn't matter for us since // we've already done validation. It's the right design that those methods return a // boolean, so maybe we should put an assert here? AdapterIdMapper.GetAdapterIdMapper().RemapDisplayConfigPathInfoAdapterIds(displayPreset, ref pathInfoArrayToApply); AdapterIdMapper.GetAdapterIdMapper().RemapDisplayConfigModeInfoAdapterIds(displayPreset, ref modeInfoArrayToApply); } else if (validationResult == AdapterIdMapper.DisplayPresetAdapterIdValidation.MissingAdapter) { // TODO: Have better interface for handling this case, cuz it's a real case where someone // upgrades a video card for example. throw new Exception("Missing adapter! Can't apply preset."); } else if (validationResult == AdapterIdMapper.DisplayPresetAdapterIdValidation.DisplayPresetMissingAdapterInformation) { // TODO: Handle this better, basically case where schema change throw new Exception("Display Preset is missing adapter name information."); } // Third validation result case is that all the adapter IDs are still valid and map correctly, so no action required. Win32Utilities.ThrowIfResultCodeNotSuccess( CCD.SetDisplayConfig( (uint)pathInfoArrayToApply.Length, pathInfoArrayToApply, (uint)modeInfoArrayToApply.Length, modeInfoArrayToApply, CCD.SdcFlags.Apply | CCD.SdcFlags.UseSuppliedDisplayConfig | CCD.SdcFlags.AllowChanges | CCD.SdcFlags.SaveToDatabase)); }
/// <summary> /// Checks the adapter IDs in use by a DisplayPreset and validates whether those IDs need to be /// remapped to new values before being applied, and whether all the display adapters the preset /// uses are still installed and available on the system. /// </summary> /// <param name="displayPreset">The preset to validate.</param> /// <returns>A DisplayPresetAdapterIdValidation value indicating the validation result.</returns> public DisplayPresetAdapterIdValidation ValidateDisplayPresetAdapterIds(DisplayPreset displayPreset) { bool allAdapterNamesAccountedFor = true; bool allAdapterIdsValid = true; if (displayPreset.AdapterNames != null) { foreach (CCD.DisplayConfigAdapterName adapterName in displayPreset.AdapterNames) { if (!mCurrentRuntimeDeviceAdapterNamesToAdapterIds.ContainsKey(adapterName.adapterDevicePath)) { allAdapterNamesAccountedFor = false; break; } if (!mCurrentRuntimeDeviceAdapterNamesToAdapterIds[adapterName.adapterDevicePath].Equals(adapterName.header.adapterId)) { allAdapterIdsValid = false; } } } if (displayPreset.AdapterNames == null) { return(DisplayPresetAdapterIdValidation.DisplayPresetMissingAdapterInformation); } else if (!allAdapterNamesAccountedFor) { return(DisplayPresetAdapterIdValidation.MissingAdapter); } else if (!allAdapterIdsValid) { return(DisplayPresetAdapterIdValidation.NeedAdapterIdRemap); } else { return(DisplayPresetAdapterIdValidation.AllValid); } }
/// <summary> /// Records the current display configuration as a display preset. /// </summary> /// <param name="presetName">Name of the preset to create</param> /// <returns>A DisplayPreset with the current configuration and supplied name. /// Throws exception if failed to get display configuration paths and modes.</returns> static public DisplayPreset RecordCurrentConfiguration(string presetName) { DisplayPreset displayPreset = null; const CCD.QueryDisplayFlags OnlyActivePathsFlag = CCD.QueryDisplayFlags.OnlyActivePaths; int numPathArrayElements; int numModeInfoArrayElements; // Get the buffer sizes needed to hold the active paths and the source/target mode table. Win32Utilities.ThrowIfResultCodeNotSuccess( CCD.GetDisplayConfigBufferSizes( OnlyActivePathsFlag, out numPathArrayElements, out numModeInfoArrayElements)); CCD.DisplayConfigPathInfo[] pathInfoArray = new CCD.DisplayConfigPathInfo[numPathArrayElements]; CCD.DisplayConfigModeInfo[] modeInfoArray = new CCD.DisplayConfigModeInfo[numModeInfoArrayElements]; // Get the active paths and their associated source/target modes. Win32Utilities.ThrowIfResultCodeNotSuccess( CCD.QueryDisplayConfig( OnlyActivePathsFlag, ref numPathArrayElements, pathInfoArray, ref numModeInfoArrayElements, modeInfoArray, IntPtr.Zero)); displayPreset = new DisplayPreset(presetName); displayPreset.PathInfoArray = pathInfoArray; displayPreset.ModeInfoArray = modeInfoArray; // Save the Target Device Name structs to log the monitor output devices. This isn't // actually used for anything but makes the XML output more readable. CCD.DisplayConfigTargetDeviceName[] targetDeviceNameArray = new CCD.DisplayConfigTargetDeviceName[pathInfoArray.Length]; for (int i = 0; i < pathInfoArray.Length; i++) { targetDeviceNameArray[i] = GetTargetDeviceName(pathInfoArray[i].targetInfo.adapterId, pathInfoArray[i].targetInfo.id); } displayPreset.TargetDeviceNames = targetDeviceNameArray; // Save the Adapter Name structs. The adapter ID values may change on reboot, as they // appear to simply be a logical, run-time value rather than a persistent identifier. // So we save the Adapter Name structs to log a device name that the adapter ID maps to. // We can use this to update the adapter IDs we save in our presets such that the presets // will still be usable after a reboot. Otherwise, when the machine is rebooted and the // user tries to apply a saved preset, the API call will fail because the adapter ID has // changed. Dictionary <CCD.LUID, CCD.DisplayConfigAdapterName> adapterIdToAdapterName = new Dictionary <CCD.LUID, CCD.DisplayConfigAdapterName>(); // Find all the unique adapter IDs used in the active paths and capture the display adapter // device names that those IDs map to. foreach (CCD.DisplayConfigPathInfo pathInfo in pathInfoArray) { if (!adapterIdToAdapterName.ContainsKey(pathInfo.sourceInfo.adapterId)) { CCD.DisplayConfigAdapterName adapterName = new CCD.DisplayConfigAdapterName(); adapterName.header.adapterId = pathInfo.sourceInfo.adapterId; adapterName.header.size = (uint)System.Runtime.InteropServices.Marshal.SizeOf(adapterName); adapterName.header.type = CCD.DisplayConfigDeviceInfoType.GetAdapterName; Win32Utilities.ThrowIfResultCodeNotSuccess(CCD.DisplayConfigGetDeviceInfo(ref adapterName)); adapterIdToAdapterName.Add(adapterName.header.adapterId, adapterName); } } displayPreset.AdapterNames = adapterIdToAdapterName.Values.ToArray(); return(displayPreset); }