Skip to content

Commit dd763aa

Browse files
Prefer bundled WinGet COM activation and log activation source
Prefer the bundled WinGet COM server over packaged COM registration when initializing the native WinGet helper. Also add structured activation failure handling and surface the selected activation mode and source in logs and manager status so fallback behavior is easier to diagnose.
1 parent 8757d9f commit dd763aa

6 files changed

Lines changed: 418 additions & 45 deletions

File tree

src/UniGetUI.Core.Logger/Logger.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@ namespace UniGetUI.Core.Logging
55
public static class Logger
66
{
77
private static readonly List<LogEntry> LogContents = [];
8+
private static readonly Lock LogWriteLock = new();
9+
private static readonly string SessionLogPath = Path.Combine(
10+
Path.GetTempPath(),
11+
"UniGetUI",
12+
"session.log"
13+
);
14+
15+
public static string GetSessionLogPath() => SessionLogPath;
16+
17+
private static void AppendToSessionLog(string text)
18+
{
19+
try
20+
{
21+
Directory.CreateDirectory(Path.GetDirectoryName(SessionLogPath)!);
22+
lock (LogWriteLock)
23+
{
24+
File.AppendAllText(
25+
SessionLogPath,
26+
$"[{DateTime.Now:yyyy-MM-dd h:mm:ss tt}] {text}{Environment.NewLine}"
27+
);
28+
}
29+
}
30+
catch { }
31+
}
832

933
// String parameter log functions
1034
public static void ImportantInfo(
@@ -13,6 +37,7 @@ public static void ImportantInfo(
1337
)
1438
{
1539
Diagnostics.Debug.WriteLine($"[{caller}] " + s);
40+
AppendToSessionLog(s);
1641
LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Success));
1742
}
1843

@@ -22,6 +47,7 @@ public static void Debug(
2247
)
2348
{
2449
Diagnostics.Debug.WriteLine($"[{caller}] " + s);
50+
AppendToSessionLog(s);
2551
LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Debug));
2652
}
2753

@@ -31,6 +57,7 @@ public static void Info(
3157
)
3258
{
3359
Diagnostics.Debug.WriteLine($"[{caller}] " + s);
60+
AppendToSessionLog(s);
3461
LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Info));
3562
}
3663

@@ -40,6 +67,7 @@ public static void Warn(
4067
)
4168
{
4269
Diagnostics.Debug.WriteLine($"[{caller}] " + s);
70+
AppendToSessionLog(s);
4371
LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Warning));
4472
}
4573

@@ -49,6 +77,7 @@ public static void Error(
4977
)
5078
{
5179
Diagnostics.Debug.WriteLine($"[{caller}] " + s);
80+
AppendToSessionLog(s);
5281
LogContents.Add(new LogEntry(s, LogEntry.SeverityLevel.Error));
5382
}
5483

@@ -59,6 +88,7 @@ public static void ImportantInfo(
5988
)
6089
{
6190
Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString());
91+
AppendToSessionLog(e.ToString());
6292
LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Success));
6393
}
6494

@@ -68,6 +98,7 @@ public static void Debug(
6898
)
6999
{
70100
Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString());
101+
AppendToSessionLog(e.ToString());
71102
LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Debug));
72103
}
73104

@@ -77,6 +108,7 @@ public static void Info(
77108
)
78109
{
79110
Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString());
111+
AppendToSessionLog(e.ToString());
80112
LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Info));
81113
}
82114

@@ -86,6 +118,7 @@ public static void Warn(
86118
)
87119
{
88120
Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString());
121+
AppendToSessionLog(e.ToString());
89122
LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Warning));
90123
}
91124

@@ -95,6 +128,7 @@ public static void Error(
95128
)
96129
{
97130
Diagnostics.Debug.WriteLine($"[{caller}] " + e.ToString());
131+
AppendToSessionLog(e.ToString());
98132
LogContents.Add(new LogEntry(e.ToString(), LogEntry.SeverityLevel.Error));
99133
}
100134

src/UniGetUI.PackageEngine.Managers.WinGet/ClientHelpers/NativeWinGetHelper.cs

Lines changed: 103 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,15 @@ namespace UniGetUI.PackageEngine.Managers.WingetManager;
1616

1717
internal sealed class NativeWinGetHelper : IWinGetManagerHelper
1818
{
19-
public WindowsPackageManagerFactory Factory;
19+
public WindowsPackageManagerFactory Factory = null!;
2020
public static WindowsPackageManagerFactory? ExternalFactory;
21-
public PackageManager WinGetManager;
21+
public PackageManager WinGetManager = null!;
2222
public static PackageManager? ExternalWinGetManager;
2323
private readonly WinGet Manager;
2424

25+
public string ActivationMode { get; private set; } = string.Empty;
26+
public string ActivationSource { get; private set; } = string.Empty;
27+
2528
public NativeWinGetHelper(WinGet manager)
2629
{
2730
Manager = manager;
@@ -32,25 +35,113 @@ public NativeWinGetHelper(WinGet manager)
3235
);
3336
}
3437

38+
if (TryInitializeBundledFactory())
39+
{
40+
return;
41+
}
42+
43+
if (TryInitializeStandardFactory())
44+
{
45+
return;
46+
}
47+
48+
InitializeLowerTrustFactory();
49+
}
50+
51+
private bool TryInitializeBundledFactory()
52+
{
3553
try
3654
{
37-
Factory = new WindowsPackageManagerStandardFactory();
38-
WinGetManager = Factory.CreatePackageManager();
39-
ExternalFactory = Factory;
40-
ExternalWinGetManager = WinGetManager;
55+
var factory = new WindowsPackageManagerBundledFactory();
56+
var winGetManager = factory.CreatePackageManager();
57+
ApplyFactory(
58+
factory,
59+
winGetManager,
60+
"bundled in-proc COM",
61+
factory.LibraryPath,
62+
"Connected to WinGet API using bundled in-proc activation."
63+
);
64+
return true;
65+
}
66+
catch (WinGetComActivationException ex)
67+
{
68+
Logger.Warn(
69+
$"Bundled WinGet in-proc activation failed ({ex.HResultHex}: {ex.Reason}), attempting packaged COM activation..."
70+
);
71+
return false;
4172
}
42-
catch
73+
catch (Exception ex)
4374
{
4475
Logger.Warn(
45-
"Couldn't connect to WinGet API, attempting to connect with lower trust... (Are you running as administrator?)"
76+
$"Bundled WinGet in-proc activation failed ({ex.Message}), attempting packaged COM activation..."
4677
);
47-
Factory = new WindowsPackageManagerStandardFactory(allowLowerTrustRegistration: true);
48-
WinGetManager = Factory.CreatePackageManager();
49-
ExternalFactory = Factory;
50-
ExternalWinGetManager = WinGetManager;
78+
return false;
5179
}
5280
}
5381

82+
private bool TryInitializeStandardFactory()
83+
{
84+
try
85+
{
86+
var factory = new WindowsPackageManagerStandardFactory();
87+
var winGetManager = factory.CreatePackageManager();
88+
ApplyFactory(
89+
factory,
90+
winGetManager,
91+
"packaged COM registration",
92+
"system COM registration",
93+
"Connected to WinGet API using packaged COM activation."
94+
);
95+
return true;
96+
}
97+
catch (WinGetComActivationException ex)
98+
{
99+
Logger.Warn(
100+
$"Packaged WinGet COM activation failed ({ex.HResultHex}: {ex.Reason}), attempting lower-trust activation..."
101+
);
102+
return false;
103+
}
104+
catch (Exception ex)
105+
{
106+
Logger.Warn(
107+
$"Packaged WinGet COM activation failed ({ex.Message}), attempting lower-trust activation..."
108+
);
109+
return false;
110+
}
111+
}
112+
113+
private void InitializeLowerTrustFactory()
114+
{
115+
var factory = new WindowsPackageManagerStandardFactory(allowLowerTrustRegistration: true);
116+
var winGetManager = factory.CreatePackageManager();
117+
ApplyFactory(
118+
factory,
119+
winGetManager,
120+
"lower-trust COM registration",
121+
"system COM registration (allow lower trust)",
122+
"Connected to WinGet API using lower-trust COM activation."
123+
);
124+
}
125+
126+
private void ApplyFactory(
127+
WindowsPackageManagerFactory factory,
128+
PackageManager winGetManager,
129+
string activationMode,
130+
string activationSource,
131+
string successMessage
132+
)
133+
{
134+
Factory = factory;
135+
WinGetManager = winGetManager;
136+
ActivationMode = activationMode;
137+
ActivationSource = activationSource;
138+
ExternalFactory = factory;
139+
ExternalWinGetManager = winGetManager;
140+
141+
Logger.Info(successMessage);
142+
Logger.Info($"WinGet activation mode selected: {ActivationMode} | Source: {ActivationSource}");
143+
}
144+
54145
public IReadOnlyList<Package> FindPackages_UnSafe(string query)
55146
{
56147
List<Package> packages = [];

src/UniGetUI.PackageEngine.Managers.WinGet/WinGet.cs

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
using UniGetUI.PackageEngine.ManagerClasses.Classes;
1515
using UniGetUI.PackageEngine.ManagerClasses.Manager;
1616
using UniGetUI.PackageEngine.PackageClasses;
17+
using WindowsPackageManager.Interop;
1718
using Architecture = UniGetUI.PackageEngine.Enums.Architecture;
1819

1920
namespace UniGetUI.PackageEngine.Managers.WingetManager
@@ -319,10 +320,24 @@ out string callArguments
319320
}
320321
catch (Exception ex)
321322
{
322-
Logger.Warn(
323-
$"Cannot instantiate {(FORCE_BUNDLED ? "Bundled" : "Native")} WinGet Helper due to error: {ex.Message}"
324-
);
325-
Logger.Warn(ex);
323+
if (
324+
!FORCE_BUNDLED
325+
&& ex is WinGetComActivationException activationEx
326+
&& activationEx.IsExpectedFallbackCondition
327+
)
328+
{
329+
Logger.Warn(
330+
$"Native WinGet helper is unavailable on this machine ({activationEx.HResultHex}: {activationEx.Reason})"
331+
);
332+
}
333+
else
334+
{
335+
Logger.Warn(
336+
$"Cannot instantiate {(FORCE_BUNDLED ? "Bundled" : "Native")} WinGet Helper due to error: {ex.Message}"
337+
);
338+
Logger.Warn(ex);
339+
}
340+
326341
Logger.Warn("WinGet will resort to using BundledWinGetHelper()");
327342
WinGetHelper.Instance = new BundledWinGetHelper(this);
328343
}
@@ -361,8 +376,16 @@ protected override void _loadManagerVersion(out string version)
361376
if (IS_BUNDLED)
362377
version += "\nUsing bundled WinGet helper (CLI parsing)";
363378
else
379+
{
364380
version += "\nUsing Native WinGet helper (COM Api)";
365381

382+
if (WinGetHelper.Instance is NativeWinGetHelper nativeHelper)
383+
{
384+
version += $"\nActivation mode: {nativeHelper.ActivationMode}";
385+
version += $"\nActivation source: {nativeHelper.ActivationSource}";
386+
}
387+
}
388+
366389
string error = process.StandardError.ReadToEnd();
367390
if (error != "")
368391
Logger.Error("WinGet STDERR not empty: " + error);
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
using System.Runtime.InteropServices;
2+
3+
namespace WindowsPackageManager.Interop;
4+
5+
public sealed class WinGetComActivationException : COMException
6+
{
7+
public Guid Clsid { get; }
8+
public Guid Iid { get; }
9+
public bool AllowLowerTrustRegistration { get; }
10+
public string HResultHex => $"0x{HResult:X8}";
11+
12+
public string Reason => DescribeHResult(HResult);
13+
14+
public bool IsExpectedFallbackCondition =>
15+
HResult is
16+
unchecked((int)0x80040154) or
17+
unchecked((int)0x80070490) or
18+
unchecked((int)0x80070002) or
19+
unchecked((int)0x8000000F)
20+
|| Message.Contains("Element not found", StringComparison.OrdinalIgnoreCase)
21+
|| Message.Contains(
22+
"Typename or Namespace was not found in metadata file",
23+
StringComparison.OrdinalIgnoreCase
24+
);
25+
26+
public WinGetComActivationException(
27+
Guid clsid,
28+
Guid iid,
29+
int hresult,
30+
bool allowLowerTrustRegistration
31+
)
32+
: base(CreateMessage(clsid, iid, hresult, allowLowerTrustRegistration), hresult)
33+
{
34+
Clsid = clsid;
35+
Iid = iid;
36+
AllowLowerTrustRegistration = allowLowerTrustRegistration;
37+
}
38+
39+
private static string CreateMessage(
40+
Guid clsid,
41+
Guid iid,
42+
int hresult,
43+
bool allowLowerTrustRegistration
44+
) =>
45+
$"WinGet COM activation failed for CLSID {clsid} (IID {iid}, AllowLowerTrustRegistration={allowLowerTrustRegistration}): {DescribeHResult(hresult)}";
46+
47+
private static string DescribeHResult(int hresult) => hresult switch
48+
{
49+
unchecked((int)0x80040154) => "Class not registered",
50+
unchecked((int)0x80070490) => "Element not found",
51+
unchecked((int)0x80070002) => "File not found",
52+
unchecked((int)0x8000000F) => "Typename or Namespace was not found in metadata file",
53+
_ => Marshal.GetExceptionForHR(hresult)?.Message ?? $"HRESULT 0x{hresult:X8}",
54+
};
55+
}

0 commit comments

Comments
 (0)