// // Copyright (c) Microsoft Corporation. All rights reserved. // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // namespace Microsoft.PackageManagement.Providers.Internal { using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Management.Automation; using System.Security.AccessControl; using PackageManagement.Internal; using PackageManagement.Internal.Implementation; using PackageManagement.Internal.Packaging; using PackageManagement.Internal.Utility.Platform; using PackageManagement.Internal.Utility.Extensions; using Win32; using File = System.IO.File; public class ProgramsProvider { /// /// The name of this Package Provider /// internal const string ProviderName = "Programs"; private static readonly Dictionary _features = new Dictionary(); /// /// Returns the name of the Provider. /// /// The name of this provider (uses the constant declared at the top of the class) public string GetPackageProviderName() { return ProviderName; } /// /// Performs one-time initialization of the PROVIDER. /// /// /// An object passed in from the CORE that contains functions that can be used to interact with /// the CORE and HOST /// public void InitializeProvider(Request request) { if (request == null) { throw new ArgumentNullException("request"); } // Nice-to-have put a debug message in that tells what's going on. request.Debug("Calling '{0}::InitializeProvider'", ProviderName); } /// /// Returns a collection of strings to the client advertizing features this provider supports. /// /// /// An object passed in from the CORE that contains functions that can be used to interact with /// the CORE and HOST /// public void GetFeatures(Request request) { if (request == null) { throw new ArgumentNullException("request"); } // Nice-to-have put a debug message in that tells what's going on. request.Debug("Calling '{0}::GetFeatures' ", ProviderName); foreach (var feature in _features) { request.Yield(feature); } } /// /// Returns dynamic option definitions to the HOST /// /// The category of dynamic options that the HOST is interested in /// /// An object passed in from the CORE that contains functions that can be used to interact with /// the CORE and HOST /// public void GetDynamicOptions(string category, Request request) { if (request == null) { throw new ArgumentNullException("request"); } category = category ?? string.Empty; // Nice-to-have put a debug message in that tells what's going on. request.Debug("Calling '{0}::GetDynamicOptions' '{1}'", ProviderName, category); switch (category.ToLowerInvariant()) { case "install": // options required for install/uninstall/getinstalledpackages request.YieldDynamicOption("IncludeWindowsInstaller", "Switch", false); request.YieldDynamicOption("IncludeSystemComponent", "Switch", false); break; case "provider": // options used with this provider. Not currently used. break; case "source": // options for package sources break; case "package": // options used when searching for packages break; } } /// /// Returns the packages that are installed /// /// the package name to match. Empty or null means match everything /// the specific version asked for. If this parameter is specified (ie, not null or empty string) then the minimum and maximum values are ignored /// the minimum version of packages to return . If the requiredVersion parameter is specified (ie, not null or empty string) this should be ignored /// the maximum version of packages to return . If the requiredVersion parameter is specified (ie, not null or empty string) this should be ignored /// /// An object passed in from the CORE that contains functions that can be used to interact with /// the CORE and HOST /// public void GetInstalledPackages(string name, string requiredVersion, string minimumVersion, string maximumVersion, Request request) { if (request == null) { throw new ArgumentNullException("request"); } // Nice-to-have put a debug message in that tells what's going on. request.Debug("Calling '{0}::GetInstalledPackages' '{1}','{2}','{3}','{4}'", ProviderName, name, requiredVersion, minimumVersion, maximumVersion); // dump out results. if (Environment.Is64BitOperatingSystem) { using (var hklm64 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", RegistryKeyPermissionCheck.ReadSubTree, RegistryRights.ReadKey)) { if (!YieldPackages("hklm64", hklm64, name, requiredVersion, minimumVersion, maximumVersion, request)) { return; } } using (var hkcu64 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry64).OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", false)) { if (!YieldPackages("hkcu64", hkcu64, name, requiredVersion, minimumVersion, maximumVersion, request)) { return; } } } using (var hklm32 = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32).OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", false)) { if (!YieldPackages("hklm32", hklm32, name, requiredVersion, minimumVersion, maximumVersion, request)) { return; } } using (var hkcu32 = RegistryKey.OpenBaseKey(RegistryHive.CurrentUser, RegistryView.Registry32).OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall", false)) { if (!YieldPackages("hkcu32", hkcu32, name, requiredVersion, minimumVersion, maximumVersion, request)) { } } } private bool YieldPackages(string hive, RegistryKey regkey, string name, string requiredVersion, string minimumVersion, string maximumVersion, Request request) { if (regkey != null) { var includeWindowsInstaller = request.GetOptionValue("IncludeWindowsInstaller").IsTrue(); var includeSystemComponent = request.GetOptionValue("IncludeSystemComponent").IsTrue(); var wildcardPattern = new WildcardPattern(name??"", WildcardOptions.CultureInvariant | WildcardOptions.IgnoreCase); foreach (var key in regkey.GetSubKeyNames()) { var subkey = regkey.OpenSubKey(key); if (subkey != null) { var properties = subkey.GetValueNames().ToDictionaryNicely(each => each.ToString(), each => (subkey.GetValue(each) ?? string.Empty).ToString(), StringComparer.OrdinalIgnoreCase); if (!includeWindowsInstaller && properties.ContainsKey("WindowsInstaller") && properties["WindowsInstaller"] == "1") { continue; } if (!includeSystemComponent && properties.ContainsKey("SystemComponent") && properties["SystemComponent"] == "1") { continue; } var productName = ""; if (!properties.TryGetValue("DisplayName", out productName)) { // no product name? continue; } if (!string.IsNullOrWhiteSpace(productName) && (string.IsNullOrWhiteSpace(name) || wildcardPattern.IsMatch(productName))) { var productVersion = properties.Get("DisplayVersion") ?? ""; var publisher = properties.Get("Publisher") ?? ""; var uninstallString = properties.Get("QuietUninstallString") ?? properties.Get("UninstallString") ?? ""; var comments = properties.Get("Comments") ?? ""; var fp = hive + @"\" + subkey; if (!string.IsNullOrEmpty(requiredVersion)) { if (SoftwareIdentityVersionComparer.CompareVersions("unknown", requiredVersion, productVersion) != 0) { continue; } } else { if (!string.IsNullOrEmpty(minimumVersion) && SoftwareIdentityVersionComparer.CompareVersions("unknown", productVersion, minimumVersion) < 0) { continue; } if (!string.IsNullOrEmpty(maximumVersion) && SoftwareIdentityVersionComparer.CompareVersions("unknown", productVersion, maximumVersion) > 0) { continue; } } if (request.YieldSoftwareIdentity(fp, productName, productVersion, "unknown", comments, "", name, "", "") != null) { if (properties.Keys.Where(each => !string.IsNullOrWhiteSpace(each)).Any(k => request.AddMetadata(fp, k.MakeSafeFileName(), properties[k]) == null)) { return false; } } } } } } return true; } } }