Skip to content

Commit d4813ca

Browse files
authored
feat: add logging and improve browser discovery (#78)
1 parent edd4417 commit d4813ca

19 files changed

Lines changed: 764 additions & 262 deletions

CmdPalWebSearchShortcut/Directory.Packages.props

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@
55
<ItemGroup>
66
<PackageVersion Include="Microsoft.CommandPalette.Extensions" Version="0.2.0" />
77
<PackageVersion Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="9.0.0-preview.24508.2" />
8+
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.5" />
9+
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.5" />
10+
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.5" />
11+
<PackageVersion Include="Microsoft.Extensions.Logging.Debug" Version="10.0.5" />
812
<PackageVersion Include="Microsoft.Web.WebView2" Version="1.0.2903.40" />
913
<PackageVersion Include="Microsoft.Windows.CsWin32" Version="0.2.46-beta" />
1014
<PackageVersion Include="Microsoft.Windows.CsWinRT" Version="2.2.0" />

CmdPalWebSearchShortcut/WebSearchShortcut.Tests/Browsers/BrowserDiscoveryTests.cs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using Microsoft.Extensions.Logging;
23
using Microsoft.VisualStudio.TestTools.UnitTesting;
34
using WebSearchShortcut.Browser;
45

@@ -10,7 +11,15 @@ public class BrowserProgIdFinderTests
1011
[TestMethod]
1112
public void FindUniqueHttpUrlAssociationProgIdsShouldPrintResults()
1213
{
13-
var browserInfos = BrowsersDiscovery.GetAllInstalledBrowsers();
14+
using var loggerFactory = LoggerFactory.Create(builder =>
15+
{
16+
builder.AddDebug();
17+
builder.SetMinimumLevel(LogLevel.Trace);
18+
});
19+
20+
BrowsersDiscovery.Logger = loggerFactory.CreateLogger("Test");
21+
22+
var browserInfos = BrowsersDiscovery.GetInstalledBrowsers();
1423

1524
foreach (var browserInfo in browserInfos)
1625
{

CmdPalWebSearchShortcut/WebSearchShortcut.Tests/WebSearchShortcut.Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,17 @@
22

33
<PropertyGroup>
44
<TargetFramework>net9.0-windows10.0.26100.0</TargetFramework>
5+
<WindowsSdkPackageVersion>10.0.26100.68-preview</WindowsSdkPackageVersion>
56
</PropertyGroup>
67

78
<ItemGroup>
89
<ProjectReference Include="..\WebSearchShortcut\WebSearchShortcut.csproj" />
910
</ItemGroup>
1011

1112
<ItemGroup>
13+
<PackageReference Include="Microsoft.Extensions.Logging" />
14+
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
15+
<PackageReference Include="Microsoft.Extensions.Logging.Debug" />
1216
<PackageReference Include="MSTest.TestFramework" />
1317
<PackageReference Include="MSTest.TestAdapter" />
1418
<PackageReference Include="Microsoft.NET.Test.Sdk" />

CmdPalWebSearchShortcut/WebSearchShortcut/Browser/BrowserExecutionInfo.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,16 +5,17 @@ namespace WebSearchShortcut.Browser;
55

66
internal sealed class BrowserExecutionInfo
77
{
8-
public string? Path { get; }
9-
public string? ArgumentsPattern { get; }
8+
public string Path { get; }
9+
public string ArgumentsPattern { get; }
1010

1111
public BrowserExecutionInfo(WebSearchShortcutDataEntry shortcut)
1212
{
1313
DefaultBrowserProvider.UpdateIfTimePassed();
14+
BrowserInfo defaultBrowser = DefaultBrowserProvider.GetDefaultBrowser();
1415

1516
Path = !string.IsNullOrWhiteSpace(shortcut.BrowserPath)
16-
? shortcut.BrowserPath
17-
: DefaultBrowserProvider.Path;
17+
? shortcut.BrowserPath.Trim()
18+
: defaultBrowser.Path.Trim();
1819

1920
string? trimmedArgs;
2021

@@ -24,20 +25,26 @@ public BrowserExecutionInfo(WebSearchShortcutDataEntry shortcut)
2425
}
2526
else if (string.IsNullOrWhiteSpace(shortcut.BrowserPath))
2627
{
27-
trimmedArgs = DefaultBrowserProvider.ArgumentsPattern;
28+
trimmedArgs = defaultBrowser.ArgumentsPattern.Trim();
2829
}
2930
else
3031
{
3132
trimmedArgs = BrowsersDiscovery
32-
.GetAllInstalledBrowsers()
33-
.FirstOrDefault(b => string.Equals(b.Path, shortcut.BrowserPath, StringComparison.OrdinalIgnoreCase))
34-
?.ArgumentsPattern.Trim();
33+
.GetInstalledBrowsers()
34+
.FirstOrDefault(browser => string.Equals(browser.Path.Trim(), shortcut.BrowserPath.Trim(), StringComparison.OrdinalIgnoreCase))?
35+
.ArgumentsPattern.Trim();
3536
}
3637

37-
trimmedArgs ??= string.Empty;
38+
if (string.IsNullOrWhiteSpace(trimmedArgs))
39+
{
40+
trimmedArgs = string.Empty;
41+
}
42+
43+
if (!trimmedArgs.Contains("%1"))
44+
{
45+
trimmedArgs += " %1";
46+
}
3847

39-
ArgumentsPattern = trimmedArgs.Contains("%1", StringComparison.Ordinal)
40-
? trimmedArgs
41-
: trimmedArgs + " %1";
48+
ArgumentsPattern = trimmedArgs.Trim();
4249
}
4350
}
Lines changed: 79 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,72 @@
11
using System;
2-
using System.Linq;
32
using System.Collections.Generic;
3+
using System.Linq;
44
using System.Threading;
5+
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.Logging.Abstractions;
57
using Microsoft.Win32;
8+
using WebSearchShortcut.Helpers;
69

710
namespace WebSearchShortcut.Browser;
811

912
public static class BrowsersDiscovery
1013
{
11-
private static Lazy<BrowserInfo[]> _installedBrowsersCache = CreateInstalledBrowsersCache();
12-
private static Lazy<BrowserInfo[]> CreateInstalledBrowsersCache() =>
13-
new(LoadInstalledBrowsers, LazyThreadSafetyMode.ExecutionAndPublication);
14-
public static IReadOnlyCollection<BrowserInfo> GetAllInstalledBrowsers() => _installedBrowsersCache.Value;
15-
16-
public static void Reload(bool warm = false)
14+
private static ILogger _logger = NullLogger.Instance;
15+
public static ILogger Logger
1716
{
18-
var newCache = CreateInstalledBrowsersCache();
17+
get => _logger;
18+
set
19+
{
20+
_logger = value;
21+
_registryService = new RegistryLogService(value);
22+
_progIdBrowserResolver = new ProgIdBrowserService(value);
23+
}
24+
}
25+
26+
private static RegistryLogService _registryService = new(NullLogger.Instance);
27+
private static ProgIdBrowserService _progIdBrowserResolver = new(NullLogger.Instance);
1928

20-
Interlocked.Exchange(ref _installedBrowsersCache, newCache);
29+
private static Lazy<BrowserInfo[]> _cache = new(ScanInstalledBrowsers);
30+
public static IReadOnlyCollection<BrowserInfo> GetInstalledBrowsers() => _cache.Value;
31+
32+
public static void Update(bool warm = false)
33+
{
34+
Interlocked.Exchange(ref _cache, new Lazy<BrowserInfo[]>(ScanInstalledBrowsers));
2135

2236
if (warm)
23-
_ = newCache.Value;
37+
_ = _cache.Value;
2438
}
2539

26-
private static BrowserInfo[] LoadInstalledBrowsers()
40+
private static BrowserInfo[] ScanInstalledBrowsers()
2741
{
42+
using var scope = Logger.BeginStaticScope(typeof(BrowsersDiscovery));
43+
2844
string[] progIds = GetAssociatedProgIds();
29-
List<BrowserInfo> result = [];
45+
List<BrowserInfo> browsers = [];
3046

3147
foreach (var progId in progIds)
3248
{
3349
try
3450
{
35-
BrowserInfo info = ProgIdBrowserResolver.GetBrowserInfoFromProgId(progId);
36-
result.Add(info);
51+
BrowserInfo browser = _progIdBrowserResolver.GetBrowserInfoFromProgId(progId);
52+
browsers.Add(browser);
3753
}
38-
catch
54+
catch (Exception ex)
3955
{
56+
Logger.LogError(ex, ex.Message);
4057
}
4158
}
4259

43-
return [.. result.OrderBy(b => b.Name, StringComparer.OrdinalIgnoreCase)];
60+
Logger.LogInformation("Browsers loaded. Found {Count} installed browsers.", browsers.Count);
61+
foreach (var browser in browsers)
62+
{
63+
Logger.LogInformation("\t'{Name}' ('{Path} {ArgumentsPattern}')",
64+
browser.Name,
65+
browser.Path,
66+
browser.ArgumentsPattern);
67+
}
68+
69+
return [.. browsers.OrderBy(browser => browser.Name, StringComparer.OrdinalIgnoreCase)];
4470
}
4571

4672
private static string[] GetAssociatedProgIds()
@@ -68,29 +94,55 @@ private static string[] GetAssociatedProgIds()
6894
return [.. progIdSet];
6995
}
7096

71-
private static HashSet<string> ScanProgIdsFromRegistry(RegistryKey baseKey, string rootSubKey, string subKeySuffix)
97+
private static HashSet<string> ScanProgIdsFromRegistry(RegistryKey hiveKey, string containerPath, string targetSubPath)
7298
{
99+
using var scope = Logger.BeginStepScope(typeof(BrowsersDiscovery));
100+
101+
Logger.LogTrace("Starting registry scan. hiveKey: '{hiveKey}', containerPath: '{containerPath}', targetSubPath: '{targetSubPath}'", hiveKey.Name, containerPath, targetSubPath);
102+
73103
HashSet<string> progIds = [];
74104

75-
using RegistryKey? root = baseKey.OpenSubKey(rootSubKey);
76-
if (root == null) return progIds;
105+
using RegistryKey? containerKey = _registryService.OpenSubKey(hiveKey, containerPath, isRequired: false);
106+
if (containerKey is null)
107+
{
108+
Logger.LogDebug("Unique ProgIds count: {Count}\n", progIds.Count);
109+
110+
return progIds;
111+
}
112+
113+
string[] subKeyNames = containerKey.GetSubKeyNames();
114+
Logger.LogDebug("\tFound {Count} subkeys under '{Path}'", subKeyNames.Length, containerKey.Name);
77115

78-
foreach (string subName in root.GetSubKeyNames())
116+
foreach (string subKeyName in subKeyNames)
79117
{
80-
using RegistryKey? browserKey = root.OpenSubKey(subName);
81-
if (browserKey == null) continue;
118+
using var subScope = Logger.BeginScope($"'{subKeyName}'");
82119

83-
using RegistryKey? urlAssocKey = browserKey.OpenSubKey(subKeySuffix);
84-
if (urlAssocKey == null) continue;
120+
using RegistryKey? itemKey = _registryService.OpenSubKey(containerKey, subKeyName, isRequired: false);
121+
if (itemKey is null)
122+
continue;
85123

86-
string? progId = urlAssocKey.GetValue("https") as string
87-
?? urlAssocKey.GetValue("http") as string;
124+
using RegistryKey? urlAssocKey = _registryService.OpenSubKey(itemKey, targetSubPath, isRequired: false);
125+
if (urlAssocKey is null)
126+
continue;
88127

89-
if (!string.IsNullOrWhiteSpace(progId))
128+
string? httpsValue = _registryService.GetValue<string>(urlAssocKey, "https", isRequired: false);
129+
string? httpValue = _registryService.GetValue<string>(urlAssocKey, "http", isRequired: false);
130+
string? progId = httpsValue ?? httpValue;
131+
132+
if (string.IsNullOrWhiteSpace(progId))
133+
{
134+
Logger.LogDebug("No valid http/https ProgId found at: '{AssocPath}'", urlAssocKey.Name);
135+
}
136+
else
90137
{
91138
progIds.Add(progId.Trim());
139+
140+
Logger.LogDebug("Successfully retrieved ProgId: '{ProgId}' from '{AssocPath}'", progId.Trim(), urlAssocKey.Name);
92141
}
93142
}
143+
144+
Logger.LogDebug("Registry scan completed for '{FullPath}'. Unique ProgIds count: {Count}\n", containerKey.Name, progIds.Count);
145+
94146
return progIds;
95147
}
96148
}

0 commit comments

Comments
 (0)