Skip to content

Commit

Permalink
- Support detecting components by key path
Browse files Browse the repository at this point in the history
- Fix detecting presence of key path: '?' denotes a shared files; 20+ denotes x64 registry view; registry key may point to default value
  • Loading branch information
nirbar committed Jul 15, 2024
1 parent cc2a876 commit b2b9f5f
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 4 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ on:
version:
description: 'Build & package version'
required: true
default: 0.1.5
default: 0.2.0
type: string
jobs:
Build:
Expand Down
35 changes: 33 additions & 2 deletions MsiZapEx/ComponentInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading;
using System.Xml.Linq;

namespace MsiZapEx
{
Expand Down Expand Up @@ -114,14 +115,32 @@ internal static List<ComponentInfo> GetAllComponents()
return _components;
}

internal static List<ComponentInfo> GetComponents(Guid productCode, bool machineContext)
internal static List<ComponentInfo> GetComponents(Guid productCode)
{
GetAllComponents();
List<ComponentInfo> components = new List<ComponentInfo>();
components.AddRange(_components.FindAll(ci => ci.ProductsKeyPath.Any(p => p.ProductCode.Equals(productCode))));
return components;
}

internal static List<ComponentInfo> GetByKeyPath(string keyPath)
{
GetAllComponents();
keyPath = keyPath.Replace("?", "");
List<ComponentInfo> components = new List<ComponentInfo>();
try
{
if (Path.IsPathRooted(keyPath))
{
keyPath = Path.GetFullPath(keyPath);
}
}
catch { }

components.AddRange(_components.FindAll(ci => ci.ProductsKeyPath.Any(p => p.KeyPath.Replace("?", "").Equals(keyPath, StringComparison.InvariantCultureIgnoreCase))));
return components;
}

internal static bool ResolveScope(Guid componentCode)
{
string obfuscatedGuid = GuidEx.MsiObfuscate(componentCode);
Expand Down Expand Up @@ -244,6 +263,7 @@ private static bool ValidateKeyPath(string keyPath)
{
return true;
}
keyPath = keyPath.Replace("?", "");

Match regMatch = registryKeyPath_.Match(keyPath);
if (regMatch.Success)
Expand All @@ -252,7 +272,7 @@ private static bool ValidateKeyPath(string keyPath)
if (int.TryParse(root, out int kr))
{
RegistryHive hive;
RegistryView view = (kr > 20) ? RegistryView.Registry64 : RegistryView.Registry32;
RegistryView view = (kr >= 20) ? RegistryView.Registry64 : RegistryView.Registry32;
switch (kr % 10)
{
case 0:
Expand All @@ -272,6 +292,17 @@ private static bool ValidateKeyPath(string keyPath)
}

string path = regMatch.Groups["path"].Value;
if (path.EndsWith($"{Path.DirectorySeparatorChar}") || path.EndsWith($"{Path.AltDirectorySeparatorChar}"))
{
using (RegistryKey rk = RegistryKey.OpenBaseKey(hive, view))
{
using (RegistryKey hk = rk.OpenSubKey(path, false))
{
return (hk != null);
}
}
}

string key = Path.GetDirectoryName(path);
string name = Path.GetFileName(path);
using (RegistryKey rk = RegistryKey.OpenBaseKey(hive, view))
Expand Down
2 changes: 1 addition & 1 deletion MsiZapEx/ProductInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,7 +319,7 @@ private void Read(string obfuscatedGuid, bool? machineScope)
}
else
{
Components = ComponentInfo.GetComponents(ProductCode, MachineScope);
Components = ComponentInfo.GetComponents(ProductCode);
if (Components.Count > 0)
{
Status |= StatusFlags.Components;
Expand Down
15 changes: 15 additions & 0 deletions MsiZapEx/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,21 @@ static void Main(string[] args)
ComponentInfo component = new ComponentInfo(componentCode);
component.PrintProducts();
}
if (!string.IsNullOrEmpty(Settings.Instance.KeyPath))
{
List<ComponentInfo> components = ComponentInfo.GetByKeyPath(Settings.Instance.KeyPath);
if (components != null && components.Count > 0)
{
foreach (ComponentInfo component in components)
{
component.PrintProducts();
}
}
else
{
Console.WriteLine($"No components found with key path '{Settings.Instance.KeyPath}'");
}
}
if (Settings.Instance.DetectOrphanProducts)
{
List<ProductInfo> orphan = ProductInfo.GetOrphanProducts();
Expand Down
3 changes: 3 additions & 0 deletions MsiZapEx/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ public class Settings
[Option("component-code", Required = false, HelpText = "Detect products by ComponentCode", Group = "codes")]
public string ComponentCode { get; set; }

[Option("key-path", Required = false, HelpText = "Detect components by key path", Group = "codes")]
public string KeyPath { get; set; }

[Option("detect-orphan-products", Required = false, HelpText = "List all products that can't be normally uninstalled due to missing registration data", Group = "codes")]
public bool DetectOrphanProducts { get; set; }

Expand Down

0 comments on commit b2b9f5f

Please sign in to comment.