diff --git a/graccesscli/analysis/ide-edit-investigation/REPORT.md b/graccesscli/analysis/ide-edit-investigation/REPORT.md new file mode 100644 index 0000000..e12be35 --- /dev/null +++ b/graccesscli/analysis/ide-edit-investigation/REPORT.md @@ -0,0 +1,82 @@ +# How the IDE edits scripts — investigation findings (corrected) + +**Status:** my original report (and the safety-check + docs note that grew out of it) was **wrong**. AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess", April 2008) documents the supported pattern, and a live round-trip probe confirmed it works on this Galaxy. graccesscli's existing `FindAttributeForMutation` already follows this pattern correctly; the apparent "writeback gap" was a reader-side gap in `MxValueDetails`. + +This document supersedes the earlier "package-only no-op" claim. The reverts on `master`: commits `4e242ca` (safety check) and `e4e5425` (docs note) plus the corrective commit on top. + +## What's actually true + +Per **AVEVA Tech Note 537** (page 6-7): + +> First we add a new script into the list of scripts. Then we save the object. Saving the object generates all the attributes required to configure the script the conventional way. […] Configurable attributes of the ScriptExtension primitive: ExecuteText, AliasReferences, Aliases, TriggerType, DataChangeDeadband, Expression, DeclarationsText, StartupText, ShutdownText, OnScanText, OffScanText, ExecutionError.Alarmed, TriggerPeriod, ScriptExecutionGroup, ScriptOrder, RunsAsync, State.Historized, ExecuteTimeout.Limit, TriggerOnQualityChange. + +The supported edit sequence is: + +```csharp +// 1. Acquire the object. +var galaxy = galaxies[GalaxyName]; +galaxy.Login(user, pwd); +var obj = galaxy.QueryObjectsByName(EgObjectIsTemplateOrInstance.gObjectIsTemplate, ref names)[1]; + +// 2. Mutation lifecycle. +obj.CheckOut(); + +// 3. Add the primitive (only when creating a new script). +obj.AddExtensionPrimitive("ScriptExtension", scriptName, true); +obj.Save(); // generates the script's child attributes + +// 4. Write text via ConfigurableAttributes — NOT Attributes. +IAttribute body = obj.ConfigurableAttributes[scriptName + ".ExecuteText"]; +var v = new MxValueClass(); +v.PutString("// new script body..."); +body.SetValue(v); +// (repeat for DeclarationsText, TriggerType, Expression, etc.) + +// 5. Persist + release. +obj.Save(); +obj.CheckIn(""); +``` + +graccesscli's `FindAttributeForMutation` (line 988-995) already prefers `ConfigurableAttributes` first and falls back to `Attributes`. `ObjectScriptSet` in the dispatcher writes via that helper. So `object scripts set --field --file ` follows the TN-537 pattern. + +## Live verification + +The probe at `analysis/ide-edit-investigation/probe_setvalue/` exercises the TN-537 sequence directly against `$TestMachine.UpdateTestChangingInt.DeclarationsText` and against `$DelmiaReceiver.ProcessRecipe.{ExecuteText,DeclarationsText}` on the local ZB galaxy. Result of the four-way verify after a marker write: + +``` +=== verdict === + marker landed on same-proxy ConfigurableAttributes: True + marker landed on same-proxy Attributes : True + marker landed on fresh-proxy ConfigurableAttributes: True + marker landed on fresh-proxy Attributes : True +``` + +Same probe also confirmed two earlier `graccesscli object scripts set` invocations had persisted (the `// roundtrip-test marker (graccesscli scripts set --field DeclarationsText)` text was still in `ProcessRecipe.DeclarationsText` when re-read directly via `attr.value.GetString()`). + +## Why my earlier diagnosis went wrong + +My earlier round-trip test wrote with graccesscli's `object scripts set`, then read back with `object attribute value get` — and the read returned `Supported: False, Value: None, Unavailable: "Attribute value is not exposed by this GRAccess attribute."` I read this as "the write didn't persist." It actually means **the reader couldn't surface the value**, while the write *had* persisted. + +The reader gap is in `GRAccessCommandDispatcher.MxValueDetails` (line 1382-1417) plus `AttributeValueDetails` (line 1340-1380): + +```csharp +var direct = MxValueDetails(ReadProperty(attr, "Value")); +``` + +`ReadProperty(attr, "Value")` uses `Type.InvokeMember(name, BindingFlags.GetProperty, ...)` which is **case-sensitive** by default. The COM-side property is exposed as `value` (lowercase) on `IAttribute` — what the probe successfully reads via `attr.value`. The capital-V `"Value"` lookup may either return `null` or hit a different sibling, and `MxValueDetails(null)` short-circuits to "not exposed." Even when `ReadProperty` does return the MxValue object, `MxValueDetails`'s scalar accessor probe (`GetBoolean` → `GetInteger` → `GetFloat` → `GetDouble` → `GetString`) can fall through silently for some types. + +This is a real gap in `attribute value get` that should be fixed separately — but it's a *reader* problem, not a write-side issue. The script body content does land on disk, and `object scripts get --llm-json` (which uses the package-export fallback) reflects it correctly. + +## What changed in this correction + +- **Reverted** `bd95ace` (the safety check `EnsureMutableViaSetValue`) and `c12fbc5` (the wrong docs note about a "package-only writeback boundary"). +- **Removed** two leftover `EnsureMutableViaSetValue` call sites the user had added in `87c0124` for the trigger-period / trigger-type write paths (both call sites also targeted `MxCategoryWriteable_C_Lockable` attributes, so they would never have fired even if the helper still existed — removing keeps the dispatcher consistent with the truth). +- **Updated** the misleading `[Command(...)]` / `[CommandOption(...)]` descriptions in `GRAccessSurfaceCommands.cs` back to honest versions citing TN-537. +- **Restored** the `--file`-using examples for `object scripts set` and `object scripts create` that `87c0124` removed across `script-editing.md`, `llm-integration.md`, `usage.md`, `zb-testmachine.md`. +- **Updated** test assertions accordingly: removed `DispatcherScriptSettings_GuardsPackageOnlyNoOpsBeforeSetValue` (asserted the wrong helper) and re-aimed `ScriptCommandDescriptions_…` at the corrected wording. +- **Rewrote** `analysis/ide-edit-investigation/REPORT.md` (this file) to document the truth. + +## Open follow-ups (≤2) + +1. **`MxValueDetails` reader gap.** `object attribute value get` against ScriptExtension text fields and similar `IAttribute` shapes returns `Supported: False` despite the value being set. Likely fix: case-insensitive `ReadProperty` lookup *and* try `GetString` first when the underlying `MxValue.DataType` is `MxString`. Out of scope for this corrective commit. +2. **The decompiled `IConfigurationAccess2` / `XxGalaxyPackageServer` analysis** under `cac/`, `appcfg/`, `scriptpkg/` is still useful as an *internal IDE map* — but it's not the *required* CLI write path, just an alternative one the IDE happens to use. Keep the artifacts; they document the IDE-internal channel honestly. diff --git a/graccesscli/analysis/ide-edit-investigation/appcfg/ArchestrA.Client.AppConfigInt.decompiled.cs b/graccesscli/analysis/ide-edit-investigation/appcfg/ArchestrA.Client.AppConfigInt.decompiled.cs new file mode 100644 index 0000000..23b0ec0 --- /dev/null +++ b/graccesscli/analysis/ide-edit-investigation/appcfg/ArchestrA.Client.AppConfigInt.decompiled.cs @@ -0,0 +1,1114 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.ComponentModel; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using ArchestrA.Client.AppConfig; +using ArchestrA.Client.AppConfigInt.Resources; +using ArchestrA.Client.CommonCore; +using ArchestrA.Client.CommonTypes; +using ArchestrA.Client.HostContracts; +using ArchestrA.Configuration; +using ArchestrA.Configuration.GalaxyGraphics; +using ArchestrA.Core; +using ArchestrA.Diagnostics; +using ArchestrA.Visualization.Client.Common; +using ArchestrA.Visualization.CommandSupport; +using ArchestrA.Visualization.CommonUI.Dialogs; +using ArchestrA.Visualization.Display.Logic; +using ArchestrA.Visualization.Display.Logic.Common; +using ArchestrA.Visualization.DisplayModuleSupport; +using ArchestrA.Visualization.EntitySupport; +using ArchestrA.Visualization.ModernEditors; +using Prism.Mvvm; + +[assembly: CompilationRelaxations(8)] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] +[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] +[assembly: AssemblyCompany("AVEVA Software, PLC.")] +[assembly: AssemblyCopyright("Copyright 2020 AVEVA Group plc and its subsidiaries. All rights reserved.")] +[assembly: AssemblyTrademark("Refer to: https://sw.aveva.com/legal/trademarks")] +[assembly: AssemblyProduct("AVEVA Application Server")] +[assembly: AssemblyInformationalVersion("20.0.000")] +[assembly: AssemblyFileVersion("2917.0626.2833.2")] +[assembly: SuppressMessage("Microsoft.Design", "CA1020:AvoidNamespacesWithFewTypes", Scope = "namespace", Target = "ArchestrA.Client.AppConfigInt")] +[assembly: AssemblyTitle("ArchestrA.Client.AppConfigInt")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en")] +[assembly: ComVisible(false)] +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] +[assembly: InternalsVisibleTo("ArchestrA.Client.AppConfigInt.Fakes,PublicKey=0024000004800000940000000602000000240000525341310004000001000100e92decb949446f688ab9f6973436c535bf50acd1fd580495aae3f875aa4e4f663ca77908c63b7f0996977cb98fcfdb35e05aa2c842002703cad835473caac5ef14107e3a7fae01120a96558785f48319f66daabc862872b2c53f5ac11fa335c0165e202b4c011334c7bc8f4c4e570cf255190f4e3e2cbc9137ca57cb687947bc")] +[assembly: InternalsVisibleTo("ArchestrA.Client.AppConfigInt.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100a79fb5ce9af5b80294b8b7ae7b476bcbf30b0713351655b2170195cd14acd8ed1462df1ab3a167d5463bd76a6151dd1766b94c350994d233a7024266591ee249882a4ee9c9e4de90dcd4c3762f274652cabac4a99704a9073872505b090f638e1a0827aeac9ff572d93d1a7800669dc0ce920817bd5aa5d70bacb7df88b4fdb3")] +[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")] +[assembly: AssemblyVersion("1.0.0.0")] +namespace ArchestrA.Client.AppConfigInt +{ + internal class AppCommandManager : IAppCommandManagerBase + { + private readonly ICommandManager commandManager; + + public AppCommandManager(ICommandManager commandManager) + { + this.commandManager = commandManager; + } + + public IAppCommandManagerBase AddPopupCommand(IAppCommandUI appCommandUI, object content) + { + ICommandUI commandUI = AppCommandUIProvider.GetCommandUI(appCommandUI); + commandManager.AddPopupCommand(commandUI, content); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandManagerBase AddSimpleCommand(ICommand command, IAppCommandUI appCommandUI) + { + ICommandUI commandUI = AppCommandUIProvider.GetCommandUI(appCommandUI); + commandManager.AddSimpleCommand(command, commandUI); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandManagerBase AddSplitCommand(ICommand command, IAppCommandUI appCommandUI, object content) + { + ICommandUI commandUI = AppCommandUIProvider.GetCommandUI(appCommandUI); + commandManager.AddSplitCommand(command, commandUI, content); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandManagerBase BeginCommandGroup(IAppCommandUI appCommandUI) + { + ICommandUI commandUI = AppCommandUIProvider.GetCommandUI(appCommandUI); + commandManager.BeginCommandGroup(commandUI); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandManagerBase BeginCommandGroup() + { + commandManager.BeginCommandGroup(); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandManagerBase ClearCommands() + { + commandManager.ClearCommands(); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandManagerBase WithoutLabel() + { + commandManager.WithoutLabel(); + return (IAppCommandManagerBase)(object)this; + } + + public IAppCommandUI CreateAppCommandUI(string label, string toolTip, DrawingBrush icon) + { + return AppCommandUIProvider.CreateAppCommandUI(label, toolTip, icon); + } + } + [SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors")] + public sealed class AppCommandUIProvider + { + private sealed class AutoCommandUI : BindableBase, IAppCommandUI + { + private readonly string label; + + private readonly string accessKey; + + private readonly string toolTip; + + private readonly DrawingBrush icon; + + public string Label => label; + + public string AccessKey => accessKey; + + public string ToolTip => toolTip; + + public DrawingBrush Icon => icon; + + public AutoCommandUI(string label, string accessKey, string toolTip, DrawingBrush icon) + { + this.label = label; + this.accessKey = accessKey; + this.toolTip = toolTip; + this.icon = icon; + } + } + + private sealed class OriginalCommandUI : BindableBase, ICommandUI + { + private readonly string getLabel; + + private readonly string getAccessKey; + + private readonly string getToolTip; + + private readonly DrawingBrush icon; + + public string Label => getLabel; + + public string AccessKey => getAccessKey; + + public string ToolTip => getToolTip; + + public DrawingBrush Icon => icon; + + public OriginalCommandUI(string getLabel, string getAccessKey, string getToolTip, DrawingBrush icon) + { + this.getLabel = getLabel; + this.getAccessKey = getAccessKey; + this.getToolTip = getToolTip; + this.icon = icon; + } + } + + internal static ICommandUI GetCommandUI(IAppCommandUI commandUI) + { + return (ICommandUI)(object)new OriginalCommandUI(commandUI.Label, commandUI.AccessKey, commandUI.ToolTip, commandUI.Icon); + } + + internal static IAppCommandUI CreateAppCommandUI(string label, string toolTip, DrawingBrush icon) + { + return (IAppCommandUI)(object)new AutoCommandUI(label, null, toolTip, icon); + } + } + public class AppPublisher + { + private static readonly object SingletonAccessLock = new object(); + + private static volatile AppPublisher appPublisher; + + private IGalaxyConfiguration galaxyConfiguration; + + private IGalaxyGraphics galaxyGraphics; + + public static AppPublisher Instance + { + get + { + if (appPublisher == null) + { + lock (SingletonAccessLock) + { + if (appPublisher == null) + { + appPublisher = new AppPublisher(); + } + } + } + return appPublisher; + } + } + + public void Init(IGalaxyConfiguration galaxyConfiguration) + { + this.galaxyConfiguration = galaxyConfiguration; + galaxyGraphics = galaxyConfiguration.GetGalaxyGraphics() as IGalaxyGraphics; + } + + public void Init(IGalaxyGraphics galaxyGraphics) + { + this.galaxyGraphics = galaxyGraphics; + } + + public void PublishIfNeeded(string objectName, string destPath) + { + int iDFromObjectName = galaxyConfiguration.GetIDFromObjectName(Namespace.VisualElement, objectName); + PublishIfNeeded(objectName, iDFromObjectName, destPath); + } + + public void PublishIfNeeded(string objectName, int objectID, string destPath) + { + PublishAndGetEditorMetadata(objectName, objectID, destPath, loadEditorMetadata: false); + } + + public Tuple PublishAndGetEditorMetadata(string objectName, int objectID, string destPath, bool loadEditorMetadata) + { + return PublishAndGetEditorMetadata(galaxyGraphics, objectName, objectID, destPath, loadEditorMetadata); + } + + public static void PublishIfNeededOnServer(IGalaxyGraphics galaxyGraphics, string objectName, int objectID, string destPath) + { + PublishAndGetEditorMetadata(galaxyGraphics, objectName, objectID, destPath, loadEditorMetadata: false); + } + + [SuppressMessage("Microsoft.Usage", "CA1801:ReviewUnusedParameters", MessageId = "objectID")] + private static Tuple PublishAndGetEditorMetadata(IGalaxyGraphics galaxyGraphics, string objectName, int objectID, string destPath, bool loadEditorMetadata) + { + if (string.IsNullOrWhiteSpace(destPath)) + { + ArchestrA.Diagnostics.Logger.LogWarning(() => "Invalid Destination Path: Value cannot be null, empty or whitespace"); + return null; + } + string sourceDir = null; + bool flag = false; + int i; + for (i = 0; i < 100; i++) + { + if (string.IsNullOrEmpty(sourceDir)) + { + sourceDir = GetLibraryPath(galaxyGraphics, objectName); + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_AppSourcePath, objectName, sourceDir)); + } + if (!string.IsNullOrEmpty(sourceDir) && Directory.Exists(sourceDir)) + { + flag = true; + ArchestrA.Diagnostics.Logger.LogTrace(() => $"App path {sourceDir} found {i * 200} ms after change notification."); + break; + } + Thread.Sleep(200); + if (string.IsNullOrEmpty(sourceDir)) + { + ArchestrA.Diagnostics.Logger.LogInfo(() => $"Checked in path for {objectName} is empty. Will retry ..."); + } + else + { + ArchestrA.Diagnostics.Logger.LogInfo(() => $"{sourceDir} in the shared folder is not available yet. Waiting ..."); + } + } + if (string.IsNullOrEmpty(sourceDir)) + { + ArchestrA.Diagnostics.Logger.LogWarning(() => $"Cannot find file storage path for visual element {objectName}."); + return null; + } + if (!flag) + { + ArchestrA.Diagnostics.Logger.LogWarning(() => $"File storage path for visual element {objectName} does not exist: {sourceDir}"); + return null; + } + Tuple tuple = IsCopyRequired(sourceDir, destPath, loadEditorMetadata); + if (tuple.Item1) + { + destPath = CopyAppAssemblies(objectName, sourceDir, destPath); + } + return new Tuple(destPath, tuple.Item2); + } + + public string[] GetGalaxyCredentialNameList() + { + try + { + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + string[] credentials = (galaxyConfiguration as IGalaxyConfigurationV31).GetGalaxyCredentialNameList(); + stopWatch.Stop(); + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.CredentialEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.GetCredentials_Label, stopWatch.ElapsedMilliseconds, (credentials != null) ? credentials.Length : 0)); + return credentials; + } + catch (Exception ex) + { + Exception ex2 = ex; + Exception ex3 = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => string.Format(CultureInfo.InvariantCulture, "Error occured while retrieving the credentials from galaxy configuration. {0}", ex3.ToString())); + return null; + } + } + + private static Tuple IsCopyRequired(string sourcePath, string destPath, bool loadEditorMetadata) + { + string text = null; + string text2 = null; + AppManifestSchemaElement val = ValidateAndLoadAppManifest(sourcePath); + if (val == null) + { + if (loadEditorMetadata) + { + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_CopyAppAssembliesInformation, sourcePath, "Don't")); + return new Tuple(item1: false, null); + } + return new Tuple(item1: true, null); + } + if (loadEditorMetadata && (string.IsNullOrEmpty(val.Editor.AssemblyName) || string.IsNullOrEmpty(val.Editor.FullName))) + { + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_EditorAssemblyFileNotfound, sourcePath)); + return new Tuple(item1: false, null); + } + text = val.AppVersion; + AppManifestSchemaElement val2 = ValidateAndLoadAppManifest(destPath); + if (val2 == null) + { + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_CopyAppAssembliesInformation, destPath, string.Empty)); + return new Tuple(item1: true, val.Editor); + } + text2 = val2.AppVersion; + if (string.IsNullOrEmpty(text)) + { + return new Tuple(item1: false, val.Editor); + } + if (string.Equals(text, text2, StringComparison.Ordinal)) + { + return new Tuple(item1: false, val2.Editor); + } + return new Tuple(item1: true, val.Editor); + } + + internal static AppManifestSchemaElement ValidateAndLoadAppManifest(string directoryPath) + { + //IL_0027: Expected O, but got Unknown + try + { + string text = Path.Combine(directoryPath, "AppManifest.xml"); + return File.Exists(text) ? DisplayLibraryManager.ValidateAndLoadXml(text) : null; + } + catch (DisplayOperationException ex) + { + DisplayOperationException ex2 = ex; + DisplayOperationException ex3 = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => ((Exception)(object)ex3).Message); + } + catch (Exception ex4) + { + Exception ex5 = ex4; + Exception ex6 = ex5; + ArchestrA.Diagnostics.Logger.LogWarning(() => ex6.Message); + } + return null; + } + + private static string CopyAppAssemblies(string objectName, string sourceDir, string destPath) + { + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_CopyAppLibrary, objectName, sourceDir, destPath)); + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + DeleteAndCopyFiles(objectName, sourceDir, ref destPath); + stopWatch.Stop(); + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditorPerformance, () => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_CopyAppPerformance, objectName, sourceDir, destPath, stopWatch.ElapsedMilliseconds)); + return destPath; + } + + private static void DeleteAndCopyFiles(string objectName, string sourceDir, ref string destPath) + { + //IL_00af: Expected O, but got Unknown + try + { + Utils.MaximumDirNameLengthCheck(sourceDir); + if (Directory.Exists(destPath)) + { + string formattedString = string.Format(CultureInfo.InvariantCulture, Strings.Directory_DeletionInformation, objectName, destPath); + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => formattedString); + Directory.Delete(destPath, recursive: true); + } + Utils.DirectoryCopy(sourceDir, destPath, true, new string[1] { "AppManifest.xml" }); + string text = Path.Combine(sourceDir, "AppManifest.xml"); + if (File.Exists(text)) + { + File.Copy(text, Path.Combine(destPath, "AppManifest.xml"), overwrite: true); + } + } + catch (DisplayOperationException ex) + { + DisplayOperationException ex2 = ex; + DisplayOperationException doe = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_ErrorMessage, objectName, doe)); + destPath = null; + } + catch (DirectoryNotFoundException ex3) + { + DirectoryNotFoundException ex4 = ex3; + DirectoryNotFoundException dnf = ex4; + ArchestrA.Diagnostics.Logger.LogWarning(() => string.Format(CultureInfo.InvariantCulture, Strings.AppPublisher_ErrorMessage, objectName, dnf)); + destPath = null; + } + catch (UnauthorizedAccessException) + { + destPath = null; + throw; + } + catch (Exception ex6) + { + Exception ex7 = ex6; + Exception ex8 = ex7; + ArchestrA.Diagnostics.Logger.LogWarning(() => string.Format(CultureInfo.InvariantCulture, Strings.CopyError, ex8.Message)); + } + } + + private static string GetLibraryPath(IGalaxyGraphics galaxyGraphics, string objectName) + { + IVisualElementReference visualElementReference = galaxyGraphics.CreateVisualElementReference(); + visualElementReference.Name = objectName; + visualElementReference.Type = "DisplayModule"; + ERRORCODE status; + string statusMessage; + return galaxyGraphics.GetVisualElementDescription(visualElementReference, preferCheckedOutVersion: true, out status, out statusMessage)?.GetAssociatedFilePath(AssociatedFilePathType.CheckedInPath); + } + } + internal class ControlLoader : Disposable + { + private readonly string libraryBasePath; + + private readonly EditorMetadata editorMetadata; + + private AppDomain appDomain; + + public ControlLoader(string path) + { + libraryBasePath = path; + appDomain = AppDomain.CurrentDomain; + } + + public ControlLoader(Tuple appPathEditorTuple) + { + libraryBasePath = appPathEditorTuple.Item1; + editorMetadata = appPathEditorTuple.Item2; + appDomain = AppDomain.CurrentDomain; + } + + internal IEditorConfig LoadEditorConfig(out bool previousVersionBinaryFound) + { + previousVersionBinaryFound = false; + try + { + appDomain.AssemblyResolve += AppDomain_AssemblyResolve; + if (!string.IsNullOrWhiteSpace(editorMetadata.AssemblyName) && !string.IsNullOrWhiteSpace(editorMetadata.FullName)) + { + Assembly assembly = Assembly.Load(editorMetadata.AssemblyName); + DateTime assemblyCreationDateTime = GetAssemblyCreationDateTime(string.Join("\\", libraryBasePath, editorMetadata.AssemblyName + ".dll")); + DateTime assemblyCreationDateTime2 = GetAssemblyCreationDateTime(assembly.Location); + if (!DateTime.Equals(assemblyCreationDateTime, assemblyCreationDateTime2)) + { + previousVersionBinaryFound = true; + return null; + } + object obj = Activator.CreateInstance(assembly.GetType(editorMetadata.FullName)); + return (IEditorConfig)((obj is IEditorConfig) ? obj : null); + } + } + catch (Exception ex) + { + Exception ex2 = ex; + Exception ex3 = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => string.Format(CultureInfo.InvariantCulture, Strings.ControlLoader_EditorLoadFailure, libraryBasePath, ex3)); + } + return null; + } + + private static DateTime GetAssemblyCreationDateTime(string path) + { + return new FileInfo(path).LastWriteTime; + } + + [SuppressMessage("Microsoft.Reliability", "CA2001:AvoidCallingProblematicMethods", MessageId = "System.Reflection.Assembly.LoadFrom", Justification = "Needed to load the dependent assmblies of app Editor")] + private Assembly AppDomain_AssemblyResolve(object sender, ResolveEventArgs args) + { + try + { + string text = Directory.GetFiles(libraryBasePath, args.Name + ".dll", SearchOption.AllDirectories).FirstOrDefault(); + if (!string.IsNullOrEmpty(text)) + { + return Assembly.LoadFrom(text); + } + } + catch (Exception ex) + { + Exception ex2 = ex; + Exception ex3 = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => string.Format(CultureInfo.InvariantCulture, Strings.ControlLoader_EditorLoadFailure, libraryBasePath, ex3)); + } + return null; + } + + protected override void Dispose(bool disposing) + { + if (disposing && appDomain != null) + { + appDomain.AssemblyResolve -= AppDomain_AssemblyResolve; + appDomain = null; + } + base.Dispose(disposing); + } + } + public sealed class AppEditorSupportEntity : DisplayModuleEntity + { + public IVisualElementReferenceList References { get; set; } + + public bool LoadAppData(Stream stream, out Dictionary appData) + { + appData = null; + if (!((DisplayModuleEntity)this).LoadAppData(stream)) + { + return false; + } + appData = ((DisplayModuleEntity)this).AppData; + return true; + } + + public bool SaveData(Stream stream, Dictionary appData) + { + ((DisplayModuleEntity)this).AppData.Clear(); + foreach (KeyValuePair appDatum in appData) + { + ((DisplayModuleEntity)this).AppData.Add(appDatum.Key, appDatum.Value); + } + return ((DisplayModuleEntity)this).SaveAppData(stream); + } + + public override bool SaveAppData(Stream stream) + { + if (!((DisplayModuleEntity)this).SaveAppData(stream)) + { + return false; + } + return true; + } + } + public class CredentialEditorControlInternal : Control + { + public static readonly DependencyProperty ItemsSourceProperty; + + public static readonly DependencyProperty TextProperty; + + internal Dictionary ItemsSource + { + get + { + return (Dictionary)GetValue(ItemsSourceProperty); + } + set + { + SetValue(ItemsSourceProperty, value); + } + } + + public string Text + { + get + { + return (string)GetValue(TextProperty); + } + set + { + SetValue(TextProperty, value); + } + } + + public event EventHandler TextChanged; + + private static void OnTextPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e) + { + (d as CredentialEditorControlInternal).TextChanged?.Invoke(null, null); + } + + public void UpdateTextProperty(string text) + { + if (Text != text) + { + Text = text; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static CredentialEditorControlInternal() + { + ItemsSourceProperty = DependencyProperty.Register("ItemsSource", typeof(Dictionary), typeof(CredentialEditorControlInternal), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault)); + TextProperty = DependencyProperty.Register("Text", typeof(string), typeof(CredentialEditorControlInternal), new FrameworkPropertyMetadata(null, FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, OnTextPropertyChanged)); + FrameworkElement.DefaultStyleKeyProperty.OverrideMetadata(typeof(CredentialEditorControlInternal), new FrameworkPropertyMetadata(typeof(CredentialEditorControlInternal))); + } + + public override void OnApplyTemplate() + { + PoupulateComboBox(); + } + + private void PoupulateComboBox() + { + try + { + string[] galaxyCredentialNameList = AppPublisher.Instance.GetGalaxyCredentialNameList(); + if (galaxyCredentialNameList == null || galaxyCredentialNameList.Length == 0) + { + ArchestrA.Diagnostics.Logger.LogWarning(() => "Querying GalaxyConfiguration for configured credentials returns zero results. Please ensure credentials are configuired in galaxy security configuration."); + return; + } + Dictionary dictionary = new Dictionary { + { + Strings.None_Label, + null + } }; + string[] array = galaxyCredentialNameList; + foreach (string text in array) + { + dictionary.Add(text, text); + } + ItemsSource = dictionary; + } + catch (Exception ex) + { + Exception ex2 = ex; + Exception ex3 = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => ex3.ToString()); + } + } + } + public class EditorControlFactoryInternal : EditorControlFactory, IExtension + { + private const string ControlFactory = "ArchestrA.Client.AppConfig.CredentialEditorControl"; + + public string ExtensionTypeName => typeof(EditorControlFactoryInternal).FullName; + + public override FrameworkElement GetControl(string type) + { + if (type == "ArchestrA.Client.AppConfig.CredentialEditorControl") + { + return new CredentialEditorControlInternal(); + } + return null; + } + } + internal static class CustomFlags + { + internal static readonly string AppEditor = "AppEditor"; + + internal static readonly string AppEditorPerformance = "AppEditorPerformance"; + + internal static readonly string CredentialEditor = "CredentialEditor"; + } + [Export(typeof(IEditorFactory))] + internal sealed class EditorFactory : IEditorFactory + { + private const string BaseAppsDir = "ArchestrA\\Apps"; + + public string Codebase => "DemoTestApp.Editor"; + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "MEF")] + [Import] + internal IDialogService DialogService { get; private set; } + + public EditorViewModelBase Create(IVisualElementConfiguration configuration, bool isReadOnly) + { + //IL_021e: Unknown result type (might be due to invalid IL or missing references) + //IL_0223: Unknown result type (might be due to invalid IL or missing references) + //IL_023d: Unknown result type (might be due to invalid IL or missing references) + //IL_0262: Expected O, but got Unknown + //IL_01db: Unknown result type (might be due to invalid IL or missing references) + //IL_01e0: Unknown result type (might be due to invalid IL or missing references) + //IL_0205: Unknown result type (might be due to invalid IL or missing references) + //IL_0215: Expected O, but got Unknown + //IL_00fb: Unknown result type (might be due to invalid IL or missing references) + //IL_0100: Unknown result type (might be due to invalid IL or missing references) + //IL_011a: Unknown result type (might be due to invalid IL or missing references) + //IL_012a: Expected O, but got Unknown + IEditorConfig control = null; + AppEditorSupportViewModel appEditorSupportViewModel = null; + bool previousVersionBinaryFound = false; + try + { + string destinationPath = GetDestinationPath(configuration); + Tuple appPathEditorTuple = AppPublisher.Instance.PublishAndGetEditorMetadata(configuration.ElementName, configuration.ObjectID, destinationPath, loadEditorMetadata: true); + Stopwatch stopWatch = new Stopwatch(); + stopWatch.Start(); + if (Directory.Exists(appPathEditorTuple.Item1)) + { + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.EditorFactory_AppPublishedPath, configuration.ElementName, appPathEditorTuple.Item1)); + using (ControlLoader controlLoader = new ControlLoader(appPathEditorTuple)) + { + control = controlLoader.LoadEditorConfig(out previousVersionBinaryFound); + } + if (control == null) + { + stopWatch.Stop(); + if (previousVersionBinaryFound) + { + DialogService.DisplayNotification(new NotificationDialogParameters + { + Details = string.Format(CultureInfo.CurrentCulture, Strings.AppEditor_LatestAppVersionFound), + Title = Strings.AppEditor_Title + }); + return null; + } + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditorPerformance, () => string.Format(CultureInfo.InvariantCulture, Strings.EditorFactory_AppEditorPerformance, configuration.ElementName, stopWatch.ElapsedMilliseconds)); + } + else + { + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.EditorFactory_EditorConfig, ((object)control).GetType())); + appEditorSupportViewModel = new AppEditorSupportViewModel(configuration, isReadOnly, control); + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditor, () => string.Format(CultureInfo.InvariantCulture, Strings.EditorFactory_InitializeAppEditor, configuration.ElementName)); + control.InitializeEditor((IEditorBase)(object)appEditorSupportViewModel); + stopWatch.Stop(); + ArchestrA.Diagnostics.Logger.LogCustom(CustomFlags.AppEditorPerformance, () => string.Format(CultureInfo.InvariantCulture, Strings.EditorFactory_AppEditorPerformance, configuration.ElementName, stopWatch.ElapsedMilliseconds)); + } + } + if (appEditorSupportViewModel == null) + { + DialogService.DisplayNotification(new NotificationDialogParameters + { + Details = string.Format(CultureInfo.CurrentCulture, Strings.AppEditorNotfoundNotificationDetail, configuration.ElementName), + Title = Strings.AppEditor_Title + }); + } + } + catch (UnauthorizedAccessException) + { + DialogService.DisplayNotification(new NotificationDialogParameters + { + Details = string.Format(CultureInfo.CurrentCulture, Strings.AppEditor_LatestAppVersionFound), + Title = string.Format(CultureInfo.CurrentCulture, Strings.AppEditor_LatestAppVersionFound_Title, configuration.ElementName) + }); + } + return (EditorViewModelBase)(object)appEditorSupportViewModel; + } + + private static string GetDestinationPath(IVisualElementConfiguration configuration) + { + string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), "ArchestrA\\Apps"); + return Path.Combine(path2: configuration.ObjectID.ToString(CultureInfo.InvariantCulture), path1: Path.Combine(path, EditorLauncher.GalaxyName)); + } + } + internal sealed class AppEditorSupportPreferences : EditorPreferencesBase + { + [SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private readonly IEditorConfig control; + + public AppEditorSupportPreferences(IEditorConfig control) + { + this.control = control; + } + } + public sealed class AppEditorSupportViewModel : EditorViewModelBase + { + private const string VisualElementDefinitionGrmAttribute = "_VisualElementDefinitionGRM"; + + private readonly IEditorConfig control; + + public override object SelectedItem { get; set; } + + public override string EditorName => ((EditorViewModelBase)this).Entity.Name + " Editor"; + + public AppEditorSupportViewModel(IVisualElementConfiguration configuration, bool isReadOnly, IEditorConfig control) + : base(configuration, isReadOnly, (IEntity)(object)new AppEditorSupportEntity(), (EditorPreferencesBase)(object)new AppEditorSupportPreferences(control)) + { + this.control = control; + } + + protected override IEnumerable CreateActivities() + { + Load(); + return control.GetActivities(); + } + + protected override void OnInitialized() + { + ((EditorViewModelBase)this).OnInitialized(); + ((EditorViewModelBase)this).AppCommandManager = (IAppCommandManagerBase)(object)new AppCommandManager(((EditorViewModelBase)this).CommandManager); + } + + [SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "VisualElementDefinitionGrmAttribute")] + private void Load() + { + try + { + byte[] array = (byte[])((EditorViewModelBase)this).Configuration.GetAttribute("_VisualElementDefinitionGRM"); + if (array == null || (array.Length == 1 && array[0] == 0)) + { + return; + } + ArchestrA.Diagnostics.Logger.LogTrace(() => "Loading existing data."); + using MemoryStream stream = new MemoryStream(array); + object[] array2 = (object[])((EditorViewModelBase)this).Configuration.GetAttribute("_VisualElementReferenceList"); + object[] array3 = (object[])((EditorViewModelBase)this).Configuration.GetAttribute("_VisualElementReferenceStatusList"); + VisualElementReferenceList references = ((array2 != null && array3 != null) ? new VisualElementReferenceList(array2.Cast().ToArray(), array3.Select((object s) => string.Equals((string)s, "1", StringComparison.Ordinal)).ToArray()) : new VisualElementReferenceList()); + AppEditorSupportEntity obj = ((EditorViewModelBase)this).Entity as AppEditorSupportEntity; + obj.References = references; + obj.LoadAppData(stream, out var appData); + control.Load(appData); + } + catch (Exception ex) + { + Exception ex2 = ex; + Exception ex3 = ex2; + ArchestrA.Diagnostics.Logger.LogError(() => $"Failed to load VisualElementDefinitionGrmAttribute value. Error details = {ex3.ToString()}"); + } + } + + public override bool Save(bool isClosing) + { + bool result = true; + try + { + ((EditorViewModelBase)this).Configuration.SetAttribute("_ConfigErrors", (object)((IEnumerable)((EditorViewModelBase)this).Errors.ToArray()).ToArray()); + ((EditorViewModelBase)this).Configuration.SetAttribute("_ConfigWarnings", (object)((IEnumerable)((EditorViewModelBase)this).InternalWarnings.ToArray()).ToArray()); + using (MemoryStream memoryStream = new MemoryStream()) + { + try + { + AppEditorSupportEntity obj = ((EditorViewModelBase)this).Entity as AppEditorSupportEntity; + Dictionary appData = control.Save(); + if (!obj.SaveData(memoryStream, appData)) + { + return false; + } + } + catch (Exception ex) + { + Exception ex2 = ex; + Exception saveException = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => $"Failed to save configuration for the Entity '{((EditorViewModelBase)this).Entity.Name}'. Error = {saveException}"); + return false; + } + ((EditorViewModelBase)this).Configuration.SetAttribute("_VisualElementDefinitionGRM", (object)memoryStream.GetBuffer()); + string a = ((EditorViewModelBase)this).Validate(isClosing); + if (string.Equals(a, ConfirmationDialogCommand.Cancel.Id, StringComparison.Ordinal)) + { + return false; + } + if (string.Equals(a, ConfirmationDialogCommand.No.Id, StringComparison.Ordinal)) + { + return true; + } + if (!((EditorViewModelBase)this).Configuration.Commit()) + { + return false; + } + } + ((EditorViewModelBase)this).IsDirty = false; + } + catch (Exception ex3) + { + Exception ex2 = ex3; + Exception ex4 = ex2; + ArchestrA.Diagnostics.Logger.LogWarning(() => $"Error while saving the configuration for the entity '{((EditorViewModelBase)this).Entity.Name}'. Error = {ex4.ToString()}"); + result = false; + } + return result; + } + } +} +namespace ArchestrA.Client.AppConfigInt.Resources +{ + [GeneratedCode("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] + [DebuggerNonUserCode] + [CompilerGenerated] + internal class Strings + { + private static ResourceManager resourceMan; + + private static CultureInfo resourceCulture; + + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal static ResourceManager ResourceManager + { + get + { + if (resourceMan == null) + { + resourceMan = new ResourceManager("ArchestrA.Client.AppConfigInt.Resources.Strings", typeof(Strings).Assembly); + } + return resourceMan; + } + } + + [EditorBrowsable(EditorBrowsableState.Advanced)] + internal static CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + + internal static string AddItem_Label => ResourceManager.GetString("AddItem_Label", resourceCulture); + + internal static string AppEditor_LatestAppVersionFound => ResourceManager.GetString("AppEditor_LatestAppVersionFound", resourceCulture); + + internal static string AppEditor_LatestAppVersionFound_Title => ResourceManager.GetString("AppEditor_LatestAppVersionFound_Title", resourceCulture); + + internal static string AppEditor_Title => ResourceManager.GetString("AppEditor_Title", resourceCulture); + + internal static string AppEditorNotfoundNotificationDetail => ResourceManager.GetString("AppEditorNotfoundNotificationDetail", resourceCulture); + + internal static string AppPublisher_AppSourcePath => ResourceManager.GetString("AppPublisher_AppSourcePath", resourceCulture); + + internal static string AppPublisher_CopyAppAssembliesInformation => ResourceManager.GetString("AppPublisher_CopyAppAssembliesInformation", resourceCulture); + + internal static string AppPublisher_CopyAppLibrary => ResourceManager.GetString("AppPublisher_CopyAppLibrary", resourceCulture); + + internal static string AppPublisher_CopyAppPerformance => ResourceManager.GetString("AppPublisher_CopyAppPerformance", resourceCulture); + + internal static string AppPublisher_EditorAssemblyFileNotfound => ResourceManager.GetString("AppPublisher_EditorAssemblyFileNotfound", resourceCulture); + + internal static string AppPublisher_ErrorMessage => ResourceManager.GetString("AppPublisher_ErrorMessage", resourceCulture); + + internal static string Compound1_Label => ResourceManager.GetString("Compound1_Label", resourceCulture); + + internal static string Compound2_Label => ResourceManager.GetString("Compound2_Label", resourceCulture); + + internal static string ControlLoader_EditorLoadFailure => ResourceManager.GetString("ControlLoader_EditorLoadFailure", resourceCulture); + + internal static string CopyError => ResourceManager.GetString("CopyError", resourceCulture); + + internal static string Directory_DeletionInformation => ResourceManager.GetString("Directory_DeletionInformation", resourceCulture); + + internal static string EditorFactory_AppEditorPerformance => ResourceManager.GetString("EditorFactory_AppEditorPerformance", resourceCulture); + + internal static string EditorFactory_AppPublishedPath => ResourceManager.GetString("EditorFactory_AppPublishedPath", resourceCulture); + + internal static string EditorFactory_EditorConfig => ResourceManager.GetString("EditorFactory_EditorConfig", resourceCulture); + + internal static string EditorFactory_InitializeAppEditor => ResourceManager.GetString("EditorFactory_InitializeAppEditor", resourceCulture); + + internal static string GetCredentials_Label => ResourceManager.GetString("GetCredentials_Label", resourceCulture); + + internal static string ItemCommands_Label => ResourceManager.GetString("ItemCommands_Label", resourceCulture); + + internal static string MoveItemDown_Label => ResourceManager.GetString("MoveItemDown_Label", resourceCulture); + + internal static string MoveItemUp_Label => ResourceManager.GetString("MoveItemUp_Label", resourceCulture); + + internal static string None_Label => ResourceManager.GetString("None_Label", resourceCulture); + + internal static string RedoStack_Label => ResourceManager.GetString("RedoStack_Label", resourceCulture); + + internal static string RemoveItem_Label => ResourceManager.GetString("RemoveItem_Label", resourceCulture); + + internal static string SetProperty_Label => ResourceManager.GetString("SetProperty_Label", resourceCulture); + + internal static string TestMainActivityViewModel_Label => ResourceManager.GetString("TestMainActivityViewModel_Label", resourceCulture); + + internal static string TestScriptActivityViewModel_Label => ResourceManager.GetString("TestScriptActivityViewModel_Label", resourceCulture); + + internal static string Toggle_Label => ResourceManager.GetString("Toggle_Label", resourceCulture); + + internal static string UndoStack_Label => ResourceManager.GetString("UndoStack_Label", resourceCulture); + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Strings() + { + } + } +} +namespace ArchestrA.Diagnostics +{ + [ExcludeFromCodeCoverage] + internal static class Logger + { + private static ILoggerClient loggerClient = LoggerClient.CreateNew(Assembly.GetExecutingAssembly().GetName().Name); + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static bool IsErrorEnabled + { + get + { + if (loggerClient == null) + { + return false; + } + return loggerClient.IsErrorEnabled; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static bool IsWarningEnabled + { + get + { + if (loggerClient == null) + { + return false; + } + return loggerClient.IsWarningEnabled; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static bool IsInfoEnabled + { + get + { + if (loggerClient == null) + { + return false; + } + return loggerClient.IsInfoEnabled; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static bool IsTraceEnabled + { + get + { + if (loggerClient == null) + { + return false; + } + return loggerClient.IsTraceEnabled; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static bool IsCustomLogEnabled(string customLogName) + { + if (loggerClient == null) + { + return false; + } + return loggerClient.IsCustomLogEnabled(customLogName); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogError(Func getMessage, Exception exception = null) + { + loggerClient?.LogError(getMessage, exception); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogWarning(Func getMessage, Exception exception = null) + { + loggerClient?.LogWarning(getMessage, exception); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogInfo(Func getMessage) + { + loggerClient?.LogInfo(getMessage); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogTrace(Func getMessage, Exception exception = null) + { + loggerClient?.LogTrace(getMessage, exception); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogCustom(string customLogName, Func getMessage) + { + loggerClient?.LogCustom(customLogName, getMessage); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogEntry(Func getMessage, [CallerMemberName] string callerName = "") + { + loggerClient?.LogEntryExit(getMessage, "Entered", callerName); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogExit(Func getMessage, [CallerMemberName] string callerName = "") + { + loggerClient?.LogEntryExit(getMessage, "Exiting", callerName); + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Method does not have to be used by all clients of the Logger.")] + public static void LogDispose(string className) + { + loggerClient?.LogWarning(() => $"Failed to call Dispose() for class {className}. Clean up of unmanaged resource was deferred to garbage collector."); + } + } +} diff --git a/graccesscli/analysis/ide-edit-investigation/cac/ArchestrA.IDE.ConfigurationAccessComponent.decompiled.cs b/graccesscli/analysis/ide-edit-investigation/cac/ArchestrA.IDE.ConfigurationAccessComponent.decompiled.cs new file mode 100644 index 0000000..863ebfc --- /dev/null +++ b/graccesscli/analysis/ide-edit-investigation/cac/ArchestrA.IDE.ConfigurationAccessComponent.decompiled.cs @@ -0,0 +1,1072 @@ +using System; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Security; +using System.Security.Permissions; +using ArchestrA.Configuration; +using ArchestrA.Core; +using ArchestrA.Diagnostics; +using ArchestrA.IDE.Extensibility; +using ArchestrA.Security; +using CASLib; +using IaaEditorFormLib; +using Microsoft.Win32; + +[assembly: CompilationRelaxations(8)] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] +[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] +[assembly: AssemblyFileVersion("5400.0017.1005.2")] +[assembly: AssemblyTitle("")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyCompany("AVEVA Software, LLC")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Copyright 2020 AVEVA Group plc and its subsidiaries. All rights reserved.")] +[assembly: AssemblyTrademark("Refer to: https://sw.aveva.com/legal/trademarks")] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("")] +[assembly: AssemblyKeyName("")] +[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")] +[assembly: AssemblyVersion("1.0.0.0")] +namespace AcmeBuildVersion +{ + internal sealed class Versioning + { + public const string Company = "AVEVA Software, LLC"; + + public const string Product = ""; + + public const string Copyright = "Copyright 2020 AVEVA Group plc and its subsidiaries. All rights reserved."; + + public const string Trademark = "Refer to: https://sw.aveva.com/legal/trademarks"; + + public const string Configuration = "Release"; + + public const string BuildNumber = "5400"; + + public const string BuildMaintenanceNumber = "1005"; + + public const string BldRevision = "2"; + + private Versioning() + { + } + } + internal sealed class ComponentVersioning + { + public const string ComponentVersion = "0017"; + + public const string ComponentMaintenanceVersion = "0000"; + + public const string ComponentName = "ConfigurationAccessComponent"; + + private ComponentVersioning() + { + } + } +} +namespace ArchestrA.IDE +{ + public class CASParameters + { + private IConfigurationEditorSite2 m_IObjectManage; + + private IGalaxyConfiguration m_IObjectConfiguration; + + private bool m_bReadOnly; + + private bool m_bKeepCheckedOut; + + private string m_DerivedFrom; + + private bool m_bTagnameIsReadOnly; + + private bool m_bContainedNameIsReadOnly; + + private bool m_bShowStandardTabs = true; + + private bool m_bPartialReadOnly; + + private Image m_imgIcon; + + public IConfigurationEditorSite2 inObjectManage + { + get + { + return m_IObjectManage; + } + set + { + m_IObjectManage = value; + } + } + + public IGalaxyConfiguration inObjectConfiguration + { + get + { + return m_IObjectConfiguration; + } + set + { + m_IObjectConfiguration = value; + } + } + + public string DerivedFrom + { + get + { + return m_DerivedFrom; + } + set + { + m_DerivedFrom = value; + } + } + + public bool ReadOnly + { + get + { + return m_bReadOnly; + } + set + { + m_bReadOnly = value; + } + } + + public bool KeepCheckedOut + { + get + { + return m_bKeepCheckedOut; + } + set + { + m_bKeepCheckedOut = value; + } + } + + public bool TagnameIsReadOnly + { + get + { + return m_bTagnameIsReadOnly; + } + set + { + m_bTagnameIsReadOnly = value; + } + } + + public bool ContainedNameIsReadOnly + { + get + { + return m_bContainedNameIsReadOnly; + } + set + { + m_bContainedNameIsReadOnly = value; + } + } + + public bool ShowStandardTabs + { + get + { + return m_bShowStandardTabs; + } + set + { + m_bShowStandardTabs = value; + } + } + + public bool PartialReadOnly + { + get + { + return m_bPartialReadOnly; + } + set + { + m_bPartialReadOnly = value; + } + } + + public Image EditorLogo + { + get + { + return m_imgIcon; + } + set + { + m_imgIcon = value; + } + } + } + public class ConfigurationAccessComponent : IaaEditorFormLib.IConfigurationAccess, IDisposable, IBaseEditorSite, IPackageErrorSubscription, DConfigurationAccessEvents + { + private IConfigurationAccess2 m_comCAS; + + private int m_intAdviseCookie; + + private string m_strLastError; + + internal string m_strSupportedLocales; + + internal bool m_bError; + + internal IaaServices m_services; + + internal CASParameters m_CASParams; + + internal IConfigurationEditorSite m_ICESite; + + internal DictionaryLoader m_DictionaryLoader; + + public object GalaxyBrowser => m_comCAS.GalaxyBrowser; + + public bool ShowStandardTabs => m_CASParams.ShowStandardTabs; + + public Image EditorLogo => m_CASParams.EditorLogo; + + public IaaServices IDEServices => m_services; + + public bool PartialReadOnly => m_CASParams.PartialReadOnly; + + public string ShapeInfo + { + get + { + string bstrShapeInfo = null; + m_ICESite.GetPackageShapeInfo(ref bstrShapeInfo); + return bstrShapeInfo; + } + } + + public event OnBeforeDataChangeEventHandler BeforeDataChange; + + public event OnDataChangeEventHandler DataChange; + + public event OnSetDataErrorEventHandler SetDataError; + + public event OnMarkDirtyEventHandler MarkDirty; + + public event OnSaveAndCloseEventHandler SaveAndClose; + + public event OnCloseEditorEventHandler CloseEditor; + + public event OnObjectHelpEventHandler ObjectHelp; + + public event OnEditObjectHelpEventHandler EditObjectHelp; + + public ConfigurationAccessComponent(IaaServices services, CASParameters CASParams) + { + m_services = services; + m_CASParams = CASParams; + m_bError = false; + m_DictionaryLoader = new DictionaryLoader(); + Type typeFromProgID = Type.GetTypeFromProgID("ConfigurationAccessComponent.ConfigurationAccessServer", throwOnError: true); + m_comCAS = (IConfigurationAccess2)Activator.CreateInstance(typeFromProgID); + m_ICESite = m_CASParams.inObjectManage; + IConfigurationEditor configurationEditor = (IConfigurationEditor)m_comCAS; + configurationEditor.Initialize("", m_ICESite, this, m_CASParams.ReadOnly); + m_CASParams.inObjectManage.SetSubscriberInfo(configurationEditor); + m_CASParams.inObjectManage.SetSubscriberInfo(this); + UCOMIConnectionPointContainer uCOMIConnectionPointContainer = (UCOMIConnectionPointContainer)m_comCAS; + Guid riid = typeof(DConfigurationAccessEvents).GUID; + UCOMIConnectionPoint ppCP = null; + uCOMIConnectionPointContainer.FindConnectionPoint(ref riid, out ppCP); + ppCP.Advise(this, out m_intAdviseCookie); + m_strSupportedLocales = GetSupportedLocales(); + set_Data("ww:SupportedLocales", "value", m_strSupportedLocales); + set_Data("ww:IPackageManager", "value", m_CASParams.inObjectConfiguration); + string pVal = (string)get_Data("Tagname", "value"); + set_Data("ww:Tagname", "Value", pVal); + set_Data("ww:Tagname", "ValueReadOnly", m_CASParams.TagnameIsReadOnly); + try + { + pVal = (string)get_Data("ContainedName", "value"); + } + catch (Exception) + { + pVal = ""; + } + set_Data("ww:ContainedName", "Value", pVal); + if (pVal.Length == 0) + { + m_CASParams.ContainedNameIsReadOnly = true; + } + set_Data("ww:ContainedName", "ValueReadOnly", m_CASParams.ContainedNameIsReadOnly); + set_Data("ww:DerivedFrom", "Value", m_CASParams.DerivedFrom); + set_Data("ww:KeepCheckedOut", "Value", m_CASParams.KeepCheckedOut); + set_Data("ww:SupportedLocales", "Value", m_strSupportedLocales); + } + + private string GetSupportedLocales() + { + string result = ""; + int processLocale = m_DictionaryLoader.ProcessLocale; + IMxValue configuredLocales = m_CASParams.inObjectConfiguration.GetConfiguredLocales(); + if (configuredLocales != null && configuredLocales.GetDataType() == ArchestrA.Core.MxDataType.MxInteger) + { + configuredLocales.GetDimensionSize(out var pSize); + result = ""; + CultureInfo cultureInfo = new CultureInfo(processLocale); + result += $""; + for (int i = 1; i <= pSize; i++) + { + IMxValue mxValue = new MxValueClass(); + configuredLocales.GetElement(i, mxValue); + int integer = mxValue.GetInteger(); + if (integer != processLocale) + { + cultureInfo = new CultureInfo(integer); + result += $""; + } + } + result += ""; + } + return result; + } + + public void set_Data(object Item, object SubItem, object pVal) + { + if (this.BeforeDataChange != null) + { + BeforeDataChangeEventArgs e = new BeforeDataChangeEventArgs(); + e.Item = Item; + e.SubItem = SubItem; + e.Value = pVal; + this.BeforeDataChange(this, e); + if (e.Cancel) + { + OnError(e.Message); + return; + } + } + m_comCAS.set_Data(Item, SubItem, pVal); + } + + public object get_Data(object Item, object SubItem) + { + if (Item as string == "beo:LastError") + { + return m_strLastError; + } + return m_comCAS.get_Data(Item, SubItem); + } + + public void Execute(string Action, object Options) + { + switch (Action.ToLower()) + { + case "markdirty": + if (!(bool)get_Data("BEO:InhibitDirtySet", "")) + { + OnStatusChange(); + } + break; + case "loginfo": + ArchestrA.Diagnostics.Logger.LogInfo((string)Options); + break; + case "logwarning": + ArchestrA.Diagnostics.Logger.LogWarning((string)Options); + break; + case "logerror": + ArchestrA.Diagnostics.Logger.LogError((string)Options); + break; + case "saveandclose": + OnSaveAndClose(); + break; + case "editobjecthelp": + { + string objectPath = GetObjectPath(2); + bool isTemplate = (bool)get_Data("_IsTemplate", ""); + string objectName = (string)get_Data("ww:tagname", ""); + OnEditObjectHelp(objectPath, objectName, isTemplate); + break; + } + case "readobjecthelp": + OnObjectHelp(Options); + break; + default: + ArchestrA.Diagnostics.Logger.LogWarning("Execute method called with invalid command: " + (string)Options); + break; + } + } + + public string get_LocalizedText(object phraseID, object Options) + { + string strVendorDictionary = (string)Options; + string strPhraseId = (string)phraseID; + return m_DictionaryLoader.GetString(strVendorDictionary, strPhraseId); + } + + public void SubscribeData(object Item, object SubItem) + { + m_comCAS.SubscribeData(Item, SubItem); + } + + public void UnsubscribeData(object Item, object SubItem) + { + m_comCAS.UnsubscribeData(Item, SubItem); + } + + public string GetObjectPath(int ObjectPathSectionEnum) + { + return m_CASParams.inObjectManage.GetObjectPath((EObjectPathSection)ObjectPathSectionEnum); + } + + public void Apply(IaaEditorForm IEditorForm) + { + IEditorForm?.OnDataChange("BEO:Apply", "BEO:Apply", true); + } + + public void GetErrorsAndWarnings(out string[] Warnings, out string[] Errors) + { + short nDimensions = 0; + int pSize = 0; + IMxValue mxValue = new MxValueClass(); + IMxValue mxValue2 = new MxValueClass(); + Warnings = null; + Errors = null; + m_CASParams.inObjectManage.Validate(); + IConfigurationEditorSite inObjectManage = m_CASParams.inObjectManage; + int attributeCookie = inObjectManage.GetAttributeCookie("_Warnings"); + inObjectManage.GetAttribute(attributeCookie, 10, mxValue); + ArchestrA.Diagnostics.Logger.LogTrace("IConfigurationAccess::Apply: Retreived warnings"); + mxValue.GetDimensionCount(out nDimensions); + if (nDimensions == 1) + { + mxValue.GetDimensionSize(out pSize); + if (pSize > 0) + { + Warnings = new string[pSize]; + for (int i = 1; i <= pSize; i++) + { + mxValue.GetElement(i, mxValue2); + Warnings[i - 1] = mxValue2.GetString(); + } + } + } + attributeCookie = inObjectManage.GetAttributeCookie("Errors"); + inObjectManage.GetAttribute(attributeCookie, 10, mxValue); + ArchestrA.Diagnostics.Logger.LogTrace("IConfigurationAccess::Apply: Retreived errors"); + mxValue.GetDimensionCount(out nDimensions); + if (nDimensions != 1) + { + return; + } + mxValue.GetDimensionSize(out pSize); + if (pSize > 0) + { + Errors = new string[pSize]; + for (int i = 1; i <= pSize; i++) + { + mxValue.GetElement(i, mxValue2); + Errors[i - 1] = mxValue2.GetString(); + } + } + } + + public bool Commit(out string Error) + { + Error = null; + EPACKAGEOPERATIONSTATUS ePACKAGEOPERATIONSTATUS = m_CASParams.inObjectManage.Commit(out Error); + return ePACKAGEOPERATIONSTATUS == EPACKAGEOPERATIONSTATUS.ePackageSuccess; + } + + public void CheckConfigurationPermissionForHostObject(out EPERMISSION_ACCESS ePermissionAccess, out EPERMISSION_ACCESS eGraphicsAccess) + { + ePermissionAccess = EPERMISSION_ACCESS.eAccessdeny; + eGraphicsAccess = EPERMISSION_ACCESS.eAccessdeny; + m_CASParams.inObjectManage.GetSecurityAccessForEditor(out var securityAccess); + if (securityAccess is IFxSecurityAccess fxSecurityAccess) + { + fxSecurityAccess.CheckConfigurationPermissionForHostObject(out ePermissionAccess, out eGraphicsAccess); + } + } + + public void GetSGetScriptCompiledAttributesWarningtext(out string strScriptWarning) + { + strScriptWarning = ""; + try + { + IConfigurationEditorSite7 configurationEditorSite = (IConfigurationEditorSite7)m_CASParams.inObjectManage; + configurationEditorSite.GetScriptCompiledAttributesWarningtext(out strScriptWarning); + } + catch (Exception ex) + { + ArchestrA.Diagnostics.Logger.LogTrace("Unable to retrieve Scirpt attributes modification status" + ex.Message); + } + } + + public void EditorClose() + { + if (this.CloseEditor != null) + { + this.CloseEditor(); + } + } + + public void OnDataChange(object Item, object SubItem, object Value) + { + if (this.DataChange != null) + { + this.DataChange(Item, SubItem, Value); + } + } + + public void OnSaveAndClose() + { + if (this.SaveAndClose != null) + { + this.SaveAndClose(); + } + } + + public void OnObjectHelp(object options) + { + if (this.ObjectHelp != null) + { + this.ObjectHelp(options); + } + } + + public void OnEditObjectHelp(string ObjectPath, string ObjectName, bool IsTemplate) + { + if (this.EditObjectHelp != null) + { + this.EditObjectHelp(ObjectPath, ObjectName, IsTemplate); + } + } + + public void OnStatusChange() + { + if (this.MarkDirty != null && !(bool)get_Data("BEO:InhibitDirtySet", "")) + { + this.MarkDirty(); + } + } + + public void OnError(string errorWarningString) + { + m_bError = true; + m_comCAS.set_Data((object)"beo:ErrorCode", (object)"value", (object)m_bError); + m_strLastError = errorWarningString; + if (this.SetDataError != null) + { + this.SetDataError(errorWarningString); + } + } + + public void Dispose() + { + int num = 1; + try + { + if (m_comCAS == null) + { + return; + } + UCOMIConnectionPointContainer uCOMIConnectionPointContainer = (UCOMIConnectionPointContainer)m_comCAS; + Guid riid = typeof(DConfigurationAccessEvents).GUID; + UCOMIConnectionPoint ppCP = null; + uCOMIConnectionPointContainer.FindConnectionPoint(ref riid, out ppCP); + if (ppCP != null) + { + ppCP.Unadvise(m_intAdviseCookie); + while (num != 0) + { + num = Marshal.ReleaseComObject(ppCP); + } + ppCP = null; + } + for (num = 1; num != 0; num = Marshal.ReleaseComObject(m_comCAS)) + { + } + m_comCAS = null; + if (m_CASParams.inObjectManage != null) + { + for (num = 1; num != 0; num = Marshal.ReleaseComObject(m_CASParams.inObjectManage)) + { + } + m_CASParams.inObjectManage = null; + } + } + catch + { + } + } + } + public class DictionaryLoader + { + private const string m_strDefaultVendor = "Archestra"; + + private const string m_strDefaultDictionary = "baseeditor.aadct"; + + private string m_strDefaultVendorDictionary; + + private HybridDictionary m_DictionaryMap; + + public int ProcessLocale + { + get + { + IaaDictionary iaaDictionary = (IaaDictionary)m_DictionaryMap[m_strDefaultVendorDictionary]; + return iaaDictionary.ProcessLocale; + } + } + + public DictionaryLoader() + { + m_DictionaryMap = new HybridDictionary(caseInsensitive: true); + m_strDefaultVendorDictionary = "Archestra:baseeditor.aadct"; + m_DictionaryMap.Add(m_strDefaultVendorDictionary, new Dictionary("baseeditor.aadct")); + } + + public string GetString(string strVendorDictionary, string strPhraseId) + { + IaaDictionary iaaDictionary = null; + if (strVendorDictionary != null) + { + iaaDictionary = (IaaDictionary)m_DictionaryMap[strVendorDictionary]; + if (iaaDictionary == null) + { + int num = strVendorDictionary.IndexOf(":"); + if (num != -1) + { + string strVendorName = strVendorDictionary.Substring(0, num); + string text = strVendorDictionary.Substring(num + 1, strVendorDictionary.Length - num - 1); + text = text.ToLower(); + if (!text.EndsWith(".aadct")) + { + text += ".xxdct"; + } + iaaDictionary = new Dictionary(strVendorName, "", text); + m_DictionaryMap.Add(strVendorDictionary, iaaDictionary); + } + } + } + if (iaaDictionary == null) + { + int count = m_DictionaryMap.Count; + if (count > 0) + { + IaaDictionary[] array = new IaaDictionary[count]; + m_DictionaryMap.Values.CopyTo(array, 0); + iaaDictionary = array[count - 1]; + } + } + if (iaaDictionary == null) + { + return ""; + } + return iaaDictionary[strPhraseId]; + } + } +} +namespace ArchestrA.Diagnostics +{ + internal static class Logger + { + public static int ErrorCount + { + get + { + if (InitLoggerDll()) + { + int errorCount = 0; + int warningCount = 0; + long ftLastError = 0L; + long ftLastWarning = 0L; + int loggerStats = ArchestrA.Diagnostics.NativeMethods.GetLoggerStats(string.Empty, ref errorCount, ref ftLastError, ref warningCount, ref ftLastWarning); + if (loggerStats <= 0) + { + return -1; + } + return errorCount; + } + return -1; + } + } + + public static bool IsLoaded { get; private set; } + + public static int WarningCount + { + get + { + if (InitLoggerDll()) + { + int errorCount = 0; + int warningCount = 0; + long ftLastError = 0L; + long ftLastWarning = 0L; + int loggerStats = ArchestrA.Diagnostics.NativeMethods.GetLoggerStats(string.Empty, ref errorCount, ref ftLastError, ref warningCount, ref ftLastWarning); + if (loggerStats <= 0) + { + return -1; + } + return warningCount; + } + return -1; + } + } + + private static bool CheckRegistry { get; set; } + + private static bool DomainUnloaded { get; set; } + + private static int LoggerClientIdentity { get; set; } + + static Logger() + { + LoggerClientIdentity = 0; + CheckRegistry = true; + } + + public static void LogConnection(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogConnection(LoggerClientIdentity, errorMessage); + } + } + + public static void LogCtorDtor(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogCtorDtor(LoggerClientIdentity, errorMessage); + } + } + + public static void LogCustom(int cookie, string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogCustom(LoggerClientIdentity, cookie, errorMessage); + } + } + + public static void LogEntryExit(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogEntryExit(LoggerClientIdentity, errorMessage); + } + } + + public static void LogError(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogError(LoggerClientIdentity, errorMessage); + } + } + + public static void LogInfo(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogInfo(LoggerClientIdentity, errorMessage); + } + } + + public static void LogRefCount(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogRefCount(LoggerClientIdentity, errorMessage); + } + } + + public static int LogRegisterCustomFlag(string flagName) + { + if (!Initialize()) + { + return 0; + } + return ArchestrA.Diagnostics.NativeMethods.RegisterLogFlag(LoggerClientIdentity, 11, flagName); + } + + public static int LogRegisterCustomFlagEx(string flagName, int defaultValue) + { + if (!Initialize()) + { + return 0; + } + return ArchestrA.Diagnostics.NativeMethods.RegisterLogFlagEx(LoggerClientIdentity, 11, flagName, defaultValue); + } + + public static void LogSQL(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogSQL(LoggerClientIdentity, errorMessage); + } + } + + public static void LogSetIdentityName(string identityName) + { + if (Initialize()) + { + int num = ArchestrA.Diagnostics.NativeMethods.SetIdentityName(LoggerClientIdentity, identityName); + } + } + + public static void LogStartStop(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogStartStop(LoggerClientIdentity, errorMessage); + } + } + + public static void LogThreadStartStop(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogThreadStartStop(LoggerClientIdentity, errorMessage); + } + } + + public static void LogTrace(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogTrace(LoggerClientIdentity, errorMessage); + } + } + + public static void LogWarning(string errorMessage) + { + if (Initialize()) + { + ArchestrA.Diagnostics.NativeMethods.InternalLogWarning(LoggerClientIdentity, errorMessage); + } + } + + public static void ResetLoggerCheck() + { + CheckRegistry = true; + } + + private static bool InitLoggerDll() + { + bool flag = IsLoaded; + if (flag) + { + return true; + } + if (CheckRegistry) + { + IntPtr intPtr = IntPtr.Zero; + CheckRegistry = false; + new RegistryPermission(RegistryPermissionAccess.Read, "HKEY_LOCAL_MACHINE\\Software\\ArchestrA\\Framework\\Logger").Assert(); + try + { + RegistryKey registryKey = Registry.LocalMachine.OpenSubKey("Software\\ArchestrA\\Framework\\Logger", writable: false); + if (registryKey != null) + { + string text = Convert.ToString(registryKey.GetValue("InstallPath", string.Empty), CultureInfo.InvariantCulture); + if (text.Length > 0) + { + intPtr = ArchestrA.Diagnostics.NativeMethods.LoadLibraryExW(wParam: new IntPtr(0), lpMdoule: Path.Combine(text, "LoggerDll.dll"), flag: 8); + } + } + } + finally + { + CodeAccessPermission.RevertAssert(); + } + flag = intPtr != IntPtr.Zero; + } + IsLoaded = flag; + return flag; + } + + private static bool Initialize() + { + if (DomainUnloaded) + { + return false; + } + if (LoggerClientIdentity != 0) + { + return true; + } + if (InitLoggerDll()) + { + int hIdentity = 0; + int num = ArchestrA.Diagnostics.NativeMethods.RegisterLoggerClient(ref hIdentity); + LoggerClientIdentity = hIdentity; + if (num != 0 && LoggerClientIdentity != 0) + { + new FileIOPermission(PermissionState.Unrestricted).Assert(); + try + { + num = ArchestrA.Diagnostics.NativeMethods.SetIdentityName(LoggerClientIdentity, Assembly.GetExecutingAssembly().GetName().Name); + } + finally + { + CodeAccessPermission.RevertAssert(); + } + AppDomain.CurrentDomain.DomainUnload += OnCurrentDomainUnload; + return true; + } + } + return false; + } + + private static void OnCurrentDomainUnload(object sender, EventArgs e) + { + UnInitialize(); + DomainUnloaded = true; + } + + private static void UnInitialize() + { + if (LoggerClientIdentity != 0) + { + int num = ArchestrA.Diagnostics.NativeMethods.UnregisterLoggerClient(LoggerClientIdentity); + LoggerClientIdentity = 0; + } + } + } + internal static class NativeMethods + { + [DllImport("kernel32")] + public static extern IntPtr LoadLibraryExW([MarshalAs(UnmanagedType.LPWStr)] string lpMdoule, IntPtr wParam, int flag); + + [DllImport("kernel32")] + internal static extern IntPtr LoadLibraryW([MarshalAs(UnmanagedType.LPWStr)] string lpMdoule); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "REGISTERLOGGERCLIENT", ExactSpelling = true)] + internal static extern int RegisterLoggerClient(ref int hIdentity); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "UNREGISTERLOGGERCLIENT", ExactSpelling = true)] + internal static extern int UnregisterLoggerClient(int hIdentity); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "SETIDENTITYNAME", ExactSpelling = true)] + internal static extern int SetIdentityName(int hIdentity, string strIdentity); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGERROR", ExactSpelling = true)] + internal static extern void InternalLogError(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGWARNING", ExactSpelling = true)] + internal static extern void InternalLogWarning(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGINFO", ExactSpelling = true)] + internal static extern void InternalLogInfo(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGTRACE", ExactSpelling = true)] + internal static extern void InternalLogTrace(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGSTARTSTOP", ExactSpelling = true)] + internal static extern void InternalLogStartStop(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGENTRYEXIT", ExactSpelling = true)] + internal static extern void InternalLogEntryExit(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGTHREADSTARTSTOP", ExactSpelling = true)] + internal static extern void InternalLogThreadStartStop(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGSQL", ExactSpelling = true)] + internal static extern void InternalLogSQL(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGCONNECTION", ExactSpelling = true)] + internal static extern void InternalLogConnection(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGCTORDTOR", ExactSpelling = true)] + internal static extern void InternalLogCtorDtor(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGREFCOUNT", ExactSpelling = true)] + internal static extern void InternalLogRefCount(int hIdentity, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "REGISTERLOGFLAG", ExactSpelling = true)] + internal static extern int RegisterLogFlag(int hIdentity, int nCustomFlag, [MarshalAs(UnmanagedType.LPWStr)] string strFlag); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "REGISTERLOGFLAGEX", ExactSpelling = true)] + internal static extern int RegisterLogFlagEx(int hIdentity, int nCustomFlag, [MarshalAs(UnmanagedType.LPWStr)] string strFlag, int nDefaultVal); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "LOGCUSTOM2", ExactSpelling = true)] + internal static extern void InternalLogCustom(int hIdentity, int nCustomFlag, [MarshalAs(UnmanagedType.LPWStr)] string errorMessage); + + [DllImport("LoggerDLL.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode, EntryPoint = "GETLOGGERSTATS", ExactSpelling = true)] + internal static extern int GetLoggerStats([MarshalAs(UnmanagedType.LPWStr)] string hostName, ref int errorCount, ref long ftLastError, ref int warningCount, ref long ftLastWarning); + } +} +namespace CASLib +{ + [ComImport] + [CompilerGenerated] + [InterfaceType(2)] + [Guid("1859E2AD-59A3-4F24-82D8-EDD1E2129B5C")] + [TypeIdentifier] + public interface DConfigurationAccessEvents + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + [DispId(1)] + void OnDataChange([MarshalAs(UnmanagedType.Struct)] object Item, [MarshalAs(UnmanagedType.Struct)] object SubItem, [MarshalAs(UnmanagedType.Struct)] object value); + } + [ComImport] + [CompilerGenerated] + [Guid("91DC002C-E2D0-40E8-8418-15965CF0F287")] + [TypeIdentifier] + public interface IConfigurationAccess + { + } + [ComImport] + [CompilerGenerated] + [Guid("EDB7C645-3152-4862-8BBF-AD7A04662CDA")] + [TypeIdentifier] + public interface IConfigurationAccess2 : IConfigurationAccess + { + [DispId(101)] + object Data + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + [DispId(101)] + [return: MarshalAs(UnmanagedType.Struct)] + get; + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + [DispId(101)] + [param: In] + [param: MarshalAs(UnmanagedType.Struct)] + set; + } + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + [DispId(102)] + void SubscribeData([MarshalAs(UnmanagedType.Struct)] object Item, [MarshalAs(UnmanagedType.Struct)] object SubItem); + + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + [DispId(103)] + void UnsubscribeData([MarshalAs(UnmanagedType.Struct)] object Item, [MarshalAs(UnmanagedType.Struct)] object SubItem); + + void _VtblGap1_2(); + + [DispId(205)] + object GalaxyBrowser + { + [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)] + [DispId(205)] + [return: MarshalAs(UnmanagedType.IUnknown)] + get; + } + } +} diff --git a/graccesscli/analysis/ide-edit-investigation/probe_setvalue/Probe.csproj b/graccesscli/analysis/ide-edit-investigation/probe_setvalue/Probe.csproj new file mode 100644 index 0000000..13f4f1e --- /dev/null +++ b/graccesscli/analysis/ide-edit-investigation/probe_setvalue/Probe.csproj @@ -0,0 +1,18 @@ + + + net48 + Exe + disable + x86 + x86 + true + Probe + Probe + + + + C:\Users\dohertj2\Desktop\wwtools\graccesscli\lib\ArchestrA.GRAccess.dll + true + + + diff --git a/graccesscli/analysis/ide-edit-investigation/probe_setvalue/Program.cs b/graccesscli/analysis/ide-edit-investigation/probe_setvalue/Program.cs new file mode 100644 index 0000000..8253e11 --- /dev/null +++ b/graccesscli/analysis/ide-edit-investigation/probe_setvalue/Program.cs @@ -0,0 +1,131 @@ +using System; +using ArchestrA.GRAccess; + +class Program +{ + static int Main(string[] args) + { + // Probe: write to $TestMachine.UpdateTestChangingInt.DeclarationsText (a + // package-only ScriptExtension text field) via the TN-537 pattern using + // ConfigurableAttributes. Read back through TWO paths to discriminate + // between (a) write-side no-op vs (b) read-side staleness: + // 1. Same IgObject proxy via obj.ConfigurableAttributes (in-memory edit view) + // 2. Fresh galaxy.QueryObjectsByName proxy via .Attributes (runtime view) + // + // Restores DeclarationsText to empty when done. + + const string GalaxyName = "ZB"; + const string Tagname = "$TestMachine"; + const string ScriptName = "UpdateTestChangingInt"; + const string FieldName = "DeclarationsText"; + const string Marker = "// graccesscli probe marker — TN537 ConfigurableAttributes path\n"; + + var user = Environment.GetEnvironmentVariable("MX_TEST_USER") ?? ""; + var pass = Environment.GetEnvironmentVariable("MX_TEST_PASSWORD") ?? ""; + var node = Environment.MachineName; + + Console.WriteLine($"[probe] connecting to {node} galaxy={GalaxyName} as {user}"); + var gr = new GRAccessAppClass(); + var galaxies = gr.QueryGalaxies(node); + var galaxy = galaxies[GalaxyName]; + if (string.IsNullOrEmpty(user)) + galaxy.Login(); + else + galaxy.Login(user, pass); + + var attrPath = ScriptName + "." + FieldName; + + // Acquire the target template object. + var names = new string[] { Tagname }; + var initial = QueryOne(galaxy, names); + + // Read current value via ConfigurableAttributes (configuration view). + var beforeCfg = TryReadString(initial.ConfigurableAttributes, attrPath, "before-cfg"); + var beforeRt = TryReadString(initial.Attributes, attrPath, "before-rt"); + Console.WriteLine($"[probe] BEFORE cfg={Quote(beforeCfg)} rt={Quote(beforeRt)}"); + + // Mutate via the canonical TN-537 sequence on the same proxy. + Console.WriteLine("[probe] CheckOut..."); + initial.CheckOut(); + ThrowIfFailed(initial.CommandResult, "CheckOut"); + + Console.WriteLine("[probe] ConfigurableAttributes[...].SetValue(MxValue)..."); + var cfgAttr = initial.ConfigurableAttributes[attrPath]; + var v = new MxValueClass(); + v.PutString(Marker); + cfgAttr.SetValue(v); + + Console.WriteLine("[probe] Save..."); + initial.Save(); + ThrowIfFailed(initial.CommandResult, "Save"); + + Console.WriteLine("[probe] CheckIn..."); + initial.CheckIn("graccesscli probe round-trip test"); + ThrowIfFailed(initial.CommandResult, "CheckIn"); + + // Now read back THREE ways: + // A. Same proxy (initial), ConfigurableAttributes — possibly stale. + // B. Same proxy (initial), Attributes — possibly stale. + // C. Fresh proxy from a fresh QueryObjectsByName call. + var afterCfgSame = TryReadString(initial.ConfigurableAttributes, attrPath, "after-cfg-same"); + var afterRtSame = TryReadString(initial.Attributes, attrPath, "after-rt-same"); + Console.WriteLine($"[probe] AFTER (same proxy) cfg={Quote(afterCfgSame)} rt={Quote(afterRtSame)}"); + + var refreshed = QueryOne(galaxy, names); + var afterCfgFresh = TryReadString(refreshed.ConfigurableAttributes, attrPath, "after-cfg-fresh"); + var afterRtFresh = TryReadString(refreshed.Attributes, attrPath, "after-rt-fresh"); + Console.WriteLine($"[probe] AFTER (fresh proxy) cfg={Quote(afterCfgFresh)} rt={Quote(afterRtFresh)}"); + + // Restore: write the original value back (or empty if there was none). + var restoreTo = beforeCfg ?? ""; + Console.WriteLine($"[probe] Restoring DeclarationsText to {Quote(restoreTo)}..."); + refreshed.CheckOut(); + ThrowIfFailed(refreshed.CommandResult, "CheckOut(restore)"); + var restoreAttr = refreshed.ConfigurableAttributes[attrPath]; + var rv = new MxValueClass(); + rv.PutString(restoreTo); + restoreAttr.SetValue(rv); + refreshed.Save(); + ThrowIfFailed(refreshed.CommandResult, "Save(restore)"); + refreshed.CheckIn("graccesscli probe restore"); + ThrowIfFailed(refreshed.CommandResult, "CheckIn(restore)"); + Console.WriteLine("[probe] Restored."); + + // Verdict. + Console.WriteLine(); + Console.WriteLine("=== verdict ==="); + Console.WriteLine($" marker landed on same-proxy ConfigurableAttributes: {(afterCfgSame == Marker)}"); + Console.WriteLine($" marker landed on same-proxy Attributes : {(afterRtSame == Marker)}"); + Console.WriteLine($" marker landed on fresh-proxy ConfigurableAttributes: {(afterCfgFresh == Marker)}"); + Console.WriteLine($" marker landed on fresh-proxy Attributes : {(afterRtFresh == Marker)}"); + + return 0; + } + + static IgObject QueryOne(IGalaxy galaxy, string[] names) + { + var objs = galaxy.QueryObjectsByName(EgObjectIsTemplateOrInstance.gObjectIsTemplate, ref names); + ThrowIfFailed(galaxy.CommandResult, "QueryObjectsByName"); + return objs[1]; + } + + static string TryReadString(IAttributes attrs, string name, string label) + { + try + { + var attr = attrs[name]; + if (attr == null) return null; + var val = attr.value; + return val?.GetString(); + } + catch (Exception ex) { Console.WriteLine($"[probe] {label} read threw: {ex.Message}"); return null; } + } + + static void ThrowIfFailed(ICommandResult r, string what) + { + if (r != null && !r.Successful) + throw new InvalidOperationException($"{what} failed: ID={r.ID} Text='{r.Text}' Custom='{r.CustomMessage}'"); + } + + static string Quote(string s) => s == null ? "" : "\"" + s.Replace("\n", "\\n").Replace("\r", "\\r") + "\""; +} diff --git a/graccesscli/analysis/ide-edit-investigation/scriptpkg/ScriptPackage.Net.decompiled.cs b/graccesscli/analysis/ide-edit-investigation/scriptpkg/ScriptPackage.Net.decompiled.cs new file mode 100644 index 0000000..4b0de54 --- /dev/null +++ b/graccesscli/analysis/ide-edit-investigation/scriptpkg/ScriptPackage.Net.decompiled.cs @@ -0,0 +1,595 @@ +using System; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.SymbolStore; +using System.IO; +using System.Reflection; +using System.Reflection.Emit; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Runtime.Versioning; +using System.Security; +using ArchestrA.QuickScript; +using ArchestrA.QuickScript.Digest; +using ArchestrA.QuickScript.Emit; +using ArchestrA.QuickScript.Model; +using ArchestrA.QuickScript.Runtime; + +[assembly: CompilationRelaxations(8)] +[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] +[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] +[assembly: AssemblyFileVersion("5800.0038.7005.1")] +[assembly: AssemblyTitle("ScriptPackage.Net Module")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("AVEVA Software, LLC")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("Copyright 2020 AVEVA Group plc and its subsidiaries. All rights reserved.")] +[assembly: AssemblyTrademark("Refer to: https://sw.aveva.com/legal/trademarks")] +[assembly: AssemblyDelaySign(false)] +[assembly: AssemblyKeyFile("..\\..\\..\\SharedComponents\\Internal\\MagellanPublic\\Includes\\WWDotNETPrivateKey\\ww.snk")] +[assembly: AssemblyKeyName("")] +[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] +[assembly: AssemblyVersion("2.0.0.0")] +namespace ArchestrA.Scripting +{ + [Guid("85963980-46DF-459d-8A96-2A2AF70F458F")] + [ClassInterface(ClassInterfaceType.AutoDual)] + public class ScriptPackage + { + private class NameHierarchy + { + public Hashtable Hashtable = new Hashtable(); + + public readonly string Name; + + public int Index; + + public NameHierarchy(string name) + { + Name = name; + Index = -1; + } + + public NameHierarchy() + : this(null) + { + } + + public void Add(string name, int index) + { + string text = null; + string text2 = null; + int num = name.IndexOf('.'); + if (num < 0) + { + text = name; + } + else + { + text = name.Substring(0, num); + text2 = name.Substring(num + 1, name.Length - num - 1); + } + string key = text.ToLower(); + if (!Hashtable.ContainsKey(key)) + { + Hashtable[key] = new NameHierarchy(text); + } + NameHierarchy nameHierarchy = (NameHierarchy)Hashtable[key]; + if (text2 != null) + { + nameHierarchy.Add(text2, index); + } + else + { + nameHierarchy.Index = index; + } + } + } + + private class TypeResolveHandler + { + private Module Module; + + private Hashtable Hashtable; + + public TypeResolveHandler(Module module, Hashtable hashtable) + { + Module = module; + Hashtable = hashtable; + } + + public Assembly ResolveEvent(object sender, ResolveEventArgs args) + { + ((TypeBuilder)Hashtable[args.Name]).CreateType(); + return Module.Assembly; + } + } + + private string BinFolder; + + private bool DebugEnabled; + + private Parser Parser; + + private string TagName; + + private string ScriptName; + + private Hashtable ReferencedAssemblies; + + private ModuleBuilder ModuleBuilder; + + private TypeBuilder TypeBuilder; + + private ILBuilder ILBuilder; + + private int NestedTypeIndex; + + private string ErrorMessage; + + private int ErrorLine; + + private int ErrorColumn; + + private static readonly FieldInfo ScriptExchange; + + private static readonly Type[] ExpessionParameterTypes; + + private static readonly MethodInfo EnableDebugSupport; + + private static string DebugFolder => Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "QuickScriptDebug"); + + private static bool DebuggerInfoEnabled => Directory.Exists(DebugFolder); + + public void Compile(string libraryPath, string repositoryPath, string tagName, string scriptName, string compilerAssemblyFile, string compilerTypeName, IntPtr outputAssemblyName, IntPtr aliases, IntPtr declarationsText, IntPtr startupText, IntPtr onScanText, IntPtr expression, IntPtr executeText, IntPtr offScanText, IntPtr shutdownText, IntPtr binary, IntPtr errorMessage, IntPtr errorLine, IntPtr errorColumn, IntPtr libaryDependencies, IntPtr externalReferences, IntPtr externalReferenceFlags, IntPtr aliasReferenceFlags) + { + IList list = null; + byte[] array = null; + ErrorMessage = ""; + BinFolder = Path.GetDirectoryName(Assembly.GetAssembly(GetType()).Location); + Parser = new Parser(); + ILBuilder = new ILBuilder(); + ReferencedAssemblies = new Hashtable(); + list = new ArrayList(); + TagName = tagName; + ScriptName = scriptName; + DebugEnabled = DebuggerInfoEnabled; + NestedTypeIndex = 0; + int mxValueElementCount = GetMxValueElementCount(aliases); + int i; + for (i = 0; i < mxValueElementCount; i++) + { + ExternalReferences.AddAlias(GetMxValueElementString(aliases, i + 1)); + } + try + { + string tempPath = Path.GetTempPath(); + AssemblyName assemblyName = new AssemblyName(); + assemblyName.Name = GetMxValueString(outputAssemblyName); + string text = assemblyName.Name + ".dll"; + Directory.SetCurrentDirectory(AppDomain.CurrentDomain.BaseDirectory); + AssemblyBuilder assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Save, tempPath); + if (DebugEnabled) + { + CustomAttributeBuilder customAttribute = new CustomAttributeBuilder(typeof(DebuggableAttribute).GetConstructor(new Type[2] + { + typeof(bool), + typeof(bool) + }), new object[2] { true, true }); + assemblyBuilder.SetCustomAttribute(customAttribute); + } + ModuleBuilder = assemblyBuilder.DefineDynamicModule(assemblyName.Name, text, DebugEnabled); + TypeBuilder = ModuleBuilder.DefineType("ArchestrA.Scripting.Script", TypeAttributes.Public, typeof(ScriptBase), new Type[0]); + ILGenerator iLGenerator = TypeBuilder.DefineConstructor(MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[0]).GetILGenerator(); + ILBuilder.ILGenerator = iLGenerator; + iLGenerator.Emit(OpCodes.Ldarg_0); + iLGenerator.Emit(OpCodes.Call, typeof(ScriptBase).GetConstructor(new Type[0])); + ILBuilder.DebugEnabled = false; + ILBuilder.FieldScriptExchange = ScriptExchange; + CompileDeclarations(GetMxValueString(declarationsText)); + ILBuilder.Emit(new Return()); + if (ErrorMessage.Length == 0 || ErrorMessage.StartsWith("Warning")) + { + CompileExpression(GetMxValueString(expression)); + CompileMethod("Startup", GetMxValueString(startupText)); + CompileMethod("OnScan", GetMxValueString(onScanText)); + CompileMethod("Execute", GetMxValueString(executeText)); + CompileMethod("OffScan", GetMxValueString(offScanText)); + CompileMethod("Shutdown", GetMxValueString(shutdownText)); + } + if (ErrorMessage.Length == 0 || ErrorMessage.StartsWith("Warning")) + { + if (DebugEnabled) + { + Hashtable hashtable = new Hashtable(); + ResolveEventHandler value = new TypeResolveHandler(ModuleBuilder, hashtable).ResolveEvent; + AppDomain.CurrentDomain.TypeResolve += value; + NameHierarchy nameHierarchy = new NameHierarchy(); + int num = 0; + foreach (ExternalReference item in ExternalReferences.List) + { + nameHierarchy.Add(item.ReferenceString.Split('[')[0], num++); + } + Build(nameHierarchy, TypeBuilder, hashtable); + RecurseNestedTypes(TypeBuilder.CreateType()); + AppDomain.CurrentDomain.TypeResolve -= value; + } + else + { + TypeBuilder.CreateType(); + } + assemblyBuilder.Save(text); + } + foreach (Assembly key in ReferencedAssemblies.Keys) + { + list.Add(key.GetName().Name); + } + if (ErrorMessage.Length == 0) + { + string path = Path.Combine(tempPath, text); + using (FileStream fileStream = File.OpenRead(path)) + { + array = new byte[fileStream.Length]; + fileStream.Read(array, 0, (int)fileStream.Length); + } + if (DebugEnabled) + { + File.Exists(Path.ChangeExtension(path, "pdb")); + } + File.Delete(path); + } + } + catch (Exception ex) + { + ErrorMessage = "Internal QuickScript compiler error: " + ex.Message; + } + if (ErrorMessage.Length > 0) + { + PutMxValueString(errorMessage, ErrorMessage); + PutMxValueInteger(errorLine, ErrorLine); + PutMxValueInteger(errorColumn, ErrorColumn); + return; + } + PutMxValueString(errorMessage, ""); + PutMxValueInteger(errorLine, 0); + PutMxValueInteger(errorColumn, 0); + PutMxValueBlob(binary, array, array.Length); + i = 1; + foreach (string item2 in list) + { + PutMxValueElementString(libaryDependencies, i++, item2); + } + i = 1; + int index = 1; + foreach (ExternalReference item3 in ExternalReferences.List) + { + if (item3.IsAlias) + { + PutMxValueElementInteger(aliasReferenceFlags, i++, item3.Flags); + continue; + } + PutMxValueElementString(externalReferences, index, item3.ReferenceString); + PutMxValueElementInteger(externalReferenceFlags, index++, item3.Flags); + } + } + + private void RecurseNestedTypes(Type type) + { + Type[] nestedTypes = type.GetNestedTypes(); + foreach (Type type2 in nestedTypes) + { + RecurseNestedTypes(type2); + } + } + + private void GetErrorsAndWarnings(string section) + { + if (ErrorMessage.Length > 0 && !ErrorMessage.StartsWith("Warning")) + { + return; + } + IEnumerator enumerator = Parser.Errors.GetEnumerator(); + try + { + if (enumerator.MoveNext()) + { + ErrorInfo errorInfo = (ErrorInfo)enumerator.Current; + ErrorLine = errorInfo.Line; + ErrorColumn = errorInfo.Column; + ErrorMessage = $"Script {ScriptName} ({section}): {errorInfo.Message} (Line: {errorInfo.Line}, Col: {errorInfo.Column})"; + return; + } + } + finally + { + IDisposable disposable = enumerator as IDisposable; + if (disposable != null) + { + disposable.Dispose(); + } + } + enumerator = Parser.Warnings.GetEnumerator(); + try + { + if (enumerator.MoveNext()) + { + ErrorInfo errorInfo2 = (ErrorInfo)enumerator.Current; + ErrorLine = errorInfo2.Line; + ErrorColumn = errorInfo2.Column; + ErrorMessage = $"Warning: Script {ScriptName} ({section}): {errorInfo2.Message} (Line: {errorInfo2.Line}, Col: {errorInfo2.Column})"; + } + } + finally + { + IDisposable disposable2 = enumerator as IDisposable; + if (disposable2 != null) + { + disposable2.Dispose(); + } + } + } + + private void CompileDeclarations(string declarationsText) + { + if (declarationsText.Length == 0) + { + return; + } + ILBuilder.DeclarationsAreFields = true; + Parser.DeclarationsAreFields = true; + Parser.Parse(declarationsText); + if (Parser.Tree is Expression) + { + CodeSpan codeSpan = (Parser.Tree as Expression).CodeSpan; + codeSpan = new CodeSpan(codeSpan.End.Line, codeSpan.End.Column + 1, codeSpan.End.Line, codeSpan.End.Column + 1); + Parser.Errors.Add(new ErrorInfo("Expected ';'", codeSpan.End.Line, codeSpan.End.Column, codeSpan.End.Line, codeSpan.End.Column)); + } + GetErrorsAndWarnings("Declarations"); + if (Parser.Errors.Count != 0) + { + return; + } + foreach (Statement item in Parser.Tree as IList) + { + Declaration declaration = item as Declaration; + FieldBuilder value = TypeBuilder.DefineField(declaration.VariableName, declaration.Type, FieldAttributes.Public); + ILBuilder.Variables[declaration.VariableName] = value; + ILBuilder.Emit(declaration); + } + Parser.GetReferencedAssemblies(ReferencedAssemblies); + } + + private void CompileExpression(string expressionText) + { + if (expressionText.Length != 0) + { + ILBuilder.DeclarationsAreFields = false; + Parser.DeclarationsAreFields = false; + Parser.Parse(expressionText); + if (Parser.Tree is Statements) + { + CodeSpan codeSpan = ((Parser.Tree as Statements)[0] as Statement).CodeSpan; + Parser.Errors.Add(new ErrorInfo("Not expecting ';'", codeSpan.End.Line, codeSpan.End.Column, codeSpan.End.Line, codeSpan.End.Column)); + } + GetErrorsAndWarnings("Expression"); + if (Parser.Errors.Count <= 0) + { + MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig; + MethodBuilder methodBuilder = TypeBuilder.DefineMethod("Expression", attributes, typeof(void), ExpessionParameterTypes); + methodBuilder.DefineParameter(1, ParameterAttributes.Out, null); + methodBuilder.DefineParameter(2, ParameterAttributes.Out, null); + ILGenerator iLGenerator = methodBuilder.GetILGenerator(); + ILBuilder.ILGenerator = iLGenerator; + ILBuilder.Variables.ClearLocals(); + ILBuilder.DebugEnabled = false; + Expression expression = Parser.Tree as Expression; + iLGenerator.Emit(OpCodes.Ldarg_1); + ILBuilder.Emit(Coerce.ChangeType(expression, typeof(object))); + iLGenerator.Emit(OpCodes.Stind_Ref); + iLGenerator.Emit(OpCodes.Ldarg_2); + ILBuilder.Emit(Digester.GetQuality(expression)); + iLGenerator.Emit(OpCodes.Conv_I4); + iLGenerator.Emit(OpCodes.Stind_I4); + ILBuilder.Emit(new Return()); + Parser.GetReferencedAssemblies(ReferencedAssemblies); + } + } + } + + private void CompileMethod(string methodName, string methodText) + { + if (methodText.Length == 0) + { + if (!(methodName == "Startup")) + { + return; + } + methodText = ";"; + } + ILBuilder.DeclarationsAreFields = false; + Parser.DeclarationsAreFields = false; + Parser.Parse(methodText); + if (Parser.Tree is Expression) + { + CodeSpan codeSpan = (Parser.Tree as Expression).CodeSpan; + codeSpan = new CodeSpan(codeSpan.End.Line, codeSpan.End.Column + 1, codeSpan.End.Line, codeSpan.End.Column + 1); + Parser.Errors.Add(new ErrorInfo("Expected ';'", codeSpan.End.Line, codeSpan.End.Column, codeSpan.End.Line, codeSpan.End.Column)); + } + GetErrorsAndWarnings(methodName); + if (Parser.Errors.Count > 0) + { + return; + } + ISymbolDocumentWriter symbolDocumentWriter = null; + if (DebugEnabled) + { + string text = DebugFolder + "\\" + TagName + "." + ScriptName + "." + methodName + ".qs"; + if (methodText.Length > 1) + { + using StreamWriter streamWriter = File.CreateText(text); + streamWriter.Write(methodText); + } + symbolDocumentWriter = ModuleBuilder.DefineDocument(text, Guid.Empty, Guid.Empty, Guid.Empty); + } + Type[] parameterTypes = new Type[0]; + switch (methodName) + { + case "Startup": + parameterTypes = new Type[1] { typeof(IScriptExchange) }; + break; + case "Execute": + parameterTypes = new Type[1] { typeof(ScriptTimer) }; + break; + } + MethodAttributes attributes = MethodAttributes.Public | MethodAttributes.Final | MethodAttributes.Virtual | MethodAttributes.HideBySig; + ILGenerator iLGenerator = TypeBuilder.DefineMethod(methodName, attributes, typeof(void), parameterTypes).GetILGenerator(); + ILBuilder.ILGenerator = iLGenerator; + ILBuilder.Variables.ClearLocals(); + ILBuilder.SymbolDocumentWriter = symbolDocumentWriter; + ILBuilder.DebugEnabled = false; + ILBuilder.CheckForTimeout = methodName == "Execute" && !DebugEnabled; + if (methodName == "Startup") + { + iLGenerator.Emit(OpCodes.Ldarg_0); + iLGenerator.Emit(OpCodes.Ldarg_1); + iLGenerator.Emit(OpCodes.Stfld, ScriptExchange); + if (DebugEnabled) + { + Expression destination = new Field(new This(), typeof(ScriptBase).GetField("TagName")); + Expression source = new ConstantString(TagName); + ILBuilder.Emit(new Assignment(destination, source)); + ILBuilder.Emit(new ExpressionStatement(new Method(null, EnableDebugSupport, new Expression[1] + { + new This() + }))); + } + } + Statements statements = Parser.Tree as Statements; + ILBuilder.DebugEnabled = DebugEnabled; + ILBuilder.LineNumber = 0; + ILBuilder.Emit(statements); + ILBuilder.Emit(new Return()); + Parser.GetReferencedAssemblies(ReferencedAssemblies); + } + + static ScriptPackage() + { + ScriptExchange = typeof(ScriptBase).GetField("ScriptExchange"); + ExpessionParameterTypes = null; + EnableDebugSupport = typeof(ArchestrA.QuickScript.Runtime.ExternalReference).GetMethod("EnableDebugSupport"); + ParameterInfo[] parameters = typeof(IScriptObject).GetMethod("Expression").GetParameters(); + ExpessionParameterTypes = new Type[parameters.Length]; + int num = 0; + ParameterInfo[] array = parameters; + foreach (ParameterInfo parameterInfo in array) + { + ExpessionParameterTypes[num++] = parameterInfo.ParameterType; + } + } + + private void Build(NameHierarchy nameHierarchy, TypeBuilder typeBuilder, Hashtable hashtable) + { + Type typeFromHandle = typeof(ArchestrA.QuickScript.Runtime.ExternalReference); + typeFromHandle.GetField("Index"); + if (nameHierarchy.Index == -1) + { + TypeBuilder typeBuilder2 = null; + if (nameHierarchy.Name != null) + { + typeBuilder2 = typeBuilder.DefineNestedType("Tag" + ++NestedTypeIndex, TypeAttributes.NestedPublic, typeFromHandle); + } + foreach (NameHierarchy value in nameHierarchy.Hashtable.Values) + { + Build(value, (typeBuilder2 != null) ? typeBuilder2 : typeBuilder, hashtable); + } + if (nameHierarchy.Name != null) + { + hashtable[typeBuilder2.Name] = typeBuilder2; + } + if (typeBuilder2 != null) + { + typeBuilder.DefineField(nameHierarchy.Name, typeBuilder2, FieldAttributes.Public); + } + } + else if (nameHierarchy.Name != null) + { + string name = "T" + nameHierarchy.Index; + TypeBuilder typeBuilder3 = typeBuilder.DefineNestedType(name, TypeAttributes.NestedPublic, typeof(ExternalReferenceDebug)); + typeBuilder.DefineField(nameHierarchy.Name, typeBuilder3, FieldAttributes.Public); + hashtable[typeBuilder3.Name] = typeBuilder3; + } + } + + [DllImport("ScriptPackage.dll", CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.BStr)] + private static extern string GetMxValueString(IntPtr rawMxValue); + + [DllImport("ScriptPackage.dll")] + [SuppressUnmanagedCodeSecurity] + private static extern void PutMxValueInteger(IntPtr rawMxValue, int value); + + [DllImport("ScriptPackage.dll", CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern void PutMxValueString(IntPtr rawMxValue, string value); + + [DllImport("ScriptPackage.dll")] + [SuppressUnmanagedCodeSecurity] + private static extern void PutMxValueBlob(IntPtr rawMxValue, [MarshalAs(UnmanagedType.LPArray)] byte[] value, int size); + + [DllImport("ScriptPackage.dll")] + [SuppressUnmanagedCodeSecurity] + private static extern int GetMxValueElementCount(IntPtr rawMxValue); + + [DllImport("ScriptPackage.dll", CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + [return: MarshalAs(UnmanagedType.BStr)] + private static extern string GetMxValueElementString(IntPtr rawMxValue, int index); + + [DllImport("ScriptPackage.dll", CharSet = CharSet.Unicode)] + [SuppressUnmanagedCodeSecurity] + private static extern void PutMxValueElementString(IntPtr rawMxValue, int index, string value); + + [DllImport("ScriptPackage.dll")] + [SuppressUnmanagedCodeSecurity] + private static extern void PutMxValueElementInteger(IntPtr rawMxValue, int index, int value); + } +} +namespace AcmeBuildVersion +{ + internal sealed class Versioning + { + public const string Company = "AVEVA Software, LLC"; + + public const string Product = ""; + + public const string Copyright = "Copyright 2020 AVEVA Group plc and its subsidiaries. All rights reserved."; + + public const string Trademark = "Refer to: https://sw.aveva.com/legal/trademarks"; + + public const string Configuration = "Release"; + + public const string BuildNumber = "5800"; + + public const string BuildMaintenanceNumber = "7005"; + + public const string BldRevision = "1"; + + private Versioning() + { + } + } + internal sealed class ComponentVersioning + { + public const string ComponentVersion = "0038"; + + public const string ComponentMaintenanceVersion = "0000"; + + public const string ComponentName = "ScriptPrimitive"; + + private ComponentVersioning() + { + } + } +} diff --git a/graccesscli/analysis/package-manager-deep-dive.md b/graccesscli/analysis/package-manager-deep-dive.md new file mode 100644 index 0000000..b5ec335 --- /dev/null +++ b/graccesscli/analysis/package-manager-deep-dive.md @@ -0,0 +1,296 @@ +# Package Manager Deep Dive + +Date: 2026-05-05 + +## Scope + +This investigation focused on the local System Platform package manager path that sits below GRAccess and appears to be the missing write path for object-level script body fields and some settings. + +Tools used: + +- `TlbImp.exe` against registered COM/type libraries. +- .NET reflection over generated interop assemblies. +- Registry inspection of `HKCR\TypeLib`. +- Binary string extraction from package/runtime DLLs. +- `dumpbin.exe` on native package/runtime DLLs. +- Existing decompiled IDE wrapper notes under `analysis/ide-edit-investigation`. + +Ghidra was available at `C:\Users\dohertj2\Desktop\focas\tools\ghidra_12.0.4_PUBLIC\support\analyzeHeadless.bat`, but the registered type libraries and embedded primitive XML exposed the actionable surface without needing a full native decompile. + +## Registered components + +### Galaxy package server + +- Type library: `xxGalaxyPackageServer 1.0 Type Library` +- TypeLib GUID: `{74B9B2CA-05E6-4BC4-81C3-1CAFF652035D}` +- Binary: `C:\Program Files (x86)\ArchestrA\Framework\Bin\xxGalaxyPackageServer.dll` +- ProgID: `XxGalaxyPackageServer.GalaxyPackage` +- CLSID: `{F6C78E0D-8EA2-48B8-9592-6D9FB507DAD3}` +- Threading model: `Apartment` + +### Configuration access component + +- Type library: `ConfigurationAccessComponent 1.0 Type Library` +- TypeLib GUID: `{2539619B-EA9B-4035-9AE6-71421DA6C8FD}` +- Binary: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ConfigurationAccessComponent.dll` +- Main COM server: `ConfigurationAccessComponent.ConfigurationAccessServer` +- CLSID from interop: `{55414847-A533-4642-8E92-76B191B24B87}` + +### Script package + +- Type library: `ScriptPackage` +- TypeLib GUID: `{8DF25C4D-C021-4080-849D-74A81D97D548}` +- Binary: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ScriptPackage.dll` +- Package CLSID: `{A261929D-90AB-4217-9234-667A83756FFB}` +- Managed compiler helper: `C:\Program Files (x86)\ArchestrA\Framework\Bin\ScriptPackage.Net.dll` + +### Script runtime primitive definition + +The `ScriptExtension` primitive definition is embedded in: + +- `C:\Program Files (x86)\ArchestrA\Framework\Bin\ScriptRuntime.dll` + +Important embedded metadata: + +- `DefaultInternalName`: `ScriptExtension` +- `Package_CLSID`: `{A261929D-90AB-4217-9234-667A83756FFB}` +- `Runtime_CLSID`: `{2B2616D6-98A0-4e94-ABAD-C75BB022E2D6}` +- Default `ExecutionGroup`: `Custom 1` + +## Package manager surface + +The important public COM interfaces are already in the local package server type library. + +### `IGalaxyConfiguration` / `IGalaxyConfigurationV30` + +This is the high-level object lifecycle and package access surface. Important methods: + +- `GetIDFromObjectName(Namespace nSpace, string objectName) -> int` +- `GetGObjectInfoFromName(string name, Namespace nSpace)` +- `CheckOutObjects(int[] gObjectIds, int hint, IGObjectOperationStatus callback)` +- `CheckinObjects(int[] gObjectIds, string comment, int hint, IGObjectOperationStatus callback)` +- `UndoCheckoutObjects(int[] gObjectIds, int hint, bool override, IGObjectOperationStatus callback)` +- `GetObject(int objectId, ECODEMODULES codeModules, out object package, out ERRORCODE status, out string message, EPACKAGEPROP prop)` +- `GetObjectInternal(int objectId, ECODEMODULES code, bool editCheckRequired, out object package, out ERRORCODE status, out string message, EPACKAGEPROP prop)` +- `GetGalaxyConfigurationNet() -> object` +- `GetIGalaxy() -> object` + +### `IPackageManager` + +This is broader and includes lower-level attribute APIs. Important methods: + +- `GetObject(...)` +- `GetObjectInternal(...)` +- `GetFullObjectByName(string name) -> object` +- `GetIDFromName(string name) -> int` +- `ReleaseFsObjectEditsession(int fsObjectId)` +- `GetObjectAttribute(Namespace, objectName, attributeName, EATTRIBUTEPROPERTY, MxValue, out result)` +- `InternalSetObjectAttributesEx(Namespace, objectName, EPACKAGETYPE, string[] attributeNames, EATTRIBUTEPROPERTY[] properties, MxValue[] values, bool saveObjectEvenErrorOccurs, out status, out statusMsg)` +- `InternalGetObjectAttributesEx(...)` + +The `InternalSetObjectAttributesEx` method is a promising direct write path because it accepts object name, package type, attribute names, property ids, and MxValues without needing the IDE configuration access component. + +### `IConfigurationEditorSite2` + +This is the direct package editor site returned by package access calls. Important methods: + +- `GetAttributeCookie(string attributeFullName) -> int` +- `GetAttribute(int cookie, short propertyId, MxValue value)` +- `SetAttribute(int cookie, short propertyId, MxValue value) -> bool` +- `Validate()` +- `Commit(out string errorString) -> EPACKAGEOPERATIONSTATUS` +- `IsReadOnly()` +- `SetSubscriberInfo(object subscription)` +- `AddExtensionPrimitive(...)` +- `DeleteExtensionPrimitive(...)` +- `RenameExtensionPrimitive(...)` +- `AddUDA(...)` +- `DeleteUDA(...)` +- `RenameUDA(...)` +- `UpdateUDA(...)` + +For normal value writes, property id `10` is `EATTRIBUTEPROPERTY.idxAttribPropValue`. + +### `IPrimitivePackageSite7` + +The script package uses the generic primitive package site contract: + +- `GetAttributeHandle(string attributeFullName) -> AttributeHandle` +- `GetAllCategoryAttributeHandle(string attributeFullName) -> AttributeHandle` +- `GetAttribute(AttributeHandle, MxValue)` +- `SetAttribute(AttributeHandle, MxValue)` +- `GetAttributeHandleEx(...)` +- `GetPrimitiveIdByInternalNameEx(...)` +- `GetPrimitiveAttributeIds(...)` +- dynamic attribute operations. + +The script package itself exposes: + +- `CoScriptPackageClass.Initialize(short primitiveId, IPrimitivePackageSite site)` +- `SetHandler(ref AttributeHandle, MxValue) -> string` +- `Validate() -> EPACKAGESTATUS` + +There is no script-specific "SetScriptText" API in the script package typelib. It is generic attribute/handler based. + +## ScriptExtension attribute facts + +`ScriptRuntime.dll` embeds the actual script primitive attributes. The important ones: + +- `ExecuteText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `DeclarationsText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `StartupText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `OnScanText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `OffScanText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `ShutdownText`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `Expression`: `MxBigString`, `PackageOnly_Lockable`, `CfgSethandler=True` +- `TriggerType`: `MxCustomEnum`, `Writeable_C_Lockable`, `CfgSethandler=True` +- `_TriggerTypeEnum`: values `WhileTrue`, `WhileFalse`, `OnTrue`, `OnFalse`, `DataChange`, `Periodic` +- `TriggerPeriod`: `MxElapsedTime`, `Writeable_C_Lockable`, `CfgSethandler=True` +- `DataChangeDeadband`: `MxDouble`, `Writeable_UC_Lockable`, `CfgSethandler=True` +- `RunsAsync`: `MxBoolean`, `Writeable_C_Lockable`, `CfgSethandler=True` +- `ExecuteTimeout.Limit`: `MxInteger`, `Writeable_C_Lockable`, `CfgSethandler=True` +- `ScriptExecutionGroup`: `MxCustomEnum`, `PackageOnly_Lockable`, `CfgSethandler=False` +- `ScriptOrder`: `MxInteger`, `PackageOnly_Lockable`, `CfgSethandler=False` + +This confirms the current GRAccess `IAttribute.SetValue` path is not enough for script bodies. The body fields are package-only and cfg-sethandler driven. + +## IDE wrapper behavior + +The decompiled IDE wrapper creates `ConfigurationAccessComponent.ConfigurationAccessServer`, casts it to `IConfigurationEditor`, and initializes it with the object package site: + +1. `configurationEditor.Initialize("", inObjectManage, this, readOnly)` +2. `inObjectManage.SetSubscriberInfo(configurationEditor)` +3. `inObjectManage.SetSubscriberInfo(this)` +4. Subscribes to `DConfigurationAccessEvents`. +5. Sets CAS data: + - `ww:SupportedLocales` + - `ww:IPackageManager` + - `ww:Tagname` + - `ww:ContainedName` + - `ww:DerivedFrom` + - `ww:KeepCheckedOut` + +This is a full IDE editor-host path. It is probably more complete than direct attribute writes, but it requires implementing enough editor-site/event plumbing in the CLI. + +## Feasible edit paths + +### Path A: direct package editor site + +Likely flow: + +1. Use existing GRAccess login to obtain `IGalaxy`. +2. Call `IGalaxy.GetGalaxyConfiguration()` and cast the returned object to `IGalaxyConfiguration` or `IGalaxyConfigurationV30`. +3. Resolve object id with `GetIDFromObjectName(Namespace.AutomationObject, tagName)`. +4. Check out the object via existing GRAccess or `CheckOutObjects`. +5. Get the editable package with `GetObjectInternal(id, eEditorAndPackageCodeModules, true, out package, ...)` or `GetObject(...)`. +6. Cast the package object to `IConfigurationEditorSite2`. +7. Resolve `scriptName.ExecuteText` with `GetAttributeCookie`. +8. Create `MxValueClass` and call `PutString(newScriptBody)`. +9. Call `SetAttribute(cookie, idxAttribPropValue, mxValue)`. +10. Call `Validate()`. +11. Call `Commit(out error)`. +12. Check in or undo checkout and release edit session on failure. + +Pros: + +- Uses typed COM APIs. +- Avoids the full IDE configuration access server. +- Directly targets the package-only fields. + +Risks: + +- Needs live validation that the object returned by `GetObjectInternal` supports `IConfigurationEditorSite2` in this context. +- Needs confirmation that `SetAttribute` invokes the script package cfg set handler and recompiles `_Binary`. +- Needs correct custom enum MxValue creation for `TriggerType`. + +### Path B: package manager internal batch set + +Likely flow: + +1. Get `IPackageManager` from the same object returned by `IGalaxy.GetGalaxyConfiguration()` or via package-manager support. +2. Check out object. +3. Call `InternalSetObjectAttributesEx` with: + - `Namespace.AutomationObject` + - object tagname + - likely `EPACKAGETYPE.eBeingEditedPackage` + - attribute names such as `UpdateTestChangingInt.ExecuteText` + - property ids `[idxAttribPropValue]` + - MxValues for the new data + - `saveObjectEvenErrorOccurs=false` initially +4. Check status and status message. +5. Check in or undo checkout. + +Pros: + +- Smallest API surface. +- No editor host required. +- Best candidate for a CLI-first package write helper. + +Risks: + +- It is explicitly named `Internal`. +- It may require exact package type and checkout state. +- Unknown whether it invokes all cfg-sethandler compile side effects for script bodies. + +### Path C: configuration access component + +Likely flow: + +1. Get editable object package as `IConfigurationEditorSite2`. +2. Instantiate `ConfigurationAccessComponent.ConfigurationAccessServer`. +3. Implement minimal `IBaseEditorSite` and event sink in the CLI. +4. Call `IConfigurationEditor.Initialize`. +5. Set the IDE wrapper's `ww:*` data entries. +6. Set `Data("ScriptName.ExecuteText", "Value") = body`. +7. Apply/commit through the editor site. + +Pros: + +- Closest to the IDE behavior. +- Most likely to run all validation/dirty/event plumbing. + +Risks: + +- Largest implementation. +- Requires COM connection-point/event hosting. +- More fragile in a headless CLI. + +## Current CLI implication + +The current `object scripts set` path writes through `IAttribute.SetValue`. That cannot persist the package-only body fields shown above. + +The current `object scripts settings set --trigger-period-ms` should be more viable because `TriggerPeriod` is `Writeable_C_Lockable`, but it still goes through generic GRAccess `IAttribute.SetValue`. + +The current `object scripts settings set --trigger-type` is suspicious because `TriggerType` is `MxCustomEnum`; the CLI currently creates an `MxValue` string for non-explicit data types. The package metadata says the valid values are backed by `_TriggerTypeEnum`, so a package-manager implementation should set `TriggerType` as a custom enum or verify that package `SetAttribute` accepts the string form. + +## Recommended next implementation + +Do not add more typelibs first. The useful typelibs already exist locally: + +- `ArchestrA.GRAccess.dll` +- `xxGalaxyPackageServer.dll` +- `ConfigurationAccessComponent.dll` +- `ScriptPackage.dll` + +The next improvement should add a small, isolated package-manager interop layer and a probe/edit helper: + +1. Add minimal `[ComImport]` declarations for only the needed package-manager interfaces, or add a checked-in interop reference generated from `xxGalaxyPackageServer.dll`. +2. Bridge from `IGalaxy.GetGalaxyConfiguration()` to `IGalaxyConfigurationV30` / `IPackageManager`. +3. Implement a read-only probe first: + - object id resolution + - `GetObjectInternal` + - cast checks for `IConfigurationEditorSite2`, `IObjectManage`, `IPackageManager` + - `GetAttributeCookie` for script fields/settings + - `GetAttribute` for `idxAttribPropValue` +4. Implement one guarded write path for `ExecuteText`. +5. Verify whether `_Binary`, `_ErrorMessage`, `_ErrorLine`, and `_ErrorColumn` update after commit. If they do not, switch from Path A/B to the CAS path. +6. Only then wire it into `object scripts set` and package-only script settings. + +## Live validation blocker + +Previous live GRAccess validation against the `ZB` galaxy failed because the local license was unavailable: + +`cmdLicenseUnavailable (105); Feature 'wspdevstudio-iocount'` + +The package-manager write path needs live validation after license access is restored because static metadata cannot prove whether script cfg-sethandler side effects fire on direct package writes. + diff --git a/graccesscli/docs/llm-integration.md b/graccesscli/docs/llm-integration.md index a9ba672..f360889 100644 --- a/graccesscli/docs/llm-integration.md +++ b/graccesscli/docs/llm-integration.md @@ -60,7 +60,7 @@ graccess object scripts get --galaxy ZB --name TestMachine --type template --scr The CLI tries direct GRAccess reads first. If direct GRAccess does not expose inheritance, attribute values, or script bodies, read commands may export the target object to a temporary `.aaPKG`, parse textual/package entries, recurse into nested package archives, parse binary UTF-16 script extension records, and delete the temp files. SQL Server repository reads are not part of normal CLI behavior and should only be used for development verification/debugging. -Script body access is adapter-dependent. For `ScriptExtension` objects, a bare script name maps to `.ExecuteText`. Mutating script and attribute commands prefer `ConfigurableAttributes[...]` before `Attributes[...]`. On the local GRAccess build, body fields such as `ExecuteText` and `Expression` are package-only and are not persisted by `IAttribute.SetValue`, so the CLI fails fast for those writes instead of returning a false success. Mutable settings such as `TriggerPeriod` and `TriggerType` remain available through `object scripts settings set`. If neither direct GRAccess nor the package fallback exposes body text, script read commands return structured unavailable details instead of pretending success. +Script body access is adapter-dependent. For `ScriptExtension` objects, a bare script name maps to `.ExecuteText`; `object scripts set --file ` writes the matching script body attribute through GRAccess. Mutating script and attribute commands prefer `ConfigurableAttributes[...]` before `Attributes[...]`, because script extension settings such as `TriggerPeriod`, `TriggerType`, `Expression`, and `ExecuteText` are configuration attributes per AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess"). If neither direct GRAccess nor the package fallback exposes body text on a read, script read commands return structured unavailable details — but writes via the TN-537 pattern persist; round-trip evidence is in `analysis/ide-edit-investigation/probe_setvalue/`. Use the script settings wrapper for IDE-style script configuration: @@ -71,7 +71,7 @@ graccess object scripts settings set --galaxy ZB --name '$TestMachine' --type te Use the create wrapper for new object-level script extensions: ```powershell -graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' --llm-json +graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --file OnScan.txt --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' --llm-json ``` ## Inheritance And Embedded Objects diff --git a/graccesscli/docs/script-editing.md b/graccesscli/docs/script-editing.md index 1571609..c816c19 100644 --- a/graccesscli/docs/script-editing.md +++ b/graccesscli/docs/script-editing.md @@ -17,7 +17,7 @@ For LLM-driven script work, read script metadata and validate guarded edits with ```powershell graccess object scripts list --galaxy ZB --name TestMachine --type template --llm-json graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json -graccess object scripts settings set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --trigger-period-ms 500 --confirm --confirm-target TestMachine --dry-run --llm-json +graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --dry-run --llm-json ``` ## What The CLI Can Edit Today @@ -28,8 +28,9 @@ graccess object scripts settings set --galaxy ZB --name TestMachine --type templ | Script library export | `script-library export` | | Script library import/add | `script-library import`, `script-library add`, `galaxy import-script-library` | | Object script metadata | `object scripts list`, `object scripts get` | -| Object script body read | `object scripts get` package fallback for script body attributes such as `ExecuteText` | +| Object script body read/write | `object scripts get` (package fallback) and `object scripts set --file [--field ]` for body fields like `ExecuteText` / `DeclarationsText` / `StartupText` / `ShutdownText` / `OnScanText` / `OffScanText` / `Expression`. See AVEVA Tech Note 537. | | Object script mutable settings | `object scripts settings set --trigger-type`, `--trigger-period-ms` | +| Object script create/delete | `object scripts create`, `object scripts delete` | | Script-like attributes | `object attribute set`, lock, security, buffer | | Extension primitives | `object extension add/delete/rename` | | Full object package script payloads | Use `objects export` and `galaxy import-objects` | @@ -91,17 +92,17 @@ $scripts = (@($attrs) + @($extended)) | Where-Object { $scripts | Select-Object Name, DataType, Category, Locked ``` -If the script-like setting is a scalar writable attribute, edit it through the normal template edit flow. Script extension attributes are looked up through `ConfigurableAttributes` first, then `Attributes`. - -For local `ScriptExtension` objects, body fields such as `ExecuteText`, `StartupText`, `OnScanText`, and `Expression` are package-only. GRAccess `IAttribute.SetValue` can return success without persisting package-only fields, so `object scripts set` and `object scripts settings set --expression` fail fast when the attribute category starts with `MxCategoryPackageOnly`. Use the IDE or a future package-rewrite path for those fields. +If the script-like setting is a scalar writable attribute, edit it through the normal template edit flow. Script extension attributes are looked up through `ConfigurableAttributes` first, then `Attributes` — the pattern AVEVA Tech Note 537 ("Creating an Application Object Script Using GRAccess") prescribes for `ScriptExtension` text fields. `object scripts set`, `object scripts settings set`, and `object attribute value set` all use that ordering. A bare `--script ` writes to `.ExecuteText`; pass `--field ` (or include the suffix in `--script`) to target `DeclarationsText`, `StartupText`, `ShutdownText`, `OnScanText`, `OffScanText`, or `Expression`. ```powershell graccess object checkout --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine -graccess object attribute value set --galaxy ZB --name TestMachine --type template --attribute SomeWritableScriptSetting --value Updated --data-type string --confirm --confirm-target TestMachine +graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine graccess object save --galaxy ZB --name TestMachine --type template --confirm --confirm-target TestMachine -graccess object checkin --galaxy ZB --name TestMachine --type template --comment 'Update writable script setting' --confirm --confirm-target TestMachine +graccess object checkin --galaxy ZB --name TestMachine --type template --comment 'Update script body' --confirm --confirm-target TestMachine ``` +> **Reader caveat.** `object attribute value get` against a script text field may report `Supported: False` ("Attribute value is not exposed by this GRAccess attribute") even when the value is set. The write itself persists; the limitation is in `MxValueDetails` reading the COM-side `value` accessor for these field types. Verify post-write content with `object scripts get --llm-json` (which uses the package-export readback) instead. Round-trip evidence is in `analysis/ide-edit-investigation/probe_setvalue/` (commit `e4e5425..`). + Update periodic script settings and lock the interval for deployment inheritance: ```powershell @@ -140,16 +141,16 @@ Delete: graccess object extension delete --galaxy ZB --name TestMachine --type template --extension-type ScriptExtension --primitive OnScan2 --object-extension --confirm --confirm-target TestMachine ``` -These commands manage primitive structure. The wrapper below creates the same `ScriptExtension` primitive and can initialize mutable settings: +These commands manage primitive structure. The wrapper below creates the same `ScriptExtension` primitive and can initialize the body and common settings in the same checkout flow (per AVEVA TN-537: AddExtensionPrimitive → Save → set body/settings via ConfigurableAttributes): ```powershell graccess object checkout --galaxy ZB --name '$MyTemplate' --type template --confirm --confirm-target '$MyTemplate' -graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' +graccess object scripts create --galaxy ZB --name '$MyTemplate' --type template --script OnScan --file .\OnScan.txt --trigger-type Periodic --trigger-period-ms 1000 --confirm --confirm-target '$MyTemplate' graccess object save --galaxy ZB --name '$MyTemplate' --type template --confirm --confirm-target '$MyTemplate' graccess object checkin --galaxy ZB --name '$MyTemplate' --type template --comment 'Add OnScan script' --confirm --confirm-target '$MyTemplate' ``` -After adding a `ScriptExtension` primitive, body text still needs the IDE/package path when the projected body attributes are package-only. +After adding a `ScriptExtension` primitive, set its body with `object scripts set --script ` (defaults to `ExecuteText`) or target a different field with `--field DeclarationsText` / `StartupText` / etc. ## Full-Fidelity Object Script Edits @@ -203,7 +204,7 @@ graccess instance deploy --galaxy ZB --name TestMachine_ScriptTest_001 --type in ```text graccess object scripts list --galaxy ZB --name TestMachine --type template --json graccess object scripts get --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --llm-json -graccess object scripts settings set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --trigger-period-ms 500 --confirm --confirm-target TestMachine --llm-json +graccess object scripts set --galaxy ZB --name TestMachine --type template --script UpdateTestChangingInt --file .\UpdateTestChangingInt.txt --confirm --confirm-target TestMachine --llm-json ``` -The local GRAccess examples sometimes show `Template.Scripts[index].ScriptString`, but this repository's installed `ArchestrA.GRAccess.dll` does not expose a public object script collection. It exposes script extension projections as attributes and package records. The CLI can read body text through exported package parsing and can write mutable settings through `IAttribute.SetValue`; package-only body/expression fields are not persisted by that GRAccess setter. +The local GRAccess examples sometimes show `Template.Scripts[index].ScriptString`, but this repository's installed `ArchestrA.GRAccess.dll` does not expose a public object script collection — it exposes script extension projections as attributes (e.g. `UpdateTestChangingInt.ExecuteText`). The CLI writes those attributes through the AVEVA TN-537 pattern: `IgObject.ConfigurableAttributes[