From cd71725617efc4df7b11b99f66c8f034619e6df7 Mon Sep 17 00:00:00 2001 From: Terry MacDonald Date: Sun, 19 Sep 2021 20:56:58 +1200 Subject: [PATCH] [WIP] Fixed error loading JSON after save There was a problem with the unused display config containing null values on save, which meant that the JSON would be invalid when loaded in the next time. The fix was to ensure that both NVIDIA and AMD both were filled with a default config when a new profiles was created. This means that the real config would overwrite whichever config was needed, and the JSON file would work as all objects were populated correctly! --- DisplayMagicianShared/AMD/AMDLibrary.cs | 15 ++++++++++ DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs | 15 ++++++++++ DisplayMagicianShared/ProfileItem.cs | 29 +++++++++++++------ DisplayMagicianShared/ProfileRepository.cs | 28 +++++++++++------- 4 files changed, 68 insertions(+), 19 deletions(-) diff --git a/DisplayMagicianShared/AMD/AMDLibrary.cs b/DisplayMagicianShared/AMD/AMDLibrary.cs index 52d5982..0c83117 100644 --- a/DisplayMagicianShared/AMD/AMDLibrary.cs +++ b/DisplayMagicianShared/AMD/AMDLibrary.cs @@ -276,7 +276,22 @@ namespace DisplayMagicianShared.AMD return _instance; } + public AMD_DISPLAY_CONFIG CreateDefaultConfig() + { + AMD_DISPLAY_CONFIG myDefaultConfig = new AMD_DISPLAY_CONFIG(); + // Fill in the minimal amount we need to avoid null references + // so that we won't break json.net when we save a default config + + myDefaultConfig.AdapterConfigs = new List(); + myDefaultConfig.SlsConfig.SLSMapConfigs = new List(); + myDefaultConfig.SlsConfig.SLSEnabledDisplayTargets = new List(); + myDefaultConfig.DisplayTargets = new List(); + myDefaultConfig.HdrConfigs = new Dictionary(); + myDefaultConfig.DisplayIdentifiers = new List(); + + return myDefaultConfig; + } public AMD_DISPLAY_CONFIG GetActiveConfig() { diff --git a/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs b/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs index b39dbc0..bfaaef5 100644 --- a/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs +++ b/DisplayMagicianShared/NVIDIA/NVIDIALibrary.cs @@ -286,7 +286,22 @@ namespace DisplayMagicianShared.NVIDIA return _instance; } + public NVIDIA_DISPLAY_CONFIG CreateDefaultConfig() + { + NVIDIA_DISPLAY_CONFIG myDefaultConfig = new NVIDIA_DISPLAY_CONFIG(); + // Fill in the minimal amount we need to avoid null references + // so that we won't break json.net when we save a default config + + myDefaultConfig.MosaicConfig.MosaicGridTopos = new NV_MOSAIC_GRID_TOPO_V2[0]; + myDefaultConfig.MosaicConfig.MosaicViewports = new List(); + myDefaultConfig.HdrConfig.HdrCapabilities = new Dictionary(); + myDefaultConfig.HdrConfig.HdrColorData = new Dictionary(); + myDefaultConfig.DisplayNames = new Dictionary(); + myDefaultConfig.DisplayIdentifiers = new List(); + + return myDefaultConfig; + } public NVIDIA_DISPLAY_CONFIG GetActiveConfig() { diff --git a/DisplayMagicianShared/ProfileItem.cs b/DisplayMagicianShared/ProfileItem.cs index 657d31d..33666a2 100644 --- a/DisplayMagicianShared/ProfileItem.cs +++ b/DisplayMagicianShared/ProfileItem.cs @@ -58,9 +58,9 @@ namespace DisplayMagicianShared private Bitmap _profileBitmap, _profileShortcutBitmap; private List _profileDisplayIdentifiers = new List(); private List _screens = new List(); - private NVIDIA_DISPLAY_CONFIG _nvidiaDisplayConfig = new NVIDIA_DISPLAY_CONFIG(); - private AMD_DISPLAY_CONFIG _amdDisplayConfig = new AMD_DISPLAY_CONFIG(); - private WINDOWS_DISPLAY_CONFIG _windowsDisplayConfig = new WINDOWS_DISPLAY_CONFIG(); + private NVIDIA_DISPLAY_CONFIG _nvidiaDisplayConfig; + private AMD_DISPLAY_CONFIG _amdDisplayConfig; + private WINDOWS_DISPLAY_CONFIG _windowsDisplayConfig; internal static string AppDataPath = System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician"); private static string AppWallpaperPath = Path.Combine(AppDataPath, $"Wallpaper"); @@ -135,6 +135,14 @@ namespace DisplayMagicianShared #endregion public ProfileItem() { + // Fill out a new NVIDIA and AMD object when a profile is being created + // so that it will save correctly. Json.NET will save null references by default + // unless we fill them up first, and that in turn causes NullReference errors when + // loading the DisplayProfiles_2.0.json into DisplayMagician next time. + // We cannot make the structs themselves create the default entry, so instead, we + // make each library create the default. + _nvidiaDisplayConfig = NVIDIALibrary.GetLibrary().CreateDefaultConfig(); + _amdDisplayConfig = AMDLibrary.GetLibrary().CreateDefaultConfig(); } public static Version Version = new Version(2, 1); @@ -456,7 +464,10 @@ namespace DisplayMagicianShared public bool CreateProfileFromCurrentDisplaySettings() { - if (VideoMode == VIDEO_MODE.NVIDIA) + // Create defaults for NVIDIA and AMD so that the JSON file can save properly + // (C# Structs populate with default values which mean that arrays start with null) + + if (VideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled) { NVIDIALibrary nvidiaLibrary = NVIDIALibrary.GetLibrary(); if (nvidiaLibrary.IsInstalled) @@ -476,7 +487,7 @@ namespace DisplayMagicianShared return false; } } - else if(VideoMode == VIDEO_MODE.AMD) + else if(VideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled) { AMDLibrary amdLibrary = AMDLibrary.GetLibrary(); if (amdLibrary.IsInstalled) @@ -664,7 +675,7 @@ namespace DisplayMagicianShared // Actually set this profile active public bool SetActive() { - if (VideoMode == VIDEO_MODE.NVIDIA) + if (VideoMode == VIDEO_MODE.NVIDIA && NVIDIALibrary.GetLibrary().IsInstalled) { NVIDIALibrary nvidiaLibrary = NVIDIALibrary.GetLibrary(); WinLibrary winLibrary = WinLibrary.GetLibrary(); @@ -716,7 +727,7 @@ namespace DisplayMagicianShared } } } - else if (VideoMode == VIDEO_MODE.AMD) + else if (VideoMode == VIDEO_MODE.AMD && AMDLibrary.GetLibrary().IsInstalled) { AMDLibrary amdLibrary = AMDLibrary.GetLibrary(); WinLibrary winLibrary = WinLibrary.GetLibrary(); @@ -1509,9 +1520,9 @@ namespace DisplayMagicianShared // Check the object fields // ProfileDisplayIdentifiers may be the same but in different order within the array, so we need to handle - // that fact. + // that fact. return NVIDIADisplayConfig.Equals(other.NVIDIADisplayConfig) && - //AMDDisplayConfig.Equals(other.AMDDisplayConfig) && + AMDDisplayConfig.Equals(other.AMDDisplayConfig) && WindowsDisplayConfig.Equals(other.WindowsDisplayConfig) && ProfileDisplayIdentifiers.SequenceEqual (other.ProfileDisplayIdentifiers); } diff --git a/DisplayMagicianShared/ProfileRepository.cs b/DisplayMagicianShared/ProfileRepository.cs index 2ec1e54..f291e34 100644 --- a/DisplayMagicianShared/ProfileRepository.cs +++ b/DisplayMagicianShared/ProfileRepository.cs @@ -225,11 +225,14 @@ namespace DisplayMagicianShared { return false; } - - if (!LoadProfiles()) + + if (!_profilesLoaded) { - return false; - } + if (!LoadProfiles()) + { + return false; + } + } return true; } @@ -692,11 +695,13 @@ namespace DisplayMagicianShared { _allProfiles = JsonConvert.DeserializeObject>(json, new JsonSerializerSettings { - MissingMemberHandling = MissingMemberHandling.Ignore, - NullValueHandling = NullValueHandling.Ignore, + MissingMemberHandling = MissingMemberHandling.Error, + NullValueHandling = NullValueHandling.Include, + //NullValueHandling = NullValueHandling.Ignore, DefaultValueHandling = DefaultValueHandling.Include, + //DefaultValueHandling = DefaultValueHandling.Ignore, TypeNameHandling = TypeNameHandling.Auto, - ObjectCreationHandling = ObjectCreationHandling.Replace + ObjectCreationHandling = ObjectCreationHandling.Auto, }); } catch (Exception ex) @@ -780,9 +785,12 @@ namespace DisplayMagicianShared var json = JsonConvert.SerializeObject(_allProfiles, Formatting.Indented, new JsonSerializerSettings { NullValueHandling = NullValueHandling.Include, - DefaultValueHandling = DefaultValueHandling.Populate, - TypeNameHandling = TypeNameHandling.Auto - + //NullValueHandling = NullValueHandling.Ignore, + DefaultValueHandling = DefaultValueHandling.Include, + //DefaultValueHandling = DefaultValueHandling.Ignore, + TypeNameHandling = TypeNameHandling.Auto, + MissingMemberHandling = MissingMemberHandling.Error, + ObjectCreationHandling = ObjectCreationHandling.Replace, });