From fab38a8b9a934f260317fc019b4405eec95af06d Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:14:32 -0400 Subject: [PATCH 1/7] Added RegistryKey Extensions/Helpers Added RegistryKey Extensions/Helper methods. Cleaned up and improved HandleGetStartupItems. --- Client/Client.csproj | 1 + Client/Core/Commands/SystemHandler.cs | 64 +++++--------- .../Core/Extensions/RegistryKeyExtensions.cs | 87 +++++++++++++++++++ 3 files changed, 110 insertions(+), 42 deletions(-) create mode 100644 Client/Core/Extensions/RegistryKeyExtensions.cs diff --git a/Client/Client.csproj b/Client/Client.csproj index c4688429..0715d7ab 100644 --- a/Client/Client.csproj +++ b/Client/Client.csproj @@ -65,6 +65,7 @@ + diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index c866f0f5..9a2d21e0 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -6,6 +6,7 @@ using Microsoft.Win32; using xClient.Core.Information; using xClient.Core.RemoteShell; +using xClient.Core.Extensions; namespace xClient.Core.Commands { @@ -56,96 +57,75 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c try { Dictionary startupItems = new Dictionary(); + int i = 0; - using ( - var key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", - false)) + using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key != null) { - foreach (var k in key.GetValueNames()) + foreach (string formattedKeyValue in key.GetFormattedKeyValues()) { - if (string.IsNullOrEmpty(k) || key.GetValue(k) == null) continue; - startupItems.Add(string.Format("{0}||{1}", k, key.GetValue(k)), 0); + startupItems.Add(formattedKeyValue, i); } } } - using ( - var key = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - false)) + using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) { - foreach (var k in key.GetValueNames()) + foreach (string formattedKeyValue in key.GetFormattedKeyValues()) { - if (string.IsNullOrEmpty(k) || key.GetValue(k) == null) continue; - startupItems.Add(string.Format("{0}||{1}", k, key.GetValue(k)), 1); + startupItems.Add(formattedKeyValue, i); } } } - using ( - var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", false) - ) + using (var key = Registry.CurrentUser.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key != null) { - foreach (var k in key.GetValueNames()) + foreach (string formattedKeyValue in key.GetFormattedKeyValues()) { - if (string.IsNullOrEmpty(k) || key.GetValue(k) == null) continue; - startupItems.Add(string.Format("{0}||{1}", k, key.GetValue(k)), 2); + startupItems.Add(formattedKeyValue, i); } } } - using ( - var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - false)) + using (var key = Registry.CurrentUser.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) { - foreach (var k in key.GetValueNames()) + foreach (string formattedKeyValue in key.GetFormattedKeyValues()) { - if (string.IsNullOrEmpty(k) || key.GetValue(k) == null) continue; - startupItems.Add(string.Format("{0}||{1}", k, key.GetValue(k)), 3); + startupItems.Add(formattedKeyValue, i); } } } if (OSInfo.Bits == 64) { - using ( - var key = - Registry.LocalMachine.OpenSubKey( - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", - false)) + using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key != null) { - foreach (var k in key.GetValueNames()) + foreach (string formattedKeyValue in key.GetFormattedKeyValues()) { - if (string.IsNullOrEmpty(k) || key.GetValue(k) == null) continue; - startupItems.Add(string.Format("{0}||{1}", k, key.GetValue(k)), 4); + startupItems.Add(formattedKeyValue, i); } } } - using ( - var key = - Registry.LocalMachine.OpenSubKey( - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", - false)) + using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) { - foreach (var k in key.GetValueNames()) + foreach (string formattedKeyValue in key.GetFormattedKeyValues()) { - if (string.IsNullOrEmpty(k) || key.GetValue(k) == null) continue; - startupItems.Add(string.Format("{0}||{1}", k, key.GetValue(k)), 5); + startupItems.Add(formattedKeyValue, i); } } } } if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { - var files = - new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); + var files = new DirectoryInfo(Environment.GetFolderPath(Environment.SpecialFolder.Startup)).GetFiles(); + foreach (var file in files) { if (file.Name != "desktop.ini") diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs new file mode 100644 index 00000000..359f3f29 --- /dev/null +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -0,0 +1,87 @@ +using System.Collections.Generic; +using Microsoft.Win32; +using System.Linq; + +namespace xClient.Core.Extensions +{ + public static class RegistryKeyExtensions + { + /// + /// Determines if the registry key by the name provided is null or has the value of null. + /// + /// The name associated with the registry key. + /// The actual registry key. + /// The string value of the registry key determined by the key's name. + /// True if the provided name is null or empty, or the key is null; False if otherwise. + public static bool IsNameOrValueNull(this string keyName, RegistryKey key) + { + return (string.IsNullOrEmpty(keyName) || (key == null)); + } + + /// + /// Attempts to get the value of the key using the specified key name. This method assumes + /// correct input. + /// + /// The key of which we obtain the value of. + /// The name of the key. + /// Returns the value of the key using the specified key name. If unable to do so, + /// string.Empty will be returned instead. + public static string GetValueSafe(this RegistryKey key, string keyName) + { + // Before calling this, use something such as "IsNameOrValueNull" to make sure + // that the input used for this method is usable. The responsibility for this + // method is to take these valid parameters and try to get the value of them, + // allowing exceptions if any are generated. + try + { + return key.GetValue(keyName).ToString(); + } + catch + { + return string.Empty; + } + } + + /// + /// Attempts to obtain a readonly (non-writable) sub key from the key provided using the + /// specified name. Exceptions thrown will be caught and will only return a null key. + /// This method assumes the caller will dispose of the key when done using it. + /// + /// The key of which the sub key is obtained from. + /// The name of the sub-key. + /// Returns the sub-key obtained from the key and name provided; Returns null if + /// unable to obtain a sub-key. + public static RegistryKey OpenReadonlySubKeySafe(this RegistryKey key, string name) + { + try + { + return Registry.LocalMachine.OpenSubKey(name, false); + } + catch + { + return null; + } + } + + public static IEnumerable GetFormattedKeyValues(this RegistryKey key) + { + if (key != null) + { + foreach (var k in key.GetValueNames().Where(keyVal => !keyVal.IsNameOrValueNull(key)) + .Select(keyVal => key.GetValueSafe(keyVal))) + { + // Less-likely, but this will ensure no empty items if an exception was thrown + // when obtaining the value. + if (string.IsNullOrEmpty(k)) + { + yield return string.Format("{0}||{1}", k, key.GetValue(k)); + } + } + } + else + { + yield break; + } + } + } +} \ No newline at end of file From c8b92581837052e2390507d667417d6d8be6d058 Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:17:02 -0400 Subject: [PATCH 2/7] More documentation on a RegistryKey extension Added some more documentation for the GetFormattedKeyValues extension method. --- Client/Core/Extensions/RegistryKeyExtensions.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 359f3f29..48b0451b 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -63,6 +63,12 @@ public static RegistryKey OpenReadonlySubKeySafe(this RegistryKey key, string na } } + /// + /// Gets all of the value names associated with the registry key and returns + /// formatted strings of the filtered values. + /// + /// The registry key of which the values are obtained. + /// Yield returns formatted strings of the key and the key value. public static IEnumerable GetFormattedKeyValues(this RegistryKey key) { if (key != null) From 901cbd3a64419a9e72521afc082a7a2b010e2e73 Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:18:02 -0400 Subject: [PATCH 3/7] Add to the dictionary correctly --- Client/Core/Commands/SystemHandler.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 9a2d21e0..c4f174f9 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -69,6 +69,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c } } } + i++; using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) @@ -79,6 +80,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c } } } + i++; using (var key = Registry.CurrentUser.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key != null) @@ -89,6 +91,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c } } } + i++; using (var key = Registry.CurrentUser.OpenReadonlySubKeySafe("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) @@ -99,6 +102,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c } } } + i++; if (OSInfo.Bits == 64) { using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) @@ -111,6 +115,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c } } } + i++; using (var key = Registry.LocalMachine.OpenReadonlySubKeySafe("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key != null) @@ -121,6 +126,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c } } } + i++; } if (Directory.Exists(Environment.GetFolderPath(Environment.SpecialFolder.Startup))) { @@ -129,7 +135,7 @@ public static void HandleGetStartupItems(Packets.ServerPackets.GetStartupItems c foreach (var file in files) { if (file.Name != "desktop.ini") - startupItems.Add(string.Format("{0}||{1}", file.Name, file.FullName), 6); + startupItems.Add(string.Format("{0}||{1}", file.Name, file.FullName), i); } } From 51464e6d287a7c273efe51779ea592e7f6132044 Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:19:35 -0400 Subject: [PATCH 4/7] Fixed possible server crash If the client cannot give us anything, don't crash over it. :) We can't use these entries so just leave. --- Server/Core/Commands/SystemHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Server/Core/Commands/SystemHandler.cs b/Server/Core/Commands/SystemHandler.cs index be4ae5c7..a4da529c 100644 --- a/Server/Core/Commands/SystemHandler.cs +++ b/Server/Core/Commands/SystemHandler.cs @@ -116,7 +116,7 @@ public static void HandleGetSystemInfoResponse(Client client, GetSystemInfoRespo public static void HandleGetStartupItemsResponse(Client client, GetStartupItemsResponse packet) { - if (client.Value.FrmStm == null) + if (client.Value.FrmStm == null || packet.StartupItems == null) return; foreach (var pair in packet.StartupItems) From 38acb88aab773e327cf960400124ac823e08076c Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:29:42 -0400 Subject: [PATCH 5/7] Added another RegistryKey extension method This makes the code more readable and the intent more obvious. --- Client/Core/Commands/SystemHandler.cs | 30 ++++--------------- .../Core/Extensions/RegistryKeyExtensions.cs | 13 ++++++++ 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index c4f174f9..2b24435d 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -154,10 +154,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com switch (command.Type) { case 0: - using ( - var key = - Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", - true)) + using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key == null) throw new Exception("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) @@ -167,10 +164,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com } break; case 1: - using ( - var key = - Registry.LocalMachine.OpenSubKey( - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", true)) + using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key == null) throw new Exception("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) @@ -180,10 +174,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com } break; case 2: - using ( - var key = - Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", - true)) + using (var key = Registry.CurrentUser.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key == null) throw new Exception("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) @@ -193,10 +184,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com } break; case 3: - using ( - var key = - Registry.CurrentUser.OpenSubKey( - "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce", true)) + using (var key = Registry.CurrentUser.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key == null) throw new Exception("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) @@ -209,10 +197,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com if (OSInfo.Bits != 64) throw new NotSupportedException("Only on 64-bit systems supported"); - using ( - var key = - Registry.LocalMachine.OpenSubKey( - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run", true)) + using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) { if (key == null) throw new Exception("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) @@ -225,10 +210,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com if (OSInfo.Bits != 64) throw new NotSupportedException("Only on 64-bit systems supported"); - using ( - var key = - Registry.LocalMachine.OpenSubKey( - "SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce", true)) + using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { if (key == null) throw new Exception("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 48b0451b..2ebc4256 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -63,6 +63,19 @@ public static RegistryKey OpenReadonlySubKeySafe(this RegistryKey key, string na } } + /// + /// Attempts to obtain a writable sub key from the key provided using the specified + /// name. This method assumes the caller will dispose of the key when done using it. + /// + /// The key of which the sub key is obtained from. + /// The name of the sub-key. + /// Returns the sub-key obtained from the key and name provided; Returns null if + /// unable to obtain a sub-key. + public static RegistryKey OpenWritableSubKey(this RegistryKey key, string name) + { + return Registry.LocalMachine.OpenSubKey(name, true); + } + /// /// Gets all of the value names associated with the registry key and returns /// formatted strings of the filtered values. From 59599759bef6a2e65794041d80b4db1d065c4d2c Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:34:05 -0400 Subject: [PATCH 6/7] Throw a more meaningful exception For those that like to see the type of exception thrown, this makes it more clear and meaningful. --- Client/Core/Commands/SystemHandler.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Client/Core/Commands/SystemHandler.cs b/Client/Core/Commands/SystemHandler.cs index 2b24435d..f4c86fac 100644 --- a/Client/Core/Commands/SystemHandler.cs +++ b/Client/Core/Commands/SystemHandler.cs @@ -156,7 +156,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com case 0: using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { - if (key == null) throw new Exception("Registry key does not exist"); + if (key == null) throw new ArgumentException("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) command.Path = "\"" + command.Path + "\""; key.SetValue(command.Name, command.Path); @@ -166,7 +166,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com case 1: using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { - if (key == null) throw new Exception("Registry key does not exist"); + if (key == null) throw new ArgumentException("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) command.Path = "\"" + command.Path + "\""; key.SetValue(command.Name, command.Path); @@ -176,7 +176,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com case 2: using (var key = Registry.CurrentUser.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run")) { - if (key == null) throw new Exception("Registry key does not exist"); + if (key == null) throw new ArgumentException("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) command.Path = "\"" + command.Path + "\""; key.SetValue(command.Name, command.Path); @@ -186,7 +186,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com case 3: using (var key = Registry.CurrentUser.OpenWritableSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { - if (key == null) throw new Exception("Registry key does not exist"); + if (key == null) throw new ArgumentException("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) command.Path = "\"" + command.Path + "\""; key.SetValue(command.Name, command.Path); @@ -199,7 +199,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\Run")) { - if (key == null) throw new Exception("Registry key does not exist"); + if (key == null) throw new ArgumentException("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) command.Path = "\"" + command.Path + "\""; key.SetValue(command.Name, command.Path); @@ -212,7 +212,7 @@ public static void HandleAddStartupItem(Packets.ServerPackets.AddStartupItem com using (var key = Registry.LocalMachine.OpenWritableSubKey("SOFTWARE\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\RunOnce")) { - if (key == null) throw new Exception("Registry key does not exist"); + if (key == null) throw new ArgumentException("Registry key does not exist"); if (!command.Path.StartsWith("\"") && !command.Path.EndsWith("\"")) command.Path = "\"" + command.Path + "\""; key.SetValue(command.Name, command.Path); From 172359262a7ea2341e87462f2333d194db1c8056 Mon Sep 17 00:00:00 2001 From: yankejustin Date: Tue, 2 Jun 2015 13:50:56 -0400 Subject: [PATCH 7/7] Correctly get entries --- Client/Core/Extensions/RegistryKeyExtensions.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Client/Core/Extensions/RegistryKeyExtensions.cs b/Client/Core/Extensions/RegistryKeyExtensions.cs index 2ebc4256..9e9f48ed 100644 --- a/Client/Core/Extensions/RegistryKeyExtensions.cs +++ b/Client/Core/Extensions/RegistryKeyExtensions.cs @@ -86,14 +86,13 @@ public static IEnumerable GetFormattedKeyValues(this RegistryKey key) { if (key != null) { - foreach (var k in key.GetValueNames().Where(keyVal => !keyVal.IsNameOrValueNull(key)) - .Select(keyVal => key.GetValueSafe(keyVal))) + foreach (var k in key.GetValueNames().Where(keyVal => !keyVal.IsNameOrValueNull(key))) { // Less-likely, but this will ensure no empty items if an exception was thrown // when obtaining the value. if (string.IsNullOrEmpty(k)) { - yield return string.Format("{0}||{1}", k, key.GetValue(k)); + yield return string.Format("{0}||{1}", k, key.GetValueSafe(k)); } } }