diff --git a/src/Tools/DynamoInstallDetective/ProductLookUp.cs b/src/Tools/DynamoInstallDetective/ProductLookUp.cs index 509c1723e30..c35ff923659 100644 --- a/src/Tools/DynamoInstallDetective/ProductLookUp.cs +++ b/src/Tools/DynamoInstallDetective/ProductLookUp.cs @@ -9,6 +9,110 @@ namespace DynamoInstallDetective { + // Utility class for interacting with the windows registry. + internal static class RegUtils + { + // Utility class to enable/disable registry caching within a scope. + class RegistryCacher : IDisposable + { + public RegistryCacher() + { + lock (mutex) + { + cacheEnabled = true; + } + } + + public void Dispose() + { + lock (mutex) + { + cacheEnabled = false; + cachedRecords?.Clear(); + cachedRecords = null; + } + } + } + + public static string REG_KEY64 = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"; + public static string REG_KEY32 = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"; + + // Cached data from windows registry. Consists of a dictionary that maps The product name/code to Display Name (Tuple.item1) and Install Path(Tuple.item2) + // Dictionary + private static Dictionary cachedRecords; + private static bool cacheEnabled = false; + + private static readonly object mutex = new object(); + + public static RegistryKey OpenKey(string key) + { + var regKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); + return regKey.OpenSubKey(key); + } + public static string GetInstallLocation(RegistryKey key) + { + if (key != null) + return key.GetValue("InstallLocation") as string; + + return string.Empty; + } + + public static string GetDisplayName(RegistryKey key) + { + if (key != null) + return key.GetValue("DisplayName") as string; + + return string.Empty; + } + + // Returns all the products registered under "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\" that have a valid DisplayName and an InstallLocation. + public static Dictionary GetInstalledProducts() + { + lock (mutex) + { + if (cacheEnabled && cachedRecords != null) + { + return cachedRecords; + } + } + + var productInfo = new Dictionary(); + + var key = OpenKey(REG_KEY64); + foreach(string s in key.GetSubKeyNames()) + { + try + { + var prodKey = key.OpenSubKey(s); + var displayName = GetDisplayName(prodKey); + var installLocation = GetInstallLocation(prodKey); + if (!string.IsNullOrEmpty(displayName) && !string.IsNullOrEmpty(installLocation)) + { + productInfo.Add(s, (displayName, installLocation)); + } + } + catch (Exception) + { + // Do nothing + } + } + + lock (mutex) + { + if (cacheEnabled) + { + cachedRecords = new Dictionary(productInfo); + } + } + return productInfo; + } + + public static IDisposable StartCache() + { + return new RegistryCacher(); + } + } + /// /// Specifies an installed product /// @@ -63,7 +167,7 @@ public interface IProductLookUp IInstalledProduct GetProductFromProductCode(string productCode); /// - /// Returns product name list based on lookup criteria + /// Returns product name list using lookup criteria based on product names. /// /// Product name list IEnumerable GetProductNameList(); @@ -120,9 +224,6 @@ public interface IProductCollection /// public class InstalledProductLookUp : IProductLookUp { - const string REG_KEY64 = @"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\"; - const string REG_KEY32 = @"SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\"; - /// /// Product name for lookup /// @@ -130,28 +231,6 @@ public class InstalledProductLookUp : IProductLookUp private readonly Func fileLocator; - static RegistryKey OpenKey(string key) - { - var regKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64); - return regKey.OpenSubKey(key); - } - - static string GetInstallLocation(RegistryKey key) - { - if (key != null) - return key.GetValue("InstallLocation") as string; - - return string.Empty; - } - - static string GetDisplayName(RegistryKey key) - { - if (key != null) - return key.GetValue("DisplayName") as string; - - return string.Empty; - } - /// /// Implements a product look up algorithm based on registry key. /// @@ -202,8 +281,9 @@ public IInstalledProduct GetProductFromProductCode(string productCode) public virtual IEnumerable GetProductNameList() { - var key = OpenKey(REG_KEY64); - return key.GetSubKeyNames().Where(s => s.Contains(ProductLookUpName)); + return RegUtils.GetInstalledProducts().Select(s => s.Value.DisplayName).Where(s => { + return s?.Contains(ProductLookUpName) ?? false; + }); } public virtual bool ExistsAtPath(string basePath) @@ -216,21 +296,21 @@ public virtual bool ExistsAtPath(string basePath) public virtual string GetInstallLocationFromProductName(string name) { - var key = OpenKey(REG_KEY64 + name); - return GetInstallLocation(key); + var key = RegUtils.OpenKey(RegUtils.REG_KEY64 + name); + return RegUtils.GetInstallLocation(key); } public virtual string GetInstallLocationFromProductCode(string productCode, out string productName) { - string issProdKey = REG_KEY32 + productCode + "_is1"; - var key = OpenKey(issProdKey); + string issProdKey = RegUtils.REG_KEY32 + productCode + "_is1"; + var key = RegUtils.OpenKey(issProdKey); if (null == key) { - issProdKey = REG_KEY64 + productCode; - key = OpenKey(issProdKey); + issProdKey = RegUtils.REG_KEY64 + productCode; + key = RegUtils.OpenKey(issProdKey); } - productName = GetDisplayName(key); - return GetInstallLocation(key); + productName = RegUtils.GetDisplayName(key); + return RegUtils.GetInstallLocation(key); } public virtual string GetCoreFilePathFromInstallation(string installPath) diff --git a/src/Tools/DynamoInstallDetective/Utilities.cs b/src/Tools/DynamoInstallDetective/Utilities.cs index 4df21a2f27b..9e42d2fc914 100644 --- a/src/Tools/DynamoInstallDetective/Utilities.cs +++ b/src/Tools/DynamoInstallDetective/Utilities.cs @@ -88,19 +88,22 @@ public static IEnumerable FindProductInstallations(string productSearchPattern, /// info. public static IEnumerable FindMultipleProductInstallations(List productSearchPatterns, string fileSearchPattern) { - var installs = new InstalledProducts(); - // Look up products with ASM installed on user's computer - foreach (var productSearchPattern in productSearchPatterns) + using (RegUtils.StartCache()) { - installs.LookUpAndInitProducts(new InstalledProductLookUp(productSearchPattern, fileSearchPattern)); - } + var installs = new InstalledProducts(); + // Look up products with ASM installed on user's computer + foreach (var productSearchPattern in productSearchPatterns) + { + installs.LookUpAndInitProducts(new InstalledProductLookUp(productSearchPattern, fileSearchPattern)); + } - return - installs.Products.Select( - p => - new KeyValuePair>( - p.InstallLocation, - p.VersionInfo)); + return + installs.Products.Select( + p => + new KeyValuePair>( + p.InstallLocation, + p.VersionInfo)); + } } } }