Fixed error when returning from Mosaic layouts in WIndows 10

There was an error that occurred in NvAPI_SetDisplayConfig when attempting to go from an NVIDIA Surround to a non-NVIDIA Surround setup on a machine that has multiple video cards installed in it. This is due to the fact that the NVIDIA driver only sees the displays connected to NVIDIA video adapters.

The previous DM logic tried to set the DisplayConfig after switching to or from a Surround display profile, but this would fail when returning from a a Surround display profile on Win 10 devices. It appears that the NVIDIA driver AUTOMATICALLY disables all additional displays in windows when it returns from a Surround profile. This simple fact means that the DisplayConfig won't be applied properly, and it errors with an NVAPI_INVALID_ARGUMENT error. This doesn't actually matter though, as the WinLibrary comes to the rescue.

WinLibrary can see all the video adapters available, and so will turn the required displays back on and set them up just right! We *may* lose are some specific DisplayConfig parameters abeing set as part of this process, but I'm not totally sure about that as WinLibrary is basically feature parity with the DisplayConfig settings as far as I can tell.

The fix is to look specifically for the NVAPI_INVALID_ARGUMENT error when attempting to set the NvAPI_DisplayConfig, and if this happens we check if we were going from a surround profile to a non-surround profile. If that is true, then we simply ignore that error. WinLibrary then clears up that problem and everything proceeds as normal.

This should (fingers crossed) fix #119!

This change also makes it far faster to grab and set the tabaskbar settings from registry, though this logic may not detect some Windows 10 formats which appear to be LOCALDISPLAY(\d,\d,\d\d) settings which I've never seen before. It should be generally much faster and more reliable.
This commit is contained in:
Terry MacDonald 2022-07-14 22:41:59 +12:00
parent 15f17f832b
commit eefa361773
4 changed files with 91 additions and 39 deletions

View File

@ -26,8 +26,8 @@ using System.Resources;
[assembly: Guid("e4ceaf5e-ad01-4695-b179-31168eb74c48")]
// Version information
[assembly: AssemblyVersion("2.4.1.4")]
[assembly: AssemblyFileVersion("2.4.1.4")]
[assembly: AssemblyVersion("2.4.1.13")]
[assembly: AssemblyFileVersion("2.4.1.13")]
[assembly: NeutralResourcesLanguageAttribute( "en" )]
[assembly: CLSCompliant(true)]

View File

@ -2727,8 +2727,18 @@ namespace DisplayMagicianShared.NVIDIA
}
else if (NVStatus == NVAPI_STATUS.NVAPI_INVALID_ARGUMENT)
{
SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: One or more arguments passed in are invalid. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}");
return false;
// We sometimes get an invalid argument here if NVIDIA has just disolved a mosaic surround screen into indivudal screens
// THis is because if there are any additional screens from other adapters, nvidia tells windows to disable them
// We need to wait until the Windows library applies the screen before the DisplayConfig will be applied.
if (!displayConfig.MosaicConfig.IsMosaicEnabled && ActiveDisplayConfig.MosaicConfig.IsMosaicEnabled)
{
SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}, but this is expected as we are changing from a Surround screen layout to a non-surround layout. Ignoring this error.");
}
else
{
SharedLogger.logger.Error($"NVIDIALibrary/SetActiveConfig: One or more arguments passed in are invalid. NvAPI_DISP_SetDisplayConfig() returned error code {NVStatus}");
return false;
}
}
else if (NVStatus == NVAPI_STATUS.NVAPI_API_NOT_INITIALIZED)
{
@ -2780,10 +2790,11 @@ namespace DisplayMagicianShared.NVIDIA
NVAPI_STATUS NVStatus = NVAPI_STATUS.NVAPI_ERROR;
// Go through the physical adapters
foreach (var physicalGPU in displayConfig.PhysicalAdapters)
{
SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Processing settings for Physical GPU #{physicalGPU.Key}");
SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Processing settings for Physical GPU #{physicalGPU.Key}");
NVIDIA_PER_ADAPTER_CONFIG myAdapter = physicalGPU.Value;
UInt32 myAdapterIndex = physicalGPU.Key;
@ -2797,7 +2808,7 @@ namespace DisplayMagicianShared.NVIDIA
if (!ActiveDisplayConfig.PhysicalAdapters[myAdapterIndex].Displays.ContainsKey(displayId))
{
SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfig: Display {displayId} doesn't exist in this setup, so skipping overriding any NVIDIA display Settings.");
SharedLogger.logger.Trace($"NVIDIALibrary/SetActiveConfigOverride: Display {displayId} doesn't exist in this setup, so skipping overriding any NVIDIA display Settings.");
continue;
}

View File

@ -710,6 +710,10 @@ namespace DisplayMagicianShared
if (itWorkedforNVIDIAColor)
{
// Lets update the screen config again for the final time.
nvidiaLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig();
SharedLogger.logger.Trace($"ProfileItem/SetActive: The NVIDIA display settings that override windows within the profile {Name} were successfully applied.");
return true;
}
@ -775,6 +779,10 @@ namespace DisplayMagicianShared
if (itWorkedforAMDColor)
{
// Lets update the screen config again for the final time.
amdLibrary.UpdateActiveConfig();
winLibrary.UpdateActiveConfig();
SharedLogger.logger.Trace($"ProfileItem/SetActive: The AMD display settings that override windows within the profile {Name} were successfully applied.");
return true;
}
@ -815,6 +823,9 @@ namespace DisplayMagicianShared
{
if (winLibrary.SetActiveConfig(_windowsDisplayConfig))
{
// Lets update the screen config again for the final time.
winLibrary.UpdateActiveConfig();
SharedLogger.logger.Trace($"ProfileItem/SetActive: The Windows CCD display settings within profile {Name} were successfully applied.");
return true;
}
@ -1476,7 +1487,8 @@ namespace DisplayMagicianShared
// IMPORTANT: This lookup WILL DEFINITELY CAUSE AN EXCEPTION right after windows changes back from
// NVIDIA Surround to a non-surround profile. This is expected, as it is caused bythe way Windows is SOOOO slow to update
// the taskbar locations in memory (it takes up to 15 seconds!). Nothing I can do, except put this protection in place :( .
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains($"UID{targetId}")).Value.Edge;
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue != null && tbr.Value.RegKeyValue.Contains($"UID{targetId}")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on display {targetId} is on the {screen.TaskBarEdge } of the screen.");
}
catch (Exception ex)
@ -1561,10 +1573,10 @@ namespace DisplayMagicianShared
// rather than the MMStuckRect reg keys
try
{
if (_windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue.Contains("Settings")) > 0)
if (_windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue != null && tbr.Value.RegKeyValue.Contains("Settings")) > 0)
{
screen.TaskBarEdge = _windowsDisplayConfig.TaskBarLayout.First(tbr => tbr.Value.RegKeyValue.Contains("Settings")).Value.Edge;
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on the primary display {targetId} is on the {screen.TaskBarEdge } of the screen.");
SharedLogger.logger.Trace($"ProfileItem/GetWindowsScreenPositions: Position of the taskbar on the primary display {targetId} is on the {screen.TaskBarEdge} of the screen.");
}
else
{
@ -1584,7 +1596,7 @@ namespace DisplayMagicianShared
{
try
{
int numMatches = _windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue.Contains($"UID{targetId}"));
int numMatches = _windowsDisplayConfig.TaskBarLayout.Count(tbr => tbr.Value.RegKeyValue != null && tbr.Value.RegKeyValue.Contains($"UID{targetId}"));
if (numMatches > 1)
{
var matchingTbls = (from tbl in _windowsDisplayConfig.TaskBarLayout where tbl.Value.RegKeyValue.Contains($"UID{targetId}") select tbl.Value).ToList();

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
@ -58,8 +59,9 @@ namespace DisplayMagicianShared.Windows
{3, new byte[] {0x30, 0x00, 0x00, 0x00, 0xFE, 0xFF, 0xFF, 0xFF}}
};
*/
public bool ReadFromRegistry(string regKeyValue)
public bool ReadFromRegistry(string regKeyValue, out bool retryNeeded)
{
retryNeeded = false;
bool MMStuckRectVerFound = false;
// Check if key exists
int version = 3;
@ -99,25 +101,33 @@ namespace DisplayMagicianShared.Windows
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
if (key.GetValueNames().Contains(regKeyValue))
{
MainScreen = false;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = false;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
// If we get here then we're done and don't need to continue with the rest of the code.
return true;
// If we get here then we're done and don't need to continue with the rest of the code.
return true;
}
else
{
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen. Screen details may not be available yet in registry.");
retryNeeded = true;
}
}
else
{
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen. Screen details may not be available yet in registry.");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Unable to find {regKeyValue} key in {address}. Screen details may not be available yet in registry.");
}
}
}
@ -173,23 +183,32 @@ namespace DisplayMagicianShared.Windows
address,
RegistryKeyPermissionCheck.ReadSubTree))
{
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
if (key.GetValueNames().Contains(regKeyValue))
{
MainScreen = true;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
var binary = key?.GetValue(regKeyValue) as byte[];
if (binary?.Length > 0)
{
MainScreen = true;
RegKeyValue = regKeyValue;
Binary = binary;
Version = version;
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
// Extract the values from the binary byte field
PopulateFieldsFromBinary();
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
return true;
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: The taskbar for {RegKeyValue} is against the {Edge} edge, is positioned at ({TaskBarLocation.X},{TaskBarLocation.Y}) and is {TaskBarLocation.Width}x{TaskBarLocation.Height} in size.");
return true;
}
else
{
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
retryNeeded = true;
return false;
}
}
else
{
SharedLogger.logger.Error($"TaskBarLayout/ReadFromRegistry: Unable to get the TaskBarStuckRectangle binary settings from {regKeyValue} screen.");
SharedLogger.logger.Trace($"TaskBarLayout/ReadFromRegistry: Unable to find {regKeyValue} key in {address}. Screen details may not be available yet in registry.");
return false;
}
}
@ -285,6 +304,11 @@ namespace DisplayMagicianShared.Windows
private bool PopulateFieldsFromBinary()
{
if (Binary == null)
{
return false;
}
// Now we decipher the binary properties features to populate the stuckrectangle
// DPI
if (Binary.Length < 44)
@ -356,6 +380,11 @@ namespace DisplayMagicianShared.Windows
public bool PopulateBinaryFromFields()
{
if (Binary == null)
{
return false;
}
// Set the DPI
if (Binary.Length < 44)
{
@ -550,8 +579,8 @@ namespace DisplayMagicianShared.Windows
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsrReadWorked = tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
if (!tbsrReadWorked)
tbsrReadWorked = tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath),out retryNeeded);
if (retryNeeded)
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar read #1 from registry didn't work.");
retryNeeded = true;
@ -587,8 +616,8 @@ namespace DisplayMagicianShared.Windows
// If it's a main screen, also add a duplicate so we track the main StuckRects settings separately too
TaskBarLayout tbsrMain = new TaskBarLayout();
tbsrReadWorked = tbsrMain.ReadFromRegistry("Settings");
if (!tbsrReadWorked)
tbsrReadWorked = tbsrMain.ReadFromRegistry("Settings",out retryNeeded) ;
if (!retryNeeded)
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar read #1 from registry didn't work.");
retryNeeded = true;
@ -647,7 +676,7 @@ namespace DisplayMagicianShared.Windows
TaskBarLayout tbsr = new TaskBarLayout();
// Now we're at the point that we should be able to update the binary that we grabbed earlier when the object was created
tbsrReadWorked = tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath));
tbsrReadWorked = tbsr.ReadFromRegistry(GetRegKeyValueFromDevicePath(displaySources[monitorInfo.szDevice][0].DevicePath), out retryNeeded);
if (!tbsrReadWorked)
{
SharedLogger.logger.Error($"TaskBarLayout/GetAllCurrentTaskBarPositions: Taskbar read #3 from registry didn't work.");