95 lines
3.8 KiB
C#
95 lines
3.8 KiB
C#
using Microsoft.Win32;
|
||
using System.Security;
|
||
|
||
namespace Zcbot.WindowsNode;
|
||
|
||
internal sealed record OriginRuntimeStatus(
|
||
string Software,
|
||
string? SoftwareVersion,
|
||
string AdapterVersion,
|
||
string Health,
|
||
string Detail);
|
||
|
||
internal static class OriginRuntimeProbe
|
||
{
|
||
private static readonly Lazy<OriginRuntimeStatus> Current = new(DetectCore);
|
||
private const string AutomationProgId = @"Origin.ApplicationSI\CLSID";
|
||
|
||
internal static OriginRuntimeStatus Detect() => Current.Value;
|
||
|
||
private static OriginRuntimeStatus DetectCore()
|
||
{
|
||
try
|
||
{
|
||
var version = FindInstalledVersion();
|
||
using var automationKey = Registry.ClassesRoot.OpenSubKey(AutomationProgId);
|
||
var automationRegistered = automationKey is not null;
|
||
if (version is null && !automationRegistered)
|
||
{
|
||
return Status(null, "unavailable", "未检测到 Origin/OriginPro 安装");
|
||
}
|
||
if (!automationRegistered)
|
||
{
|
||
return Status(version, "unavailable", "已检测到 Origin,但 COM 自动化组件未注册");
|
||
}
|
||
if (!Environment.UserInteractive)
|
||
{
|
||
return Status(version, "unavailable", "Origin 需要交互式 Windows 桌面会话");
|
||
}
|
||
return Status(version, "ready", "Origin COM 自动化组件可用");
|
||
}
|
||
catch (Exception exception) when (
|
||
exception is SecurityException or UnauthorizedAccessException or IOException)
|
||
{
|
||
return Status(null, "unavailable", $"Origin 运行时探测失败:{exception.Message}");
|
||
}
|
||
}
|
||
|
||
private static OriginRuntimeStatus Status(string? version, string health, string detail) =>
|
||
new("OriginPro", version, "0.1.0", health, detail);
|
||
|
||
private static string? FindInstalledVersion()
|
||
{
|
||
var candidates = new List<string>();
|
||
foreach (var hive in new[] { RegistryHive.LocalMachine, RegistryHive.CurrentUser })
|
||
{
|
||
foreach (var view in new[] { RegistryView.Registry64, RegistryView.Registry32 })
|
||
{
|
||
using var baseKey = RegistryKey.OpenBaseKey(hive, view);
|
||
using var uninstall = baseKey.OpenSubKey(
|
||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall");
|
||
if (uninstall is null)
|
||
{
|
||
continue;
|
||
}
|
||
foreach (var keyName in uninstall.GetSubKeyNames())
|
||
{
|
||
using var product = uninstall.OpenSubKey(keyName);
|
||
var name = product?.GetValue("DisplayName") as string;
|
||
var publisher = product?.GetValue("Publisher") as string;
|
||
if (!IsOriginProduct(name, publisher))
|
||
{
|
||
continue;
|
||
}
|
||
var version = product?.GetValue("DisplayVersion") as string;
|
||
if (!string.IsNullOrWhiteSpace(version))
|
||
{
|
||
candidates.Add(version.Trim());
|
||
}
|
||
}
|
||
}
|
||
}
|
||
return candidates.OrderByDescending(ParseVersion).ThenByDescending(x => x).FirstOrDefault();
|
||
}
|
||
|
||
private static bool IsOriginProduct(string? name, string? publisher) =>
|
||
!string.IsNullOrWhiteSpace(name)
|
||
&& (name.Equals("Origin", StringComparison.OrdinalIgnoreCase)
|
||
|| name.StartsWith("Origin ", StringComparison.OrdinalIgnoreCase)
|
||
|| name.StartsWith("OriginPro", StringComparison.OrdinalIgnoreCase))
|
||
&& (publisher?.Contains("OriginLab", StringComparison.OrdinalIgnoreCase) ?? false);
|
||
|
||
private static Version ParseVersion(string value) =>
|
||
Version.TryParse(value, out var version) ? version : new Version(0, 0);
|
||
}
|