using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Microsoft.Win32;
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.TestSupport.Probes;
///
/// Reads HKLM registry keys to confirm ArchestrA Framework / System Platform install
/// markers. Matches the registered paths documented in
/// docs/v2/implementation/ — System Platform is 32-bit so keys live under
/// HKLM\SOFTWARE\WOW6432Node\ArchestrA\....
///
public static class RegistryProbe
{
// Canonical install roots per the research on our dev box (System Platform 2020 R2).
public const string ArchestrARootKey = @"SOFTWARE\WOW6432Node\ArchestrA";
public const string FrameworkKey = @"SOFTWARE\WOW6432Node\ArchestrA\Framework";
public const string PlatformKey = @"SOFTWARE\WOW6432Node\ArchestrA\Framework\Platform";
public const string MsiInstallKey = @"SOFTWARE\WOW6432Node\ArchestrA\MSIInstall";
public static PrerequisiteCheck CheckFrameworkInstalled()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Skip, "Registry probes only run on Windows.");
}
return FrameworkInstalledWindows();
}
public static PrerequisiteCheck CheckPlatformDeployed()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new PrerequisiteCheck("registry:ArchestrA.Platform", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Skip, "Registry probes only run on Windows.");
}
return PlatformDeployedWindows();
}
public static PrerequisiteCheck CheckRebootPending()
{
if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Skip, "Registry probes only run on Windows.");
}
return RebootPendingWindows();
}
[SupportedOSPlatform("windows")]
private static PrerequisiteCheck FrameworkInstalledWindows()
{
try
{
using var key = Registry.LocalMachine.OpenSubKey(FrameworkKey);
if (key is null)
{
return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Fail,
$"Missing {FrameworkKey} — ArchestrA Framework isn't installed. Install AVEVA System Platform from the setup media.");
}
var installPath = key.GetValue("InstallPath") as string;
var rootPath = key.GetValue("RootPath") as string;
if (string.IsNullOrWhiteSpace(installPath) || string.IsNullOrWhiteSpace(rootPath))
{
return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
$"Framework key exists but InstallPath/RootPath values missing — install may be incomplete.");
}
return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Pass,
$"Installed at {installPath} (RootPath {rootPath}).");
}
catch (Exception ex)
{
return new PrerequisiteCheck("registry:ArchestrA.Framework", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
$"Probe failed: {ex.GetType().Name}: {ex.Message}");
}
}
[SupportedOSPlatform("windows")]
private static PrerequisiteCheck PlatformDeployedWindows()
{
try
{
using var key = Registry.LocalMachine.OpenSubKey(PlatformKey);
var pfeConfig = key?.GetValue("PfeConfigOptions") as string;
if (string.IsNullOrWhiteSpace(pfeConfig))
{
return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
$"No Platform object deployed locally (Platform\\PfeConfigOptions empty). MXAccess will connect but subscriptions will fail. Deploy a Platform from the IDE.");
}
// PfeConfigOptions format: "PlatformId=N,EngineId=N,EngineName=...,..."
// A non-deployed state leaves PlatformId=0 or the key empty.
if (pfeConfig.Contains("PlatformId=0,", StringComparison.OrdinalIgnoreCase))
{
return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
$"Platform never deployed (PfeConfigOptions has PlatformId=0). Deploy a Platform from the IDE before running live tests.");
}
return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Pass,
$"Platform deployed ({pfeConfig}).");
}
catch (Exception ex)
{
return new PrerequisiteCheck("registry:ArchestrA.Platform.Deployed", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
$"Probe failed: {ex.GetType().Name}: {ex.Message}");
}
}
[SupportedOSPlatform("windows")]
private static PrerequisiteCheck RebootPendingWindows()
{
try
{
using var key = Registry.LocalMachine.OpenSubKey(MsiInstallKey);
var rebootRequired = key?.GetValue("RebootRequired") as string;
if (string.Equals(rebootRequired, "True", StringComparison.OrdinalIgnoreCase))
{
return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
"An ArchestrA patch has been installed but the machine hasn't rebooted. Post-patch behavior is undefined until a reboot.");
}
return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Pass,
"No pending reboot flagged.");
}
catch (Exception ex)
{
return new PrerequisiteCheck("registry:ArchestrA.RebootPending", PrerequisiteCategory.AvevaInstall,
PrerequisiteStatus.Warn,
$"Probe failed: {ex.GetType().Name}: {ex.Message}");
}
}
///
/// Read the registered CLSID for the given ProgID and
/// resolve the 32-bit InprocServer32 file path. Returns null when either is missing.
///
[SupportedOSPlatform("windows")]
internal static (string? Clsid, string? InprocDllPath) ResolveProgIdToInproc(string progId)
{
using var progIdKey = Registry.ClassesRoot.OpenSubKey($@"{progId}\CLSID");
var clsid = progIdKey?.GetValue(null) as string;
if (string.IsNullOrWhiteSpace(clsid)) return (null, null);
// 32-bit COM server under Wow6432Node\CLSID\{guid}\InprocServer32 default value.
using var inproc = Registry.LocalMachine.OpenSubKey(
$@"SOFTWARE\Classes\WOW6432Node\CLSID\{clsid}\InprocServer32");
var dll = inproc?.GetValue(null) as string;
return (clsid, dll);
}
}