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); } }