using System.Runtime.InteropServices; using System.Runtime.Versioning; using Microsoft.Win32; namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Proxy.Tests.LiveStack; /// /// Resolves the pipe name + shared secret the live needs /// to connect to a running OtOpcUaGalaxyHost Windows service. Two sources are /// consulted, first match wins: /// /// Explicit env vars (OTOPCUA_GALAXY_PIPE, OTOPCUA_GALAXY_SECRET) — lets CI / benchwork override. /// The service's per-process Environment registry values under /// HKLM\SYSTEM\CurrentControlSet\Services\OtOpcUaGalaxyHost — what /// Install-Services.ps1 writes at install time. Requires the test to run as a /// principal with read access to that registry key (typically Administrators). /// /// /// /// Explicitly NOT baked-in-to-source: the shared secret is rotated per install (the /// installer generates 32 random bytes and stores the base64 string). A hard-coded secret /// in tests would diverge from production the moment someone re-installed the service. /// public sealed record LiveStackConfig(string PipeName, string SharedSecret, string? Source) { public const string EnvPipeName = "OTOPCUA_GALAXY_PIPE"; public const string EnvSharedSecret = "OTOPCUA_GALAXY_SECRET"; public const string ServiceRegistryKey = @"SYSTEM\CurrentControlSet\Services\OtOpcUaGalaxyHost"; public const string DefaultPipeName = "OtOpcUaGalaxy"; public static LiveStackConfig? Resolve() { var envPipe = Environment.GetEnvironmentVariable(EnvPipeName); var envSecret = Environment.GetEnvironmentVariable(EnvSharedSecret); if (!string.IsNullOrWhiteSpace(envPipe) && !string.IsNullOrWhiteSpace(envSecret)) return new LiveStackConfig(envPipe, envSecret, "env vars"); if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return null; return FromServiceRegistry(); } [SupportedOSPlatform("windows")] private static LiveStackConfig? FromServiceRegistry() { try { using var key = Registry.LocalMachine.OpenSubKey(ServiceRegistryKey); if (key is null) return null; var env = key.GetValue("Environment") as string[]; if (env is null || env.Length == 0) return null; string? pipe = null, secret = null; foreach (var line in env) { var eq = line.IndexOf('='); if (eq <= 0) continue; var name = line[..eq]; var value = line[(eq + 1)..]; if (name.Equals(EnvPipeName, StringComparison.OrdinalIgnoreCase)) pipe = value; else if (name.Equals(EnvSharedSecret, StringComparison.OrdinalIgnoreCase)) secret = value; } if (string.IsNullOrWhiteSpace(secret)) return null; return new LiveStackConfig(pipe ?? DefaultPipeName, secret, "service registry"); } catch { // Access denied / key missing / malformed — caller gets null and surfaces a Skip. return null; } } }