- Product key dialog: Fluent overlay with 5-field input + paste support
- Activate button on each license card asks for key first (/inpkey + /act)
- Auto-refresh lists after removal/activation on all pages
- Access checkbox disabled for Standard and Home & Business editions
- MSI uninstall: only accept {GUID} product codes (fix msiexec help popup)
- Window height properly resets when Details expander is collapsed
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
117 sor
4.4 KiB
C#
117 sor
4.4 KiB
C#
using System.Collections.Generic;
|
|
using System.Text.RegularExpressions;
|
|
using InstaSoftOfficeTool.Models;
|
|
using Microsoft.Win32;
|
|
|
|
namespace InstaSoftOfficeTool.Services
|
|
{
|
|
public static class OfficeDetector
|
|
{
|
|
public static List<InstalledOffice> Detect()
|
|
{
|
|
var results = new List<InstalledOffice>();
|
|
|
|
// Check Click-to-Run
|
|
DetectClickToRun(results);
|
|
|
|
// Check MSI-based installs
|
|
DetectMsi(results);
|
|
|
|
return results;
|
|
}
|
|
|
|
private static void DetectClickToRun(List<InstalledOffice> results)
|
|
{
|
|
try
|
|
{
|
|
using (var key = Registry.LocalMachine.OpenSubKey(
|
|
@"SOFTWARE\Microsoft\Office\ClickToRun\Configuration"))
|
|
{
|
|
if (key == null) return;
|
|
|
|
var productIds = key.GetValue("ProductReleaseIds") as string;
|
|
var versionToReport = key.GetValue("VersionToReport") as string;
|
|
|
|
if (string.IsNullOrEmpty(productIds)) return;
|
|
|
|
// Split into individual products (e.g. "O365BusinessRetail,VisioProRetail")
|
|
var ids = productIds.Split(',');
|
|
foreach (var id in ids)
|
|
{
|
|
var trimmedId = id.Trim();
|
|
if (string.IsNullOrEmpty(trimmedId)) continue;
|
|
|
|
results.Add(new InstalledOffice
|
|
{
|
|
DisplayName = "Microsoft Office Click-to-Run (" + trimmedId + ")",
|
|
Version = versionToReport ?? "",
|
|
ProductCode = trimmedId,
|
|
IsClickToRun = true,
|
|
IsSelected = true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private static void DetectMsi(List<InstalledOffice> results)
|
|
{
|
|
var uninstallPaths = new[]
|
|
{
|
|
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
|
|
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
|
|
};
|
|
|
|
foreach (var path in uninstallPaths)
|
|
{
|
|
try
|
|
{
|
|
using (var key = Registry.LocalMachine.OpenSubKey(path))
|
|
{
|
|
if (key == null) continue;
|
|
|
|
foreach (var subKeyName in key.GetSubKeyNames())
|
|
{
|
|
using (var subKey = key.OpenSubKey(subKeyName))
|
|
{
|
|
if (subKey == null) continue;
|
|
|
|
var displayName = subKey.GetValue("DisplayName") as string;
|
|
var publisher = subKey.GetValue("Publisher") as string;
|
|
|
|
if (displayName != null &&
|
|
publisher != null &&
|
|
publisher.Contains("Microsoft") &&
|
|
(displayName.Contains("Microsoft Office") ||
|
|
displayName.Contains("Microsoft 365")))
|
|
{
|
|
var version = subKey.GetValue("DisplayVersion") as string ?? "";
|
|
|
|
// Skip Click-to-Run updater entries
|
|
if (displayName.Contains("Click-to-Run") &&
|
|
!displayName.Contains("Microsoft Office"))
|
|
continue;
|
|
|
|
// Only accept {GUID} product codes for MSI uninstall
|
|
bool isGuid = Regex.IsMatch(subKeyName, @"^\{[0-9A-Fa-f\-]+\}$");
|
|
|
|
results.Add(new InstalledOffice
|
|
{
|
|
DisplayName = displayName,
|
|
Version = version,
|
|
ProductCode = isGuid ? subKeyName : null,
|
|
IsClickToRun = false,
|
|
IsSelected = true
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch { }
|
|
}
|
|
}
|
|
}
|
|
}
|