I was wrong. AVEVA Tech Note 537 ("Creating an Application Object Script
Using GRAccess", April 2008) documents the supported pattern:
ConfigurableAttributes[<script>.<field>].SetValue(MxValue) inside a
CheckOut/Save/CheckIn cycle. graccesscli's existing
FindAttributeForMutation already follows this — writes to MxCategoryPackageOnly_Lockable
script-text fields persist correctly.
The earlier "writeback gap" diagnosis was a phantom caused by a reader-side
issue. `object attribute value get` against a script body returns
"Supported: False / Attribute value is not exposed" because
MxValueDetails uses a case-sensitive `ReadProperty(attr, "Value")` lookup
plus an accessor probe (GetBoolean -> GetInteger -> GetFloat -> GetDouble
-> GetString) that can fall through silently for some MxValue shapes. The
COM-side property is exposed as `value` (lowercase), readable as
`attr.value.GetString()` -- which the live probe at
`analysis/ide-edit-investigation/probe_setvalue/` does and confirms the
post-write content matches the marker exactly.
Live verification on $TestMachine.UpdateTestChangingInt.DeclarationsText
and $DelmiaReceiver.ProcessRecipe.{ExecuteText,DeclarationsText}:
=== 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
The probe also confirmed that two earlier graccesscli `object scripts set`
invocations (which I had wrongly believed failed) had persisted -- the
marker text I wrote previously was still on disk in
ProcessRecipe.{ExecuteText,DeclarationsText} when read directly via
attr.value.GetString(). The probe restored both fields to their original
values.
This commit:
- Updates the misleading [Command(...)] / [CommandOption(...)]
descriptions in GRAccessSurfaceCommands.cs back to honest versions
citing TN-537.
- Restores the --file-using examples for `object scripts set` and
`object scripts create` across script-editing.md, llm-integration.md,
usage.md, and zb-testmachine.md.
- Removes the test that asserted the (wrong) EnsureMutableViaSetValue
guard. Re-aims ScriptCommandDescriptions_… at the corrected wording.
- Removes two leftover EnsureMutableViaSetValue calls in the trigger-period
/ trigger-type write paths (both targeted MxCategoryWriteable_C_Lockable
attributes; would never have fired even if the helper still existed).
- Adds analysis/ide-edit-investigation/REPORT.md (replacing the earlier
wrong report) plus the probe sources under probe_setvalue/.
The MxValueDetails reader gap (case-sensitive ReadProperty + accessor
probe) is a real follow-up: `object attribute value get` should
case-insensitively read `value` and try GetString first when the
underlying MxValue.DataType is MxString. Out of scope here -- that's a
separate, smaller fix.
Test count delta: 67 -> 66 (-2 wrong tests, +1 corrected description test).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
13 KiB
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.exeagainst registered COM/type libraries.- .NET reflection over generated interop assemblies.
- Registry inspection of
HKCR\TypeLib. - Binary string extraction from package/runtime DLLs.
dumpbin.exeon 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:ScriptExtensionPackage_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) -> intGetGObjectInfoFromName(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() -> objectGetIGalaxy() -> object
IPackageManager
This is broader and includes lower-level attribute APIs. Important methods:
GetObject(...)GetObjectInternal(...)GetFullObjectByName(string name) -> objectGetIDFromName(string name) -> intReleaseFsObjectEditsession(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) -> intGetAttribute(int cookie, short propertyId, MxValue value)SetAttribute(int cookie, short propertyId, MxValue value) -> boolValidate()Commit(out string errorString) -> EPACKAGEOPERATIONSTATUSIsReadOnly()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) -> AttributeHandleGetAllCategoryAttributeHandle(string attributeFullName) -> AttributeHandleGetAttribute(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) -> stringValidate() -> 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=TrueDeclarationsText:MxBigString,PackageOnly_Lockable,CfgSethandler=TrueStartupText:MxBigString,PackageOnly_Lockable,CfgSethandler=TrueOnScanText:MxBigString,PackageOnly_Lockable,CfgSethandler=TrueOffScanText:MxBigString,PackageOnly_Lockable,CfgSethandler=TrueShutdownText:MxBigString,PackageOnly_Lockable,CfgSethandler=TrueExpression:MxBigString,PackageOnly_Lockable,CfgSethandler=TrueTriggerType:MxCustomEnum,Writeable_C_Lockable,CfgSethandler=True_TriggerTypeEnum: valuesWhileTrue,WhileFalse,OnTrue,OnFalse,DataChange,PeriodicTriggerPeriod:MxElapsedTime,Writeable_C_Lockable,CfgSethandler=TrueDataChangeDeadband:MxDouble,Writeable_UC_Lockable,CfgSethandler=TrueRunsAsync:MxBoolean,Writeable_C_Lockable,CfgSethandler=TrueExecuteTimeout.Limit:MxInteger,Writeable_C_Lockable,CfgSethandler=TrueScriptExecutionGroup:MxCustomEnum,PackageOnly_Lockable,CfgSethandler=FalseScriptOrder: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:
configurationEditor.Initialize("", inObjectManage, this, readOnly)inObjectManage.SetSubscriberInfo(configurationEditor)inObjectManage.SetSubscriberInfo(this)- Subscribes to
DConfigurationAccessEvents. - Sets CAS data:
ww:SupportedLocalesww:IPackageManagerww:Tagnameww:ContainedNameww:DerivedFromww: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:
- Use existing GRAccess login to obtain
IGalaxy. - Call
IGalaxy.GetGalaxyConfiguration()and cast the returned object toIGalaxyConfigurationorIGalaxyConfigurationV30. - Resolve object id with
GetIDFromObjectName(Namespace.AutomationObject, tagName). - Check out the object via existing GRAccess or
CheckOutObjects. - Get the editable package with
GetObjectInternal(id, eEditorAndPackageCodeModules, true, out package, ...)orGetObject(...). - Cast the package object to
IConfigurationEditorSite2. - Resolve
scriptName.ExecuteTextwithGetAttributeCookie. - Create
MxValueClassand callPutString(newScriptBody). - Call
SetAttribute(cookie, idxAttribPropValue, mxValue). - Call
Validate(). - Call
Commit(out error). - 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
GetObjectInternalsupportsIConfigurationEditorSite2in this context. - Needs confirmation that
SetAttributeinvokes 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:
- Get
IPackageManagerfrom the same object returned byIGalaxy.GetGalaxyConfiguration()or via package-manager support. - Check out object.
- Call
InternalSetObjectAttributesExwith:Namespace.AutomationObject- object tagname
- likely
EPACKAGETYPE.eBeingEditedPackage - attribute names such as
UpdateTestChangingInt.ExecuteText - property ids
[idxAttribPropValue] - MxValues for the new data
saveObjectEvenErrorOccurs=falseinitially
- Check status and status message.
- 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:
- Get editable object package as
IConfigurationEditorSite2. - Instantiate
ConfigurationAccessComponent.ConfigurationAccessServer. - Implement minimal
IBaseEditorSiteand event sink in the CLI. - Call
IConfigurationEditor.Initialize. - Set the IDE wrapper's
ww:*data entries. - Set
Data("ScriptName.ExecuteText", "Value") = body. - 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.dllxxGalaxyPackageServer.dllConfigurationAccessComponent.dllScriptPackage.dll
The next improvement should add a small, isolated package-manager interop layer and a probe/edit helper:
- Add minimal
[ComImport]declarations for only the needed package-manager interfaces, or add a checked-in interop reference generated fromxxGalaxyPackageServer.dll. - Bridge from
IGalaxy.GetGalaxyConfiguration()toIGalaxyConfigurationV30/IPackageManager. - Implement a read-only probe first:
- object id resolution
GetObjectInternal- cast checks for
IConfigurationEditorSite2,IObjectManage,IPackageManager GetAttributeCookiefor script fields/settingsGetAttributeforidxAttribPropValue
- Implement one guarded write path for
ExecuteText. - Verify whether
_Binary,_ErrorMessage,_ErrorLine, and_ErrorColumnupdate after commit. If they do not, switch from Path A/B to the CAS path. - Only then wire it into
object scripts setand 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.