zcbot/windows-node/Zcbot.WindowsNode/OriginRuntimeProbe.cs

103 lines
4.1 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 桌面会话");
}
var interpreter = OriginWorkerRuntime.ResolveInterpreter();
if (interpreter is null)
{
return Status(
version,
"unavailable",
"Origin 可用,但固定 Python 运行时缺失;请配置 ZCBOT_ORIGIN_PYTHON");
}
return Status(version, "ready", $"Origin COM 与固定 Python 运行时可用({interpreter}");
}
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.3.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);
}